浏览代码

通报联络员管理

abai 2 天之前
父节点
当前提交
f2589b6a90
共有 29 个文件被更改,包括 1664 次插入0 次删除
  1. 16 0
      ruoyi-common/ruoyi-common-mybatis/pom.xml
  2. 61 0
      ruoyi-modules/ruoyi-business/pom.xml
  3. 56 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/annotation/Query.java
  4. 9 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/entity/BaseEntity.java
  5. 7 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/mapper/CoreMapper.java
  6. 8 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/service/BaseService.java
  7. 63 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/service/impl/BaseServiceImpl.java
  8. 88 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/utils/BusinessUtil.java
  9. 172 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/utils/QueryHelpPlus.java
  10. 34 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/web/param/OrderQueryParam.java
  11. 40 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/web/param/QueryParam.java
  12. 60 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/web/vo/Paging.java
  13. 16 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/constants/BusinessConstants.java
  14. 59 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/ContactPersonController.java
  15. 137 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/domain/ContactPerson.java
  16. 42 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/service/ContactPersonService.java
  17. 86 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/service/dto/ContactPersonAdd.java
  18. 99 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/service/dto/ContactPersonDto.java
  19. 54 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/service/dto/ContactPersonQueryCriteria.java
  20. 108 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/service/impl/ContactPersonServiceImpl.java
  21. 14 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/service/mapper/ContactPersonMapper.java
  22. 37 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/domain/BaseDomain.java
  23. 73 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/domain/BaseDomainDto.java
  24. 32 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/domain/PageResult.java
  25. 61 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/dozer/service/IGenerator.java
  26. 52 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/enums/ErrorEnums.java
  27. 108 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/exception/ApiCode.java
  28. 21 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/exception/BadRequestException.java
  29. 51 0
      ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/exception/BusinessException.java

+ 16 - 0
ruoyi-common/ruoyi-common-mybatis/pom.xml

@@ -55,6 +55,22 @@
             <groupId>com.mysql</groupId>
             <artifactId>mysql-connector-j</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+            <version>1.2.5</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>mybatis-spring</artifactId>
+                    <groupId>org.mybatis</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>mybatis</artifactId>
+                    <groupId>org.mybatis</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
 <!--        &lt;!&ndash; Oracle &ndash;&gt;-->
 <!--        <dependency>-->
 <!--            <groupId>com.oracle.database.jdbc</groupId>-->

+ 61 - 0
ruoyi-modules/ruoyi-business/pom.xml

@@ -15,6 +15,14 @@
         ruoyi-business业务模块
     </description>
 
+    <properties>
+        <swagger.version>3.0.0</swagger.version>
+        <swagger-annotations.version>1.5.21</swagger-annotations.version>
+        <swagger-models.version>1.5.24</swagger-models.version>
+        <hutool.version>5.5.7</hutool.version>
+        <poi.version>4.1.2</poi.version>
+    </properties>
+
     <dependencies>
 
         <dependency>
@@ -110,6 +118,59 @@
             <artifactId>ruoyi-api-workflow</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>${swagger.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>io.swagger</groupId>
+                    <artifactId>swagger-annotations</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>io.swagger</groupId>
+                    <artifactId>swagger-models</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>${swagger.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>${swagger-annotations.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-models</artifactId>
+            <version>${swagger-models.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>${hutool.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.dromara.easy-es</groupId>
+            <artifactId>easy-es-core</artifactId>
+            <version>3.0.0</version>
+            <scope>compile</scope>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 56 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/annotation/Query.java

@@ -0,0 +1,56 @@
+package org.dromara.business.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author abai
+ * @date 2025-09-04
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Query {
+
+    // 基本对象的属性名
+    String propName() default "";
+
+    // 查询方式
+    Type type() default Type.EQUAL;
+
+    /**
+     * 多字段模糊搜索,仅支持String类型字段,多个用逗号隔开, 如@Query(blurry = "email,username")
+     */
+    String blurry() default "";
+
+    enum Type {
+        // 相等
+        EQUAL
+        // 不等于
+        , NOT_EQUAL
+        // 大于等于
+        , GREATER_THAN
+        // 大于
+        , GREATER_THAN_NQ
+        // 小于等于
+        , LESS_THAN
+        // 小于
+        , LESS_THAN_NQ
+        // 中模糊查询
+        , INNER_LIKE
+        // 左模糊查询
+        , LEFT_LIKE
+        // 右模糊查询
+        , RIGHT_LIKE
+        // 包含
+        , IN
+        // between
+        , BETWEEN
+        // 不为空
+        , NOT_NULL
+        // 查询时间
+        , UNIX_TIMESTAMP
+    }
+
+}

+ 9 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/entity/BaseEntity.java

@@ -0,0 +1,9 @@
+package org.dromara.business.common.entity;
+
+import io.swagger.annotations.ApiModel;
+
+import java.io.Serializable;
+
+@ApiModel("BaseEntity")
+public abstract class BaseEntity implements Serializable {
+}

+ 7 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/mapper/CoreMapper.java

@@ -0,0 +1,7 @@
+package org.dromara.business.common.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+public interface CoreMapper<T> extends BaseMapper<T> {
+
+}

+ 8 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/service/BaseService.java

@@ -0,0 +1,8 @@
+package org.dromara.business.common.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+
+
+public interface BaseService<T> extends IService<T> {
+
+}

+ 63 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/service/impl/BaseServiceImpl.java

@@ -0,0 +1,63 @@
+package org.dromara.business.common.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.github.pagehelper.PageHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.business.common.service.BaseService;
+import org.dromara.business.common.web.param.OrderQueryParam;
+import org.dromara.business.common.web.param.QueryParam;
+import org.springframework.data.domain.Pageable;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Slf4j
+@SuppressWarnings("unchecked")
+public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements BaseService<T> {
+
+    protected Page setPageParam(QueryParam queryParam) {
+        return setPageParam(queryParam, null);
+    }
+
+    protected Page setPageParam(QueryParam queryParam, OrderItem defaultOrder) {
+        Page page = new Page();
+        // 设置当前页码
+        page.setCurrent(queryParam.getPage());
+        // 设置页大小
+        page.setSize(queryParam.getLimit());
+        /**
+         * 如果是queryParam是OrderQueryParam,并且不为空,则使用前端排序
+         * 否则使用默认排序
+         */
+        if (queryParam instanceof OrderQueryParam) {
+            OrderQueryParam orderQueryParam = (OrderQueryParam) queryParam;
+            List<OrderItem> orderItems = orderQueryParam.getOrders();
+            if (CollectionUtil.isEmpty(orderItems)) {
+                page.setOrders(Arrays.asList(defaultOrder));
+            } else {
+                page.setOrders(orderItems);
+            }
+        } else {
+            page.setOrders(Arrays.asList(defaultOrder));
+        }
+
+        return page;
+    }
+
+    protected void getPage(Pageable pageable) {
+        String order = null;
+        if (pageable.getSort() != null) {
+            order = pageable.getSort().toString();
+            order = order.replace(":", "");
+            if ("UNSORTED".equals(order)) {
+                order = "id desc";
+            }
+        }
+        PageHelper.startPage(pageable.getPageNumber() + 1, pageable.getPageSize(), order);
+    }
+
+}

+ 88 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/utils/BusinessUtil.java

@@ -0,0 +1,88 @@
+package org.dromara.business.common.utils;
+
+import org.apache.commons.lang3.StringUtils;
+import org.dromara.business.constants.BusinessConstants;
+import org.dromara.business.enums.ErrorEnums;
+import org.dromara.business.exception.BusinessException;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.reflect.Field;
+import java.util.Objects;
+
+/**
+ * 公共Util类
+ *
+ * @author abai
+ * @date 2025/09/05
+ */
+public class BusinessUtil {
+
+    /**
+     * 反射判断版本号是否为空
+     *
+     * @param entity 实体类
+     */
+    public static void checkVersion(Object entity) {
+        try {
+            checkPropertyIsNull(entity, "version");
+        } catch (Exception e) {
+            throw new BusinessException(ErrorEnums.VERSION_NULL_ERR.getCode(), ErrorEnums.VERSION_NULL_ERR.getInfo());
+        }
+    }
+
+    /**
+     * 反射判断指定属性是否为空
+     *
+     * @param entity 实体类
+     * @param fields 需要判断的属性名称
+     */
+    public static void checkPropertyIsNull(Object entity, Object... fields) {
+        try {
+            // 循环需要校验的属性
+            for (Object obj : fields) {
+                String fieldName = obj.toString();
+                // 获取属性
+                Field field = ReflectionUtils.findField(entity.getClass(), fieldName);
+                if (Objects.isNull(field)) {
+                    int code = ErrorEnums.PROPERTY_NOT_EXIST.getCode();
+                    throw new BusinessException(code, ErrorEnums.getInfo(code, fieldName));
+                }
+                ReflectionUtils.makeAccessible(field);
+                Object value = field.get(entity);
+                if (Objects.isNull(value) || StringUtils.isBlank(value.toString())) {
+                    int code = ErrorEnums.PROPERTY_EMPTY.getCode();
+                    throw new BusinessException(code, ErrorEnums.getInfo(code, fieldName));
+                }
+            }
+        } catch (Exception e) {
+            throw new BusinessException(e);
+        }
+    }
+
+    /**
+     * 获取新的乐观锁版本号
+     *
+     * @param version 版本号
+     * @return long 新版本号
+     */
+    public static synchronized long getNewVersion(Long version) {
+        if (version == null) {
+            return BusinessConstants.DEFAULT_VERSION;
+        }
+        return version + 1;
+    }
+
+    /**
+     * 检查数据是否更新成功
+     *
+     * @param flag 更新返回状态
+     * @return boolean true
+     */
+    public static boolean checkUpdateState(boolean flag) {
+        if (!flag) {
+            throw new BusinessException(ErrorEnums.UPDATE_FAIL.getCode(), ErrorEnums.UPDATE_FAIL.getInfo());
+        }
+        return true;
+    }
+
+}

+ 172 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/utils/QueryHelpPlus.java

@@ -0,0 +1,172 @@
+package org.dromara.business.common.utils;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.business.annotation.Query;
+
+import java.lang.reflect.Field;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+@Slf4j
+@SuppressWarnings({"unchecked", "all"})
+public class QueryHelpPlus {
+
+    public static <R, Q> QueryWrapper getPredicate(R obj, Q query) {
+        QueryWrapper<R> queryWrapper = new QueryWrapper<R>();
+        if (query == null) {
+            return queryWrapper;
+        }
+        try {
+            List<Field> fields = getAllFields(query.getClass(), new ArrayList<>());
+            for (Field field : fields) {
+                boolean accessible = field.isAccessible();
+                field.setAccessible(true);
+                Query q = field.getAnnotation(Query.class);
+                if (q != null) {
+                    String propName = q.propName();
+                    String blurry = q.blurry();
+                    String attributeName = isBlank(propName) ? field.getName() : propName;
+                    attributeName = humpToUnderline(attributeName);
+                    Class<?> fieldType = field.getType();
+                    Object val = field.get(query);
+                    if (ObjectUtil.isNull(val) || "".equals(val)) {
+                        continue;
+                    }
+                    // 模糊多字段
+                    if (ObjectUtil.isNotEmpty(blurry)) {
+                        String[] blurrys = blurry.split(",");
+                        //queryWrapper.or();
+                        queryWrapper.and(wrapper -> {
+                            for (int i = 0; i < blurrys.length; i++) {
+                                String column = humpToUnderline(blurrys[i]);
+                                //if(i!=0){
+                                wrapper.or();
+                                //}
+                                wrapper.like(column, val.toString());
+                            }
+                        });
+                        continue;
+                    }
+                    String finalAttributeName = attributeName;
+                    switch (q.type()) {
+                        case EQUAL:
+                            //queryWrapper.and(wrapper -> wrapper.eq(finalAttributeName, val));
+                            queryWrapper.eq(attributeName, val);
+                            break;
+                        case GREATER_THAN:
+                            queryWrapper.ge(finalAttributeName, val);
+                            break;
+                        case GREATER_THAN_NQ:
+                            queryWrapper.gt(finalAttributeName, val);
+                            break;
+                        case LESS_THAN:
+                            queryWrapper.le(finalAttributeName, val);
+                            break;
+                        case LESS_THAN_NQ:
+                            queryWrapper.lt(finalAttributeName, val);
+                            break;
+                        case INNER_LIKE:
+                            queryWrapper.like(finalAttributeName, val);
+                            break;
+                        case LEFT_LIKE:
+                            queryWrapper.likeLeft(finalAttributeName, val);
+                            break;
+                        case RIGHT_LIKE:
+                            queryWrapper.likeRight(finalAttributeName, val);
+                            break;
+                        case IN:
+                            if (CollUtil.isNotEmpty((Collection<Long>) val)) {
+                                queryWrapper.in(finalAttributeName, (Collection<Long>) val);
+                            }
+                            break;
+                        case NOT_EQUAL:
+                            queryWrapper.ne(finalAttributeName, val);
+                            break;
+                        case NOT_NULL:
+                            queryWrapper.isNotNull(finalAttributeName);
+                            break;
+                        case BETWEEN:
+                            List<Object> between = new ArrayList<>((List<Object>) val);
+                            queryWrapper.between(finalAttributeName, between.get(0), between.get(1));
+                            break;
+                        case UNIX_TIMESTAMP:
+                            List<Object> UNIX_TIMESTAMP = new ArrayList<>((List<Object>) val);
+                            if (!UNIX_TIMESTAMP.isEmpty()) {
+                                SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                                Date time1 = fm.parse(UNIX_TIMESTAMP.get(0).toString());
+                                Date time2 = fm.parse(UNIX_TIMESTAMP.get(1).toString());
+                                queryWrapper.between(finalAttributeName, time1, time2);
+                            }
+                            break;
+                        default:
+                            break;
+                    }
+                }
+                field.setAccessible(accessible);
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+
+        return queryWrapper;
+    }
+
+
+    private static boolean isBlank(final CharSequence cs) {
+        int strLen;
+        if (cs == null || (strLen = cs.length()) == 0) {
+            return true;
+        }
+        for (int i = 0; i < strLen; i++) {
+            if (!Character.isWhitespace(cs.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static List<Field> getAllFields(Class clazz, List<Field> fields) {
+        if (clazz != null) {
+            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
+            getAllFields(clazz.getSuperclass(), fields);
+        }
+        return fields;
+    }
+
+    /***
+     * 驼峰命名转为下划线命名
+     *
+     * @param para
+     *        驼峰命名的字符串
+     */
+
+    public static String humpToUnderline(String para) {
+        StringBuilder sb = new StringBuilder(para);
+        int temp = 0;//定位
+        if (!para.contains("_")) {
+            for (int i = 0; i < para.length(); i++) {
+                if (Character.isUpperCase(para.charAt(i))) {
+                    sb.insert(i + temp, "_");
+                    temp += 1;
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+//    public static void main(String[] args) {
+//        QueryWrapper<Paging> query = new QueryWrapper<Paging>();
+//        //query.or();
+//        query.or(wrapper -> wrapper.eq("store_id", 1).or().eq("store_id", 2));
+//        //query.like("a",1);
+//        //query.or();
+//        //query.like("b",2);
+//        //query.and(wrapper->wrapper.eq("c",1));
+//        query.eq("1", 1);
+//
+//        System.out.println(query.getSqlSegment());
+//    }
+}

+ 34 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/web/param/OrderQueryParam.java

@@ -0,0 +1,34 @@
+package org.dromara.business.common.web.param;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel("可排序查询参数对象")
+public abstract class OrderQueryParam extends QueryParam {
+    private static final long serialVersionUID = 57714391204790143L;
+
+    @ApiModelProperty(value = "排序")
+    private List<OrderItem> orders;
+
+    public void defaultOrder(OrderItem orderItem) {
+        this.defaultOrders(Arrays.asList(orderItem));
+    }
+
+    public void defaultOrders(List<OrderItem> orderItems) {
+        if (CollectionUtil.isEmpty(orderItems)) {
+            return;
+        }
+        this.orders = orderItems;
+    }
+
+}

+ 40 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/web/param/QueryParam.java

@@ -0,0 +1,40 @@
+package org.dromara.business.common.web.param;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+
+@Data
+@ApiModel("查询参数对象")
+public abstract class QueryParam implements Serializable {
+
+    private static final long serialVersionUID = -3263921252635611410L;
+
+    @ApiModelProperty(value = "页码,默认为1")
+    private Integer page = 1;
+    @ApiModelProperty(value = "页大小,默认为10")
+    private Integer limit = 10;
+    @ApiModelProperty(value = "搜索字符串")
+    private String keyword;
+
+    @ApiModelProperty(value = "当前第几页")
+    public void setCurrent(Integer current) {
+        if (current == null || current <= 0) {
+            this.page = 1;
+        } else {
+            this.page = current;
+        }
+    }
+
+    public void setSize(Integer size) {
+        if (size == null || size <= 0) {
+            this.limit = 10;
+        } else {
+            this.limit = size;
+        }
+    }
+
+}

+ 60 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/common/web/vo/Paging.java

@@ -0,0 +1,60 @@
+package org.dromara.business.common.web.vo;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+
+@ApiModel("分页")
+@SuppressWarnings("unchecked")
+public class Paging<T> implements Serializable {
+    private static final long serialVersionUID = -1683800405530086022L;
+
+    @ApiModelProperty("总行数")
+    @JSONField(name = "total")
+    @JsonProperty("total")
+    private long total = 0;
+
+    @ApiModelProperty("数据列表")
+    @JSONField(name = "records")
+    @JsonProperty("records")
+    private List<T> records = Collections.emptyList();
+
+    public Paging() {
+    }
+
+    public Paging(IPage page) {
+        this.total = page.getTotal();
+        this.records = page.getRecords();
+    }
+
+    public long getTotal() {
+        return total;
+    }
+
+    public void setTotal(long total) {
+        this.total = total;
+    }
+
+    public List<T> getRecords() {
+        return records;
+    }
+
+    public void setRecords(List<T> records) {
+        this.records = records;
+    }
+
+    @Override
+    public String toString() {
+        return "Paging{" +
+                "total=" + total +
+                ", records=" + records +
+                '}';
+    }
+}

+ 16 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/constants/BusinessConstants.java

@@ -0,0 +1,16 @@
+package org.dromara.business.constants;
+
+/**
+ * 公共常量类
+ *
+ * @author abai
+ * @date 2025/09/05
+ */
+public class BusinessConstants {
+
+    /**
+     * 默认版本
+     */
+    public static final long DEFAULT_VERSION = 1;
+
+}

+ 59 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/ContactPersonController.java

@@ -0,0 +1,59 @@
+package org.dromara.business.contact_person.rest;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.dromara.business.contact_person.rest.domain.ContactPerson;
+import org.dromara.business.contact_person.rest.service.ContactPersonService;
+import org.dromara.business.contact_person.rest.service.dto.ContactPersonAdd;
+import org.dromara.business.contact_person.rest.service.dto.ContactPersonDto;
+import org.dromara.business.contact_person.rest.service.dto.ContactPersonQueryCriteria;
+import org.dromara.business.domain.PageResult;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+
+/**
+ * @author abai
+ * @date 2025-09-05
+ */
+@AllArgsConstructor
+@Api(tags = "通报联络员管理")
+@RestController
+@RequestMapping("/api/contactPerson")
+public class ContactPersonController {
+
+    private final ContactPersonService contactPersonService;
+
+    @GetMapping
+    @ApiOperation("分页查询通报联络员数据")
+    public ResponseEntity<PageResult<ContactPersonDto>> getContactPersons(ContactPersonQueryCriteria criteria, Pageable pageable) {
+        return new ResponseEntity<>(contactPersonService.queryAll(criteria, pageable), HttpStatus.OK);
+    }
+
+    @PostMapping
+    @ApiOperation("新增通报联络员数据")
+    public ResponseEntity<Object> create(@Validated @RequestBody ContactPersonAdd resources) {
+        ContactPerson contactPerson = ContactPerson.builder().build();
+        contactPerson.copy(resources);
+        return new ResponseEntity<>(contactPersonService.saveOrUpdateContactPerson(contactPerson), HttpStatus.CREATED);
+    }
+
+    @PutMapping
+    @ApiOperation("修改通报联络员数据")
+    public ResponseEntity<Object> update(@Validated @RequestBody ContactPerson resources) {
+        contactPersonService.saveOrUpdateContactPerson(resources);
+        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+    }
+
+    @ApiOperation("删除通报联络员数据")
+    @DeleteMapping
+    public ResponseEntity<Object> deleteAll(@RequestBody Long[] ids) {
+        contactPersonService.removeByIds(Arrays.asList(ids));
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+}

+ 137 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/domain/ContactPerson.java

@@ -0,0 +1,137 @@
+package org.dromara.business.contact_person.rest.domain;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.bean.copier.CopyOptions;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.dromara.business.contact_person.rest.service.dto.ContactPersonAdd;
+import org.dromara.business.domain.BaseDomain;
+
+/**
+ * @author abai
+ * @date 2025-09-05
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@TableName("contact_person")
+public class ContactPerson extends BaseDomain {
+
+    /**
+     * 主键id
+     */
+    @ApiModelProperty(value = "主键ID", name = "id")
+    @TableId
+    private Integer id;
+
+    /**
+     * 行业类型,1:行业内 2:行业外
+     */
+    @ApiModelProperty(value = "行业类型,1:行业内 2:行业外", name = "type")
+    private Integer type;
+
+    /**
+     * 单位ID;该单位ID为本业务系统中的单位信息
+     */
+    @ApiModelProperty(value = "单位ID", name = "workUnitId")
+    private String workUnitId;
+
+    /**
+     * 联络人姓名
+     */
+    @ApiModelProperty(value = "联络人姓名", name = "userName")
+    private String userName;
+
+    /**
+     * 部门名称
+     */
+    @ApiModelProperty(value = "部门名称", name = "deptName")
+    private String deptName;
+
+    /**
+     * 岗位名称
+     */
+    @ApiModelProperty(value = "岗位名称", name = "postName")
+    private String postName;
+
+    /**
+     * 微信
+     */
+    @ApiModelProperty(value = "微信", name = "weChat")
+    private String weChat;
+
+    /**
+     * 移动电话
+     */
+    @ApiModelProperty(value = "移动电话", name = "mobilePhone")
+    private String mobilePhone;
+
+    /**
+     * 固定电话
+     */
+    @ApiModelProperty(value = "固定电话", name = "telephone")
+    private String telephone;
+
+    /**
+     * 传真号
+     */
+    @ApiModelProperty(value = "传真号", name = "faxNumber")
+    private String faxNumber;
+
+    /**
+     * 电子邮箱
+     */
+    @ApiModelProperty(value = "电子邮箱", name = "email")
+    private String email;
+
+    /**
+     * 短信接收人
+     */
+    @ApiModelProperty(value = "短信接收人", name = "smsRecipient")
+    private Integer smsRecipient;
+
+    /**
+     * 状态,1:可用 0:禁用
+     */
+    @ApiModelProperty(value = "状态,1:可用 0:禁用", name = "status")
+    private Integer status;
+
+    /**
+     * 备注
+     */
+    @ApiModelProperty(value = "备注", name = "memo")
+    private String memo;
+
+    /**
+     * 创建用户ID
+     */
+    @ApiModelProperty(value = "创建用户ID", name = "createUserId")
+    private Long createUserId;
+
+    /**
+     * 更新用户ID
+     */
+    @ApiModelProperty(value = "更新用户ID", name = "updateUserId")
+    private Long updateUserId;
+
+    /**
+     * 乐观锁版本号
+     */
+    @ApiModelProperty(value = "乐观锁版本号", name = "version")
+    private Long version;
+
+
+    public void copy(ContactPerson source) {
+        BeanUtil.copyProperties(source, this, CopyOptions.create().setIgnoreNullValue(true));
+    }
+
+    public void copy(ContactPersonAdd source) {
+        BeanUtil.copyProperties(source, this, CopyOptions.create().setIgnoreNullValue(true));
+    }
+}

+ 42 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/service/ContactPersonService.java

@@ -0,0 +1,42 @@
+package org.dromara.business.contact_person.rest.service;
+
+import org.dromara.business.common.service.BaseService;
+import org.dromara.business.contact_person.rest.domain.ContactPerson;
+import org.dromara.business.contact_person.rest.service.dto.ContactPersonDto;
+import org.dromara.business.contact_person.rest.service.dto.ContactPersonQueryCriteria;
+import org.dromara.business.domain.PageResult;
+import org.springframework.data.domain.Pageable;
+
+import java.util.List;
+
+/**
+ * @author abai
+ * @date 2025-09-05
+ */
+public interface ContactPersonService extends BaseService<ContactPerson> {
+
+    /**
+     * 查询数据分页
+     *
+     * @param criteria 条件
+     * @param pageable 分页参数
+     * @return Map<String, Object>
+     */
+    PageResult<ContactPersonDto> queryAll(ContactPersonQueryCriteria criteria, Pageable pageable);
+
+    /**
+     * 查询所有数据不分页
+     *
+     * @param criteria 条件参数
+     * @return List<ContactPersonDto>
+     */
+    List<ContactPersonDto> queryAll(ContactPersonQueryCriteria criteria);
+
+    /**
+     * 添加或修改数据
+     *
+     * @param resources 数据对象
+     * @return ContactPerson
+     */
+    ContactPerson saveOrUpdateContactPerson(ContactPerson resources);
+}

+ 86 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/service/dto/ContactPersonAdd.java

@@ -0,0 +1,86 @@
+package org.dromara.business.contact_person.rest.service.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * @author abai
+ * @date 2025-09-05
+ */
+@Data
+public class ContactPersonAdd {
+
+    /**
+     * 行业类型,1:行业内 2:行业外
+     */
+    @NotNull(message = "行业类型不能为空")
+    @ApiModelProperty(value = "行业类型,1:行业内 2:行业外", name = "type")
+    private Integer type;
+
+    /**
+     * 单位ID;该单位ID为本业务系统中的单位信息
+     */
+    @NotNull(message = "请选择单位名称")
+    @ApiModelProperty(value = "单位ID", name = "workUnitId")
+    private Integer contactDeptId;
+
+    /**
+     * 联络人姓名
+     */
+    @NotNull(message = "联络人姓名不能为空")
+    @ApiModelProperty(value = "联络人姓名", name = "userName")
+    private String userName;
+
+    /**
+     * 部门名称
+     */
+    @NotNull(message = "部门名称不能为空")
+    @ApiModelProperty(value = "部门名称", name = "deptName")
+    private String deptName;
+
+    /**
+     * 岗位名称
+     */
+    @NotNull(message = "岗位名称不能为空")
+    @ApiModelProperty(value = "岗位名称", name = "postName")
+    private String postName;
+
+    /**
+     * 微信
+     */
+    @ApiModelProperty(value = "微信", name = "weChat")
+    private String weChat;
+
+    /**
+     * 移动电话
+     */
+    @NotNull(message = "联系电话不能为空")
+    @ApiModelProperty(value = "移动电话", name = "mobilePhone")
+    private String mobilePhone;
+
+    /**
+     * 固定电话
+     */
+    @ApiModelProperty(value = "固定电话", name = "telephone")
+    private String telephone;
+
+    /**
+     * 传真号
+     */
+    @ApiModelProperty(value = "传真号", name = "faxNumber")
+    private String faxNumber;
+
+    /**
+     * 电子邮箱
+     */
+    @ApiModelProperty(value = "电子邮箱", name = "email")
+    private String email;
+
+    /**
+     * 短信接收人
+     */
+    @NotNull(message = "请选择短信接收人")
+    @ApiModelProperty(value = "短信接收人", name = "smsRecipient")
+    private Integer smsRecipient;
+}

+ 99 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/service/dto/ContactPersonDto.java

@@ -0,0 +1,99 @@
+package org.dromara.business.contact_person.rest.service.dto;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.dromara.business.domain.BaseDomainDto;
+
+/**
+ * @author abai
+ * @date 2025-09-05
+ */
+@Data
+public class ContactPersonDto extends BaseDomainDto {
+
+    /**
+     * 主键id
+     */
+    @ApiModelProperty(value = "主键ID", name = "id")
+    @TableId
+    private Integer id;
+
+    /**
+     * 行业类型,1:行业内 2:行业外
+     */
+    @ApiModelProperty(value = "行业类型,1:行业内 2:行业外", name = "type")
+    private Integer type;
+
+    /**
+     * 单位ID;该单位ID为本业务系统中的单位信息
+     */
+    @ApiModelProperty(value = "单位ID", name = "workUnitId")
+    private String workUnitId;
+
+    /**
+     * 联络人姓名
+     */
+    @ApiModelProperty(value = "联络人姓名", name = "userName")
+    private String userName;
+
+    /**
+     * 部门名称
+     */
+    @ApiModelProperty(value = "部门名称", name = "deptName")
+    private String deptName;
+
+    /**
+     * 岗位名称
+     */
+    @ApiModelProperty(value = "岗位名称", name = "postName")
+    private String postName;
+
+    /**
+     * 微信
+     */
+    @ApiModelProperty(value = "微信", name = "weChat")
+    private String weChat;
+
+    /**
+     * 移动电话
+     */
+    @ApiModelProperty(value = "移动电话", name = "mobilePhone")
+    private String mobilePhone;
+
+    /**
+     * 固定电话
+     */
+    @ApiModelProperty(value = "固定电话", name = "telephone")
+    private String telephone;
+
+    /**
+     * 传真号
+     */
+    @ApiModelProperty(value = "传真号", name = "faxNumber")
+    private String faxNumber;
+
+    /**
+     * 电子邮箱
+     */
+    @ApiModelProperty(value = "电子邮箱", name = "email")
+    private String email;
+
+    /**
+     * 短信接收人
+     */
+    @ApiModelProperty(value = "短信接收人", name = "smsRecipient")
+    private Integer smsRecipient;
+
+    /**
+     * 状态,1:可用 0:禁用
+     */
+    @ApiModelProperty(value = "状态,1:可用 0:禁用", name = "status")
+    private Integer status;
+
+    /**
+     * 备注
+     */
+    @ApiModelProperty(value = "备注", name = "memo")
+    private String memo;
+}

+ 54 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/service/dto/ContactPersonQueryCriteria.java

@@ -0,0 +1,54 @@
+package org.dromara.business.contact_person.rest.service.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.dromara.business.annotation.Query;
+
+/**
+ * @author abai
+ * @date 2025-09-05
+ */
+@Data
+public class ContactPersonQueryCriteria {
+
+    /**
+     * 精确
+     */
+    @ApiModelProperty(value = "行业类型,1:行业内 2:行业外", name = "type")
+    @Query(type = Query.Type.EQUAL)
+    private Integer type;
+
+    /**
+     * 模糊
+     */
+    @ApiModelProperty(value = "联络人姓名", name = "userName")
+    @Query(type = Query.Type.INNER_LIKE)
+    private String userName;
+
+    /**
+     * 模糊
+     */
+    @Query(type = Query.Type.INNER_LIKE)
+    @ApiModelProperty(value = "部门名称", name = "deptName")
+    private String deptName;
+
+    /**
+     * 模糊
+     */
+    @Query(type = Query.Type.INNER_LIKE)
+    @ApiModelProperty(value = "移动电话", name = "mobilePhone")
+    private String mobilePhone;
+
+    /**
+     * 精确
+     */
+    @Query(type = Query.Type.EQUAL)
+    @ApiModelProperty(value = "短信接收人", name = "smsRecipient")
+    private Integer smsRecipient;
+
+    /**
+     * 精确
+     */
+    @Query(type = Query.Type.EQUAL)
+    private Integer status;
+}

+ 108 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/service/impl/ContactPersonServiceImpl.java

@@ -0,0 +1,108 @@
+package org.dromara.business.contact_person.rest.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.github.pagehelper.PageInfo;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.dromara.business.common.service.impl.BaseServiceImpl;
+import org.dromara.business.common.utils.BusinessUtil;
+import org.dromara.business.common.utils.QueryHelpPlus;
+import org.dromara.business.constants.BusinessConstants;
+import org.dromara.business.contact_person.rest.domain.ContactPerson;
+import org.dromara.business.contact_person.rest.service.ContactPersonService;
+import org.dromara.business.contact_person.rest.service.dto.ContactPersonDto;
+import org.dromara.business.contact_person.rest.service.dto.ContactPersonQueryCriteria;
+import org.dromara.business.contact_person.rest.service.mapper.ContactPersonMapper;
+import org.dromara.business.domain.PageResult;
+import org.dromara.business.dozer.service.IGenerator;
+import org.dromara.business.enums.ErrorEnums;
+import org.dromara.business.exception.BusinessException;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * @author abai
+ * @date 2025-09-05
+ */
+@Slf4j
+@Service
+public class ContactPersonServiceImpl extends BaseServiceImpl<ContactPersonMapper, ContactPerson> implements ContactPersonService {
+
+    private final IGenerator generator;
+
+    public ContactPersonServiceImpl(IGenerator generator) {
+        this.generator = generator;
+    }
+
+    @Override
+    public PageResult<ContactPersonDto> queryAll(ContactPersonQueryCriteria criteria, Pageable pageable) {
+        getPage(pageable);
+        PageInfo<ContactPersonDto> page = new PageInfo<>(queryAll(criteria));
+        return generator.convertPageInfo(page, ContactPersonDto.class);
+    }
+
+    @Override
+    public List<ContactPersonDto> queryAll(ContactPersonQueryCriteria criteria) {
+        QueryWrapper wrapper = QueryHelpPlus.getPredicate(ContactPerson.class, criteria);
+        wrapper.orderByDesc("create_time");
+        return baseMapper.selectList(wrapper);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public ContactPerson saveOrUpdateContactPerson(ContactPerson resources) {
+        // 检查唯一性
+        unique(resources);
+        // 获取当前登录用户ID
+        // 新增
+        if (resources.getId() == null) {
+            // 设置初始乐观锁版本号
+            resources.setVersion(BusinessConstants.DEFAULT_VERSION);
+            // 保存数据
+            save(resources);
+        } else {
+            // 检查版本号是否为空
+            BusinessUtil.checkVersion(resources);
+            // 更新条件
+            LambdaQueryWrapper<ContactPerson> wrapper = new LambdaQueryWrapper<ContactPerson>()
+                    .eq(ContactPerson::getId, resources.getId())
+                    .eq(ContactPerson::getVersion, resources.getVersion());
+            // 设置新的乐观锁版本号
+            resources.setVersion(BusinessUtil.getNewVersion(resources.getVersion()));
+            // 更新数据
+            BusinessUtil.checkUpdateState(update(resources, wrapper));
+        }
+        return resources;
+    }
+
+    /**
+     * 判断数据是否唯一
+     */
+    private void unique(ContactPerson resources) {
+        // 手机号
+        String phone = resources.getMobilePhone();
+        ContactPersonDto person = this.queryByPhone(phone);
+        if (person != null) {
+            if (resources.getId() == null || !resources.getId().equals(person.getId())) {
+                int code = ErrorEnums.MOBILE_USE_ERR.getCode();
+                throw new BusinessException(code, ErrorEnums.getInfo(code, phone, person.getUserName()));
+            }
+        }
+    }
+
+    public ContactPersonDto queryByPhone(String mobilePhone) {
+        List<ContactPerson> personList = baseMapper.selectList(new LambdaQueryWrapper<ContactPerson>()
+                .eq(ContactPerson::getMobilePhone, mobilePhone));
+        if (CollectionUtils.isNotEmpty(personList)) {
+            if (personList.size() > 1) {
+                throw new BusinessException(String.format("手机号%s有多条信息", mobilePhone));
+            }
+            return generator.convert(personList.get(0), ContactPersonDto.class);
+        }
+        return null;
+    }
+}

+ 14 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/contact_person/rest/service/mapper/ContactPersonMapper.java

@@ -0,0 +1,14 @@
+package org.dromara.business.contact_person.rest.service.mapper;
+
+import org.dromara.business.common.mapper.CoreMapper;
+import org.dromara.business.contact_person.rest.domain.ContactPerson;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author abai
+ * @date 2025-09-05
+ */
+@Repository
+public interface ContactPersonMapper extends CoreMapper<ContactPerson> {
+
+}

+ 37 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/domain/BaseDomain.java

@@ -0,0 +1,37 @@
+package org.dromara.business.domain;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * @author abai
+ * @date 2025-09-05
+ **/
+@Getter
+@Setter
+public class BaseDomain implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @TableField(fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+
+    @TableLogic
+    @JsonIgnore
+    @TableField(fill = FieldFill.INSERT)
+    private Integer isDel;
+}

+ 73 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/domain/BaseDomainDto.java

@@ -0,0 +1,73 @@
+
+package org.dromara.business.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 公共Dto模型
+ *
+ * @author abai
+ * @date 2025-09-05
+ */
+@Getter
+@Setter
+public class BaseDomainDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 创建用户ID
+     */
+    @ApiModelProperty(value = "创建用户ID", name = "createUserId")
+    private Long createUserId;
+
+    /**
+     * 创建用户姓名
+     */
+    @ApiModelProperty(value = "创建用户姓名", name = "createUserName")
+    private String createUserName;
+
+    /**
+     * 更新用户ID
+     */
+    @ApiModelProperty(value = "更新用户ID", name = "updateUserId")
+    private Long updateUserId;
+
+    /**
+     * 更新用户姓名
+     */
+    @ApiModelProperty(value = "更新用户姓名", name = "updateUserName")
+    private String updateUserName;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "创建时间", name = "createTime")
+    private Date createTime;
+
+    /**
+     * 更新时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty(value = "更新时间", name = "updateTime")
+    private Date updateTime;
+
+    /**
+     * 乐观锁版本号
+     */
+    @ApiModelProperty(value = "乐观锁版本号", name = "version")
+    private Long version;
+
+    /**
+     * 逻辑删除:0-未删除,1-已删除
+     */
+    @ApiModelProperty(value = "逻辑删除:0-未删除,1-已删除", name = "isDel")
+    private Integer isDel;
+}

+ 32 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/domain/PageResult.java

@@ -0,0 +1,32 @@
+package org.dromara.business.domain;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@Accessors(chain = true)
+@Builder
+public class PageResult<T> implements Serializable {
+
+    /**
+     * 总数量
+     */
+    private long totalElements;
+
+    /**
+     * 内容
+     */
+    private List<T> content;
+
+    public PageResult(long totalElements, List<T> content) {
+        this.totalElements = totalElements;
+        this.content = content;
+    }
+
+    public PageResult() {
+    }
+}

+ 61 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/dozer/service/IGenerator.java

@@ -0,0 +1,61 @@
+package org.dromara.business.dozer.service;
+
+import com.github.pagehelper.PageInfo;
+import org.dromara.business.domain.PageResult;
+
+import java.util.List;
+import java.util.Set;
+
+public interface IGenerator {
+
+    /**
+     * 转换
+     *
+     * @param s   数据对象
+     * @param clz 复制目标类型
+     * @return {@link T}
+     * @Description: 单个对象的深度复制及类型转换,vo/domain , po
+     */
+    <T, S> T convert(S s, Class<T> clz);
+
+    /**
+     * @Description: 深度复制结果集(ResultSet为自定义的分页结果集)
+     * @param s 数据对象
+     * @param clz 复制目标类型
+     * @return
+     */
+    //<T, S> Result<T> convert(Result<S> s, Class<T> clz);
+
+    /**
+     * 转换
+     *
+     * @param s   数据对象
+     * @param clz 复制目标类型
+     * @return {@link List<T>}
+     * @Description: list深度复制
+     */
+    <T, S> List<T> convert(List<S> s, Class<T> clz);
+
+    /**
+     * @param s   数据对象
+     * @param clz 复制目标类型
+     * @return
+     * @Description: set深度复制
+     */
+    <T, S> Set<T> convert(Set<S> s, Class<T> clz);
+
+    /**
+     * @param s   数据对象
+     * @param clz 复制目标类型
+     * @return
+     * @Description: 数组深度复制
+     */
+    <T, S> T[] convert(S[] s, Class<T> clz);
+
+    /**
+     * 分页信息转换
+     *
+     * @return {@link PageResult <T>}
+     */
+    <T, S> PageResult<T> convertPageInfo(PageInfo<S> s, Class<T> clz);
+}

+ 52 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/enums/ErrorEnums.java

@@ -0,0 +1,52 @@
+package org.dromara.business.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.stream.Stream;
+
+/**
+ * 错误信息枚举类
+ *
+ * @author abai
+ * @date 2025/09/05
+ */
+@Getter
+@AllArgsConstructor
+public enum ErrorEnums {
+
+    /**
+     * 业务代码通用错误信息
+     */
+    UPDATE_FAIL(1100, "当前数据已变更,请刷新页面重新提交"),
+    VERSION_NULL_ERR(1101, "版本号不能为空"),
+    PROPERTY_NOT_EXIST(1102, "%s property does not exist"),
+    PROPERTY_EMPTY(1103, "%s property cannot be empty"),
+    MOBILE_USE_ERR(1200, "手机号%s已被%s使用"),
+    MOBILE_NULL_ERR(1201, "手机号不能为空");
+
+    private int code;
+
+    private String info;
+
+    public static ErrorEnums toType(Integer value) {
+        if (value == null) {
+            return null;
+        }
+        return Stream.of(ErrorEnums.values())
+                .filter(c -> c.code == value)
+                .findAny()
+                .orElse(null);
+    }
+
+    /**
+     * 获取错误信息带【%s】占位符格式化后的信息
+     *
+     * @param value 枚举code
+     * @param args  参数值,占位符数量和参数值需对应
+     */
+    public static String getInfo(int value, Object... args) {
+        return String.format(toType(value).getInfo(), args);
+    }
+
+}

+ 108 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/exception/ApiCode.java

@@ -0,0 +1,108 @@
+package org.dromara.business.exception;
+
+public enum ApiCode {
+
+    /**
+     * 操作成功
+     **/
+    SUCCESS(200, "操作成功"),
+    /**
+     * 非法访问
+     **/
+    UNAUTHORIZED(401, "非法访问"),
+    /**
+     * 没有权限
+     **/
+    NOT_PERMISSION(403, "没有权限"),
+    /**
+     * 你请求的资源不存在
+     **/
+    NOT_FOUND(404, "你请求的资源不存在"),
+    /**
+     * 操作失败
+     **/
+    FAIL(500, "操作失败"),
+    /**
+     * 系统异常
+     **/
+    SYSTEM_EXCEPTION(5000, "系统异常"),
+    /**
+     * 请求参数校验异常
+     **/
+    PARAMETER_EXCEPTION(5001, "请求参数校验异常"),
+    /**
+     * 请求参数解析异常
+     **/
+    PARAMETER_PARSE_EXCEPTION(5002, "请求参数解析异常"),
+    /**
+     * HTTP内容类型异常
+     **/
+    HTTP_MEDIA_TYPE_EXCEPTION(5003, "HTTP内容类型异常"),
+    /**
+     * 系统处理异常
+     **/
+    yxs_SYSTEM_EXCEPTION(5100, "系统处理异常"),
+    /**
+     * 业务处理异常
+     **/
+    BUSINESS_EXCEPTION(5101, "业务处理异常"),
+    /**
+     * 数据库处理异常
+     **/
+    DAO_EXCEPTION(5102, "数据库处理异常"),
+    /**
+     * 验证码校验异常
+     **/
+    VERIFICATION_CODE_EXCEPTION(5103, "验证码校验异常"),
+    /**
+     * 登录授权异常
+     **/
+    AUTHENTICATION_EXCEPTION(5104, "登录授权异常"),
+    /**
+     * 没有访问权限
+     **/
+    UNAUTHENTICATED_EXCEPTION(5105, "没有访问权限"),
+    /**
+     * 没有访问权限
+     **/
+    UNAUTHORIZED_EXCEPTION(5106, "没有访问权限"),
+    /**
+     * JWT Token解析异常
+     **/
+    JWTDECODE_EXCEPTION(5107, "Token解析异常"),
+
+    HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION(5108, "METHOD NOT SUPPORTED"),
+
+    /**
+     * 访问次数受限制
+     **/
+    BAD_LIMIT_EXCEPTION(5109, "访问次数受限制"),
+    ;
+
+    private final int code;
+    private final String message;
+
+    ApiCode(final int code, final String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public static ApiCode getApiCode(int code) {
+        ApiCode[] ecs = ApiCode.values();
+        for (ApiCode ec : ecs) {
+            if (ec.getCode() == code) {
+                return ec;
+            }
+        }
+        return SUCCESS;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+}

+ 21 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/exception/BadRequestException.java

@@ -0,0 +1,21 @@
+package org.dromara.business.exception;
+
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+
+import static org.springframework.http.HttpStatus.BAD_REQUEST;
+
+@Getter
+public class BadRequestException extends RuntimeException {
+
+    private Integer status = BAD_REQUEST.value();
+
+    public BadRequestException(String msg) {
+        super(msg);
+    }
+
+    public BadRequestException(HttpStatus status, String msg) {
+        super(msg);
+        this.status = status.value();
+    }
+}

+ 51 - 0
ruoyi-modules/ruoyi-business/src/main/java/org/dromara/business/exception/BusinessException.java

@@ -0,0 +1,51 @@
+package org.dromara.business.exception;
+
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 自定义异常
+ *
+ * @author abai
+ * @date 2025/09/05
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class BusinessException extends RuntimeException {
+
+    private static final long serialVersionUID = -2470461654663264392L;
+
+    private Integer errorCode;
+    private String message;
+
+    public BusinessException() {
+        super();
+    }
+
+    public BusinessException(String message) {
+        super(message);
+        this.message = message;
+    }
+
+    public BusinessException(Integer errorCode, String message) {
+        super(message);
+        this.errorCode = errorCode;
+        this.message = message;
+    }
+
+    public BusinessException(ApiCode apiCode) {
+        super(apiCode.getMessage());
+        this.errorCode = apiCode.getCode();
+        this.message = apiCode.getMessage();
+    }
+
+    public BusinessException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public BusinessException(Throwable cause) {
+        super(cause);
+    }
+
+}