Kaynağa Gözat

Merge branch 'refs/heads/master' into develop_rayson

# Conflicts:
#	menduner/menduner-system-biz/src/test/java/com/citu/module/menduner/system/service/job/JobAdvertisedServiceImplTest.java
rayson 1 yıl önce
ebeveyn
işleme
cc9d6e6688
96 değiştirilmiş dosya ile 9207 ekleme ve 112 silme
  1. 3 0
      citu-framework/citu-common/src/main/java/com/citu/framework/common/exception/enums/ServiceErrorCodeRange.java
  2. 27 27
      citu-framework/citu-spring-boot-starter-web/src/main/java/com/citu/framework/banner/core/BannerApplicationRunner.java
  3. 5 0
      citu-module-infra/citu-module-infra-biz/pom.xml
  4. 243 0
      citu-module-mall/citu-module-product-biz/src/main/java/com/citu/module/product/dal/BeanToGenerateTable.java
  5. 4 1
      citu-module-mall/citu-module-product-biz/src/main/resources/application.yaml
  6. 4 0
      citu-module-mall/citu-module-promotion-biz/src/main/resources/application.yaml
  7. 4 0
      citu-module-mall/citu-module-statistics-biz/src/main/resources/application.yaml
  8. 207 0
      citu-module-mall/citu-module-trade-biz/src/main/java/com/citu/module/trade/dal/BeanToGenerateTable.java
  9. 4 0
      citu-module-mall/citu-module-trade-biz/src/main/resources/application.yaml
  10. 8 40
      citu-module-member/citu-module-member-biz/src/main/java/com/citu/module/member/dal/dataobject/BeanToGenerateTable.java
  11. 32 0
      menduner/menduner-im-api/pom.xml
  12. 23 0
      menduner/menduner-im-api/src/main/java/com/citu/module/menduner/im/enums/ApiConstants.java
  13. 25 0
      menduner/menduner-im-api/src/main/java/com/citu/module/menduner/im/enums/ErrorCodeConstants.java
  14. 16 0
      menduner/menduner-im-biz/Dockerfile
  15. 77 0
      menduner/menduner-im-biz/pom.xml
  16. 22 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/MendunerIMApplication.java
  17. 57 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/config/WuKongConfig.java
  18. 4 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/config/package-info.java
  19. 40 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/config/permission/CustomDataPermissionAutoConfiguration.java
  20. 197 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/config/permission/CustomDataPermissionRule.java
  21. 18 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/config/permission/CustomDataPermissionRuleCustomizer.java
  22. 6 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/config/permission/package-info.java
  23. 41 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/controller/UserController.java
  24. 14 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/controller/base/user/ImUserReqVo.java
  25. 53 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/controller/base/user/ImUserResoVO.java
  26. 56 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/controller/base/wukong/UserTokenReqVO.java
  27. 22 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/controller/base/wukong/WukongApiResp.java
  28. 15 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/convert/UserConvert.java
  29. 69 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/dal/dataobject/UserDO.java
  30. 10 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/dal/mysql/UserMapper.java
  31. 25 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/framework/datapermission/config/DataPermissionConfiguration.java
  32. 4 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/framework/datapermission/package-info.java
  33. 14 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/framework/rpc/config/RpcConfiguration.java
  34. 4 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/framework/rpc/package-info.java
  35. 38 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/framework/security/config/SecurityConfiguration.java
  36. 4 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/framework/security/core/package-info.java
  37. 23 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/interceptor/WuKongWuKongApiInterceptor.java
  38. 4 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/package-info.java
  39. 9 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/service/UserService.java
  40. 68 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/service/UserServiceImpl.java
  41. 22 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/service/wukong/WuKongApiService.java
  42. 20 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/service/wukong/WuKongUserService.java
  43. 58 0
      menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/service/wukong/WuKongUserServiceImpl.java
  44. 177 0
      menduner/menduner-im-biz/src/main/resources/application-dev.yaml
  45. 205 0
      menduner/menduner-im-biz/src/main/resources/application-local.yaml
  46. 146 0
      menduner/menduner-im-biz/src/main/resources/application.yaml
  47. 23 0
      menduner/menduner-im-biz/src/main/resources/bootstrap-local.yaml
  48. 14 0
      menduner/menduner-im-biz/src/main/resources/bootstrap.yaml
  49. 0 0
      menduner/menduner-im-biz/src/main/resources/i18n/messages.properties
  50. 244 0
      menduner/menduner-im-biz/src/main/resources/i18n/messages_en_US.properties
  51. 247 0
      menduner/menduner-im-biz/src/main/resources/i18n/messages_zh_CN.properties
  52. 0 0
      menduner/menduner-im-biz/src/main/resources/i18n/messages_zh_HK.properties
  53. 76 0
      menduner/menduner-im-biz/src/main/resources/logback-spring.xml
  54. 817 0
      menduner/menduner-im-biz/src/main/resources/major/major.csv
  55. 5 0
      menduner/menduner-im-biz/src/main/resources/major/readme.txt
  56. 136 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/area/AreaServiceImplTest.java
  57. 140 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/certificate/CertificateServiceImplTest.java
  58. 136 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/cvattachment/CvAttachmentServiceImplTest.java
  59. 166 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/eduexp/EduExpServiceImplTest.java
  60. 133 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseAddressServiceImplTest.java
  61. 179 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseBusinessServiceImplTest.java
  62. 144 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterprisePostServiceImplTest.java
  63. 144 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseRegisterServiceImplTest.java
  64. 152 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseServiceImplTest.java
  65. 144 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseUserApplyServiceImplTest.java
  66. 141 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseUserBindServiceImplTest.java
  67. 144 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseUserLookServiceImplTest.java
  68. 132 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/hire/HireCommissionRatioServiceImplTest.java
  69. 160 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/hire/HireJobCvRelServiceImplTest.java
  70. 133 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/industry/IndustryServiceImplTest.java
  71. 167 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/job/JobAdvertisedServiceImplTest.java
  72. 143 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/job/JobCvRelServiceImplTest.java
  73. 156 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/job/JobInterestedServiceImplTest.java
  74. 136 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/major/MajorServiceImplTest.java
  75. 208 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/person/PersonBrowseRecordServiceImplTest.java
  76. 133 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/person/PersonCertificateServiceImplTest.java
  77. 132 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/person/PersonEnterpriseSubscribeServiceImplTest.java
  78. 173 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/person/PersonInfoServiceImplTest.java
  79. 132 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/person/PersonJobFavoriteServiceImplTest.java
  80. 137 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/person/PersonSkillServiceImplTest.java
  81. 140 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/position/PositionServiceImplTest.java
  82. 133 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/positiontag/PositionTagServiceImplTest.java
  83. 145 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/projectexp/ProjectExpServiceImplTest.java
  84. 148 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/school/SchoolServiceImplTest.java
  85. 138 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/skill/SkillServiceImplTest.java
  86. 149 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/trainexp/TrainExpServiceImplTest.java
  87. 144 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/user/MdeUserServiceImplTest.java
  88. 132 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/user/UserAccountServiceImplTest.java
  89. 170 0
      menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/workexp/WorkExpServiceImplTest.java
  90. 60 0
      menduner/menduner-im-biz/src/test/resources/application-unit-test.yaml
  91. 4 0
      menduner/menduner-im-biz/src/test/resources/logback.xml
  92. 32 0
      menduner/menduner-im-biz/src/test/resources/sql/clean.sql
  93. 556 0
      menduner/menduner-im-biz/src/test/resources/sql/create_tables.sql
  94. 0 22
      menduner/menduner-system-biz/src/main/resources/mapper/enterprise/EnterpriseMapper.xml
  95. 0 22
      menduner/menduner-system-biz/src/main/resources/mapper/school/SchoolMapper.xml
  96. 2 0
      menduner/pom.xml

+ 3 - 0
citu-framework/citu-common/src/main/java/com/citu/framework/common/exception/enums/ServiceErrorCodeRange.java

@@ -47,4 +47,7 @@ public class ServiceErrorCodeRange {
 
     // 模块 menduner-reward [1_110_000_000 ~ 1_110_999_999]
 
+
+    // 模块 menduner-im [1_200_000_000 ~ 1_110_999_999]
+
 }

+ 27 - 27
citu-framework/citu-spring-boot-starter-web/src/main/java/com/citu/framework/banner/core/BannerApplicationRunner.java

@@ -17,33 +17,33 @@ public class BannerApplicationRunner implements ApplicationRunner {
 
     @Override
     public void run(ApplicationArguments args) {
-        ThreadUtil.execute(() -> {
-            ThreadUtil.sleep(1, TimeUnit.SECONDS); // 延迟 1 秒,保证输出到结尾
-            log.info("\n----------------------------------------------------------\n\t" +
-                            "项目启动成功!\n\t" +
-                            "接口文档: \t{} \n\t" +
-                            "开发文档: \t{} \n\t" +
-                            "视频教程: \t{} \n" +
-                            "----------------------------------------------------------",
-                    "https://cloud.iocoder.cn/api-doc/",
-                    "https://cloud.iocoder.cn",
-                    "https://t.zsxq.com/02Yf6M7Qn");
-
-            // 数据报表
-            System.out.println("[报表模块 citu-module-report 教程][参考 https://cloud.iocoder.cn/report/ 开启]");
-            // 工作流
-            System.out.println("[工作流模块 citu-module-bpm 教程][参考 https://cloud.iocoder.cn/bpm/ 开启]");
-            // 商城系统
-            System.out.println("[商城系统 citu-module-mall 教程][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
-            // ERP 系统
-            System.out.println("[ERP 系统 citu-module-erp - 教程][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
-            // CRM 系统
-            System.out.println("[CRM 系统 citu-module-crm - 教程][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
-            // 微信公众号
-            System.out.println("[微信公众号 citu-module-mp 教程][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
-            // 支付平台
-            System.out.println("[支付系统 citu-module-pay - 教程][参考 https://doc.iocoder.cn/pay/build/ 开启]");
-        });
+//        ThreadUtil.execute(() -> {
+//            ThreadUtil.sleep(1, TimeUnit.SECONDS); // 延迟 1 秒,保证输出到结尾
+//            log.info("\n----------------------------------------------------------\n\t" +
+//                            "项目启动成功!\n\t" +
+//                            "接口文档: \t{} \n\t" +
+//                            "开发文档: \t{} \n\t" +
+//                            "视频教程: \t{} \n" +
+//                            "----------------------------------------------------------",
+//                    "https://cloud.iocoder.cn/api-doc/",
+//                    "https://cloud.iocoder.cn",
+//                    "https://t.zsxq.com/02Yf6M7Qn");
+//
+//            // 数据报表
+//            System.out.println("[报表模块 citu-module-report 教程][参考 https://cloud.iocoder.cn/report/ 开启]");
+//            // 工作流
+//            System.out.println("[工作流模块 citu-module-bpm 教程][参考 https://cloud.iocoder.cn/bpm/ 开启]");
+//            // 商城系统
+//            System.out.println("[商城系统 citu-module-mall 教程][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
+//            // ERP 系统
+//            System.out.println("[ERP 系统 citu-module-erp - 教程][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
+//            // CRM 系统
+//            System.out.println("[CRM 系统 citu-module-crm - 教程][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
+//            // 微信公众号
+//            System.out.println("[微信公众号 citu-module-mp 教程][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
+//            // 支付平台
+//            System.out.println("[支付系统 citu-module-pay - 教程][参考 https://doc.iocoder.cn/pay/build/ 开启]");
+//        });
     }
 
 }

+ 5 - 0
citu-module-infra/citu-module-infra-biz/pom.xml

@@ -19,6 +19,11 @@
     </description>
 
     <dependencies>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <version>1.3.2</version>
+        </dependency>
         <!-- Spring Cloud 基础 -->
         <dependency>
             <groupId>org.springframework.cloud</groupId>

+ 243 - 0
citu-module-mall/citu-module-product-biz/src/main/java/com/citu/module/product/dal/BeanToGenerateTable.java

@@ -0,0 +1,243 @@
+package com.citu.module.product.dal;
+
+import cn.hutool.core.io.FileUtil;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * @author Rayson
+ * @description generateTable
+ * @create 2024/5/11 下午3:10
+ **/
+public class BeanToGenerateTable {
+
+
+//    public static void main(String[] args) {
+//
+//        String sql = generateTableMysql("com.citu.module.member.dal.dataobject.address.MemberAddressDO", true);
+//        System.out.println(sql);
+//
+//        String sql2 = generateTableMysql("com.citu.module.member.dal.dataobject.config.MemberConfigDO", true);
+//        System.out.println(sql2);
+//
+//        String sql3 = generateTableMysql("com.citu.module.member.dal.dataobject.group.MemberGroupDO", true);
+//        System.out.println(sql3);
+//
+//        String sql4 = generateTableMysql("com.citu.module.member.dal.dataobject.level.MemberExperienceRecordDO", true);
+//        System.out.println(sql4);
+//
+//        String sql5 = generateTableMysql("com.citu.module.member.dal.dataobject.level.MemberLevelDO", true);
+//        System.out.println(sql5);
+//
+//        String sql6 = generateTableMysql("com.citu.module.member.dal.dataobject.level.MemberLevelRecordDO", true);
+//        System.out.println(sql6);
+//
+//        String sql7 = generateTableMysql("com.citu.module.member.dal.dataobject.point.MemberPointRecordDO", true);
+//        System.out.println(sql7);
+//
+//        String sql8 = generateTableMysql("com.citu.module.member.dal.dataobject.signin.MemberSignInConfigDO", true);
+//        System.out.println(sql8);
+//
+//        String sql9 = generateTableMysql("com.citu.module.member.dal.dataobject.signin.MemberSignInRecordDO", true);
+//        System.out.println(sql9);
+//
+//        String sql10 = generateTableMysql("com.citu.module.member.dal.dataobject.tag.MemberTagDO", true);
+//        System.out.println(sql10);
+//
+//        String sql11 = generateTableMysql("com.citu.module.member.dal.dataobject.user.MemberUserDO", true);
+//        System.out.println(sql11);
+//
+//    }
+
+
+    public static void main(String[] args) {
+        List<File> files = FileUtil.loopFiles("C:\\Users\\Administrator\\Documents\\citu-menduner\\citu-module-mall\\citu-module-product-biz\\src\\main\\java\\com\\citu\\module\\product\\dal\\dataobject");
+
+        StringBuilder sb =  new StringBuilder();
+        sb.append("\n\n\n\n\n===================================");
+        for (File file : files) {
+            String path = file.getPath().split("src\\\\main\\\\java")[1].replaceAll("\\\\",".");
+            String packageName =  path.substring(1,path.length()-5);
+//            System.out.println(packageName);
+            String sql = generateTableMysql(packageName, true);
+            sb.append(sql).append("\n\n\n");
+        }
+
+        System.out.println(sb);
+
+    }
+
+    /***
+     * 根据实体类自动生成Mysql建表sql
+     * @param beanName  实体类路径
+     * @param isConvert  是否需要转换驼峰格式
+     * @return
+     */
+    public static String generateTableMysql(String beanName, boolean isConvert) {
+        StringBuilder sqlSb = new StringBuilder();
+
+        if (null != beanName && !"".equals(beanName)) {
+            Object obj = null;
+            try {
+                obj = Class.forName(beanName).newInstance();
+            } catch (InstantiationException e) {
+                e.printStackTrace();
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            } catch (ClassNotFoundException e) {
+                e.printStackTrace();
+            }
+            // 拿到该类
+            Class<?> clz = obj.getClass();
+            // 获取实体类的所有属性,返回Field数组
+            Field[] fields = clz.getDeclaredFields();
+            if (fields != null) {
+
+                //获取实体类的Table注解表名(导入persistence包)
+                TableName tableClass = clz.getAnnotation(TableName.class);
+                String tableName = "";
+                if (tableClass != null) {
+                    tableName = tableClass.value();
+                }
+                sqlSb.append("drop table if exists " + tableName + "; \n");
+
+                sqlSb.append("create table " + tableName + "( \n");
+
+                String idKey = "";
+
+                for (Field field : fields) {
+                    if (!field.isAccessible()) {
+                        field.setAccessible(true);
+                    }
+                    String type = field.getGenericType().toString();
+                    if (type.indexOf(".") == -1 || type.indexOf("java.") != -1) {//过滤实体对象
+                        //截取最后一个.后面的字符
+                        type = type.substring(type.lastIndexOf(".") + 1);
+                        if (!field.getType().equals(List.class)) {// 不匹配list类型
+                            //字段名称
+                            String name = field.getName();
+
+                            Method doSomeMethod = null;
+//                            try {
+//                                //获取get方法
+//                                doSomeMethod = clz.getDeclaredMethod("get" + name.substring(0, 1).toUpperCase() + name.substring(1));
+//                            } catch (NoSuchMethodException e) {
+//                                e.printStackTrace();
+//                            }
+                            boolean isExist = true;
+                            //属于表字段
+                            if (isExist) {
+                                StringBuilder convertName = new StringBuilder();
+                                convertName.append(name);
+                                //如果需要转换驼峰格式
+                                if (isConvert) {
+                                    convertName = new StringBuilder();
+                                    for (int i = 0; i < name.length(); i++) {
+                                        //如果是大写前面先加一个_
+                                        if (isUpperCase(name.charAt(i))) {
+                                            convertName.append("_");
+                                        }
+                                        convertName.append(name.charAt(i));
+                                    }
+                                }
+                                name = convertName.toString();
+                                sqlSb.append("    `" + name.toLowerCase() + "` ");
+                                //java 数据类型转换成 Oracle 字段数据类型
+                                sqlSb.append(" " + societyMysql(type));
+                                //判断该字段是否是Id主键(导入persistence包)
+                                if (field.isAnnotationPresent(TableId.class)) {
+                                    TableId id = field.getAnnotation(TableId.class);
+                                    idKey = name.toLowerCase();//id主键字段
+                                    sqlSb.append("  NOT NULL  ");
+                                }
+                                sqlSb.append(" comment ");
+                                //字段属性说明(没有ApiModelProperty包的把这段代码注释掉)
+//                                if(field.isAnnotationPresent(ApiModelProperty.class)){
+//                                    ApiModelProperty explain = field.getAnnotation(ApiModelProperty.class);
+//                                    System.out.println("字段说明:"+explain.value());
+//                                    sqlSb.append(" '"+explain.value()+"',\n" );
+//                                }else{
+                                sqlSb.append(" '',\n");
+//                                }
+
+                                /**
+                                 * 字段属性说明
+                                 * 没有ApiModelProperty包的把上面那段代码注释掉,用这个
+                                 */
+                                /*
+                                sqlSb.append(" '',\n" );
+                                */
+
+                            }
+
+                        }
+                    }
+
+                }
+                sqlSb.append("    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n" +
+                        "    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n" +
+                        "    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n" +
+                        "    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n" +
+                        "    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n" +
+                        "    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',");
+                if (null != idKey && !"".equals(idKey)) {
+                    sqlSb.append(" primary key (" + idKey + ") \n");
+                } else {
+                    String lastStr = sqlSb.substring(0, sqlSb.length() - 1);
+                    //如果最后一个字符是逗号结尾
+                    if (lastStr.equals(",")) {
+                        //删除最后一个字符
+                        sqlSb = sqlSb.deleteCharAt(sqlSb.length() - 1);
+                    }
+                }
+                sqlSb.append(" ); \n");
+
+            }
+        }
+
+        return sqlSb.toString();
+    }
+
+    //字母是否是大写
+    public static boolean isUpperCase(char c) {
+        return c >= 65 && c <= 90;
+    }
+
+    public static String societyMysql(String javaType) {
+        String oracleType = "";
+        if (javaType.equals("String")) {
+            oracleType = "varchar(255)";
+        }
+        //不区分大小写
+        else if (javaType.equalsIgnoreCase("BigDecimal")) {
+            oracleType = "decimal(6)";
+        } else if (javaType.equals("Date")) {
+            oracleType = "date";
+        } else if (javaType.equals("LocalDateTime")) {
+            oracleType = "datetime";
+        } else if (javaType.equals("Boolean") || javaType.equals("boolean")) {
+            oracleType = "bit(1)";
+        } else if (javaType.equalsIgnoreCase("Timestamp")) {
+            oracleType = "datetime";
+        } else if (javaType.equals("byte[]")) {
+            oracleType = "blob";
+        } else if (javaType.equals("int")) {
+            oracleType = "tinyint";
+        } else if (javaType.equals("Integer")) {
+            oracleType = "tinyint";
+        } else if (javaType.equals("Long") || javaType.equals("long")) {
+            oracleType = "bigint";
+        } else if (javaType.equalsIgnoreCase("double")) {
+            oracleType = "double";
+        } else if (javaType.equalsIgnoreCase("float")) {
+            oracleType = "float";
+        }
+        return oracleType;
+    }
+
+}

+ 4 - 1
citu-module-mall/citu-module-product-biz/src/main/resources/application.yaml

@@ -105,5 +105,8 @@ citu:
     enable: true
     ignore-urls:
     ignore-tables:
-
+  error-code: # 错误码相关配置项
+    enable: false
+    constants-class-list:
+      - com.citu.module.menduner.system.enums.ErrorCodeConstants
 debug: false

+ 4 - 0
citu-module-mall/citu-module-promotion-biz/src/main/resources/application.yaml

@@ -101,6 +101,10 @@ citu:
     base-package: ${citu.info.base-package}
   captcha:
     enable: true # 验证码的开关,默认为 true;
+  error-code: # 错误码相关配置项
+    enable: false
+    constants-class-list:
+      - com.citu.module.menduner.system.enums.ErrorCodeConstants
   tenant: # 多租户相关配置项
     enable: true
     ignore-urls:

+ 4 - 0
citu-module-mall/citu-module-statistics-biz/src/main/resources/application.yaml

@@ -101,6 +101,10 @@ citu:
     base-package: ${citu.info.base-package}
   captcha:
     enable: true # 验证码的开关,默认为 true;
+  error-code: # 错误码相关配置项
+    enable: false
+    constants-class-list:
+      - com.citu.module.menduner.system.enums.ErrorCodeConstants
   tenant: # 多租户相关配置项
     enable: true
     ignore-urls:

+ 207 - 0
citu-module-mall/citu-module-trade-biz/src/main/java/com/citu/module/trade/dal/BeanToGenerateTable.java

@@ -0,0 +1,207 @@
+package com.citu.module.trade.dal;
+
+import cn.hutool.core.io.FileUtil;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * @author Rayson
+ * @description generateTable
+ * @create 2024/5/11 下午3:10
+ **/
+public class BeanToGenerateTable {
+
+
+
+
+    public static void main(String[] args) {
+        List<File> files = FileUtil.loopFiles("C:\\Users\\Administrator\\Documents\\citu-menduner\\citu-module-mall\\citu-module-trade-biz\\src\\main\\java\\com\\citu\\module\\trade\\dal\\dataobject");
+
+        StringBuilder sb =  new StringBuilder();
+        sb.append("\n\n\n\n\n===================================");
+        for (File file : files) {
+            String path = file.getPath().split("src\\\\main\\\\java")[1].replaceAll("\\\\",".");
+            String packageName =  path.substring(1,path.length()-5);
+//            System.out.println(packageName);
+            String sql = generateTableMysql(packageName, true);
+            sb.append(sql).append("\n\n\n");
+        }
+
+        System.out.println(sb);
+
+    }
+
+    /***
+     * 根据实体类自动生成Mysql建表sql
+     * @param beanName  实体类路径
+     * @param isConvert  是否需要转换驼峰格式
+     * @return
+     */
+    public static String generateTableMysql(String beanName, boolean isConvert) {
+        StringBuilder sqlSb = new StringBuilder();
+
+        if (null != beanName && !"".equals(beanName)) {
+            Object obj = null;
+            try {
+                obj = Class.forName(beanName).newInstance();
+            } catch (InstantiationException e) {
+                e.printStackTrace();
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            } catch (ClassNotFoundException e) {
+                e.printStackTrace();
+            }
+            // 拿到该类
+            Class<?> clz = obj.getClass();
+            // 获取实体类的所有属性,返回Field数组
+            Field[] fields = clz.getDeclaredFields();
+            if (fields != null) {
+
+                //获取实体类的Table注解表名(导入persistence包)
+                TableName tableClass = clz.getAnnotation(TableName.class);
+                String tableName = "";
+                if (tableClass != null) {
+                    tableName = tableClass.value();
+                }
+                sqlSb.append("drop table if exists " + tableName + "; \n");
+
+                sqlSb.append("create table " + tableName + "( \n");
+
+                String idKey = "";
+
+                for (Field field : fields) {
+                    if (!field.isAccessible()) {
+                        field.setAccessible(true);
+                    }
+                    String type = field.getGenericType().toString();
+                    if (type.indexOf(".") == -1 || type.indexOf("java.") != -1) {//过滤实体对象
+                        //截取最后一个.后面的字符
+                        type = type.substring(type.lastIndexOf(".") + 1);
+                        if (!field.getType().equals(List.class)) {// 不匹配list类型
+                            //字段名称
+                            String name = field.getName();
+
+                            Method doSomeMethod = null;
+//                            try {
+//                                //获取get方法
+//                                doSomeMethod = clz.getDeclaredMethod("get" + name.substring(0, 1).toUpperCase() + name.substring(1));
+//                            } catch (NoSuchMethodException e) {
+//                                e.printStackTrace();
+//                            }
+                            boolean isExist = true;
+                            //属于表字段
+                            if (isExist) {
+                                StringBuilder convertName = new StringBuilder();
+                                convertName.append(name);
+                                //如果需要转换驼峰格式
+                                if (isConvert) {
+                                    convertName = new StringBuilder();
+                                    for (int i = 0; i < name.length(); i++) {
+                                        //如果是大写前面先加一个_
+                                        if (isUpperCase(name.charAt(i))) {
+                                            convertName.append("_");
+                                        }
+                                        convertName.append(name.charAt(i));
+                                    }
+                                }
+                                name = convertName.toString();
+                                sqlSb.append("    `" + name.toLowerCase() + "` ");
+                                //java 数据类型转换成 Oracle 字段数据类型
+                                sqlSb.append(" " + societyMysql(type));
+                                //判断该字段是否是Id主键(导入persistence包)
+                                if (field.isAnnotationPresent(TableId.class)) {
+                                    TableId id = field.getAnnotation(TableId.class);
+                                    idKey = name.toLowerCase();//id主键字段
+                                    sqlSb.append("  NOT NULL  ");
+                                }
+                                sqlSb.append(" comment ");
+                                //字段属性说明(没有ApiModelProperty包的把这段代码注释掉)
+//                                if(field.isAnnotationPresent(ApiModelProperty.class)){
+//                                    ApiModelProperty explain = field.getAnnotation(ApiModelProperty.class);
+//                                    System.out.println("字段说明:"+explain.value());
+//                                    sqlSb.append(" '"+explain.value()+"',\n" );
+//                                }else{
+                                sqlSb.append(" '',\n");
+//                                }
+
+                                /**
+                                 * 字段属性说明
+                                 * 没有ApiModelProperty包的把上面那段代码注释掉,用这个
+                                 */
+                                /*
+                                sqlSb.append(" '',\n" );
+                                */
+
+                            }
+
+                        }
+                    }
+
+                }
+                sqlSb.append("    `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',\n" +
+                        "    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n" +
+                        "    `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',\n" +
+                        "    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n" +
+                        "    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',\n" +
+                        "    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',");
+                if (null != idKey && !"".equals(idKey)) {
+                    sqlSb.append(" primary key (" + idKey + ") \n");
+                } else {
+                    String lastStr = sqlSb.substring(0, sqlSb.length() - 1);
+                    //如果最后一个字符是逗号结尾
+                    if (lastStr.equals(",")) {
+                        //删除最后一个字符
+                        sqlSb = sqlSb.deleteCharAt(sqlSb.length() - 1);
+                    }
+                }
+                sqlSb.append(" ); \n");
+
+            }
+        }
+
+        return sqlSb.toString();
+    }
+
+    //字母是否是大写
+    public static boolean isUpperCase(char c) {
+        return c >= 65 && c <= 90;
+    }
+
+    public static String societyMysql(String javaType) {
+        String oracleType = "";
+        if (javaType.equals("String")) {
+            oracleType = "varchar(255)";
+        }
+        //不区分大小写
+        else if (javaType.equalsIgnoreCase("BigDecimal")) {
+            oracleType = "decimal(6)";
+        } else if (javaType.equals("Date")) {
+            oracleType = "date";
+        } else if (javaType.equals("LocalDateTime")) {
+            oracleType = "datetime";
+        } else if (javaType.equals("Boolean") || javaType.equals("boolean")) {
+            oracleType = "bit(1)";
+        } else if (javaType.equalsIgnoreCase("Timestamp")) {
+            oracleType = "datetime";
+        } else if (javaType.equals("byte[]")) {
+            oracleType = "blob";
+        } else if (javaType.equals("int")) {
+            oracleType = "tinyint";
+        } else if (javaType.equals("Integer")) {
+            oracleType = "tinyint";
+        } else if (javaType.equals("Long") || javaType.equals("long")) {
+            oracleType = "bigint";
+        } else if (javaType.equalsIgnoreCase("double")) {
+            oracleType = "double";
+        } else if (javaType.equalsIgnoreCase("float")) {
+            oracleType = "float";
+        }
+        return oracleType;
+    }
+
+}

+ 4 - 0
citu-module-mall/citu-module-trade-biz/src/main/resources/application.yaml

@@ -105,6 +105,10 @@ citu:
     enable: true
     ignore-urls:
     ignore-tables:
+  error-code: # 错误码相关配置项
+    enable: false
+    constants-class-list:
+      - com.citu.module.menduner.system.enums.ErrorCodeConstants
   trade:
     order:
       app-id: 1 # 商户编号

+ 8 - 40
citu-module-member/citu-module-member-biz/src/main/java/com/citu/module/member/dal/dataobject/BeanToGenerateTable.java

@@ -1,5 +1,6 @@
 package com.citu.module.member.dal.dataobject;
 
+import cn.hutool.core.io.FileUtil;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 
@@ -15,42 +16,9 @@ import java.util.List;
 public class BeanToGenerateTable {
 
 
-    public static void main(String[] args) {
 
-        String sql = generateTableMysql("com.citu.module.member.dal.dataobject.address.MemberAddressDO", true);
-        System.out.println(sql);
 
-        String sql2 = generateTableMysql("com.citu.module.member.dal.dataobject.config.MemberConfigDO", true);
-        System.out.println(sql2);
 
-        String sql3 = generateTableMysql("com.citu.module.member.dal.dataobject.group.MemberGroupDO", true);
-        System.out.println(sql3);
-
-        String sql4 = generateTableMysql("com.citu.module.member.dal.dataobject.level.MemberExperienceRecordDO", true);
-        System.out.println(sql4);
-
-        String sql5 = generateTableMysql("com.citu.module.member.dal.dataobject.level.MemberLevelDO", true);
-        System.out.println(sql5);
-
-        String sql6 = generateTableMysql("com.citu.module.member.dal.dataobject.level.MemberLevelRecordDO", true);
-        System.out.println(sql6);
-
-        String sql7 = generateTableMysql("com.citu.module.member.dal.dataobject.point.MemberPointRecordDO", true);
-        System.out.println(sql7);
-
-        String sql8 = generateTableMysql("com.citu.module.member.dal.dataobject.signin.MemberSignInConfigDO", true);
-        System.out.println(sql8);
-
-        String sql9 = generateTableMysql("com.citu.module.member.dal.dataobject.signin.MemberSignInRecordDO", true);
-        System.out.println(sql9);
-
-        String sql10 = generateTableMysql("com.citu.module.member.dal.dataobject.tag.MemberTagDO", true);
-        System.out.println(sql10);
-
-        String sql11 = generateTableMysql("com.citu.module.member.dal.dataobject.user.MemberUserDO", true);
-        System.out.println(sql11);
-
-    }
 
     /***
      * 根据实体类自动生成Mysql建表sql
@@ -102,13 +70,13 @@ public class BeanToGenerateTable {
                             //字段名称
                             String name = field.getName();
 
-                            Method doSomeMethod = null;
-                            try {
-                                //获取get方法
-                                doSomeMethod = clz.getDeclaredMethod("get" + name.substring(0, 1).toUpperCase() + name.substring(1));
-                            } catch (NoSuchMethodException e) {
-                                e.printStackTrace();
-                            }
+//                            Method doSomeMethod = null;
+//                            try {
+//                                //获取get方法
+//                                doSomeMethod = clz.getDeclaredMethod("get" + name.substring(0, 1).toUpperCase() + name.substring(1));
+//                            } catch (NoSuchMethodException e) {
+//                                e.printStackTrace();
+//                            }
                             boolean isExist = true;
                             //属于表字段
                             if (isExist) {

+ 32 - 0
menduner/menduner-im-api/pom.xml

@@ -0,0 +1,32 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.citu</groupId>
+        <artifactId>menduner</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>menduner-im-api</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description>
+        门墩儿聊天通讯api
+    </description>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>com.citu</groupId>
+            <artifactId>menduner-common</artifactId>
+            <version>${revision}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.citu</groupId>
+                    <artifactId>citu-spring-boot-starter-es</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+    </dependencies>
+</project>

+ 23 - 0
menduner/menduner-im-api/src/main/java/com/citu/module/menduner/im/enums/ApiConstants.java

@@ -0,0 +1,23 @@
+package com.citu.module.menduner.reward.enums;
+
+import com.citu.framework.common.enums.RpcConstants;
+
+/**
+ * API 相关的枚举
+ *
+ * @author Rayson
+ */
+public class ApiConstants {
+
+    /**
+     * 服务名
+     *
+     * 注意,需要保证和 spring.application.name 保持一致
+     */
+    public static final String NAME = "menduner-im-server";
+
+    public static final String PREFIX = RpcConstants.RPC_API_PREFIX +  "/menduner/im";
+
+    public static final String VERSION = "1.0.0";
+
+}

+ 25 - 0
menduner/menduner-im-api/src/main/java/com/citu/module/menduner/im/enums/ErrorCodeConstants.java

@@ -0,0 +1,25 @@
+package com.citu.module.menduner.reward.enums;
+
+import com.citu.framework.common.exception.ErrorCode;
+import com.citu.framework.common.exception.enums.GlobalErrorCodeConstants;
+import com.citu.framework.common.exception.enums.ServiceErrorCodeRange;
+
+/**
+ * 门墩儿 积分 错误码枚举类
+ * 错误码增减原则 规范
+ * 1、每个模块都有单独的错误码;
+ * 2、假设招聘职位模块要效验企业id,则在”招聘职位模块"下新增"企业id不能为空"错误码,不允许引用"企业模块"的"id不能为空";
+ * 3、列外,所有模块都可以引用 {公共 1_110_000_000} or {@link GlobalErrorCodeConstants} 的错误码,除此之外不可引用不同业务块的错误码;
+ * 参见 {@link ServiceErrorCodeRange}
+ *
+ * @author Rayson
+ **/
+public interface ErrorCodeConstants {
+
+    // ========== 公共配置  1_110_000_000 ==========
+    ErrorCode POINT_RULE_CONFIG_STATUS_CAN_NOT_BE_EMPTY = new ErrorCode(1_110_000_001, "状态不能为空");
+    ErrorCode POINT_RULE_CONFIG_ID_CAN_NOT_BE_EMPTY = new ErrorCode(1_110_000_002, "id不能为空");
+    ErrorCode USER_ID_CAN_NOT_BE_EMPTY = new ErrorCode(1_110_000_003, "用户id不能为空");
+
+
+}

+ 16 - 0
menduner/menduner-im-biz/Dockerfile

@@ -0,0 +1,16 @@
+## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
+## 感谢复旦核博士的建议!灰子哥,牛皮!
+FROM adoptopenjdk/openjdk11
+
+## 将后端项目的 Jar 文件,复制到镜像中
+ADD target/menduner-system-biz.jar app.jar
+
+## 设置 TZ 时区
+## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
+ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m"
+
+## 暴露后端项目的 48080 端口
+EXPOSE 48200
+
+## 启动后端项目
+CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar

+ 77 - 0
menduner/menduner-im-biz/pom.xml

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.citu</groupId>
+        <artifactId>menduner</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>menduner-im-biz</artifactId>
+    <packaging>jar</packaging>
+
+    <name>${project.artifactId}</name>
+    <description>
+        门墩儿聊天通讯 模块
+    </description>
+
+    <dependencies>
+        <!-- Spring Cloud 基础 -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-bootstrap</artifactId>
+        </dependency>
+
+        <!-- 依赖服务 -->
+        <dependency>
+            <groupId>com.citu</groupId>
+            <artifactId>menduner-im-api</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
+
+
+        <!-- Registry 注册中心相关 -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+
+        <!-- Config 配置中心相关 -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+
+        <!-- Test 测试相关 -->
+        <dependency>
+            <groupId>com.citu</groupId>
+            <artifactId>citu-spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <!-- 设置构建的 jar 包名 -->
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <!-- 打包 -->
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring.boot.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 22 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/MendunerIMApplication.java

@@ -0,0 +1,22 @@
+package com.citu.module.menduner.im;
+
+import com.citu.module.menduner.im.config.WuKongConfig;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * 项目的启动类
+ * 门墩儿 招聘模块 启动类
+ * @author Rayson
+ */
+@SpringBootApplication
+@EnableConfigurationProperties(WuKongConfig.class)
+public class MendunerIMApplication {
+    public static ConfigurableApplicationContext applicationContext;
+
+    public static void main(String[] args) {
+        applicationContext=SpringApplication.run(MendunerIMApplication.class, args);
+    }
+}

+ 57 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/config/WuKongConfig.java

@@ -0,0 +1,57 @@
+package com.citu.module.menduner.im.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+@Configuration
+@ConfigurationProperties
+@Component
+public class WuKongConfig {
+
+    @Value("${im.wukong.url:127.0.0.1:5001}")
+    private String url;
+
+    @Value("${im.wukong.tcpurl:127.0.0.1:5001}")
+    private String tcpUrl;
+
+    @Value("${im.wukong.wsurl:127.0.0.1:5001}")
+    private String wsUrl;
+
+    @Value("${im.wukong.wssurl:127.0.0.1:5001}")
+    private String wssUrl;
+
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getTcpUrl() {
+        return tcpUrl;
+    }
+
+    public void setTcpUrl(String tcpUrl) {
+        this.tcpUrl = tcpUrl;
+    }
+
+    public String getWsUrl() {
+        return wsUrl;
+    }
+
+    public void setWsUrl(String wsUrl) {
+        this.wsUrl = wsUrl;
+    }
+
+    public String getWssUrl() {
+        return wssUrl;
+    }
+
+    public void setWssUrl(String wssUrl) {
+        this.wssUrl = wssUrl;
+    }
+}

+ 4 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/config/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * @author Rayson
+ */
+package com.citu.module.menduner.system.config;

+ 40 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/config/permission/CustomDataPermissionAutoConfiguration.java

@@ -0,0 +1,40 @@
+package com.citu.module.menduner.im.config.permission;
+
+import cn.hutool.extra.spring.SpringUtil;
+//import com.citu.module.menduner.system.api.permission.MendunerPermissionApi;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+/**
+ * 自定义的数据权限 AutoConfiguration
+ *
+ * @author Rayson
+ */
+@Configuration
+//@ConditionalOnClass(LoginUser.class)
+//@ConditionalOnBean(value = CustomDataPermissionRuleCustomizer.class)
+public class CustomDataPermissionAutoConfiguration {
+
+//    @Bean
+//    public CustomDataPermissionRule customDataPermissionRule(MendunerPermissionApi permissionApi,
+//                                                             List<CustomDataPermissionRuleCustomizer> customizers) {
+//        // Cloud 专属逻辑:优先使用本地的 MendunerPermissionApi 实现类,而不是 Feign 调用
+//        // 原因:在创建租户时,租户还没创建好,导致 Feign 调用获取数据权限时,报“租户不存在”的错误
+//        try {
+//            MendunerPermissionApi permissionApiImpl = SpringUtil.getBean("mendunerPermissionApi", MendunerPermissionApi.class);
+//            if (permissionApiImpl != null) {
+//                permissionApi = permissionApiImpl;
+//            }
+//        } catch (Exception ignored) {
+//        }
+//
+//        // 创建 CustomDataPermissionRule 对象
+//        CustomDataPermissionRule rule = new CustomDataPermissionRule(permissionApi);
+//        // 补全表配置
+//        customizers.forEach(customizer -> customizer.customize(rule));
+//        return rule;
+//    }
+
+}

+ 197 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/config/permission/CustomDataPermissionRule.java

@@ -0,0 +1,197 @@
+package com.citu.module.menduner.im.config.permission;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.CharSequenceUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import com.citu.framework.common.enums.UserTypeEnum;
+import com.citu.framework.common.util.collection.CollectionUtils;
+import com.citu.framework.common.util.json.JsonUtils;
+import com.citu.framework.datapermission.core.rule.DataPermissionRule;
+import com.citu.framework.mybatis.core.dataobject.BaseDO;
+import com.citu.framework.mybatis.core.util.MyBatisUtils;
+import com.citu.framework.security.core.LoginUser;
+import com.citu.framework.security.core.util.SecurityFrameworkUtils;
+//import com.citu.module.menduner.im.api.permission.MendunerPermissionApi;
+import com.citu.module.menduner.common.util.LoginUserContext;
+import com.citu.module.system.api.permission.dto.DataPermissionRespDTO;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.jsqlparser.expression.*;
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
+import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
+import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
+import net.sf.jsqlparser.expression.operators.relational.InExpression;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static com.citu.module.menduner.common.util.LoginUserContext.checkEnterprise;
+
+/**
+ * @author Rayson
+ * @description CustomDataPermissionRule
+ * 基于自定义的 {@link DataPermissionRule} 数据权限规则实现
+ * @create 2024/5/8 下午7:41
+ **/
+@AllArgsConstructor
+@Slf4j
+public class CustomDataPermissionRule implements DataPermissionRule {
+
+    /**
+     * LoginUser 的 Context 缓存 Key
+     */
+    protected static final String CONTEXT_KEY = CustomDataPermissionRule.class.getSimpleName();
+
+    static final Expression EXPRESSION_NULL = new NullValue();
+    /**
+     * 基于数据的表字段配置 TODO 目前只支持一个数据权限字段,xx_id 和 user_id 不能同时配置。
+     * 如基于部门的数据编号字段是 自定义xx_id,基于店铺的数据编号字段是 shop_id,通过该配置自定义,如 group_id。
+     * <p>
+     * key:表名
+     * value:字段名
+     */
+    private final static Map<String, String> DATA_COLUMNS = new HashMap<>();
+    /**
+     * 基于用户的表字段配置
+     * 一般情况下,每个表的数据编号字段是 user_id,通过该配置自定义,如 admin_user_id。
+     * <p>
+     * key:表名
+     * value:字段名
+     */
+    private final static Map<String, String> USER_COLUMNS = new HashMap<>();
+    /**
+     * 所有表名,是 {@link #DATA_COLUMNS} 和 {@link #USER_COLUMNS} 的合集
+     */
+    private final static Set<String> TABLE_NAMES = new HashSet<>();
+
+//    private final MendunerPermissionApi permissionApi;
+
+    @Override
+    public Set<String> getTableNames() {
+        return TABLE_NAMES;
+    }
+
+    @Override
+    public Expression getExpression(String tableName, Alias tableAlias) {
+        // 只有登陆用户的情况下,才进行数据权限的处理
+        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
+        if (loginUser == null) {
+            return null;
+        }
+        // 只有MEMBER_ADMIN类型的用户,才进行数据权限的处理
+        if (ObjectUtil.notEqual(loginUser.getUserType(), UserTypeEnum.MEMBER_ADMIN.getValue())) {
+            return null;
+        }
+
+        if (!checkEnterprise(loginUser)) {
+            // 没有企业id
+            return null;
+        }
+        // 获得数据权限
+        DataPermissionRespDTO dataPermission = loginUser.getContext(CONTEXT_KEY, DataPermissionRespDTO.class);
+        // 从上下文中拿不到,则调用逻辑进行获取
+        if (dataPermission == null) {
+            Long enterpriseId = LoginUserContext.getEnterpriseId(loginUser);
+//            dataPermission = permissionApi.getDataPermission(loginUser.getId(), enterpriseId).getCheckedData();
+//            if (dataPermission == null) {
+//                log.error("[getExpression][LoginUser({}) 获取数据权限为 null]", JsonUtils.toJsonString(loginUser));
+//                throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 未返回数据权限",
+//                        loginUser.getId(), tableName, tableAlias.getName()));
+//            }
+            // 添加到上下文中,避免重复计算
+            loginUser.setContext(CONTEXT_KEY, dataPermission);
+        }
+
+        // 情况一,如果是 ALL 可查看全部,则无需拼接条件
+        if (dataPermission.getAll()) {
+            return null;
+        }
+
+        // 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限
+        if (CollUtil.isEmpty(dataPermission.getDataIds())
+                && Boolean.FALSE.equals(dataPermission.getSelf())) {
+            return new EqualsTo(null, null); // WHERE null = null,可以保证返回的数据为空
+        }
+
+        // 情况三,拼接 Dept 和 User 的条件,最后组合
+        Expression dataExpression = buildDeptExpression(tableName, tableAlias, dataPermission.getDataIds());
+        Expression userExpression = buildUserExpression(tableName, tableAlias, dataPermission.getSelf(), loginUser.getId());
+        if (dataExpression == null && userExpression == null) {
+            // TODO 芋艿:获得不到条件的时候,暂时不抛出异常,而是不返回数据
+            log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]",
+                    JsonUtils.toJsonString(loginUser), tableName, tableAlias, JsonUtils.toJsonString(dataPermission));
+            return EXPRESSION_NULL; // AND NULL
+        }
+
+        if (dataExpression == null) {
+            return userExpression;
+        }
+        if (userExpression == null) {
+            return dataExpression;
+        }
+        // // 目前,如果有指定数据 + 可查看自己,采用 OR 条件。即,WHERE (xx_id IN ? OR user_id = ?)
+        // return new Parenthesis(new OrExpression(dataExpression, userExpression));
+        // 改为 AND 条件。即,WHERE (xx_id IN ? and user_id = ?)
+        return new Parenthesis(new AndExpression(dataExpression, userExpression));
+    }
+
+    private Expression buildDeptExpression(String tableName, Alias tableAlias, Set<Long> dataIds) {
+        // 如果不存在配置,则无需作为条件
+        String columnName = DATA_COLUMNS.get(tableName);
+        if (StrUtil.isEmpty(columnName)) {
+            return null;
+        }
+        // 如果为空,则无条件
+        if (CollUtil.isEmpty(dataIds)) {
+            return null;
+        }
+        // 拼接条件
+        return new InExpression(MyBatisUtils.buildColumn(tableName, tableAlias, columnName),
+                new ExpressionList(CollectionUtils.convertList(dataIds, LongValue::new)));
+    }
+
+    private Expression buildUserExpression(String tableName, Alias tableAlias, Boolean self, Long userId) {
+        // 如果不查看自己,则无需作为条件
+        if (Boolean.FALSE.equals(self)) {
+            return null;
+        }
+        String columnName = USER_COLUMNS.get(tableName);
+        if (StrUtil.isEmpty(columnName)) {
+            return null;
+        }
+        // 拼接条件
+        return new EqualsTo(MyBatisUtils.buildColumn(tableName, tableAlias, columnName), new LongValue(userId));
+    }
+
+    // ==================== 添加配置 ====================
+    public void addDataColumn(Class<? extends BaseDO> entityClass, String columnName) {
+        String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();
+        addDataColumn(tableName, columnName);
+    }
+
+
+    public void addDataColumn(String tableName, String columnName) {
+        if (CharSequenceUtil.isEmpty(tableName)) {
+            return;
+        }
+        DATA_COLUMNS.put(tableName, columnName);
+        TABLE_NAMES.add(tableName);
+    }
+
+    public void addUserColumn(Class<? extends BaseDO> entityClass, String columnName) {
+        String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();
+        addUserColumn(tableName, columnName);
+    }
+
+    public void addUserColumn(String tableName, String columnName) {
+        if (CharSequenceUtil.isEmpty(tableName)) {
+            return;
+        }
+        USER_COLUMNS.put(tableName, columnName);
+        TABLE_NAMES.add(tableName);
+    }
+}

+ 18 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/config/permission/CustomDataPermissionRuleCustomizer.java

@@ -0,0 +1,18 @@
+package com.citu.module.menduner.im.config.permission;
+
+/**
+ * @author Rayson
+ * @description CustomDataPermissionRuleCustomizer {@link CustomDataPermissionRule} 的自定义配置接口
+ **/
+@FunctionalInterface
+public interface CustomDataPermissionRuleCustomizer {
+
+    /**
+     * 自定义该权限规则
+     * 1. 调用 {@link CustomDataPermissionRule#addDataColumn(Class, String)} 方法,配置基于数据编号的过滤规则
+     * 2. 调用 {@link CustomDataPermissionRule#addUserColumn(Class, String)} 方法,配置基于用户编号的过滤规则
+     *
+     * @param rule 权限规则
+     */
+    void customize(CustomDataPermissionRule rule);
+}

+ 6 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/config/permission/package-info.java

@@ -0,0 +1,6 @@
+/**
+ * 基于企业的数据权限规则
+ *
+ * @author Rayson
+ */
+package com.citu.module.menduner.system.config.permission;

+ 41 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/controller/UserController.java

@@ -0,0 +1,41 @@
+package com.citu.module.menduner.im.controller;
+
+import com.citu.framework.common.pojo.CommonResult;
+import com.citu.module.menduner.im.controller.base.user.ImUserReqVo;
+import com.citu.module.menduner.im.controller.base.user.ImUserResoVO;
+import com.citu.module.menduner.im.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @eo.api-type http
+ * @eo.groupName 新门墩儿.聊天.用户信息获取
+ * @eo.path
+ */
+
+@RestController
+public class UserController {
+
+    UserService userService;
+
+    /**
+     * @eo.name 获取聊天秘钥信息
+     * @eo.url /im/user/get
+     * @eo.method post
+     * @eo.request-type json
+     * @param reqVo
+     * @return CommonResult
+     */
+    @PostMapping("/im/user/get")
+    public CommonResult<ImUserResoVO> getUser(@RequestBody ImUserReqVo reqVo){
+        return userService.getUser(reqVo);
+    }
+
+
+    @Autowired
+    public void setUserService(UserService userService) {
+        this.userService = userService;
+    }
+}

+ 14 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/controller/base/user/ImUserReqVo.java

@@ -0,0 +1,14 @@
+package com.citu.module.menduner.im.controller.base.user;
+
+public class ImUserReqVo {
+
+    private Long userId;
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+}

+ 53 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/controller/base/user/ImUserResoVO.java

@@ -0,0 +1,53 @@
+package com.citu.module.menduner.im.controller.base.user;
+
+public class ImUserResoVO {
+    private String uid;
+
+    private String token;
+
+    private String tcpUrl;
+
+    private String wsUrl;
+
+    private String wssUrl;
+
+    public String getUid() {
+        return uid;
+    }
+
+    public void setUid(String uid) {
+        this.uid = uid;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    public String getTcpUrl() {
+        return tcpUrl;
+    }
+
+    public void setTcpUrl(String tcpUrl) {
+        this.tcpUrl = tcpUrl;
+    }
+
+    public String getWsUrl() {
+        return wsUrl;
+    }
+
+    public void setWsUrl(String wsUrl) {
+        this.wsUrl = wsUrl;
+    }
+
+    public String getWssUrl() {
+        return wssUrl;
+    }
+
+    public void setWssUrl(String wssUrl) {
+        this.wssUrl = wssUrl;
+    }
+}

+ 56 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/controller/base/wukong/UserTokenReqVO.java

@@ -0,0 +1,56 @@
+package com.citu.module.menduner.im.controller.base.wukong;
+
+public class UserTokenReqVO {
+
+    /**
+     * 通信的用户唯一ID,可以随机uuid (建议自己服务端的用户唯一uid) (WuKongIMSDK需要)
+     */
+    private String uid;
+
+    /**
+     * 校验的token,随机uuid(建议使用自己服务端的用户的token)(WuKongIMSDK需要)
+     */
+    private String token;
+
+    /**
+     * 0.app 1.web (相同用户相同设备标记的主设备登录会互相踢,从设备将共存)
+     */
+    private Integer deviceFlag=1;
+
+    /**
+     * 设备等级 0.为从设备 1.为主设备
+     */
+    private Integer deviceLevel=1;
+
+    public String getUid() {
+        return uid;
+    }
+
+    public void setUid(String uid) {
+        this.uid = uid;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    public Integer getDeviceFlag() {
+        return deviceFlag;
+    }
+
+    public void setDeviceFlag(Integer deviceFlag) {
+        this.deviceFlag = deviceFlag;
+    }
+
+    public Integer getDeviceLevel() {
+        return deviceLevel;
+    }
+
+    public void setDeviceLevel(Integer deviceLevel) {
+        this.deviceLevel = deviceLevel;
+    }
+}

+ 22 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/controller/base/wukong/WukongApiResp.java

@@ -0,0 +1,22 @@
+package com.citu.module.menduner.im.controller.base.wukong;
+
+public class WukongApiResp {
+
+    private Integer status;
+
+    private Boolean statusBool;
+
+    public Boolean getStatusBool() {
+        return null!=status && 200 == status;
+    }
+
+
+
+    public Integer getStatus() {
+        return status;
+    }
+
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+}

+ 15 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/convert/UserConvert.java

@@ -0,0 +1,15 @@
+package com.citu.module.menduner.im.convert;
+
+import com.citu.module.menduner.im.controller.base.user.ImUserResoVO;
+import com.citu.module.menduner.im.dal.dataobject.UserDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+@Mapper
+public interface UserConvert {
+
+    UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
+
+
+    public ImUserResoVO userDOConvertUserResoVO(UserDO userDO);
+}

+ 69 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/dal/dataobject/UserDO.java

@@ -0,0 +1,69 @@
+package com.citu.module.menduner.im.dal.dataobject;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.time.LocalDateTime;
+
+@TableName("im_user")
+public class UserDO {
+
+    @TableId(type= IdType.INPUT)
+    private Long userId;
+
+
+
+
+
+    private String uid;
+
+
+    private String token;
+
+    private LocalDateTime createTime;
+
+    private LocalDateTime updateTime;
+
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getUid() {
+        return uid;
+    }
+
+    public void setUid(String uid) {
+        this.uid = uid;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    public LocalDateTime getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(LocalDateTime createTime) {
+        this.createTime = createTime;
+    }
+
+    public LocalDateTime getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(LocalDateTime updateTime) {
+        this.updateTime = updateTime;
+    }
+}

+ 10 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/dal/mysql/UserMapper.java

@@ -0,0 +1,10 @@
+package com.citu.module.menduner.im.dal.mysql;
+
+import com.citu.framework.mybatis.core.mapper.BaseMapperX;
+import com.citu.module.menduner.im.dal.dataobject.UserDO;
+import com.github.yulichang.base.MPJBaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface UserMapper extends BaseMapperX<UserDO> {
+}

+ 25 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/framework/datapermission/config/DataPermissionConfiguration.java

@@ -0,0 +1,25 @@
+package com.citu.module.menduner.im.framework.datapermission.config;
+
+
+import com.citu.framework.common.enums.UserTypeEnum;
+import com.citu.module.menduner.im.config.permission.CustomDataPermissionRuleCustomizer;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * menduner 模块的数据权限 Configuration
+ *
+ * @author Rayson
+ */
+@Configuration(proxyBeanMethods = false)
+public class DataPermissionConfiguration {
+
+
+    @Bean
+    public CustomDataPermissionRuleCustomizer customDataPermissionRuleCustomizer() {
+        return rule -> {
+
+        };
+    }
+}

+ 4 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/framework/datapermission/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * system 模块的数据权限配置
+ */
+package com.citu.module.menduner.im.framework.datapermission;

+ 14 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/framework/rpc/config/RpcConfiguration.java

@@ -0,0 +1,14 @@
+package com.citu.module.menduner.im.framework.rpc.config;
+
+import com.citu.module.menduner.im.service.wukong.WuKongApiService;
+import com.citu.module.system.api.logger.LoginLogApi;
+import com.citu.module.system.api.sms.SmsCodeApi;
+import com.citu.module.system.api.social.SocialClientApi;
+import com.citu.module.system.api.social.SocialUserApi;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration(proxyBeanMethods = false)
+@EnableFeignClients(clients = {WuKongApiService.class})
+public class RpcConfiguration {
+}

+ 4 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/framework/rpc/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * 占位
+ */
+package com.citu.module.menduner.im.framework.rpc;

+ 38 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/framework/security/config/SecurityConfiguration.java

@@ -0,0 +1,38 @@
+package com.citu.module.menduner.im.framework.security.config;
+
+import com.citu.framework.security.config.AuthorizeRequestsCustomizer;
+import com.citu.module.system.enums.ApiConstants;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
+
+/**
+ * menduner 模块的 Security 配置
+ */
+@Configuration("mendunerSecurityConfiguration")
+public class SecurityConfiguration {
+
+    @Bean("mendunerAuthorizeRequestsCustomizer")
+    public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
+        return new AuthorizeRequestsCustomizer() {
+
+            @Override
+            public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) {
+                // TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案
+                // Swagger 接口文档
+                registry.antMatchers("/v3/api-docs/**").permitAll() // 元数据
+                        .antMatchers("/swagger-ui.html").permitAll(); // Swagger UI
+                // Druid 监控
+                registry.antMatchers("/druid/**").anonymous();
+                // Spring Boot Actuator 的安全配置
+                registry.antMatchers("/actuator").anonymous()
+                        .antMatchers("/actuator/**").anonymous();
+                // RPC 服务的安全配置
+                registry.antMatchers(ApiConstants.PREFIX + "/**").permitAll();
+            }
+
+        };
+    }
+
+}

+ 4 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/framework/security/core/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * 占位
+ */
+package com.citu.module.menduner.im.framework.security.core;

+ 23 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/interceptor/WuKongWuKongApiInterceptor.java

@@ -0,0 +1,23 @@
+//package com.citu.module.menduner.im.interceptor;
+//
+//import com.alibaba.excel.util.StringUtils;
+//import com.citu.module.menduner.im.MendunerIMApplication;
+//import feign.RequestInterceptor;
+//import feign.RequestTemplate;
+//
+//public class WuKongWuKongApiInterceptor implements RequestInterceptor {
+//
+//    private String uri;
+//
+//
+//    @Override
+//    public void apply(RequestTemplate requestTemplate) {
+//        if(StringUtils.isBlank(uri)){
+//            uri =  MendunerIMApplication.applicationContext.getEnvironment().getProperty("im.wulong.url");
+//        }
+//        requestTemplate.uri(uri);
+//
+//
+//        System.out.println(requestTemplate);
+//    }
+//}

+ 4 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * menduner 招聘模块
+ */
+package com.citu.module.menduner.system;

+ 9 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/service/UserService.java

@@ -0,0 +1,9 @@
+package com.citu.module.menduner.im.service;
+
+import com.citu.framework.common.pojo.CommonResult;
+import com.citu.module.menduner.im.controller.base.user.ImUserReqVo;
+import com.citu.module.menduner.im.controller.base.user.ImUserResoVO;
+
+public interface UserService {
+    CommonResult<ImUserResoVO> getUser(ImUserReqVo reqVo);
+}

+ 68 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/service/UserServiceImpl.java

@@ -0,0 +1,68 @@
+package com.citu.module.menduner.im.service;
+
+import cn.hutool.core.util.IdUtil;
+import com.citu.framework.common.exception.enums.GlobalErrorCodeConstants;
+import com.citu.framework.common.pojo.CommonResult;
+import com.citu.framework.security.core.util.SecurityFrameworkUtils;
+import com.citu.module.menduner.im.controller.base.user.ImUserReqVo;
+import com.citu.module.menduner.im.controller.base.user.ImUserResoVO;
+import com.citu.module.menduner.im.convert.UserConvert;
+import com.citu.module.menduner.im.dal.dataobject.UserDO;
+import com.citu.module.menduner.im.dal.mysql.UserMapper;
+import com.citu.module.menduner.im.service.wukong.WuKongUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+
+@Service
+public class UserServiceImpl implements UserService{
+
+    UserMapper mapper;
+
+    WuKongUserService wuKongUserService;
+
+
+    @Override
+    @Transactional()
+    public CommonResult<ImUserResoVO> getUser(ImUserReqVo reqVo) {
+        UserDO userDO = mapper.selectById(reqVo.getUserId());
+        Long userLoginId = SecurityFrameworkUtils.getLoginUser().getId();
+        if(null == userDO){
+            // 开始注册
+            userDO =new UserDO();
+            userDO.setCreateTime(LocalDateTime.now());
+            userDO.setUpdateTime(LocalDateTime.now());
+            userDO.setUserId(reqVo.getUserId());
+            userDO.setToken(IdUtil.fastSimpleUUID());
+            userDO.setUid(IdUtil.fastSimpleUUID());
+            mapper.insert(userDO);
+            CommonResult<Boolean> booleanCommonResult = wuKongUserService.userToken(userDO.getUid(), userDO.getToken());
+            if(!booleanCommonResult.isSuccess()){
+                return CommonResult.error(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR);
+            }
+        }
+        if(userLoginId!=reqVo.getUserId()){
+            userDO.setToken(null);
+        }
+        ImUserResoVO imUserResoVO = UserConvert.INSTANCE.userDOConvertUserResoVO(userDO);
+        imUserResoVO.setWsUrl(wuKongUserService.getWsUrl(userDO.getUid()));
+        imUserResoVO.setTcpUrl(wuKongUserService.getTcpUrl(userDO.getUid()));
+        imUserResoVO.setWssUrl(wuKongUserService.getWssUrl(userDO.getUid()));
+        return CommonResult.success(imUserResoVO);
+    }
+
+
+
+
+    @Autowired
+    public void setWuKongUserService(WuKongUserService wuKongUserService) {
+        this.wuKongUserService = wuKongUserService;
+    }
+
+    @Autowired
+    public void setMapper(UserMapper mapper) {
+        this.mapper = mapper;
+    }
+}

+ 22 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/service/wukong/WuKongApiService.java

@@ -0,0 +1,22 @@
+package com.citu.module.menduner.im.service.wukong;
+
+
+import com.citu.module.menduner.im.controller.base.wukong.UserTokenReqVO;
+import com.citu.module.menduner.im.controller.base.wukong.WukongApiResp;
+//import com.citu.module.menduner.im.interceptor.WuKongWuKongApiInterceptor;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import java.util.List;
+import java.util.Map;
+
+@FeignClient(url = "${im.wukong.url:127.0.0.1:5001}",name = "wulongApiService" )
+public interface WuKongApiService {
+
+    @PostMapping("/user/token")
+    public WukongApiResp userToken(@RequestBody UserTokenReqVO reqVO);
+
+    @PostMapping("/user/onlinestatus")
+    public  List<String> userOnlinestatus(@RequestBody List<String> uids );
+}

+ 20 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/service/wukong/WuKongUserService.java

@@ -0,0 +1,20 @@
+package com.citu.module.menduner.im.service.wukong;
+
+import com.citu.framework.common.pojo.CommonResult;
+
+import java.util.List;
+
+public interface WuKongUserService {
+
+    public CommonResult<Boolean> userToken(String uid,String token);
+
+
+    public CommonResult<List<String>> userOnlinestatus(List<String> uids);
+
+
+    String getWsUrl(String uid);
+
+    String getTcpUrl(String uid);
+
+    String getWssUrl(String uid);
+}

+ 58 - 0
menduner/menduner-im-biz/src/main/java/com/citu/module/menduner/im/service/wukong/WuKongUserServiceImpl.java

@@ -0,0 +1,58 @@
+package com.citu.module.menduner.im.service.wukong;
+
+import com.citu.framework.common.pojo.CommonResult;
+import com.citu.module.menduner.im.config.WuKongConfig;
+import com.citu.module.menduner.im.controller.base.wukong.UserTokenReqVO;
+import com.citu.module.menduner.im.controller.base.wukong.WukongApiResp;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class WuKongUserServiceImpl implements WuKongUserService {
+    WuKongApiService wuKongApiService;
+
+
+    WuKongConfig config;
+
+
+    public CommonResult<Boolean> userToken(String uid,String token){
+        UserTokenReqVO reqVO  =new UserTokenReqVO();
+        reqVO.setUid(uid);
+        reqVO.setToken(token);
+        WukongApiResp wukongApiResp = wuKongApiService.userToken(reqVO);
+        return CommonResult.success(wukongApiResp.getStatusBool());
+    }
+
+    @Override
+    public CommonResult<List<String>> userOnlinestatus(List<String> uids) {
+        return CommonResult.success(wuKongApiService.userOnlinestatus(uids));
+    }
+
+    @Override
+    public String getWsUrl(String uid) {
+        return config.getWsUrl();
+    }
+
+    @Override
+    public String getTcpUrl(String uid) {
+        return config.getTcpUrl();
+    }
+
+    @Override
+    public String getWssUrl(String uid) {
+        return config.getWssUrl();
+    }
+
+    @Autowired
+    public void setConfig( WuKongConfig wuKongConfig) {
+        this.config = wuKongConfig;
+    }
+
+    @Autowired
+    public void setWuKongApiService(WuKongApiService wuKongApiService) {
+        this.wuKongApiService = wuKongApiService;
+    }
+}

+ 177 - 0
menduner/menduner-im-biz/src/main/resources/application-dev.yaml

@@ -0,0 +1,177 @@
+--- #################### 数据库相关配置 ####################
+spring:
+  # 数据源配置项
+  autoconfigure:
+    exclude:
+      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源
+  datasource:
+    druid: # Druid 【监控】相关的全局配置
+      web-stat-filter:
+        enabled: true
+      stat-view-servlet:
+        enabled: true
+        allow: # 设置白名单,不填则允许所有访问
+        url-pattern: /druid/*
+        login-username: # 控制台管理用户名和密码
+        login-password:
+      filter:
+        stat:
+          enabled: true
+          log-slow-sql: true # 慢 SQL 记录
+          slow-sql-millis: 100
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+    dynamic: # 多数据源配置
+      druid: # Druid 【连接池】相关的全局配置
+        initial-size: 5 # 初始连接数
+        min-idle: 10 # 最小连接池数量
+        max-active: 20 # 最大连接池数量
+        max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
+        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
+        min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
+        max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
+        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+        test-while-idle: true
+        test-on-borrow: false
+        test-on-return: false
+      primary: master
+      datasource:
+        master:
+          name: ruoyi-vue-pro
+          url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT&nullCatalogMeansCurrent=true
+          driver-class-name: com.mysql.jdbc.Driver
+          username: root
+          password: 3WLiVUBEwTbvAfsh
+        slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改
+          name: ruoyi-vue-pro
+          url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT&nullCatalogMeansCurrent=true
+          driver-class-name: com.mysql.jdbc.Driver
+          username: root
+          password: 3WLiVUBEwTbvAfsh
+
+  # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
+  redis:
+    host: 400-infra.server.iocoder.cn # 地址
+    port: 6379 # 端口
+    database: 1 # 数据库索引
+#    password: 123456 # 密码,建议生产环境开启
+
+--- #################### MQ 消息队列相关配置 ####################
+
+# rocketmq 配置项,对应 RocketMQProperties 配置类
+rocketmq:
+  name-server: 127.0.0.1:9876 # RocketMQ Namesrv
+
+spring:
+  # RabbitMQ 配置项,对应 RabbitProperties 配置类
+  rabbitmq:
+    host: 127.0.0.1 # RabbitMQ 服务的地址
+    port: 5672 # RabbitMQ 服务的端口
+    username: guest # RabbitMQ 服务的账号
+    password: guest # RabbitMQ 服务的密码
+  # Kafka 配置项,对应 KafkaProperties 配置类
+  kafka:
+    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔
+  # elasticsearch 配置项
+  elasticsearch:
+    uris: http://192.168.3.80:9200
+
+--- #################### 定时任务相关配置 ####################
+xxl:
+  job:
+    admin:
+      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址
+
+--- #################### 服务保障相关配置 ####################
+
+# Lock4j 配置项
+lock4j:
+  acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
+  expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
+
+--- #################### 监控相关配置 ####################
+
+# Actuator 监控端点的配置项
+management:
+  endpoints:
+    web:
+      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
+      exposure:
+        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
+
+# Spring Boot Admin 配置项
+spring:
+  boot:
+    admin:
+      # Spring Boot Admin Client 客户端的相关配置
+      client:
+        instance:
+          service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]
+      # Spring Boot Admin Server 服务端的相关配置
+      context-path: /admin # 配置 Spring
+
+--- #################### 微信公众号、小程序相关配置 ####################
+wx:
+  mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档
+    #    app-id: wx041349c6f39b268b
+    #    secret: 5abee519483bc9f8cb37ce280e814bd0
+    app-id: wx5b23ba7a5589ecbb # 测试号
+    secret: 2a7b3b20c537e52e74afd395eb85f61f
+    # 存储配置,解决 AccessToken 的跨节点的共享
+    config-storage:
+      type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
+      key-prefix: wx # Redis Key 的前缀
+      http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
+  miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
+    #    appid: wx62056c0d5e8db250
+    #    secret: 333ae72f41552af1e998fe1f54e1584a
+    appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
+    secret: 6f270509224a7ae1296bbf1c8cb97aed
+    config-storage:
+      type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
+      key-prefix: wa # Redis Key 的前缀
+      http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
+
+--- #################### 芋道相关配置 ####################
+
+# 芋道配置项,设置当前项目所有自定义的配置
+citu:
+  xss:
+    enable: false
+    exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系
+      - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
+      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
+  pay:
+    pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
+    pay-return-url: http://niubi.natapp1.cc/api/pay/order/return
+    refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify
+  demo: true # 开启演示模式
+
+justauth:
+  enabled: true
+  type:
+    DINGTALK: # 钉钉
+      client-id: dingvrnreaje3yqvzhxg
+      client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI
+      ignore-check-redirect-uri: true
+    WECHAT_ENTERPRISE: # 企业微信
+      client-id: wwd411c69a39ad2e54
+      client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw
+      agent-id: 1000004
+      ignore-check-redirect-uri: true
+    # noinspection SpringBootApplicationYaml
+    WECHAT_MINI_APP: # 微信小程序
+      client-id: ${wx.miniapp.appid}
+      client-secret: ${wx.miniapp.secret}
+      ignore-check-redirect-uri: true
+      ignore-check-state: true # 微信小程序,不会使用到 state,所以不进行校验
+    WECHAT_MP: # 微信公众号
+      client-id: ${wx.mp.app-id}
+      client-secret: ${wx.mp.secret}
+      ignore-check-redirect-uri: true
+  cache:
+    type: REDIS
+    prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE::
+    timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟

+ 205 - 0
menduner/menduner-im-biz/src/main/resources/application-local.yaml

@@ -0,0 +1,205 @@
+--- #################### 数据库相关配置 ####################
+spring:
+  # 数据源配置项
+  autoconfigure:
+    exclude:
+      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源
+      - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置
+  datasource:
+    druid: # Druid 【监控】相关的全局配置
+      web-stat-filter:
+        enabled: true
+      stat-view-servlet:
+        enabled: true
+        allow: # 设置白名单,不填则允许所有访问
+        url-pattern: /druid/*
+        login-username: # 控制台管理用户名和密码
+        login-password:
+      filter:
+        stat:
+          enabled: true
+          log-slow-sql: true # 慢 SQL 记录
+          slow-sql-millis: 100
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+    dynamic: # 多数据源配置
+      druid: # Druid 【连接池】相关的全局配置
+        initial-size: 1 # 初始连接数
+        min-idle: 1 # 最小连接池数量
+        max-active: 20 # 最大连接池数量
+        max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
+        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
+        min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
+        max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
+        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+        test-while-idle: true
+        test-on-borrow: false
+        test-on-return: false
+      primary: master
+      datasource:
+        master:
+          name: citu
+          url: jdbc:mysql://192.168.3.80:3306/${spring.datasource.dynamic.datasource.master.name}?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
+          #          url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
+          #          url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.master.name} # PostgreSQL 连接的示例
+          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
+          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.master.name} # SQLServer 连接的示例
+          username: root
+          password: 123456
+        #          username: sa
+        #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
+        slave: # 模拟从库,可根据自己需要修改
+          name: citu
+          url: jdbc:mysql://192.168.3.80:3306/${spring.datasource.dynamic.datasource.slave.name}?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
+          #          url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
+          #          url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
+          #          url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
+          #          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.slave.name} # SQLServer 连接的示例
+          username: root
+          password: 123456
+  #          username: sa
+  #          password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
+
+  # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
+  redis:
+    host: 192.168.3.80 # 地址
+    port: 6379 # 端口
+    database: 0 # 数据库索引
+#    password: 123456 # 密码,建议生产环境开启
+
+--- #################### MQ 消息队列相关配置 ####################
+
+# rocketmq 配置项,对应 RocketMQProperties 配置类
+rocketmq:
+  name-server: 127.0.0.1:9876 # RocketMQ Namesrv
+
+spring:
+  # RabbitMQ 配置项,对应 RabbitProperties 配置类
+  rabbitmq:
+    host: 127.0.0.1 # RabbitMQ 服务的地址
+    port: 5672 # RabbitMQ 服务的端口
+    username: guest # RabbitMQ 服务的账号
+    password: guest # RabbitMQ 服务的密码
+  # Kafka 配置项,对应 KafkaProperties 配置类
+  kafka:
+    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔
+
+--- #################### 定时任务相关配置 ####################
+
+xxl:
+  job:
+    enabled: false # 是否开启调度中心,默认为 true 开启
+    admin:
+      addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址
+
+--- #################### 服务保障相关配置 ####################
+
+# Lock4j 配置项
+lock4j:
+  acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
+  expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
+
+--- #################### 监控相关配置 ####################
+
+# Actuator 监控端点的配置项
+management:
+  endpoints:
+    web:
+      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
+      exposure:
+        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
+
+# Spring Boot Admin 配置项
+spring:
+  boot:
+    admin:
+      # Spring Boot Admin Client 客户端的相关配置
+      client:
+        instance:
+          service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]
+
+# 日志文件配置
+logging:
+  level:
+    # 配置自己写的 MyBatis Mapper 打印日志
+    com.citu.module.system.dal.mysql: debug
+    com.citu.module.system.dal.mysql.sensitiveword.SensitiveWordMapper: INFO # 配置 SensitiveWordMapper 的日志级别为 info
+    com.citu.module.system.dal.mysql.sms.SmsChannelMapper: INFO # 配置 SmsChannelMapper 的日志级别为 info
+
+--- #################### 微信公众号、小程序相关配置 ####################
+wx:
+  mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档
+    #    app-id: wx041349c6f39b268b # 测试号(牛希尧提供的)
+    #    secret: 5abee519483bc9f8cb37ce280e814bd0
+    app-id: wx5b23ba7a5589ecbb # 测试号(自己的)
+    secret: 2a7b3b20c537e52e74afd395eb85f61f
+    #    app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的)
+    #    secret: bd4f9fab889591b62aeac0d7b8d8b4a0
+    # 存储配置,解决 AccessToken 的跨节点的共享
+    config-storage:
+      type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
+      key-prefix: wx # Redis Key 的前缀
+      http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
+  miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
+    #    appid: wx62056c0d5e8db250 # 测试号(牛希尧提供的)
+    #    secret: 333ae72f41552af1e998fe1f54e1584a
+    appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
+    secret: 6f270509224a7ae1296bbf1c8cb97aed
+    #    appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的)
+    #    secret: 4a1a04e07f6a4a0751b39c3064a92c8b
+    config-storage:
+      type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
+      key-prefix: wa # Redis Key 的前缀
+      http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
+
+--- #################### 芋道相关配置 ####################
+
+# 芋道配置项,设置当前项目所有自定义的配置
+citu:
+  env: # 多环境的配置项
+    tag: ${HOSTNAME}
+  captcha:
+    enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试
+  security:
+    mock-enable: true
+  xss:
+    enable: false
+    exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系
+      - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
+      - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
+  pay:
+    pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
+    pay-return-url: http://niubi.natapp1.cc/api/pay/order/return
+    refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify
+  access-log: # 访问日志的配置项
+    enable: false
+  demo: false # 关闭演示模式
+
+justauth:
+  enabled: true
+  type:
+    DINGTALK: # 钉钉
+      client-id: dingvrnreaje3yqvzhxg
+      client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI
+      ignore-check-redirect-uri: true
+    WECHAT_ENTERPRISE: # 企业微信
+      client-id: wwd411c69a39ad2e54
+      client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw
+      agent-id: 1000004
+      ignore-check-redirect-uri: true
+    # noinspection SpringBootApplicationYaml
+    WECHAT_MINI_APP: # 微信小程序
+      client-id: ${wx.miniapp.appid}
+      client-secret: ${wx.miniapp.secret}
+      ignore-check-redirect-uri: true
+      ignore-check-state: true # 微信小程序,不会使用到 state,所以不进行校验
+    WECHAT_MP: # 微信公众号
+      client-id: ${wx.mp.app-id}
+      client-secret: ${wx.mp.secret}
+      ignore-check-redirect-uri: true
+  cache:
+    type: REDIS
+    prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE::
+    timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟

+ 146 - 0
menduner/menduner-im-biz/src/main/resources/application.yaml

@@ -0,0 +1,146 @@
+spring:
+  main:
+    allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。
+    allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务
+
+  # 国际化
+  messages:
+    # 默认名称
+    basename: i18n/messages
+    encoding: UTF-8
+    # 找不到对应区域的语言时,是否回退到系统区域的语言,默认 true
+    fallback-to-system-locale: true
+    # 找不到code时,是否直接返回code值,而不是抛异常,默认false,抛异常
+    use-code-as-default-message: true
+    # 是否始终使用MessageFormat格式化国际化消息,即使没有国际化消息参数,默认false
+    always-use-message-format: false
+    # 加载国际化资源后的过期时间,不设置不过期,单位秒
+    cache-duration:
+
+  # Servlet 配置
+  servlet:
+    # 文件上传相关配置项
+    multipart:
+      max-file-size: 16MB # 单个文件大小
+      max-request-size: 32MB # 设置总上传的文件大小
+  mvc:
+    pathmatch:
+      matching-strategy: ANT_PATH_MATCHER # 解决 SpringFox 与 SpringBoot 2.6.x 不兼容的问题,参见 SpringFoxHandlerProviderBeanPostProcessor 类
+
+  # Jackson 配置项
+  jackson:
+    serialization:
+      write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳
+      write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401
+      write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳
+      fail-on-empty-beans: false # 允许序列化无属性的 Bean
+
+  # Cache 配置项
+  cache:
+    type: REDIS
+    redis:
+      time-to-live: 1h # 设置过期时间为 1 小时
+
+--- #################### 接口文档配置 ####################
+
+springdoc:
+  api-docs:
+    enabled: true # 1. 是否开启 Swagger 接文档的元数据
+    path: /v3/api-docs
+  swagger-ui:
+    enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面
+    path: /swagger-ui.html
+    tags-sorter: alpha
+    operations-sorter: alpha
+  default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档
+
+knife4j:
+  enable: true # 2.2 是否开启 Swagger 文档的 Knife4j UI 界面
+  setting:
+    language: zh_cn
+
+# MyBatis Plus 的配置项
+mybatis-plus:
+  configuration:
+    map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印日志
+  global-config:
+    db-config:
+      id-type: ASSIGN_ID # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
+      #      id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库
+      #      id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
+      #      id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
+      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
+      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
+    banner: true # 关闭控制台的 Banner 打印
+  type-aliases-package: ${citu.info.base-package}.dal.dataobject
+  encryptor:
+    password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成
+
+mybatis-plus-join:
+  banner: true # 关闭控制台的 Banner 打印
+
+# Spring Data Redis 配置
+spring:
+  data:
+    redis:
+      repositories:
+        enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度
+
+# VO 转换(数据翻译)相关
+easy-trans:
+  is-enable-global: true # 启用全局翻译(拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置,或通过 @IgnoreTrans 忽略某个接口
+  is-enable-cloud: false # 禁用 TransType.RPC 微服务模式
+
+--- #################### RPC 远程调用相关配置 ####################
+
+--- #################### MQ 消息队列相关配置 ####################
+rocketmq:
+  producer:
+    #生产者分组
+    group: ${spring.application.name}_PRODUCER
+
+--- #################### 定时任务相关配置 ####################
+
+xxl:
+  job:
+    executor:
+      appname: ${spring.application.name} # 执行器 AppName
+      logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径
+    accessToken: default_token # 执行器通讯TOKEN
+
+--- #################### 芋道相关配置 ####################
+
+citu:
+  info:
+    version: 1.0.0
+    base-package: com.citu.module.menduner.im
+  web:
+    admin-ui:
+      url: http://192.168.3.80 # Admin 管理后台 UI 的地址
+  swagger:
+    title: 管理后台
+    description: 提供管理员管理的所有功能
+    version: ${citu.info.version}
+    base-package: ${citu.info.base-package}
+  # 是否开启国际化
+  i18n:
+    enable: true
+  # db相关
+  db:
+    # 是否开启自动切换数据源 主写从读
+    transform:
+      enable: true
+  captcha:
+    enable: false # 验证码的开关,默认为 true;
+  error-code: # 错误码相关配置项
+    enable: false
+    constants-class-list:
+      - com.citu.module.menduner.system.enums.ErrorCodeConstants
+  tenant: # 多租户相关配置项
+    enable: true
+    ignore-urls:
+    ignore-tables:
+
+debug: false
+

+ 23 - 0
menduner/menduner-im-biz/src/main/resources/bootstrap-local.yaml

@@ -0,0 +1,23 @@
+--- #################### 注册中心相关配置 ####################
+
+spring:
+  cloud:
+    nacos:
+      server-addr: ${nacosHost:127.0.0.1:8848}
+      discovery:
+        namespace: dev # 命名空间。这里使用 dev 开发环境
+        metadata:
+          version: 1.0.0 # 服务实例的版本号,可用于灰度发布
+
+--- #################### 配置中心相关配置 ####################
+
+spring:
+  cloud:
+    nacos:
+      # Nacos Config 配置项,对应 NacosConfigProperties 配置属性类
+      config:
+        server-addr: ${nacosHost:127.0.0.1:8848} # Nacos 服务器地址
+        namespace: dev # 命名空间 dev 的ID,不能直接使用 dev 名称。创建命名空间的时候需要指定ID为 dev,这里使用 dev 开发环境
+        group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
+        name: ${spring.application.name} # 使用的 Nacos 配置集的 dataId,默认为 spring.application.name
+        file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties

+ 14 - 0
menduner/menduner-im-biz/src/main/resources/bootstrap.yaml

@@ -0,0 +1,14 @@
+spring:
+  application:
+    name: im-server
+
+  profiles:
+    active: local
+
+server:
+  port: 48201
+
+# 日志文件配置。注意,如果 logging.file.name 不放在 bootstrap.yaml 配置文件,而是放在 application.yaml 中,会导致出现 LOG_FILE_IS_UNDEFINED 文件
+logging:
+  file:
+    name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径

+ 0 - 0
menduner/menduner-im-biz/src/main/resources/i18n/messages.properties


+ 244 - 0
menduner/menduner-im-biz/src/main/resources/i18n/messages_en_US.properties

@@ -0,0 +1,244 @@
+# ========== 系统 ==========
+0=Successful
+# ========== 客户端错误段 ==========
+400=incorrect request parameters
+401=account not logged in
+403=no permission for this operation
+404=request not found
+405=incorrect request method
+423=request failed, please try again later # concurrent request, not allowed
+429=request too frequent, please try again later
+
+# ========== 服务端错误段 ==========
+500=system abnormality
+501=function not implemented/not enabled
+502=incorrect configuration item
+# ========== 自定义错误段 ==========
+900=duplicate request, please try again later # duplicate request
+901=demonstration mode, write operation prohibited
+999=unknown error
+# ========== 公共 1_099_000_000 ==========
+1_099_000_001=Status cannot be empty
+1_099_000_002=The modification status must be {value}
+1_099_000_003=Display order cannot be empty
+1_099_000_004=Superior ID cannot be empty
+1_099_000_005=id cannot be empty
+1_099_000_006=User ID cannot be empty
+1_099_000_007=Enterprise ID cannot be empty
+# ========== 人才信息-人才档案 1_100_001_000 ==========
+1_100_001_001=Personal Profile Does Not Exist
+1_100_001_002=Personal advantage cannot be empty
+1_100_001_003=Real name cannot be empty
+1_100_001_004=Contact phone number cannot be empty
+1_100_001_005=Birthdate cannot be empty
+1_100_001_006=Work experience cannot be empty
+1_100_001_007=Education cannot be empty
+1_100_001_008=Job search status cannot be empty
+1_100_001_009=City cannot be empty
+1_100_001_010=Frequently used email cannot be empty
+1_100_001_011=Please fill in your personal basic information first
+# ========== 行业信息 1_100_002_000 ==========
+1_100_002_001=Industry information does not exist
+# ========== 职位信息 1_100_003_000 ==========
+1_100_003_001=Position information does not exist
+1_100_003_002=Position type cannot be empty
+1_100_003_003=Position name cannot be empty
+# ========== 地区 1_100_004_000 ==========
+1_100_004_001=Region does not exist
+1_100_004_002=Popular regions already exist, please do not add them again
+1_100_004_003=Work area cannot be empty
+# ========== 简历附件 1_100_005_000 ==========
+1_100_005_001=Resume attachment does not exist
+1_100_005_002=Upload a maximum of 5 resume attachments
+# ========== 教育经历 1_100_006_000 ==========
+1_100_006_001=Educational experience does not exist
+1_100_006_002=No education selected
+1_100_006_003=No educational system type selected
+1_100_006_004=No school start date selected
+1_100_006_005=No end date selected for school
+# ========== 求职意向 1_100_007_000 ==========
+1_100_007_001=Job search intention does not exist
+1_100_007_002=No expected position selected
+1_100_007_003=No expected industry selected
+1_100_007_004=No job search type selected
+1_100_007_005=No minimum salary requirement filled in
+1_100_007_006=No maximum salary requirement filled in
+1_100_007_007=No work city selected
+# ========== 专业 1_100_008_000 ==========
+1_100_008_001=Major does not exist
+# ========== 职位标签 1_100_009_000 ==========
+1_100_009_001=Position tag does not exist
+1_100_009_002=Position tag cannot be empty
+# ========== 学校 1_100_010_000 ==========
+1_100_010_001=School does not exist
+# ========== 人才证书 1_100_011_000 ==========
+1_100_011_001=Personnel certificate does not exist
+1_100_011_002=Certificate Chinese name cannot be empty
+1_100_011_003=Certificate English name cannot be empty
+# ========== 工作经历 1_100_012_000 ==========
+1_100_012_001=Work experience does not exist
+1_100_012_002=No start date selected for employment
+1_100_027_003=No end time selected for employment
+# ========== 人才技能 1_100_013_000 ==========
+1_100_013_001=Personnel skill does not exist
+1_100_013_002=The skill already exists
+# ========== 证书 1_100_014_000 ==========
+1_100_014_001=Certificate does not exist
+# ========== 访问量 1_100_015_000 ==========
+1_100_015_001=The traffic does not exist
+1_100_015_002=Business type does not exist
+1_100_015_003=Business ID does not exist
+1_100_015_004=Access date does not exist
+# ========== 用户登录 1_100_016_000 ==========
+1_100_016_001=User does not exist
+1_100_016_002=Unregistered user with phone number
+1_100_016_003=Modifying phone failed, the phone number ({}) has already been used
+1_100_016_004=User account already exists
+1_100_016_005=Phone number already exists
+1_100_016_006=Email already exists
+1_100_016_007=Phone number cannot be empty
+1_100_016_008=Importing user data cannot be empty!
+1_100_016_009=User password verification failed
+1_100_016_010=The user named [{}] has been disabled
+1_100_016_011=User creation failed due to exceeding the maximum tenant quota ({}) for tenants!
+1_100_016_012=The new password cannot be empty
+1_100_016_013=Phone verification code cannot be empty
+1_100_016_014=The length of the mobile verification code is 4-6 digits
+1_100_016_015=The length of the phone number is 8-11 digits
+1_100_016_016=Phone verification codes must all be numbers
+1_100_016_017=Password length is 4-16 bits
+1_100_016_018=User ID cannot be empty
+1_100_016_019=Password cannot be empty
+1_100_016_020=User account cannot be empty
+# ========== AUTH 模块 1_100_017_000 ==========
+1_100_017_001=Login failed, incorrect account and password
+1_100_017_002=Login failed, account disabled
+1_100_017_003=Login failed, unable to resolve third-party login information
+1_100_017_004=The phone number has already been used
+1_100_017_005=Incorrect verification code, reason: {}
+1_100_017_006=Account not bound, needs to be bound
+1_100_017_007=Token has expired
+1_100_017_008=Phone number does not exist
+1_100_017_009=Sending scene cannot be empty
+1_100_017_010=Phone code cannot be empty
+1_100_017_011=Login code cannot be empty
+1_100_017_012=State cannot be empty
+1_100_017_013=Authorization code cannot be empty
+1_100_017_014=The type of social platform cannot be empty
+1_100_017_015=The verification code cannot be empty
+1_100_017_016=No enterprise selected
+1_100_017_017=Unregistered user, please register individual user first before applying for enterprise user
+1_100_017_018=Access token cannot be empty
+# ========== 角色模块 1_100_018_000 ==========
+1_100_018_001=Character does not exist
+1_100_018_002=A role named [{}] already exists
+1_100_018_003=A role with code [{}] already exists
+1_100_018_004=Cannot operate on roles with built-in system types
+1_100_018_005=The character named [{}] has been disabled
+1_100_018_006=Encoding [{}] cannot be used
+1_100_018_007=Role number cannot be empty
+1_100_018_008=Role name cannot be empty
+1_100_018_009=The length of the character name cannot exceed 30 characters
+1_100_018_010=Character flag cannot be empty
+1_100_018_011=The character flag cannot exceed 100 characters in length
+1_100_018_012=Data range cannot be empty
+1_100_018_013=The data range must be {value}
+# ========== 企业信息 1_100_019_000 ==========
+1_100_019_001=Enterprise information does not exist
+1_100_019_002=Enterprise code cannot be empty
+1_100_019_003=No company name filled in
+1_100_019_004=Enterprise alias not filled in
+1_100_019_005=Company profile not filled in
+1_100_019_006=No industry selected
+1_100_019_007=No financing stage selected
+1_100_019_008=No personnel size selected
+1_100_019_009=No work hours filled in
+# ========== 企业工商信息  1_100_020_001 ==========
+1_100_020_001=Enterprise business information does not exist
+1_100_020_002=No company name filled in
+1_100_020_003=Unified social credit code not filled in
+# ========== 企业注册申请 1_100_021_000 ==========
+1_100_021_001=The enterprise registration application does not exist
+1_100_021_002=Enterprise registration application has been approved
+1_100_021_003=The enterprise registration application has been approved and cannot be rejected
+1_100_021_004=Enterprise name cannot be empty
+1_100_021_005=Contact number cannot be empty
+1_100_021_006=Contact email cannot be empty
+1_100_021_007=Business license not uploaded
+1_100_021_008=Unified social credit code cannot be empty
+1_100_021_009=Enterprise registered
+1_100_021_010=The enterprise has applied for review, please do not resubmit
+# ========== 企业地址 1_100_022_001 ==========
+1_100_022_001=The company address does not exist
+# ========== 企业登录用户 1_100_023_000 ==========
+1_100_023_001=Enterprise login user does not exist
+1_100_023_002=Name cannot be empty
+1_100_023_003=The account has been disabled
+1_100_023_004=This account is an administrator account and cannot be operated
+# ========== 企业岗位信息 1_100_024_000 ==========
+1_100_024_001=Enterprise position information does not exist
+1_100_024_002=The Chinese name of the position cannot be empty
+1_100_024_003=Position English cannot be empty
+# ========== 招聘职位 1_100_025_000 ==========
+1_100_025_001=Recruitment position does not exist
+1_100_025_002=Recruitment position ID cannot be empty
+1_100_025_003=Position name cannot be empty
+1_100_025_004=Recruitment type cannot be empty
+1_100_025_005=The publishing user ID cannot be empty
+1_100_025_006=Work experience cannot be empty
+1_100_025_007=Educational requirements cannot be empty
+# ========== 企业招聘者浏览 1_100_026_000 ==========
+1_100_026_001=Enterprise recruiter browsing does not exist
+# ========== 人才-企业关注订阅 1_100_027_000 ==========
+1_100_027_001=Enterprise follow subscription does not exist
+1_100_027_002=Enterprise has followed and subscribed, please do not click again
+# ========== 人才-浏览记录 1_100_028_000 ==========
+1_100_028_001=Browsing history does not exist
+# ========== 人才-招聘职位收藏 1_100_029_000 ==========
+1_100_029_001=Recruitment position collection does not exist
+1_100_029_002=Position has been saved, please do not click again
+# ========== 培训经历 1_100_030_000 ==========
+1_100_030_001=Training experience does not exist
+1_100_030_002=Training start time cannot be empty
+1_100_030_003=Training end time cannot be empty
+1_100_030_004=Training institution name cannot be empty
+1_100_030_005=Training course cannot be empty
+# ========== 项目经历 1_100_031_000 ==========
+1_100_031_001=Project experience does not exist
+1_100_031_002=Project name not filled in
+1_100_031_003=Project start time cannot be empty
+1_100_031_004=Project end time cannot be empty
+# ========== 职业技能 1_100_032_000 ==========
+1_100_032_001=Vocational skill does not exist
+1_100_032_002=There are sub profession skills that cannot be deleted
+1_100_032_003=Parent level vocational skill does not exist
+1_100_032_004=Unable to set oneself as a parent vocational skill
+1_100_032_005=There is already a vocational skill with the Chinese name for this skill
+1_100_032_006=Cannot set one's own child Skill as the parent Skill
+# ========== 企业用户申请 1_100_034_000 ==========
+1_100_034_001=Enterprise user application does not exist
+1_100_034_002=User application has been approved
+1_100_034_003=The user's application has been approved and cannot be rejected
+1_100_034_005=Applied for review, please do not resubmit
+# ========== 招聘职位简历投递 1_100_034_000 ==========
+1_100_035_001=Recruitment position resume submission does not exist
+1_100_035_002=The position has been submitted
+1_100_035_003=No resume attachment selected
+1_100_035_004=Recruitment position ID cannot be empty
+1_100_035_005=This position is currently not allowed for delivery
+1_100_035_006=Recruitment for this position has been suspended
+# ========== 众聘-简历投递 1_100_036_000 ==========
+1_100_036_001=The submitted resume does not exist
+1_100_036_002=The posted position ID cannot be empty
+1_100_036_003=The user ID for posting positions cannot be empty
+1_100_036_004=Referee ID cannot be empty
+1_100_036_005=The delivery user ID cannot be empty
+1_100_036_006=The name of the sender cannot be empty
+1_100_036_007=The contact phone number of the deliveryman cannot be empty
+1_100_036_008=Resume attachment cannot be empty
+# ========== 众聘-佣金比例 1_100_037_000 ==========
+1_100_037_001=Commission ratio does not exist
+1_100_037_002=Commission ratio cannot be empty
+1_100_037_003=Commission ratio cannot be less than 0
+# ========== 用户账户 1_100_038_000 ==========
+1_100_038_001=User account does not exist

+ 247 - 0
menduner/menduner-im-biz/src/main/resources/i18n/messages_zh_CN.properties

@@ -0,0 +1,247 @@
+# ========== 系统 ==========
+0=成功
+# ========== 客户端错误段 ==========
+400=请求参数不正确
+401=账号未登录
+403=没有该操作权限
+404=请求未找到
+405=请求方法不正确
+423=请求失败,请稍后重试 # 并发请求,不允许
+429=请求过于频繁,请稍后重试
+
+# ========== 服务端错误段 ==========
+500=系统异常
+501=功能未实现/未开启
+502=错误的配置项
+# ========== 自定义错误段 ==========
+900=重复请求,请稍后重试 # 重复请求
+901=演示模式,禁止写操作
+999=未知错误
+# ========== 公共 1_099_000_000 ==========
+1_099_000_001=帐号状态不能为空
+1_099_000_002=修改状态必须是 {value}
+1_099_000_003=显示顺序不能为空
+1_099_000_004=上级id不能为空
+1_099_000_005=id不能为空
+1_099_000_006=用户id不能为空
+1_099_000_007=企业id不能为空
+# ========== 人才信息-人才档案 1_100_001_000 ==========
+1_100_001_001=人才档案不存在
+1_100_001_002=人才优势不能为空
+1_100_001_003=真实姓名不能为空
+1_100_001_004=联系手机号不能为空
+1_100_001_005=出生日期不能为空
+1_100_001_006=工作经验不能为空
+1_100_001_007=学历不能为空
+1_100_001_008=求职状态不能为空
+1_100_001_009=所在城市不能为空
+1_100_001_010=常用邮箱不能为空
+1_100_001_011=请先填写人才基本信息
+# ========== 行业信息 1_100_002_000 ==========
+1_100_002_001=行业信息不存在
+# ========== 职位信息 1_100_003_000 ==========
+1_100_003_001=职位信息不存在
+1_100_003_002=职位类型不能为空
+1_100_003_003=职位名称不能为空
+# ========== 地区 1_100_004_000 ==========
+1_100_004_001=地区不存在
+1_100_004_002=热门地区已存在,请勿重复添加
+1_100_004_003=工作地区不能为空
+# ========== 简历附件 1_100_005_000 ==========
+1_100_005_001=简历附件不存在
+1_100_005_002=简历附件最多上传5份
+# ========== 教育经历 1_100_006_000 ==========
+1_100_006_001=教育经历不存在
+1_100_006_002=未选择学历
+1_100_006_003=未选择学制类型
+1_100_006_004=未选择在校开始日期
+1_100_006_005=未选择在校结束日期
+# ========== 求职意向 1_100_007_000 ==========
+1_100_007_001=求职意向不存在
+1_100_007_002=未选择期望职位
+1_100_007_003=未选择期望行业
+1_100_007_004=未选择求职类型
+1_100_007_005=未填写薪酬最低要求
+1_100_007_006=未填写薪酬最高要求
+1_100_007_007=未选择工作城市
+# ========== 专业 1_100_008_000 ==========
+1_100_008_001=专业不存在
+# ========== 职位标签 1_100_009_000 ==========
+1_100_009_001=职位标签不存在
+1_100_009_002=职位标签不能为空
+# ========== 学校 1_100_010_000 ==========
+1_100_010_001=学校不存在
+# ========== 人才证书 1_100_011_000 ==========
+1_100_011_001=人才证书不存在
+1_100_011_002=证书中文名称不能为空
+1_100_011_003=证书英文名称不能为空
+# ========== 工作经历 1_100_012_000 ==========
+1_100_012_001=工作经历不存在
+1_100_012_002=未选择在职开始日期
+1_100_027_003=未选择在职结束时间
+# ========== 人才技能 1_100_013_000 ==========
+1_100_013_001=人才技能不存在
+1_100_013_002=该技能已存在
+# ========== 证书 1_100_014_000 ==========
+1_100_014_001=证书不存在
+# ========== 访问量 1_100_015_000 ==========
+1_100_015_001=访问量不存在
+1_100_015_002=业务类型不存在
+1_100_015_003=业务id不存在
+1_100_015_004=访问日期不存在
+# ========== 用户登录 1_100_016_000 ==========
+1_100_016_001=用户不存在
+1_100_016_002=手机号未注册用户
+1_100_016_003=修改手机失败,该手机号({})已经被使用
+1_100_016_004=用户账号已经存在
+1_100_016_005=手机号已经存在
+1_100_016_006=邮箱已经存在
+1_100_016_007=手机号不能为空
+1_100_016_008=导入用户数据不能为空!
+1_100_016_009=用户密码校验失败
+1_100_016_010=名字为【{}】的用户已被禁用
+1_100_016_011=创建用户失败,原因:超过租户最大租户配额({})!
+1_100_016_012=新密码不能为空
+1_100_016_013=手机验证码不能为空
+1_100_016_014=手机验证码长度为 4-6 位
+1_100_016_015=手机号码长度为 8-11 位
+1_100_016_016=手机验证码必须都是数字
+1_100_016_017=密码长度为 4-16 位
+1_100_016_018=用户编号不能为空
+1_100_016_019=密码不能为空
+1_100_016_020=用户账号不能为空
+# ========== AUTH 模块 1_100_017_000 ==========
+1_100_017_001=登录失败,账号密码不正确
+1_100_017_002=登录失败,账号被禁用
+1_100_017_003=登录失败,解析不到三方登录信息
+1_100_017_004=手机号已经被使用
+1_100_017_005=验证码不正确,原因:{}
+1_100_017_006=未绑定账号,需要进行绑定
+1_100_017_007=Token 已经过期
+1_100_017_008=手机号不存在
+1_100_017_009=发送场景不能为空
+1_100_017_010=手机code不能为空
+1_100_017_011=登录code不能为空
+1_100_017_012=state不能为空
+1_100_017_013=授权码不能为空
+1_100_017_014=社交平台的类型不能为空
+1_100_017_015=验证码不能为空
+1_100_017_016=未选择企业
+1_100_017_017=未注册用户,请先注册人才用户再申请企业用户
+1_100_017_018=访问令牌不能为空
+# ========== 角色模块 1_100_018_000 ==========
+1_100_018_001=角色不存在
+1_100_018_002=已经存在名为【{}】的角色
+1_100_018_003=已经存在编码为【{}】的角色
+1_100_018_004=不能操作类型为系统内置的角色
+1_100_018_005=名字为【{}】的角色已被禁用
+1_100_018_006=编码【{}】不能使用
+1_100_018_007=角色编号不能为空
+1_100_018_008=角色名称不能为空
+1_100_018_009=角色名称长度不能超过 30 个字符
+1_100_018_010=角色标志不能为空
+1_100_018_011=角色标志长度不能超过 100 个字符
+1_100_018_012=数据范围不能为空
+1_100_018_013=数据范围必须是 {value}
+# ========== 企业信息 1_100_019_000 ==========
+1_100_019_001=企业信息不存在
+1_100_019_002=企业编码不能为空
+1_100_019_003=未填写企业名称
+1_100_019_004=未填写企业别称
+1_100_019_005=未填写公司简介
+1_100_019_006=未选择所在行业
+1_100_019_007=未选择融资阶段
+1_100_019_008=未选择人员规模
+1_100_019_009=未填写上班时间
+# ========== 企业工商信息  1_100_020_001 ==========
+1_100_020_001=企业工商信息不存在
+1_100_020_002=未填写企业名称
+1_100_020_003=未填写统一社会信用代码
+# ========== 企业注册申请 1_100_021_000 ==========
+1_100_021_001=企业注册申请不存在
+1_100_021_002=企业注册申请已审批
+1_100_021_003=企业注册申请已审批通过,无法拒绝
+1_100_021_004=企业名称不能为空
+1_100_021_005=联系电话不能为空
+1_100_021_006=联系邮箱不能为空
+1_100_021_007=未上传营业执照
+1_100_021_008=统一社会信用代码不能为空
+1_100_021_009=企业已注册
+1_100_021_010=企业已申请审核中,请勿重复提交
+# ========== 企业地址 1_100_022_001 ==========
+1_100_022_001=企业地址不存在
+# ========== 企业登录用户 1_100_023_000 ==========
+1_100_023_001=企业登录用户不存在
+1_100_023_002=名称不能为空
+1_100_023_003=该账户已被禁用
+1_100_023_004=该账户为管理员账户,无法操作
+# ========== 企业岗位信息 1_100_024_000 ==========
+1_100_024_001=企业岗位信息不存在
+1_100_024_002=岗位中文名称不能为空
+1_100_024_003=岗位英文不能为空
+# ========== 招聘职位 1_100_025_000 ==========
+1_100_025_001=招聘职位不存在
+1_100_025_002=招聘职位id不能为空
+1_100_025_003=职位名称不能为空
+1_100_025_004=招聘类型不能为空
+1_100_025_005=发布用户id不能为空
+1_100_025_006=工作经验不能为空
+1_100_025_007=学历要求不能为空
+# ========== 企业招聘者浏览 1_100_026_000 ==========
+1_100_026_001=企业招聘者浏览不存在
+# ========== 人才-企业关注订阅 1_100_027_000 ==========
+1_100_027_001=企业关注订阅不存在
+1_100_027_002=企业已关注订阅,请勿重复点击
+# ========== 人才-浏览记录 1_100_028_000 ==========
+1_100_028_001=浏览记录不存在
+# ========== 人才-招聘职位收藏 1_100_029_000 ==========
+1_100_029_001=招聘职位收藏不存在
+1_100_029_002=职位已收藏,请勿重复点击
+# ========== 培训经历 1_100_030_000 ==========
+1_100_030_001=培训经历不存在
+1_100_030_002=培训开始时间不能为空
+1_100_030_003=培训结束时间不能为空
+1_100_030_004=培训机构名称不能为空
+1_100_030_005=培训课程不能为空
+# ========== 项目经历 1_100_031_000 ==========
+1_100_031_001=项目经历不存在
+1_100_031_002=未填写项目名称
+1_100_031_003=项目开始时间不能为空
+1_100_031_004=项目结束时间不能为空
+# ========== 职业技能 1_100_032_000 ==========
+1_100_032_001=职业技能不存在
+1_100_032_002=存在存在子职业技能,无法删除
+1_100_032_003=父级职业技能不存在
+1_100_032_004=不能设置自己为父职业技能
+1_100_032_005=已经存在该技能中文名称的职业技能
+1_100_032_006=不能设置自己的子Skill为父Skill
+# ========== 企业用户申请 1_100_034_000 ==========
+1_100_034_001=企业用户申请不存在
+1_100_034_002=用户申请已审批
+1_100_034_003=用户申请申请已审批通过,无法拒绝
+1_100_034_005=已申请审核中,请勿重复提交
+# ========== 招聘职位简历投递 1_100_035_000 ==========
+1_100_035_001=招聘职位简历投递不存在
+1_100_035_002=该职位已投递
+1_100_035_003=未选择简历附件
+1_100_035_004=招聘职位id不能为空
+1_100_035_005=该职位暂不允许投递
+1_100_035_006=该职位停止招聘
+1_100_035_007=简历附件标题不能为空
+1_100_035_008=简历附件地址不能为空
+1_100_035_009=投递的简历状态不能为空
+# ========== 众聘-简历投递 1_100_036_000 ==========
+1_100_036_001=投递简历不存在
+1_100_036_002=发布的职位id不能为空
+1_100_036_003=发布职位的用户id不能为空
+1_100_036_004=推荐人id不能为空
+1_100_036_005=投递用户id不能为空
+1_100_036_006=投递人名称不能为空
+1_100_036_007=投递人联系电话不能为空
+1_100_036_008=简历附件不能为空
+# ========== 众聘-佣金比例 1_100_037_000 ==========
+1_100_037_001=佣金比例不存在
+1_100_037_002=佣金比例不能为空
+1_100_037_003=佣金比例不能小于0
+# ========== 用户账户 1_100_038_000 ==========
+1_100_038_001=用户账户不存在

+ 0 - 0
menduner/menduner-im-biz/src/main/resources/i18n/messages_zh_HK.properties


+ 76 - 0
menduner/menduner-im-biz/src/main/resources/logback-spring.xml

@@ -0,0 +1,76 @@
+<configuration>
+    <!-- 引用 Spring Boot 的 logback 基础配置 -->
+    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
+    <!-- 变量 citu.info.base-package,基础业务包 -->
+    <springProperty scope="context" name="citu.info.base-package" source="citu.info.base-package"/>
+    <!-- 格式化输出:%d 表示日期,%X{tid} SkWalking 链路追踪编号,%thread 表示线程名,%-5level:级别从左显示 5 个字符宽度,%msg:日志消息,%n是换行符 -->
+    <property name="PATTERN_DEFAULT" value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} | %highlight(${LOG_LEVEL_PATTERN:-%5p} ${PID:- }) | %boldYellow(%thread [%tid]) %boldGreen(%-40.40logger{39}) | %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
+
+    <!-- 控制台 Appender -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">     
+        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
+            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
+                <pattern>${PATTERN_DEFAULT}</pattern>
+            </layout>
+        </encoder>
+    </appender>
+
+    <!-- 文件 Appender -->
+    <!-- 参考 Spring Boot 的 file-appender.xml 编写 -->
+    <appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
+            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
+                <pattern>${PATTERN_DEFAULT}</pattern>
+            </layout>
+        </encoder>
+        <!-- 日志文件名 -->
+        <file>${LOG_FILE}</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <!-- 滚动后的日志文件名 -->
+            <fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern>
+            <!-- 启动服务时,是否清理历史日志,一般不建议清理 -->
+            <cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
+            <!-- 日志文件,到达多少容量,进行滚动 -->
+            <maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize>
+            <!-- 日志文件的总大小,0 表示不限制 -->
+            <totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}</totalSizeCap>
+            <!-- 日志文件的保留天数 -->
+            <maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30}</maxHistory>
+        </rollingPolicy>
+    </appender>
+    <!-- 异步写入日志,提升性能 -->
+    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
+        <!-- 不丢失日志。默认的,如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->
+        <discardingThreshold>0</discardingThreshold>
+        <!-- 更改默认的队列的深度,该值会影响性能。默认值为 256 -->
+        <queueSize>256</queueSize>
+        <appender-ref ref="FILE"/>
+    </appender>
+
+    <!-- SkyWalking GRPC 日志收集,实现日志中心。注意:SkyWalking 8.4.0 版本开始支持 -->
+    <appender name="GRPC" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
+        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
+            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
+                <pattern>${PATTERN_DEFAULT}</pattern>
+            </layout>
+        </encoder>
+    </appender>
+
+    <!-- 本地环境 -->
+    <springProfile name="local">
+        <root level="INFO">
+            <appender-ref ref="STDOUT"/>
+            <appender-ref ref="GRPC"/> <!-- 本地环境下,如果不想接入 SkyWalking 日志服务,可以注释掉本行 -->
+            <appender-ref ref="ASYNC"/>  <!-- 本地环境下,如果不想打印日志,可以注释掉本行 -->
+        </root>
+    </springProfile>
+    <!-- 其它环境 -->
+    <springProfile name="dev,test,stage,prod,default">
+        <root level="INFO">
+            <appender-ref ref="STDOUT"/>
+            <appender-ref ref="ASYNC"/>
+            <appender-ref ref="GRPC"/>
+        </root>
+    </springProfile>
+
+</configuration>

+ 817 - 0
menduner/menduner-im-biz/src/main/resources/major/major.csv

@@ -0,0 +1,817 @@
+id,categorize,code,name_cn
+1,哲学类,010101,哲学
+2,哲学类,010102,逻辑学
+3,哲学类,010103K,宗教学
+4,哲学类,010104T,伦理学
+5,经济学类,020101,经济学
+6,经济学类,020102,经济统计学
+7,经济学类,020103T,国民经济管理
+8,经济学类,020104T,资源与环境经济学
+9,经济学类,020105T,商务经济学
+10,经济学类,020106T,能源经济
+11,经济学类,020107T,劳动经济学
+12,经济学类,020108T,经济工程
+13,经济学类,020109T,数字经济
+14,财政学类,020201K,财政学
+15,财政学类,020202,税收学
+16,财政学类,020203TK,国际税收
+17,金融学类,020301K,金融学
+18,金融学类,020302,金融工程
+19,金融学类,020303,保险学
+20,金融学类,020304,投资学
+21,金融学类,020305T,金融数学
+22,金融学类,020306T,信用管理
+23,金融学类,020307T,经济与金融
+24,金融学类,020308T,精算学
+25,金融学类,020309T,互联网金融
+26,金融学类,020310T,金融科技
+27,金融学类,020311TK,金融审计
+28,经济与贸易类,020401,国际经济与贸易
+29,经济与贸易类,020402,贸易经济
+30,经济与贸易类,020403T,国际经济发展合作
+31,法学类,030101K,法学
+32,法学类,030102T,知识产权
+33,法学类,030103T,监狱学
+34,法学类,030104T,信用风险管理与法律防控
+35,法学类,030105T,国际经贸规则
+36,法学类,030106TK,司法警察学
+37,法学类,030107TK,社区矫正
+38,法学类,030108TK,纪检监察
+39,法学类,030109TK,国际法
+40,法学类,030110TK,司法鉴定学
+41,法学类,030111TK,国家安全学
+42,法学类,030112TK,海外利益安全
+43,政治学类,030201,政治学与行政学
+44,政治学类,030202,国际政治
+45,政治学类,030203,外交学
+46,政治学类,030204T,国际事务与国际关系
+47,政治学类,030205T,政治学、经济学与哲学
+48,政治学类,030206TK,国际组织与全球治理
+49,社会学类,030301,社会学
+50,社会学类,030302,社会工作
+51,社会学类,030303T,人类学
+52,社会学类,030304T,女性学
+53,社会学类,030305T,家政学
+54,社会学类,030306T,老年学
+55,社会学类,030307T,社会政策
+56,民族学类,030401,民族学
+57,马克思主义理论类,030501,科学社会主义
+58,马克思主义理论类,030502,中国共产党历史
+59,马克思主义理论类,030503,思想政治教育
+60,马克思主义理论类,030504T,马克思主义理论
+61,马克思主义理论类,030505TK,工会学
+62,公安学类,030601K,治安学
+63,公安学类,030602K,侦查学
+64,公安学类,030603K,边防管理
+65,公安学类,030604TK,禁毒学
+66,公安学类,030605TK,警犬技术
+67,公安学类,030606TK,经济犯罪侦查
+68,公安学类,030607TK,边防指挥
+69,公安学类,030608TK,消防指挥
+70,公安学类,030609TK,警卫学
+71,公安学类,030610TK,公安情报学
+72,公安学类,030611TK,犯罪学
+73,公安学类,030612TK,公安管理学
+74,公安学类,030613TK,涉外警务
+75,公安学类,030614TK,国内安全保卫
+76,公安学类,030615TK,警务指挥与战术
+77,公安学类,030616TK,技术侦查学
+78,公安学类,030617TK,海警执法
+79,公安学类,030618TK,公安政治工作
+80,公安学类,030619TK,移民管理
+81,公安学类,030620TK,出入境管理
+82,公安学类,030621TK,反恐警务
+83,公安学类,030622TK,消防政治工作
+84,公安学类,030623TK,铁路警务
+85,教育学类,040101,教育学
+86,教育学类,040102,科学教育
+87,教育学类,040103,人文教育
+88,教育学类,040104,教育技术学
+89,教育学类,040105,艺术教育
+90,教育学类,040106,学前教育
+91,教育学类,040107,小学教育
+92,教育学类,040108,特殊教育
+93,教育学类,040109T,华文教育
+94,教育学类,040110TK,教育康复学
+95,教育学类,040111T,卫生教育
+96,教育学类,040112T,认知科学与技术
+97,教育学类,040113T,融合教育
+98,教育学类,040114TK,劳动教育
+99,教育学类,040115T,家庭教育
+100,教育学类,040116TK,孤独症儿童教育
+101,体育学类,040201,体育教育
+102,体育学类,040202K,运动训练
+103,体育学类,040203,社会体育指导与管理
+104,体育学类,040204K,武术与民族传统体育
+105,体育学类,040205,运动人体科学
+106,体育学类,040206T,运动康复
+107,体育学类,040207T,休闲体育
+108,体育学类,040208T,体能训练
+109,体育学类,040209T,冰雪运动
+110,体育学类,040210TK,电子竞技运动与管理
+111,体育学类,040211TK,智能体育工程
+112,体育学类,040212TK,体育旅游
+113,体育学类,040213T,运动能力开发
+114,体育学类,040214TK,足球运动
+115,体育学类,040215TK,马术运动与管理
+116,体育学类,040216T,体育康养
+117,中国语言文学类,050101,汉语言文学
+118,中国语言文学类,050102,汉语言
+119,中国语言文学类,050103,汉语国际教育
+120,中国语言文学类,050104,中国少数民族语言文学
+121,中国语言文学类,050105,古典文献学
+122,中国语言文学类,050106T,应用语言学
+123,中国语言文学类,050107T,秘书学
+124,中国语言文学类,050108T,中国语言与文化
+125,中国语言文学类,050109T,手语翻译
+126,中国语言文学类,050110T,数字人文
+127,中国语言文学类,050111T,中国古典学
+128,中国语言文学类,050112T,汉学与中国学
+129,中国语言文学类,050113T,应用中文
+130,外国语言文学类,050201,英语
+131,外国语言文学类,050202,俄语
+132,外国语言文学类,050203,德语
+133,外国语言文学类,050204,法语
+134,外国语言文学类,050205,西班牙语
+135,外国语言文学类,050206,阿拉伯语
+136,外国语言文学类,050207,日语
+137,外国语言文学类,050208,波斯语
+138,外国语言文学类,050209,朝鲜语
+139,外国语言文学类,050210,菲律宾语
+140,外国语言文学类,050211,梵语巴利语
+141,外国语言文学类,050212,印度尼西亚语
+142,外国语言文学类,050213,印地语
+143,外国语言文学类,050214,柬埔寨语
+144,外国语言文学类,050215,老挝语
+145,外国语言文学类,050216,缅甸语
+146,外国语言文学类,050217,马来语
+147,外国语言文学类,050218,蒙古语
+148,外国语言文学类,050219,僧伽罗语
+149,外国语言文学类,050220,泰语
+150,外国语言文学类,050221,乌尔都语
+151,外国语言文学类,050222,希伯来语
+152,外国语言文学类,050223,越南语
+153,外国语言文学类,050224,豪萨语
+154,外国语言文学类,050225,斯瓦希里语
+155,外国语言文学类,050226,阿尔巴尼亚语
+156,外国语言文学类,050227,保加利亚语
+157,外国语言文学类,050228,波兰语
+158,外国语言文学类,050229,捷克语
+159,外国语言文学类,050230,斯洛伐克语
+160,外国语言文学类,050231,罗马尼亚语
+161,外国语言文学类,050232,葡萄牙语
+162,外国语言文学类,050233,瑞典语
+163,外国语言文学类,050234,塞尔维亚语
+164,外国语言文学类,050235,土耳其语
+165,外国语言文学类,050236,希腊语
+166,外国语言文学类,050237,匈牙利语
+167,外国语言文学类,050238,意大利语
+168,外国语言文学类,050239,泰米尔语
+169,外国语言文学类,050240,普什图语
+170,外国语言文学类,050241,世界语
+171,外国语言文学类,050242,孟加拉语
+172,外国语言文学类,050243,尼泊尔语
+173,外国语言文学类,050244,克罗地亚语
+174,外国语言文学类,050245,荷兰语
+175,外国语言文学类,050246,芬兰语
+176,外国语言文学类,050247,乌克兰语
+177,外国语言文学类,050248,挪威语
+178,外国语言文学类,050249,丹麦语
+179,外国语言文学类,050250,冰岛语
+180,外国语言文学类,050251,爱尔兰语
+181,外国语言文学类,050252,拉脱维亚语
+182,外国语言文学类,050253,立陶宛语
+183,外国语言文学类,050254,斯洛文尼亚语
+184,外国语言文学类,050255,爱沙尼亚语
+185,外国语言文学类,050256,马耳他语
+186,外国语言文学类,050257,哈萨克语
+187,外国语言文学类,050258,乌兹别克语
+188,外国语言文学类,050259,祖鲁语
+189,外国语言文学类,050260,拉丁语
+190,外国语言文学类,050261,翻译
+191,外国语言文学类,050262,商务英语
+192,外国语言文学类,050263T,阿姆哈拉语
+193,外国语言文学类,050264T,吉尔吉斯语
+194,外国语言文学类,050265T,索马里语
+195,外国语言文学类,050266T,土库曼语
+196,外国语言文学类,050267T,加泰罗尼亚语
+197,外国语言文学类,050268T,约鲁巴语
+198,外国语言文学类,050269T,亚美尼亚语
+199,外国语言文学类,050270T,马达加斯加语
+200,外国语言文学类,050271T,格鲁吉亚语
+201,外国语言文学类,050272T,阿塞拜疆语
+202,外国语言文学类,050273T,阿非利卡语
+203,外国语言文学类,050274T,马其顿语
+204,外国语言文学类,050275T,塔吉克语
+205,外国语言文学类,050276T,茨瓦纳语
+206,外国语言文学类,050277T,恩德贝莱语
+207,外国语言文学类,050278T,科摩罗语
+208,外国语言文学类,050279T,克里奥尔语
+209,外国语言文学类,050280T,绍纳语
+210,外国语言文学类,050281T,提格雷尼亚语
+211,外国语言文学类,050282T,白俄罗斯语
+212,外国语言文学类,050283T,毛利语
+213,外国语言文学类,050284T,汤加语
+214,外国语言文学类,050285T,萨摩亚语
+215,外国语言文学类,050286T,库尔德语
+216,外国语言文学类,050287T,比斯拉马语
+217,外国语言文学类,050288T,达里语
+218,外国语言文学类,050289T,德顿语
+219,外国语言文学类,050290T,迪维希语
+220,外国语言文学类,050291T,斐济语
+221,外国语言文学类,050292T,库克群岛毛利语
+222,外国语言文学类,050293T,隆迪语
+223,外国语言文学类,050294T,卢森堡语
+224,外国语言文学类,050295T,卢旺达语
+225,外国语言文学类,050296T,纽埃语
+226,外国语言文学类,050297T,皮金语
+227,外国语言文学类,050298T,切瓦语
+228,外国语言文学类,050299T,塞苏陀语
+229,外国语言文学类,050200T,桑戈语
+230,外国语言文学类,0502100T,语言学
+231,外国语言文学类,0502101T,塔玛齐格特语
+232,外国语言文学类,0502102T,爪哇语
+233,外国语言文学类,0502103T,旁遮普语
+234,新闻传播学类,050301,新闻学
+235,新闻传播学类,050302,广播电视学
+236,新闻传播学类,050303,广告学
+237,新闻传播学类,050304,传播学
+238,新闻传播学类,050305,编辑出版学
+239,新闻传播学类,050306T,网络与新媒体
+240,新闻传播学类,050307T,数字出版
+241,新闻传播学类,050308T,时尚传播
+242,新闻传播学类,050309T,国际新闻与传播
+243,新闻传播学类,050310T,会展
+244,历史学类,060101,历史学
+245,历史学类,060102,世界史
+246,历史学类,060103,考古学
+247,历史学类,060104,文物与博物馆学
+248,历史学类,060105T,文物保护技术
+249,历史学类,060106T,外国语言与外国历史
+250,历史学类,060107T,文化遗产
+251,历史学类,060108T,古文字学
+252,历史学类,060109T,科学史
+253,数学类,070101,数学与应用数学
+254,数学类,070102,信息与计算科学
+255,数学类,070103T,数理基础科学
+256,数学类,070104T,数据计算及应用
+257,物理学类,070201,物理学
+258,物理学类,070202,应用物理学
+259,物理学类,070203,核物理
+260,物理学类,070204T,声学
+261,物理学类,070205T,系统科学与工程
+262,物理学类,070206T,量子信息科学
+263,化学类,070301,化学
+264,化学类,070302,应用化学
+265,化学类,070303T,化学生物学
+266,化学类,070304T,分子科学与工程
+267,化学类,070305T,能源化学
+268,化学类,070306T,化学测量学与技术
+269,化学类,070307T,资源化学
+270,天文学类,070401,天文学
+271,地理科学类,070501,地理科学
+272,地理科学类,070502,自然地理与资源环境
+273,地理科学类,070503,人文地理与城乡规划
+274,地理科学类,070504,地理信息科学
+275,大气科学类,070601,大气科学
+276,大气科学类,070602,应用气象学
+277,大气科学类,070603T,气象技术与工程
+278,大气科学类,070604T,地球系统科学
+279,海洋科学类,070701,海洋科学
+280,海洋科学类,070702,海洋技术
+281,海洋科学类,070703T,海洋资源与环境
+282,海洋科学类,070704T,军事海洋学
+283,地球物理学类,070801,地球物理学
+284,地球物理学类,070802,空间科学与技术
+285,地球物理学类,070803T,防灾减灾科学与工程
+286,地球物理学类,070804TK,行星科学
+287,地质学类,070901,地质学
+288,地质学类,070902,地球化学
+289,地质学类,070903T,地球信息科学与技术
+290,地质学类,070904T,古生物学
+291,生物科学类,071001,生物科学
+292,生物科学类,071002,生物技术
+293,生物科学类,071003,生物信息学
+294,生物科学类,071004,生态学
+295,生物科学类,071005T,整合科学
+296,生物科学类,071006T,神经科学
+297,心理学类,071101,心理学
+298,心理学类,071102,应用心理学
+299,统计学类,071201,统计学
+300,统计学类,071202,应用统计学
+301,统计学类,071203T,数据科学
+302,统计学类,071204T,生物统计学
+303,力学类,080101,理论与应用力学
+304,力学类,080102,工程力学
+305,机械类,080201,机械工程
+306,机械类,080202,机械设计制造及其自动化
+307,机械类,080203,材料成型及控制工程
+308,机械类,080204,机械电子工程
+309,机械类,080205,工业设计
+310,机械类,080206,过程装备与控制工程
+311,机械类,080207,车辆工程
+312,机械类,080208,汽车服务工程
+313,机械类,080209T,机械工艺技术
+314,机械类,080210T,微机电系统工程
+315,机械类,080211T,机电技术教育
+316,机械类,080212T,汽车维修工程教育
+317,机械类,080213T,智能制造工程
+318,机械类,080214T,智能车辆工程
+319,机械类,080215T,仿生科学与工程
+320,机械类,080216T,新能源汽车工程
+321,机械类,080217T,增材制造工程
+322,机械类,080218T,智能交互设计
+323,机械类,080219T,应急装备技术与工程
+324,机械类,080220T,农林智能装备工程
+325,仪器类,080301,测控技术与仪器
+326,仪器类,080302T,精密仪器
+327,仪器类,080303T,智能感知工程
+328,材料类,080401,材料科学与工程
+329,材料类,080402,材料物理
+330,材料类,080403,材料化学
+331,材料类,080404,冶金工程
+332,材料类,080405,金属材料工程
+333,材料类,080406,无机非金属材料工程
+334,材料类,080407,高分子材料与工程
+335,材料类,080408,复合材料与工程
+336,材料类,080409T,粉体材料科学与工程
+337,材料类,080410T,宝石及材料工艺学
+338,材料类,080411T,焊接技术与工程
+339,材料类,080412T,功能材料
+340,材料类,080413T,纳米材料与技术
+341,材料类,080414T,新能源材料与器件
+342,材料类,080415T,材料设计科学与工程
+343,材料类,080416T,复合材料成型工程
+344,材料类,080417T,智能材料与结构
+345,材料类,080418T,光电信息材料与器件
+346,材料类,080419T,生物材料
+347,材料类,080420T,材料智能技术
+348,材料类,080421T,电子信息材料
+349,材料类,080422T,软物质科学与工程
+350,材料类,080423T,稀土材料科学与工程
+351,能源动力类,080501,能源与动力工程
+352,能源动力类,080502T,能源与环境系统工程
+353,能源动力类,080503T,新能源科学与工程
+354,能源动力类,080504T,储能科学与工程
+355,能源动力类,080505T,能源服务工程
+356,能源动力类,080506TK,氢能科学与工程
+357,能源动力类,080507TK,可持续能源
+358,电气类,080601,电气工程及其自动化
+359,电气类,080602T,智能电网信息工程
+360,电气类,080603T,光源与照明
+361,电气类,080604T,电气工程与智能控制
+362,电气类,080605T,电机电器智能化
+363,电气类,080606T,电缆工程
+364,电气类,080607T,能源互联网工程
+365,电气类,080608TK,智慧能源工程
+366,电气类,080609T,电动载运工程
+367,电气类,080610TK,大功率半导体科学与工程
+368,电子信息类,080701,电子信息工程
+369,电子信息类,080702,电子科学与技术
+370,电子信息类,080703,通信工程
+371,电子信息类,080704,微电子科学与工程
+372,电子信息类,080705,光电信息科学与工程
+373,电子信息类,080706,信息工程
+374,电子信息类,080707T,广播电视工程
+375,电子信息类,080708T,水声工程
+376,电子信息类,080709T,电子封装技术
+377,电子信息类,080710T,集成电路设计与集成系统
+378,电子信息类,080711T,医学信息工程
+379,电子信息类,080712T,电磁场与无线技术
+380,电子信息类,080713T,电波传播与天线
+381,电子信息类,080714T,电子信息科学与技术
+382,电子信息类,080715T,电信工程及管理
+383,电子信息类,080716T,应用电子技术教育
+384,电子信息类,080717T,人工智能
+385,电子信息类,080718T,海洋信息工程
+386,电子信息类,080719T,柔性电子学
+387,电子信息类,080720T,智能测控工程
+388,电子信息类,080721T,智能视觉工程
+389,自动化类,080801,自动化
+390,自动化类,080802T,轨道交通信号与控制
+391,自动化类,080803T,机器人工程
+392,自动化类,080804T,邮政工程
+393,自动化类,080805T,核电技术与控制工程
+394,自动化类,080806T,智能装备与系统
+395,自动化类,080807T,工业智能
+396,自动化类,080808T,智能工程与创意设计
+397,计算机类,080901,计算机科学与技术
+398,计算机类,080902,软件工程
+399,计算机类,080903,网络工程
+400,计算机类,080904K,信息安全
+401,计算机类,080905,物联网工程
+402,计算机类,080906,数字媒体技术
+403,计算机类,080907T,智能科学与技术
+404,计算机类,080908T,空间信息与数字技术
+405,计算机类,080909T,电子与计算机工程
+406,计算机类,080910T,数据科学与大数据技术
+407,计算机类,080911TK,网络空间安全
+408,计算机类,080912T,新媒体技术
+409,计算机类,080913T,电影制作
+410,计算机类,080914TK,保密技术
+411,计算机类,080915T,服务科学与工程
+412,计算机类,080916T,虚拟现实技术
+413,计算机类,080917T,区块链工程
+414,计算机类,080918TK,密码科学与技术
+415,土木类,081001,土木工程
+416,土木类,081002,建筑环境与能源应用工程
+417,土木类,081003,给排水科学与工程
+418,土木类,081004,建筑电气与智能化
+419,土木类,081005T,城市地下空间工程
+420,土木类,081006T,道路桥梁与渡河工程
+421,土木类,081007T,铁道工程
+422,土木类,081008T,智能建造
+423,土木类,081009T,土木、水利与海洋工程
+424,土木类,081010T,土木、水利与交通工程
+425,土木类,081011T,城市水系统工程
+426,土木类,081012T,智能建造与智慧交通
+427,土木类,081013T,工程软件
+428,水利类,081101,水利水电工程
+429,水利类,081102,水文与水资源工程
+430,水利类,081103,港口航道与海岸工程
+431,水利类,081104T,水务工程
+432,水利类,081105T,水利科学与工程
+433,水利类,081106T,智慧水利
+434,测绘类,081201,测绘工程
+435,测绘类,081202,遥感科学与技术
+436,测绘类,081203T,导航工程
+437,测绘类,081204T,地理国情监测
+438,测绘类,081205T,地理空间信息工程
+439,化工与制药类,081301,化学工程与工艺
+440,化工与制药类,081302,制药工程
+441,化工与制药类,081303T,资源循环科学与工程
+442,化工与制药类,081304T,能源化学工程
+443,化工与制药类,081305T,化学工程与工业生物工程
+444,化工与制药类,081306T,化工安全工程
+445,化工与制药类,081307T,涂料工程
+446,化工与制药类,081308T,精细化工
+447,地质类,081401,地质工程
+448,地质类,081402,勘查技术与工程
+449,地质类,081403K,资源勘查工程
+450,地质类,081404T,地下水科学与工程
+451,地质类,081405T,旅游地学与规划工程
+452,地质类,081406T,智能地球探测
+453,地质类,081407T,资源环境大数据工程
+454,矿业类,081501,采矿工程
+455,矿业类,081502,石油工程
+456,矿业类,081503,矿物加工工程
+457,矿业类,081504,油气储运工程
+458,矿业类,081505T,矿物资源工程
+459,矿业类,081506T,海洋油气工程
+460,矿业类,081507T,智能采矿工程
+461,矿业类,081508TK,碳储科学与工程
+462,纺织类,081601,纺织工程
+463,纺织类,081602,服装设计与工程
+464,纺织类,081603T,非织造材料与工程
+465,纺织类,081604T,服装设计与工艺教育
+466,纺织类,081605T,丝绸设计与工程
+467,轻工类,081701,轻化工程
+468,轻工类,081702,包装工程
+469,轻工类,081703,印刷工程
+470,轻工类,081704T,香料香精技术与工程
+471,轻工类,081705T,化妆品技术与工程
+472,轻工类,081706TK,生物质能源与材料
+473,交通运输类,081801,交通运输
+474,交通运输类,081802,交通工程
+475,交通运输类,081803K,航海技术
+476,交通运输类,081804K,轮机工程
+477,交通运输类,081805K,飞行技术
+478,交通运输类,081806T,交通设备与控制工程
+479,交通运输类,081807T,救助与打捞工程
+480,交通运输类,081808TK,船舶电子电气工程
+481,交通运输类,081809T,轨道交通电气与控制
+482,交通运输类,081810T,邮轮工程与管理
+483,交通运输类,081811T,智慧交通
+484,交通运输类,081812T,智能运输工程
+485,海洋工程类,081901,船舶与海洋工程
+486,海洋工程类,081902T,海洋工程与技术
+487,海洋工程类,081903T,海洋资源开发技术
+488,海洋工程类,081904T,海洋机器人
+489,海洋工程类,081905T,智慧海洋技术
+490,海洋工程类,081906T,智能海洋装备
+491,航空航天类,082001,航空航天工程
+492,航空航天类,082002,飞行器设计与工程
+493,航空航天类,082003,飞行器制造工程
+494,航空航天类,082004,飞行器动力工程
+495,航空航天类,082005,飞行器环境与生命保障工程
+496,航空航天类,082006T,飞行器质量与可靠性
+497,航空航天类,082007T,飞行器适航技术
+498,航空航天类,082008T,飞行器控制与信息工程
+499,航空航天类,082009T,无人驾驶航空器系统工程
+500,航空航天类,082010T,智能飞行器技术
+501,航空航天类,082011T,空天智能电推进技术
+502,航空航天类,082012T,飞行器运维工程
+503,兵器类,082101,武器系统与工程
+504,兵器类,082102,武器发射工程
+505,兵器类,082103,探测制导与控制技术
+506,兵器类,082104,弹药工程与爆炸技术
+507,兵器类,082105,特种能源技术与工程
+508,兵器类,082106,装甲车辆工程
+509,兵器类,082107,信息对抗技术
+510,兵器类,082108T,智能无人系统技术
+511,核工程类,082201,核工程与核技术
+512,核工程类,082202,辐射防护与核安全
+513,核工程类,082203,工程物理
+514,核工程类,082204,核化工与核燃料工程
+515,农业工程类,082301,农业工程
+516,农业工程类,082302,农业机械化及其自动化
+517,农业工程类,082303,农业电气化
+518,农业工程类,082304,农业建筑环境与能源工程
+519,农业工程类,082305,农业水利工程
+520,农业工程类,082306T,土地整治工程
+521,农业工程类,082307T,农业智能装备工程
+522,林业工程类,082401,森林工程
+523,林业工程类,082402,木材科学与工程
+524,林业工程类,082403,林产化工
+525,林业工程类,082404T,家具设计与工程
+526,林业工程类,082405T,木结构建筑与材料
+527,环境科学与工程类,082501,环境科学与工程
+528,环境科学与工程类,082502,环境工程
+529,环境科学与工程类,082503,环境科学
+530,环境科学与工程类,082504,环境生态工程
+531,环境科学与工程类,082505T,环保设备工程
+532,环境科学与工程类,082506T,资源环境科学
+533,环境科学与工程类,082507T,水质科学与技术
+534,生物医学工程类,082601,生物医学工程
+535,生物医学工程类,082602T,假肢矫形工程
+536,生物医学工程类,082603T,临床工程技术
+537,生物医学工程类,082604T,康复工程
+538,生物医学工程类,082605T,健康科学与技术
+539,食品科学与工程类,082701,食品科学与工程
+540,食品科学与工程类,082702,食品质量与安全
+541,食品科学与工程类,082703,粮食工程
+542,食品科学与工程类,082704,乳品工程
+543,食品科学与工程类,082705,酿酒工程
+544,食品科学与工程类,082706T,葡萄与葡萄酒工程
+545,食品科学与工程类,082707T,食品营养与检验教育
+546,食品科学与工程类,082708T,烹饪与营养教育
+547,食品科学与工程类,082709T,食品安全与检测
+548,食品科学与工程类,082710T,食品营养与健康
+549,食品科学与工程类,082711T,食用菌科学与工程
+550,食品科学与工程类,082712T,白酒酿造工程
+551,食品科学与工程类,082713T,咖啡科学与工程
+552,建筑类,082801,建筑学
+553,建筑类,082802,城乡规划
+554,建筑类,082803,风景园林
+555,建筑类,082804T,历史建筑保护工程
+556,建筑类,082805T,人居环境科学与技术
+557,建筑类,082806T,城市设计
+558,建筑类,082807T,智慧建筑与建造
+559,安全科学与工程类,082901,安全工程
+560,安全科学与工程类,082902T,应急技术与管理
+561,安全科学与工程类,082903T,职业卫生工程
+562,安全科学与工程类,082904T,安全生产监管
+563,生物工程类,083001,生物工程
+564,生物工程类,083002T,生物制药
+565,生物工程类,083003T,合成生物学
+566,公安技术类,083101K,刑事科学技术
+567,公安技术类,083102K,消防工程
+568,公安技术类,083103TK,交通管理工程
+569,公安技术类,083104TK,安全防范工程
+570,公安技术类,083105TK,公安视听技术
+571,公安技术类,083106TK,抢险救援指挥与技术
+572,公安技术类,083107TK,火灾勘查
+573,公安技术类,083108TK,网络安全与执法
+574,公安技术类,083109TK,核生化消防
+575,公安技术类,083110TK,海警舰艇指挥与技术
+576,公安技术类,083111TK,数据警务技术
+577,公安技术类,083112TK,食品药品环境犯罪侦查技术
+578,交叉工程类,083201TK,未来机器人
+579,交叉工程类,083202TK,交叉工程
+580,植物生产类,090101,农学
+581,植物生产类,090102,园艺
+582,植物生产类,090103,植物保护
+583,植物生产类,090104,植物科学与技术
+584,植物生产类,090105,种子科学与工程
+585,植物生产类,090106,设施农业科学与工程
+586,植物生产类,090107T,茶学
+587,植物生产类,090108T,烟草
+588,植物生产类,090109T,应用生物科学
+589,植物生产类,090110T,农艺教育
+590,植物生产类,090111T,园艺教育
+591,植物生产类,090112T,智慧农业
+592,植物生产类,090113T,菌物科学与工程
+593,植物生产类,090114T,农药化肥
+594,植物生产类,090115T,生物农药科学与工程
+595,植物生产类,090116TK,生物育种科学
+596,植物生产类,090117TK,生物育种技术
+597,自然保护与环境生态类,090201,农业资源与环境
+598,自然保护与环境生态类,090202,野生动物与自然保护区管理
+599,自然保护与环境生态类,090203,水土保持与荒漠化防治
+600,自然保护与环境生态类,090204T,生物质科学与工程
+601,自然保护与环境生态类,090205T,土地科学与技术
+602,自然保护与环境生态类,090206T,湿地保护与恢复
+603,自然保护与环境生态类,090207TK,国家公园建设与管理
+604,自然保护与环境生态类,090208TK,生态修复学
+605,动物生产类,090301,动物科学
+606,动物生产类,090302T,蚕学
+607,动物生产类,090303T,蜂学
+608,动物生产类,090304T,经济动物学
+609,动物生产类,090305T,马业科学
+610,动物生产类,090306T,饲料工程
+611,动物生产类,090307T,智慧牧业科学与工程
+612,动物医学类,090401,动物医学
+613,动物医学类,090402,动物药学
+614,动物医学类,090403T,动植物检疫
+615,动物医学类,090404T,实验动物学
+616,动物医学类,090405T,中兽医学
+617,动物医学类,090406TK,兽医公共卫生
+618,林学类,090501,林学
+619,林学类,090502,园林
+620,林学类,090503,森林保护
+621,林学类,090504T,经济林
+622,林学类,090505T,智慧林业
+623,水产类,090601,水产养殖学
+624,水产类,090602,海洋渔业科学与技术
+625,水产类,090603T,水族科学与技术
+626,水产类,090604TK,水生动物医学
+627,草学类,090701,草业科学
+628,草学类,090702T,草坪科学与工程
+629,基础医学类,100101K,基础医学
+630,基础医学类,100102TK,生物医学
+631,基础医学类,100103T,生物医学科学
+632,临床医学类,100201K,临床医学
+633,临床医学类,100202TK,麻醉学
+634,临床医学类,100203TK,医学影像学
+635,临床医学类,100204TK,眼视光医学
+636,临床医学类,100205TK,精神医学
+637,临床医学类,100206TK,放射医学
+638,临床医学类,100207TK,儿科学
+639,口腔医学类,100301K,口腔医学
+640,公共卫生与预防医学类,100401K,预防医学
+641,公共卫生与预防医学类,100402,食品卫生与营养学
+642,公共卫生与预防医学类,100403TK,妇幼保健医学
+643,公共卫生与预防医学类,100404TK,卫生监督
+644,公共卫生与预防医学类,100405TK,全球健康学
+645,公共卫生与预防医学类,100406T,运动与公共健康
+646,中医学类,100501K,中医学
+647,中医学类,100502K,针灸推拿学
+648,中医学类,100503K,藏医学
+649,中医学类,100504K,蒙医学
+650,中医学类,100505K,维医学
+651,中医学类,100506K,壮医学
+652,中医学类,100507K,哈医学
+653,中医学类,100508TK,傣医学
+654,中医学类,100509TK,回医学
+655,中医学类,100510TK,中医康复学
+656,中医学类,100511TK,中医养生学
+657,中医学类,100512TK,中医儿科学
+658,中医学类,100513TK,中医骨伤科学
+659,中西医结合类,100601K,中西医临床医学
+660,药学类,100701,药学
+661,药学类,100702,药物制剂
+662,药学类,100703TK,临床药学
+663,药学类,100704T,药事管理
+664,药学类,100705T,药物分析
+665,药学类,100706T,药物化学
+666,药学类,100707T,海洋药学
+667,药学类,100708T,化妆品科学与技术
+668,中药学类,100801,中药学
+669,中药学类,100802,中药资源与开发
+670,中药学类,100803T,藏药学
+671,中药学类,100804T,蒙药学
+672,中药学类,100805T,中药制药
+673,中药学类,100806T,中草药栽培与鉴定
+674,法医学类,100901K,法医学
+675,医学技术类,101001,医学检验技术
+676,医学技术类,101002,医学实验技术
+677,医学技术类,101003,医学影像技术
+678,医学技术类,101004,眼视光学
+679,医学技术类,101005,康复治疗学
+680,医学技术类,101006,口腔医学技术
+681,医学技术类,101007,卫生检验与检疫
+682,医学技术类,101008T,听力与言语康复学
+683,医学技术类,101009T,康复物理治疗
+684,医学技术类,101010T,康复作业治疗
+685,医学技术类,101011T,智能医学工程
+686,医学技术类,101012T,生物医药数据科学
+687,医学技术类,101013T,智能影像工程
+688,医学技术类,101014TK,医工学
+689,护理学类,101101K,护理学
+690,护理学类,101102TK,助产学
+691,管理科学与工程类,120101,管理科学
+692,管理科学与工程类,120102,信息管理与信息系统
+693,管理科学与工程类,120103,工程管理
+694,管理科学与工程类,120104,房地产开发与管理
+695,管理科学与工程类,120105,工程造价
+696,管理科学与工程类,120106TK,保密管理
+697,管理科学与工程类,120107T,邮政管理
+698,管理科学与工程类,120108T,大数据管理与应用
+699,管理科学与工程类,120109T,工程审计
+700,管理科学与工程类,120110T,计算金融
+701,管理科学与工程类,120111T,应急管理
+702,工商管理类,120201K,工商管理
+703,工商管理类,120202,市场营销
+704,工商管理类,120203K,会计学
+705,工商管理类,120204,财务管理
+706,工商管理类,120205,国际商务
+707,工商管理类,120206,人力资源管理
+708,工商管理类,120207,审计学
+709,工商管理类,120208,资产评估
+710,工商管理类,120209,物业管理
+711,工商管理类,120210,文化产业管理
+712,工商管理类,120211T,劳动关系
+713,工商管理类,120212T,体育经济与管理
+714,工商管理类,120213T,财务会计教育
+715,工商管理类,120214T,市场营销教育
+716,工商管理类,120215T,零售业管理
+717,工商管理类,120216T,创业管理
+718,工商管理类,120217TK,海关稽查
+719,工商管理类,120218T,内部审计
+720,农业经济管理类,120301,农林经济管理
+721,农业经济管理类,120302,农村区域发展
+722,农业经济管理类,120303TK,乡村治理
+723,公共管理类,120401,公共事业管理
+724,公共管理类,120402,行政管理
+725,公共管理类,120403,劳动与社会保障
+726,公共管理类,120404,土地资源管理
+727,公共管理类,120405,城市管理
+728,公共管理类,120406TK,海关管理
+729,公共管理类,120407T,交通管理
+730,公共管理类,120408T,海事管理
+731,公共管理类,120409T,公共关系学
+732,公共管理类,120410T,健康服务与管理
+733,公共管理类,120411TK,海警后勤管理
+734,公共管理类,120412T,医疗产品管理
+735,公共管理类,120413T,医疗保险
+736,公共管理类,120414T,养老服务管理
+737,公共管理类,120415TK,海关检验检疫安全
+738,公共管理类,120416TK,海外安全管理
+739,公共管理类,120417T,自然资源登记与管理
+740,公共管理类,120418T,慈善管理
+741,公共管理类,120419TK,航空安防管理
+742,公共管理类,120420TK,无障碍管理
+743,图书情报与档案管理类,120501,图书馆学
+744,图书情报与档案管理类,120502,档案学
+745,图书情报与档案管理类,120503,信息资源管理
+746,物流管理与工程类,120601,物流管理
+747,物流管理与工程类,120602,物流工程
+748,物流管理与工程类,120603T,采购管理
+749,物流管理与工程类,120604T,供应链管理
+750,工业工程类,120701,工业工程
+751,工业工程类,120702T,标准化工程
+752,工业工程类,120703T,质量管理工程
+753,电子商务类,120801,电子商务
+754,电子商务类,120802T,电子商务及法律
+755,电子商务类,120803T,跨境电子商务
+756,旅游管理类,120901K,旅游管理
+757,旅游管理类,120902,酒店管理
+758,旅游管理类,120903,会展经济与管理
+759,旅游管理类,120904T,旅游管理与服务教育
+760,艺术学理论类,130101,艺术史论
+761,艺术学理论类,130102T,艺术管理
+762,艺术学理论类,130103T,非物质文化遗产保护
+763,音乐与舞蹈学类,130201,音乐表演
+764,音乐与舞蹈学类,130202,音乐学
+765,音乐与舞蹈学类,130203,作曲与作曲技术理论
+766,音乐与舞蹈学类,130204,舞蹈表演
+767,音乐与舞蹈学类,130205,舞蹈学
+768,音乐与舞蹈学类,130206,舞蹈编导
+769,音乐与舞蹈学类,130207T,舞蹈教育
+770,音乐与舞蹈学类,130208TK,航空服务艺术与管理
+771,音乐与舞蹈学类,130209T,流行音乐
+772,音乐与舞蹈学类,130210T,音乐治疗
+773,音乐与舞蹈学类,130211T,流行舞蹈
+774,音乐与舞蹈学类,130212T,音乐教育
+775,音乐与舞蹈学类,130213TK,冰雪舞蹈表演
+776,戏剧与影视学类,130301,表演
+777,戏剧与影视学类,130302,戏剧学
+778,戏剧与影视学类,130303,电影学
+779,戏剧与影视学类,130304,戏剧影视文学
+780,戏剧与影视学类,130305,广播电视编导
+781,戏剧与影视学类,130306,戏剧影视导演
+782,戏剧与影视学类,130307,戏剧影视美术设计
+783,戏剧与影视学类,130308,录音艺术
+784,戏剧与影视学类,130309,播音与主持艺术
+785,戏剧与影视学类,130310,动画
+786,戏剧与影视学类,130311T,影视摄影与制作
+787,戏剧与影视学类,130312T,影视技术
+788,戏剧与影视学类,130313T,戏剧教育
+789,戏剧与影视学类,130314TK,曲艺
+790,戏剧与影视学类,130315TK,音乐剧
+791,美术学类,130401,美术学
+792,美术学类,130402,绘画
+793,美术学类,130403,雕塑
+794,美术学类,130404,摄影
+795,美术学类,130405T,书法学
+796,美术学类,130406T,中国画
+797,美术学类,130407TK,实验艺术
+798,美术学类,130408TK,跨媒体艺术
+799,美术学类,130409T,文物保护与修复
+800,美术学类,130410T,漫画
+801,美术学类,130411T,纤维艺术
+802,美术学类,130412TK,科技艺术
+803,美术学类,130413TK,美术教育
+804,设计学类,130501,艺术设计学
+805,设计学类,130502,视觉传达设计
+806,设计学类,130503,环境设计
+807,设计学类,130504,产品设计
+808,设计学类,130505,服装与服饰设计
+809,设计学类,130506,公共艺术
+810,设计学类,130507,工艺美术
+811,设计学类,130508,数字媒体艺术
+812,设计学类,130509T,艺术与科技
+813,设计学类,130510TK,陶瓷艺术设计
+814,设计学类,130511T,新媒体艺术
+815,设计学类,130512T,包装设计
+816,设计学类,130513TK,珠宝首饰设计与工艺

+ 5 - 0
menduner/menduner-im-biz/src/main/resources/major/readme.txt

@@ -0,0 +1,5 @@
+来源
+http://www.moe.gov.cn/srcsite/A08/moe_1034/s4930/202403/W020240319305498791768.pdf
+
+转换成Excel
+https://smallpdf.com/cn/pdf-to-excel#r=convert-to-excel

+ 136 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/area/AreaServiceImplTest.java

@@ -0,0 +1,136 @@
+package com.citu.module.menduner.system.service.area;
+
+import com.citu.module.menduner.system.controller.base.area.AreaPageReqVO;
+import com.citu.module.menduner.system.controller.base.area.AreaSaveReqVO;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.area.AreaDO;
+import com.citu.module.menduner.system.dal.mysql.area.AreaMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_AREA_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link AreaServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(AreaServiceImpl.class)
+public class AreaServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private AreaServiceImpl areaService;
+
+    @Resource
+    private AreaMapper areaMapper;
+
+    @Test
+    public void testCreateArea_success() {
+        // 准备参数
+        AreaSaveReqVO createReqVO = randomPojo(AreaSaveReqVO.class).setId(null);
+
+        // 调用
+        Long areaId = areaService.createArea(createReqVO);
+        // 断言
+        assertNotNull(areaId);
+        // 校验记录的属性是否正确
+        AreaDO area = areaMapper.selectById(areaId);
+        assertPojoEquals(createReqVO, area, "id");
+    }
+
+    @Test
+    public void testUpdateArea_success() {
+        // mock 数据
+        AreaDO dbArea = randomPojo(AreaDO.class);
+        areaMapper.insert(dbArea);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        AreaSaveReqVO updateReqVO = randomPojo(AreaSaveReqVO.class, o -> {
+            o.setId(dbArea.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        areaService.updateArea(updateReqVO);
+        // 校验是否更新正确
+        AreaDO area = areaMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, area);
+    }
+
+    @Test
+    public void testUpdateArea_notExists() {
+        // 准备参数
+        AreaSaveReqVO updateReqVO = randomPojo(AreaSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> areaService.updateArea(updateReqVO), MDE_AREA_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteArea_success() {
+        // mock 数据
+        AreaDO dbArea = randomPojo(AreaDO.class);
+        areaMapper.insert(dbArea);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbArea.getId();
+
+        // 调用
+        areaService.deleteArea(id);
+       // 校验数据不存在了
+       assertNull(areaMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteArea_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> areaService.deleteArea(id), MDE_AREA_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetAreaPage() {
+       // mock 数据
+       AreaDO dbArea = randomPojo(AreaDO.class, o -> { // 等会查询到
+           o.setName(null);
+           o.setType(null);
+           o.setParentId(null);
+           o.setCreateTime(null);
+       });
+       areaMapper.insert(dbArea);
+       // 测试 name 不匹配
+       areaMapper.insert(cloneIgnoreId(dbArea, o -> o.setName(null)));
+       // 测试 type 不匹配
+       areaMapper.insert(cloneIgnoreId(dbArea, o -> o.setType(null)));
+       // 测试 parentId 不匹配
+       areaMapper.insert(cloneIgnoreId(dbArea, o -> o.setParentId(null)));
+       // 测试 createTime 不匹配
+       areaMapper.insert(cloneIgnoreId(dbArea, o -> o.setCreateTime(null)));
+       // 准备参数
+       AreaPageReqVO reqVO = new AreaPageReqVO();
+       reqVO.setName(null);
+       reqVO.setType(null);
+       reqVO.setParentId(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<AreaDO> pageResult = areaService.getAreaPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbArea, pageResult.getList().get(0));
+    }
+
+}

+ 140 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/certificate/CertificateServiceImplTest.java

@@ -0,0 +1,140 @@
+package com.citu.module.menduner.system.service.certificate;
+
+import com.citu.module.menduner.system.controller.base.certificate.CertificatePageReqVO;
+import com.citu.module.menduner.system.controller.base.certificate.CertificateSaveReqVO;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.certificate.CertificateDO;
+import com.citu.module.menduner.system.dal.mysql.certificate.CertificateMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_CERTIFICATE_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link CertificateServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(CertificateServiceImpl.class)
+public class CertificateServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private CertificateServiceImpl certificateService;
+
+    @Resource
+    private CertificateMapper certificateMapper;
+
+    @Test
+    public void testCreateCertificate_success() {
+        // 准备参数
+        CertificateSaveReqVO createReqVO = randomPojo(CertificateSaveReqVO.class).setId(null);
+
+        // 调用
+        Long certificateId = certificateService.createCertificate(createReqVO);
+        // 断言
+        assertNotNull(certificateId);
+        // 校验记录的属性是否正确
+        CertificateDO certificate = certificateMapper.selectById(certificateId);
+        assertPojoEquals(createReqVO, certificate, "id");
+    }
+
+    @Test
+    public void testUpdateCertificate_success() {
+        // mock 数据
+        CertificateDO dbCertificate = randomPojo(CertificateDO.class);
+        certificateMapper.insert(dbCertificate);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        CertificateSaveReqVO updateReqVO = randomPojo(CertificateSaveReqVO.class, o -> {
+            o.setId(dbCertificate.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        certificateService.updateCertificate(updateReqVO);
+        // 校验是否更新正确
+        CertificateDO certificate = certificateMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, certificate);
+    }
+
+    @Test
+    public void testUpdateCertificate_notExists() {
+        // 准备参数
+        CertificateSaveReqVO updateReqVO = randomPojo(CertificateSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> certificateService.updateCertificate(updateReqVO), MDE_CERTIFICATE_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteCertificate_success() {
+        // mock 数据
+        CertificateDO dbCertificate = randomPojo(CertificateDO.class);
+        certificateMapper.insert(dbCertificate);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbCertificate.getId();
+
+        // 调用
+        certificateService.deleteCertificate(id);
+       // 校验数据不存在了
+       assertNull(certificateMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteCertificate_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> certificateService.deleteCertificate(id), MDE_CERTIFICATE_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetCertificatePage() {
+       // mock 数据
+       CertificateDO dbCertificate = randomPojo(CertificateDO.class, o -> { // 等会查询到
+           o.setNameCn(null);
+           o.setNameEn(null);
+           o.setParentId(null);
+           o.setLevel(null);
+           o.setCreateTime(null);
+       });
+       certificateMapper.insert(dbCertificate);
+       // 测试 nameCn 不匹配
+       certificateMapper.insert(cloneIgnoreId(dbCertificate, o -> o.setNameCn(null)));
+       // 测试 nameEn 不匹配
+       certificateMapper.insert(cloneIgnoreId(dbCertificate, o -> o.setNameEn(null)));
+       // 测试 parentId 不匹配
+       certificateMapper.insert(cloneIgnoreId(dbCertificate, o -> o.setParentId(null)));
+       // 测试 level 不匹配
+       certificateMapper.insert(cloneIgnoreId(dbCertificate, o -> o.setLevel(null)));
+       // 测试 createTime 不匹配
+       certificateMapper.insert(cloneIgnoreId(dbCertificate, o -> o.setCreateTime(null)));
+       // 准备参数
+       CertificatePageReqVO reqVO = new CertificatePageReqVO();
+       reqVO.setNameCn(null);
+       reqVO.setNameEn(null);
+       reqVO.setParentId(null);
+       reqVO.setLevel(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<CertificateDO> pageResult = certificateService.getCertificatePage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbCertificate, pageResult.getList().get(0));
+    }
+
+}

+ 136 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/cvattachment/CvAttachmentServiceImplTest.java

@@ -0,0 +1,136 @@
+package com.citu.module.menduner.system.service.cvattachment;
+
+import com.citu.module.menduner.system.controller.base.cvattachment.CvAttachmentPageReqVO;
+import com.citu.module.menduner.system.controller.base.cvattachment.CvAttachmentSaveReqVO;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.cvattachment.CvAttachmentDO;
+import com.citu.module.menduner.system.dal.mysql.cvattachment.CvAttachmentMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_CV_ATTACHMENT_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link CvAttachmentServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(CvAttachmentServiceImpl.class)
+public class CvAttachmentServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private CvAttachmentServiceImpl cvAttachmentService;
+
+    @Resource
+    private CvAttachmentMapper cvAttachmentMapper;
+
+    @Test
+    public void testCreateCvAttachment_success() {
+        // 准备参数
+        CvAttachmentSaveReqVO createReqVO = randomPojo(CvAttachmentSaveReqVO.class).setId(null);
+
+        // 调用
+        Long cvAttachmentId = cvAttachmentService.createCvAttachment(createReqVO);
+        // 断言
+        assertNotNull(cvAttachmentId);
+        // 校验记录的属性是否正确
+        CvAttachmentDO cvAttachment = cvAttachmentMapper.selectById(cvAttachmentId);
+        assertPojoEquals(createReqVO, cvAttachment, "id");
+    }
+
+    @Test
+    public void testUpdateCvAttachment_success() {
+        // mock 数据
+        CvAttachmentDO dbCvAttachment = randomPojo(CvAttachmentDO.class);
+        cvAttachmentMapper.insert(dbCvAttachment);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        CvAttachmentSaveReqVO updateReqVO = randomPojo(CvAttachmentSaveReqVO.class, o -> {
+            o.setId(dbCvAttachment.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        cvAttachmentService.updateCvAttachment(updateReqVO);
+        // 校验是否更新正确
+        CvAttachmentDO cvAttachment = cvAttachmentMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, cvAttachment);
+    }
+
+    @Test
+    public void testUpdateCvAttachment_notExists() {
+        // 准备参数
+        CvAttachmentSaveReqVO updateReqVO = randomPojo(CvAttachmentSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> cvAttachmentService.updateCvAttachment(updateReqVO), MDE_CV_ATTACHMENT_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteCvAttachment_success() {
+        // mock 数据
+        CvAttachmentDO dbCvAttachment = randomPojo(CvAttachmentDO.class);
+        cvAttachmentMapper.insert(dbCvAttachment);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbCvAttachment.getId();
+
+        // 调用
+        cvAttachmentService.deleteCvAttachment(id);
+       // 校验数据不存在了
+       assertNull(cvAttachmentMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteCvAttachment_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> cvAttachmentService.deleteCvAttachment(id), MDE_CV_ATTACHMENT_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetCvAttachmentPage() {
+       // mock 数据
+       CvAttachmentDO dbCvAttachment = randomPojo(CvAttachmentDO.class, o -> { // 等会查询到
+           o.setUserId(null);
+           o.setTitle(null);
+           o.setUrl(null);
+           o.setCreateTime(null);
+       });
+       cvAttachmentMapper.insert(dbCvAttachment);
+       // 测试 userId 不匹配
+       cvAttachmentMapper.insert(cloneIgnoreId(dbCvAttachment, o -> o.setUserId(null)));
+       // 测试 title 不匹配
+       cvAttachmentMapper.insert(cloneIgnoreId(dbCvAttachment, o -> o.setTitle(null)));
+       // 测试 url 不匹配
+       cvAttachmentMapper.insert(cloneIgnoreId(dbCvAttachment, o -> o.setUrl(null)));
+       // 测试 createTime 不匹配
+       cvAttachmentMapper.insert(cloneIgnoreId(dbCvAttachment, o -> o.setCreateTime(null)));
+       // 准备参数
+       CvAttachmentPageReqVO reqVO = new CvAttachmentPageReqVO();
+       reqVO.setUserId(null);
+       reqVO.setTitle(null);
+       reqVO.setUrl(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<CvAttachmentDO> pageResult = cvAttachmentService.getCvAttachmentPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbCvAttachment, pageResult.getList().get(0));
+    }
+
+}

+ 166 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/eduexp/EduExpServiceImplTest.java

@@ -0,0 +1,166 @@
+package com.citu.module.menduner.system.service.eduexp;
+
+import com.citu.module.menduner.system.controller.base.eduexp.EduExpPageReqVO;
+import com.citu.module.menduner.system.controller.base.eduexp.EduExpSaveReqVO;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.eduexp.EduExpDO;
+import com.citu.module.menduner.system.dal.mysql.eduexp.EduExpMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import java.time.LocalDateTime;
+
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_EDU_EXP_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link EduExpServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(EduExpServiceImpl.class)
+public class EduExpServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private EduExpServiceImpl eduExpService;
+
+    @Resource
+    private EduExpMapper eduExpMapper;
+
+    @Test
+    public void testCreateEduExp_success() {
+        // 准备参数
+        EduExpSaveReqVO createReqVO = randomPojo(EduExpSaveReqVO.class).setId(null);
+
+        // 调用
+        Long eduExpId = eduExpService.createEduExp(createReqVO);
+        // 断言
+        assertNotNull(eduExpId);
+        // 校验记录的属性是否正确
+        EduExpDO eduExp = eduExpMapper.selectById(eduExpId);
+        assertPojoEquals(createReqVO, eduExp, "id");
+    }
+
+    @Test
+    public void testUpdateEduExp_success() {
+        // mock 数据
+        EduExpDO dbEduExp = randomPojo(EduExpDO.class);
+        eduExpMapper.insert(dbEduExp);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        EduExpSaveReqVO updateReqVO = randomPojo(EduExpSaveReqVO.class, o -> {
+            o.setId(dbEduExp.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        eduExpService.updateEduExp(updateReqVO);
+        // 校验是否更新正确
+        EduExpDO eduExp = eduExpMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, eduExp);
+    }
+
+    @Test
+    public void testUpdateEduExp_notExists() {
+        // 准备参数
+        EduExpSaveReqVO updateReqVO = randomPojo(EduExpSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> eduExpService.updateEduExp(updateReqVO), MDE_EDU_EXP_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteEduExp_success() {
+        // mock 数据
+        EduExpDO dbEduExp = randomPojo(EduExpDO.class);
+        eduExpMapper.insert(dbEduExp);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbEduExp.getId();
+
+        // 调用
+        eduExpService.deleteEduExp(id);
+       // 校验数据不存在了
+       assertNull(eduExpMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteEduExp_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> eduExpService.deleteEduExp(id), MDE_EDU_EXP_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetEduExpPage() {
+       // mock 数据
+       EduExpDO dbEduExp = randomPojo(EduExpDO.class, o -> { // 等会查询到
+           o.setUserId(null);
+           o.setSchoolId(null);
+           o.setSchoolName(null);
+           o.setEducationType(null);
+           o.setEducationSystemType(null);
+           o.setMajorId(null);
+           o.setMajor(null);
+           o.setStartTime(null);
+           o.setEndTime(null);
+           o.setContent(null);
+           o.setCreateTime(null);
+       });
+       eduExpMapper.insert(dbEduExp);
+       // 测试 userId 不匹配
+       eduExpMapper.insert(cloneIgnoreId(dbEduExp, o -> o.setUserId(null)));
+       // 测试 schoolId 不匹配
+       eduExpMapper.insert(cloneIgnoreId(dbEduExp, o -> o.setSchoolId(null)));
+       // 测试 schoolName 不匹配
+       eduExpMapper.insert(cloneIgnoreId(dbEduExp, o -> o.setSchoolName(null)));
+       // 测试 educationType 不匹配
+       eduExpMapper.insert(cloneIgnoreId(dbEduExp, o -> o.setEducationType(null)));
+       // 测试 educationSystemType 不匹配
+       eduExpMapper.insert(cloneIgnoreId(dbEduExp, o -> o.setEducationSystemType(null)));
+       // 测试 majorId 不匹配
+       eduExpMapper.insert(cloneIgnoreId(dbEduExp, o -> o.setMajorId(null)));
+       // 测试 major 不匹配
+       eduExpMapper.insert(cloneIgnoreId(dbEduExp, o -> o.setMajor(null)));
+       // 测试 startTime 不匹配
+       eduExpMapper.insert(cloneIgnoreId(dbEduExp, o -> o.setStartTime(null)));
+       // 测试 endTime 不匹配
+       eduExpMapper.insert(cloneIgnoreId(dbEduExp, o -> o.setEndTime(null)));
+       // 测试 content 不匹配
+       eduExpMapper.insert(cloneIgnoreId(dbEduExp, o -> o.setContent(null)));
+       // 测试 createTime 不匹配
+       eduExpMapper.insert(cloneIgnoreId(dbEduExp, o -> o.setCreateTime(null)));
+       // 准备参数
+       EduExpPageReqVO reqVO = new EduExpPageReqVO();
+       reqVO.setUserId(null);
+       reqVO.setSchoolId(null);
+       reqVO.setSchoolName(null);
+       reqVO.setEducationType(null);
+       reqVO.setEducationSystemType(null);
+       reqVO.setMajorId(null);
+       reqVO.setMajor(null);
+       reqVO.setStartTime(LocalDateTime.now());
+       reqVO.setEndTime(LocalDateTime.now());
+       reqVO.setContent(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<EduExpDO> pageResult = eduExpService.getEduExpPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbEduExp, pageResult.getList().get(0));
+    }
+
+}

+ 133 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseAddressServiceImplTest.java

@@ -0,0 +1,133 @@
+package com.citu.module.menduner.system.service.enterprise;
+
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.enterprise.address.EnterpriseAddressPageReqVO;
+import com.citu.module.menduner.system.controller.base.enterprise.address.EnterpriseAddressSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.enterprise.EnterpriseAddressDO;
+import com.citu.module.menduner.system.dal.mysql.enterprise.EnterpriseAddressMapper;
+import com.citu.module.menduner.system.service.enterprise.address.EnterpriseAddressServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_ENTERPRISE_ADDRESS_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link EnterpriseAddressServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(EnterpriseAddressServiceImpl.class)
+public class EnterpriseAddressServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private EnterpriseAddressServiceImpl enterpriseAddressService;
+
+    @Resource
+    private EnterpriseAddressMapper enterpriseAddressMapper;
+
+    @Test
+    public void testCreateEnterpriseAddress_success() {
+        // 准备参数
+        EnterpriseAddressSaveReqVO createReqVO = randomPojo(EnterpriseAddressSaveReqVO.class).setId(null);
+
+        // 调用
+        Long enterpriseAddressId = enterpriseAddressService.createEnterpriseAddress(createReqVO);
+        // 断言
+        assertNotNull(enterpriseAddressId);
+        // 校验记录的属性是否正确
+        EnterpriseAddressDO enterpriseAddress = enterpriseAddressMapper.selectById(enterpriseAddressId);
+        assertPojoEquals(createReqVO, enterpriseAddress, "id");
+    }
+
+    @Test
+    public void testUpdateEnterpriseAddress_success() {
+        // mock 数据
+        EnterpriseAddressDO dbEnterpriseAddress = randomPojo(EnterpriseAddressDO.class);
+        enterpriseAddressMapper.insert(dbEnterpriseAddress);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        EnterpriseAddressSaveReqVO updateReqVO = randomPojo(EnterpriseAddressSaveReqVO.class, o -> {
+            o.setId(dbEnterpriseAddress.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        enterpriseAddressService.updateEnterpriseAddress(updateReqVO);
+        // 校验是否更新正确
+        EnterpriseAddressDO enterpriseAddress = enterpriseAddressMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, enterpriseAddress);
+    }
+
+    @Test
+    public void testUpdateEnterpriseAddress_notExists() {
+        // 准备参数
+        EnterpriseAddressSaveReqVO updateReqVO = randomPojo(EnterpriseAddressSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseAddressService.updateEnterpriseAddress(updateReqVO), MDE_ENTERPRISE_ADDRESS_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteEnterpriseAddress_success() {
+        // mock 数据
+        EnterpriseAddressDO dbEnterpriseAddress = randomPojo(EnterpriseAddressDO.class);
+        enterpriseAddressMapper.insert(dbEnterpriseAddress);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbEnterpriseAddress.getId();
+
+        // 调用
+        enterpriseAddressService.deleteEnterpriseAddress(id);
+        // 校验数据不存在了
+        assertNull(enterpriseAddressMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteEnterpriseAddress_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseAddressService.deleteEnterpriseAddress(id), MDE_ENTERPRISE_ADDRESS_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetEnterpriseAddressPage() {
+        // mock 数据
+        EnterpriseAddressDO dbEnterpriseAddress = randomPojo(EnterpriseAddressDO.class, o -> { // 等会查询到
+            o.setEnterpriseId(null);
+            o.setAddress(null);
+            o.setCreateTime(null);
+        });
+        enterpriseAddressMapper.insert(dbEnterpriseAddress);
+        // 测试 enterpriseId 不匹配
+        enterpriseAddressMapper.insert(cloneIgnoreId(dbEnterpriseAddress, o -> o.setEnterpriseId(null)));
+        // 测试 address 不匹配
+        enterpriseAddressMapper.insert(cloneIgnoreId(dbEnterpriseAddress, o -> o.setAddress(null)));
+        // 测试 createTime 不匹配
+        enterpriseAddressMapper.insert(cloneIgnoreId(dbEnterpriseAddress, o -> o.setCreateTime(null)));
+        // 准备参数
+        EnterpriseAddressPageReqVO reqVO = new EnterpriseAddressPageReqVO();
+        reqVO.setEnterpriseId(null);
+        reqVO.setAddress(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<EnterpriseAddressDO> pageResult = enterpriseAddressService.getEnterpriseAddressPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbEnterpriseAddress, pageResult.getList().get(0));
+    }
+
+}

+ 179 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseBusinessServiceImplTest.java

@@ -0,0 +1,179 @@
+package com.citu.module.menduner.system.service.enterprise;
+
+
+import com.citu.module.menduner.system.controller.base.enterprise.business.EnterpriseBusinessPageReqVO;
+import com.citu.module.menduner.system.controller.base.enterprise.business.EnterpriseBusinessSaveReqVO;
+import com.citu.module.menduner.system.service.enterprise.business.EnterpriseBusinessServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.enterprise.EnterpriseBusinessDO;
+import com.citu.module.menduner.system.dal.mysql.enterprise.EnterpriseBusinessMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import java.time.LocalDateTime;
+
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_ENTERPRISE_BUSINESS_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link EnterpriseBusinessServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(EnterpriseBusinessServiceImpl.class)
+public class EnterpriseBusinessServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private EnterpriseBusinessServiceImpl enterpriseBusinessService;
+
+    @Resource
+    private EnterpriseBusinessMapper enterpriseBusinessMapper;
+
+    @Test
+    public void testCreateEnterpriseBusiness_success() {
+        // 准备参数
+        EnterpriseBusinessSaveReqVO createReqVO = randomPojo(EnterpriseBusinessSaveReqVO.class).setId(null);
+
+        // 调用
+        Long enterpriseBusinessId = enterpriseBusinessService.createEnterpriseBusiness(createReqVO);
+        // 断言
+        assertNotNull(enterpriseBusinessId);
+        // 校验记录的属性是否正确
+        EnterpriseBusinessDO enterpriseBusiness = enterpriseBusinessMapper.selectById(enterpriseBusinessId);
+        assertPojoEquals(createReqVO, enterpriseBusiness, "id");
+    }
+
+    @Test
+    public void testUpdateEnterpriseBusiness_success() {
+        // mock 数据
+        EnterpriseBusinessDO dbEnterpriseBusiness = randomPojo(EnterpriseBusinessDO.class);
+        enterpriseBusinessMapper.insert(dbEnterpriseBusiness);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        EnterpriseBusinessSaveReqVO updateReqVO = randomPojo(EnterpriseBusinessSaveReqVO.class, o -> {
+            o.setId(dbEnterpriseBusiness.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        enterpriseBusinessService.updateEnterpriseBusiness(updateReqVO);
+        // 校验是否更新正确
+        EnterpriseBusinessDO enterpriseBusiness = enterpriseBusinessMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, enterpriseBusiness);
+    }
+
+    @Test
+    public void testUpdateEnterpriseBusiness_notExists() {
+        // 准备参数
+        EnterpriseBusinessSaveReqVO updateReqVO = randomPojo(EnterpriseBusinessSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseBusinessService.updateEnterpriseBusiness(updateReqVO), MDE_ENTERPRISE_BUSINESS_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteEnterpriseBusiness_success() {
+        // mock 数据
+        EnterpriseBusinessDO dbEnterpriseBusiness = randomPojo(EnterpriseBusinessDO.class);
+        enterpriseBusinessMapper.insert(dbEnterpriseBusiness);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbEnterpriseBusiness.getId();
+
+        // 调用
+        enterpriseBusinessService.deleteEnterpriseBusiness(id);
+        // 校验数据不存在了
+        assertNull(enterpriseBusinessMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteEnterpriseBusiness_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseBusinessService.deleteEnterpriseBusiness(id), MDE_ENTERPRISE_BUSINESS_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetEnterpriseBusinessPage() {
+        // mock 数据
+        EnterpriseBusinessDO dbEnterpriseBusiness = randomPojo(EnterpriseBusinessDO.class, o -> { // 等会查询到
+            o.setEnterpriseId(null);
+            o.setCode(null);
+            o.setName(null);
+            o.setType(null);
+            o.setArea(null);
+            o.setAddress(null);
+            o.setRepresentative(null);
+            o.setEstablishmentTime(null);
+            o.setApprovalTime(null);
+            o.setFormerName(null);
+            o.setIndustry(null);
+            o.setRegistrationAuthority(null);
+            o.setBusinessStatus(null);
+            o.setCreateTime(null);
+        });
+        enterpriseBusinessMapper.insert(dbEnterpriseBusiness);
+        // 测试 enterpriseId 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setEnterpriseId(null)));
+        // 测试 code 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setCode(null)));
+        // 测试 name 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setName(null)));
+        // 测试 type 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setType(null)));
+        // 测试 area 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setArea(null)));
+        // 测试 address 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setAddress(null)));
+        // 测试 representative 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setRepresentative(null)));
+        // 测试 establishmentTime 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setEstablishmentTime(null)));
+        // 测试 approvalTime 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setApprovalTime(null)));
+        // 测试 formerName 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setFormerName(null)));
+        // 测试 industry 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setIndustry(null)));
+        // 测试 registrationAuthority 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setRegistrationAuthority(null)));
+        // 测试 businessStatus 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setBusinessStatus(null)));
+        // 测试 createTime 不匹配
+        enterpriseBusinessMapper.insert(cloneIgnoreId(dbEnterpriseBusiness, o -> o.setCreateTime(null)));
+        // 准备参数
+        EnterpriseBusinessPageReqVO reqVO = new EnterpriseBusinessPageReqVO();
+        reqVO.setEnterpriseId(null);
+        reqVO.setCode(null);
+        reqVO.setName(null);
+        reqVO.setType(null);
+        reqVO.setArea(null);
+        reqVO.setAddress(null);
+        reqVO.setRepresentative(null);
+        reqVO.setEstablishmentTime(LocalDateTime.now());
+        reqVO.setFormerName(null);
+        reqVO.setIndustry(null);
+        reqVO.setRegistrationAuthority(null);
+        reqVO.setBusinessStatus(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<EnterpriseBusinessDO> pageResult = enterpriseBusinessService.getEnterpriseBusinessPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbEnterpriseBusiness, pageResult.getList().get(0));
+    }
+
+}

+ 144 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterprisePostServiceImplTest.java

@@ -0,0 +1,144 @@
+package com.citu.module.menduner.system.service.enterprise;
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.enterprise.post.EnterprisePostPageReqVO;
+import com.citu.module.menduner.system.controller.base.enterprise.post.EnterprisePostSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.enterprise.EnterprisePostDO;
+import com.citu.module.menduner.system.dal.mysql.enterprise.EnterprisePostMapper;
+import com.citu.module.menduner.system.service.enterprise.post.EnterprisePostServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.ENTERPRISE_POST_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link EnterprisePostServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(EnterprisePostServiceImpl.class)
+public class EnterprisePostServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private EnterprisePostServiceImpl enterprisePostService;
+
+    @Resource
+    private EnterprisePostMapper enterprisePostMapper;
+
+    @Test
+    public void testCreateEnterprisePost_success() {
+        // 准备参数
+        EnterprisePostSaveReqVO createReqVO = randomPojo(EnterprisePostSaveReqVO.class).setId(null);
+
+        // 调用
+        Long enterprisePostId = enterprisePostService.createEnterprisePost(createReqVO);
+        // 断言
+        assertNotNull(enterprisePostId);
+        // 校验记录的属性是否正确
+        EnterprisePostDO enterprisePost = enterprisePostMapper.selectById(enterprisePostId);
+        assertPojoEquals(createReqVO, enterprisePost, "id");
+    }
+
+    @Test
+    public void testUpdateEnterprisePost_success() {
+        // mock 数据
+        EnterprisePostDO dbEnterprisePost = randomPojo(EnterprisePostDO.class);
+        enterprisePostMapper.insert(dbEnterprisePost);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        EnterprisePostSaveReqVO updateReqVO = randomPojo(EnterprisePostSaveReqVO.class, o -> {
+            o.setId(dbEnterprisePost.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        enterprisePostService.updateEnterprisePost(updateReqVO);
+        // 校验是否更新正确
+        EnterprisePostDO enterprisePost = enterprisePostMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, enterprisePost);
+    }
+
+    @Test
+    public void testUpdateEnterprisePost_notExists() {
+        // 准备参数
+        EnterprisePostSaveReqVO updateReqVO = randomPojo(EnterprisePostSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterprisePostService.updateEnterprisePost(updateReqVO), ENTERPRISE_POST_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteEnterprisePost_success() {
+        // mock 数据
+        EnterprisePostDO dbEnterprisePost = randomPojo(EnterprisePostDO.class);
+        enterprisePostMapper.insert(dbEnterprisePost);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbEnterprisePost.getId();
+
+        // 调用
+        enterprisePostService.deleteEnterprisePost(id);
+        // 校验数据不存在了
+        assertNull(enterprisePostMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteEnterprisePost_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterprisePostService.deleteEnterprisePost(id), ENTERPRISE_POST_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetEnterprisePostPage() {
+        // mock 数据
+        EnterprisePostDO dbEnterprisePost = randomPojo(EnterprisePostDO.class, o -> { // 等会查询到
+            o.setEnterpriseId(null);
+            o.setCode(null);
+            o.setNameCn(null);
+            o.setNameEn(null);
+            o.setStatus(null);
+            o.setCreateTime(null);
+        });
+        enterprisePostMapper.insert(dbEnterprisePost);
+        // 测试 enterpriseId 不匹配
+        enterprisePostMapper.insert(cloneIgnoreId(dbEnterprisePost, o -> o.setEnterpriseId(null)));
+        // 测试 code 不匹配
+        enterprisePostMapper.insert(cloneIgnoreId(dbEnterprisePost, o -> o.setCode(null)));
+        // 测试 nameCn 不匹配
+        enterprisePostMapper.insert(cloneIgnoreId(dbEnterprisePost, o -> o.setNameCn(null)));
+        // 测试 nameEn 不匹配
+        enterprisePostMapper.insert(cloneIgnoreId(dbEnterprisePost, o -> o.setNameEn(null)));
+        // 测试 status 不匹配
+        enterprisePostMapper.insert(cloneIgnoreId(dbEnterprisePost, o -> o.setStatus(null)));
+        // 测试 createTime 不匹配
+        enterprisePostMapper.insert(cloneIgnoreId(dbEnterprisePost, o -> o.setCreateTime(null)));
+        // 准备参数
+        EnterprisePostPageReqVO reqVO = new EnterprisePostPageReqVO();
+        reqVO.setEnterpriseId(null);
+        reqVO.setCode(null);
+        reqVO.setNameCn(null);
+        reqVO.setNameEn(null);
+        reqVO.setStatus(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<EnterprisePostDO> pageResult = enterprisePostService.getEnterprisePostPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbEnterprisePost, pageResult.getList().get(0));
+    }
+
+}

+ 144 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseRegisterServiceImplTest.java

@@ -0,0 +1,144 @@
+package com.citu.module.menduner.system.service.enterprise;
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.enterprise.register.EnterpriseRegisterPageReqVO;
+import com.citu.module.menduner.system.controller.base.enterprise.register.EnterpriseRegisterSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.enterprise.EnterpriseRegisterDO;
+import com.citu.module.menduner.system.dal.mysql.enterprise.EnterpriseRegisterMapper;
+import com.citu.module.menduner.system.service.enterprise.register.EnterpriseRegisterServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_ENTERPRISE_REGISTER_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link EnterpriseRegisterServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(EnterpriseRegisterServiceImpl.class)
+public class EnterpriseRegisterServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private EnterpriseRegisterServiceImpl enterpriseRegisterService;
+
+    @Resource
+    private EnterpriseRegisterMapper enterpriseRegisterMapper;
+
+    @Test
+    public void testCreateEnterpriseRegister_success() {
+        // 准备参数
+        EnterpriseRegisterSaveReqVO createReqVO = randomPojo(EnterpriseRegisterSaveReqVO.class).setId(null);
+
+        // 调用
+        Long enterpriseRegisterId = enterpriseRegisterService.createEnterpriseRegister(createReqVO);
+        // 断言
+        assertNotNull(enterpriseRegisterId);
+        // 校验记录的属性是否正确
+        EnterpriseRegisterDO enterpriseRegister = enterpriseRegisterMapper.selectById(enterpriseRegisterId);
+        assertPojoEquals(createReqVO, enterpriseRegister, "id");
+    }
+
+    @Test
+    public void testUpdateEnterpriseRegister_success() {
+        // mock 数据
+        EnterpriseRegisterDO dbEnterpriseRegister = randomPojo(EnterpriseRegisterDO.class);
+        enterpriseRegisterMapper.insert(dbEnterpriseRegister);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        EnterpriseRegisterSaveReqVO updateReqVO = randomPojo(EnterpriseRegisterSaveReqVO.class, o -> {
+            o.setId(dbEnterpriseRegister.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        enterpriseRegisterService.updateEnterpriseRegister(updateReqVO);
+        // 校验是否更新正确
+        EnterpriseRegisterDO enterpriseRegister = enterpriseRegisterMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, enterpriseRegister);
+    }
+
+    @Test
+    public void testUpdateEnterpriseRegister_notExists() {
+        // 准备参数
+        EnterpriseRegisterSaveReqVO updateReqVO = randomPojo(EnterpriseRegisterSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseRegisterService.updateEnterpriseRegister(updateReqVO), MDE_ENTERPRISE_REGISTER_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteEnterpriseRegister_success() {
+        // mock 数据
+        EnterpriseRegisterDO dbEnterpriseRegister = randomPojo(EnterpriseRegisterDO.class);
+        enterpriseRegisterMapper.insert(dbEnterpriseRegister);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbEnterpriseRegister.getId();
+
+        // 调用
+        enterpriseRegisterService.deleteEnterpriseRegister(id);
+        // 校验数据不存在了
+        assertNull(enterpriseRegisterMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteEnterpriseRegister_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseRegisterService.deleteEnterpriseRegister(id), MDE_ENTERPRISE_REGISTER_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetEnterpriseRegisterPage() {
+        // mock 数据
+        EnterpriseRegisterDO dbEnterpriseRegister = randomPojo(EnterpriseRegisterDO.class, o -> { // 等会查询到
+            o.setUserId(null);
+            o.setName(null);
+            o.setPhone(null);
+            o.setEmail(null);
+            o.setStatus(null);
+            o.setCreateTime(null);
+        });
+        enterpriseRegisterMapper.insert(dbEnterpriseRegister);
+        // 测试 userId 不匹配
+        enterpriseRegisterMapper.insert(cloneIgnoreId(dbEnterpriseRegister, o -> o.setUserId(null)));
+        // 测试 name 不匹配
+        enterpriseRegisterMapper.insert(cloneIgnoreId(dbEnterpriseRegister, o -> o.setName(null)));
+        // 测试 phone 不匹配
+        enterpriseRegisterMapper.insert(cloneIgnoreId(dbEnterpriseRegister, o -> o.setPhone(null)));
+        // 测试 email 不匹配
+        enterpriseRegisterMapper.insert(cloneIgnoreId(dbEnterpriseRegister, o -> o.setEmail(null)));
+        // 测试 status 不匹配
+        enterpriseRegisterMapper.insert(cloneIgnoreId(dbEnterpriseRegister, o -> o.setStatus(null)));
+        // 测试 createTime 不匹配
+        enterpriseRegisterMapper.insert(cloneIgnoreId(dbEnterpriseRegister, o -> o.setCreateTime(null)));
+        // 准备参数
+        EnterpriseRegisterPageReqVO reqVO = new EnterpriseRegisterPageReqVO();
+        reqVO.setUserId(null);
+        reqVO.setName(null);
+        reqVO.setPhone(null);
+        reqVO.setEmail(null);
+        reqVO.setStatus(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<EnterpriseRegisterDO> pageResult = enterpriseRegisterService.getEnterpriseRegisterPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbEnterpriseRegister, pageResult.getList().get(0));
+    }
+
+}

+ 152 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseServiceImplTest.java

@@ -0,0 +1,152 @@
+package com.citu.module.menduner.system.service.enterprise;
+
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.enterprise.vo.EnterprisePageReqVO;
+import com.citu.module.menduner.system.controller.base.enterprise.vo.EnterpriseSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.enterprise.EnterpriseDO;
+import com.citu.module.menduner.system.dal.mysql.enterprise.EnterpriseMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_ENTERPRISE_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link EnterpriseServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(EnterpriseServiceImpl.class)
+public class EnterpriseServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private EnterpriseServiceImpl enterpriseService;
+
+    @Resource
+    private EnterpriseMapper enterpriseMapper;
+
+    @Test
+    public void testCreateEnterprise_success() {
+        // 准备参数
+        EnterpriseSaveReqVO createReqVO = randomPojo(EnterpriseSaveReqVO.class).setId(null);
+
+        // 调用
+        Long enterpriseId = enterpriseService.createEnterprise(createReqVO);
+        // 断言
+        assertNotNull(enterpriseId);
+        // 校验记录的属性是否正确
+        EnterpriseDO enterprise = enterpriseMapper.selectById(enterpriseId);
+        assertPojoEquals(createReqVO, enterprise, "id");
+    }
+
+    @Test
+    public void testUpdateEnterprise_success() {
+        // mock 数据
+        EnterpriseDO dbEnterprise = randomPojo(EnterpriseDO.class);
+        enterpriseMapper.insert(dbEnterprise);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        EnterpriseSaveReqVO updateReqVO = randomPojo(EnterpriseSaveReqVO.class, o -> {
+            o.setId(dbEnterprise.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        enterpriseService.updateEnterprise(updateReqVO);
+        // 校验是否更新正确
+        EnterpriseDO enterprise = enterpriseMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, enterprise);
+    }
+
+    @Test
+    public void testUpdateEnterprise_notExists() {
+        // 准备参数
+        EnterpriseSaveReqVO updateReqVO = randomPojo(EnterpriseSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseService.updateEnterprise(updateReqVO), MDE_ENTERPRISE_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteEnterprise_success() {
+        // mock 数据
+        EnterpriseDO dbEnterprise = randomPojo(EnterpriseDO.class);
+        enterpriseMapper.insert(dbEnterprise);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbEnterprise.getId();
+
+        // 调用
+        enterpriseService.deleteEnterprise(id);
+        // 校验数据不存在了
+        assertNull(enterpriseMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteEnterprise_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseService.deleteEnterprise(id), MDE_ENTERPRISE_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetEnterprisePage() {
+        // mock 数据
+        EnterpriseDO dbEnterprise = randomPojo(EnterpriseDO.class, o -> { // 等会查询到
+            o.setName(null);
+            o.setAnotherName(null);
+            o.setContact(null);
+            o.setPhone(null);
+            o.setIndustryId(null);
+            o.setFinancingStatus(null);
+            o.setScale(null);
+            o.setCreateTime(null);
+        });
+        enterpriseMapper.insert(dbEnterprise);
+        // 测试 name 不匹配
+        enterpriseMapper.insert(cloneIgnoreId(dbEnterprise, o -> o.setName(null)));
+        // 测试 anotherName 不匹配
+        enterpriseMapper.insert(cloneIgnoreId(dbEnterprise, o -> o.setAnotherName(null)));
+        // 测试 contact 不匹配
+        enterpriseMapper.insert(cloneIgnoreId(dbEnterprise, o -> o.setContact(null)));
+        // 测试 phone 不匹配
+        enterpriseMapper.insert(cloneIgnoreId(dbEnterprise, o -> o.setPhone(null)));
+        // 测试 industryId 不匹配
+        enterpriseMapper.insert(cloneIgnoreId(dbEnterprise, o -> o.setIndustryId(null)));
+        // 测试 financingStatus 不匹配
+        enterpriseMapper.insert(cloneIgnoreId(dbEnterprise, o -> o.setFinancingStatus(null)));
+        // 测试 scale 不匹配
+        enterpriseMapper.insert(cloneIgnoreId(dbEnterprise, o -> o.setScale(null)));
+        // 测试 createTime 不匹配
+        enterpriseMapper.insert(cloneIgnoreId(dbEnterprise, o -> o.setCreateTime(null)));
+        // 准备参数
+        EnterprisePageReqVO reqVO = new EnterprisePageReqVO();
+        reqVO.setName(null);
+        reqVO.setAnotherName(null);
+        reqVO.setContact(null);
+        reqVO.setPhone(null);
+        reqVO.setIndustryId(null);
+        reqVO.setFinancingStatus(null);
+        reqVO.setScale(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<EnterpriseDO> pageResult = enterpriseService.getEnterprisePage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbEnterprise, pageResult.getList().get(0));
+    }
+
+}

+ 144 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseUserApplyServiceImplTest.java

@@ -0,0 +1,144 @@
+package com.citu.module.menduner.system.service.enterprise;
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.enterprise.user.EnterpriseUserApplyPageReqVO;
+import com.citu.module.menduner.system.controller.base.enterprise.user.EnterpriseUserApplySaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.enterprise.EnterpriseUserApplyDO;
+import com.citu.module.menduner.system.dal.mysql.enterprise.EnterpriseUserApplyMapper;
+import com.citu.module.menduner.system.service.enterprise.user.EnterpriseUserApplyServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.ENTERPRISE_USER_APPLY_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link EnterpriseUserApplyServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(EnterpriseUserApplyServiceImpl.class)
+public class EnterpriseUserApplyServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private EnterpriseUserApplyServiceImpl enterpriseUserApplyService;
+
+    @Resource
+    private EnterpriseUserApplyMapper enterpriseUserApplyMapper;
+
+    @Test
+    public void testCreateEnterpriseUserApply_success() {
+        // 准备参数
+        EnterpriseUserApplySaveReqVO createReqVO = randomPojo(EnterpriseUserApplySaveReqVO.class).setId(null);
+
+        // 调用
+        Long enterpriseUserApplyId = enterpriseUserApplyService.createEnterpriseUserApply(createReqVO);
+        // 断言
+        assertNotNull(enterpriseUserApplyId);
+        // 校验记录的属性是否正确
+        EnterpriseUserApplyDO enterpriseUserApply = enterpriseUserApplyMapper.selectById(enterpriseUserApplyId);
+        assertPojoEquals(createReqVO, enterpriseUserApply, "id");
+    }
+
+    @Test
+    public void testUpdateEnterpriseUserApply_success() {
+        // mock 数据
+        EnterpriseUserApplyDO dbEnterpriseUserApply = randomPojo(EnterpriseUserApplyDO.class);
+        enterpriseUserApplyMapper.insert(dbEnterpriseUserApply);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        EnterpriseUserApplySaveReqVO updateReqVO = randomPojo(EnterpriseUserApplySaveReqVO.class, o -> {
+            o.setId(dbEnterpriseUserApply.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        enterpriseUserApplyService.updateEnterpriseUserApply(updateReqVO);
+        // 校验是否更新正确
+        EnterpriseUserApplyDO enterpriseUserApply = enterpriseUserApplyMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, enterpriseUserApply);
+    }
+
+    @Test
+    public void testUpdateEnterpriseUserApply_notExists() {
+        // 准备参数
+        EnterpriseUserApplySaveReqVO updateReqVO = randomPojo(EnterpriseUserApplySaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseUserApplyService.updateEnterpriseUserApply(updateReqVO), ENTERPRISE_USER_APPLY_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteEnterpriseUserApply_success() {
+        // mock 数据
+        EnterpriseUserApplyDO dbEnterpriseUserApply = randomPojo(EnterpriseUserApplyDO.class);
+        enterpriseUserApplyMapper.insert(dbEnterpriseUserApply);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbEnterpriseUserApply.getId();
+
+        // 调用
+        enterpriseUserApplyService.deleteEnterpriseUserApply(id);
+        // 校验数据不存在了
+        assertNull(enterpriseUserApplyMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteEnterpriseUserApply_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseUserApplyService.deleteEnterpriseUserApply(id), ENTERPRISE_USER_APPLY_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetEnterpriseUserApplyPage() {
+        // mock 数据
+        EnterpriseUserApplyDO dbEnterpriseUserApply = randomPojo(EnterpriseUserApplyDO.class, o -> { // 等会查询到
+            o.setUserId(null);
+            o.setEnterpriseId(null);
+            o.setPostId(null);
+            o.setName(null);
+            o.setStatus(null);
+            o.setCreateTime(null);
+        });
+        enterpriseUserApplyMapper.insert(dbEnterpriseUserApply);
+        // 测试 userId 不匹配
+        enterpriseUserApplyMapper.insert(cloneIgnoreId(dbEnterpriseUserApply, o -> o.setUserId(null)));
+        // 测试 enterpriseId 不匹配
+        enterpriseUserApplyMapper.insert(cloneIgnoreId(dbEnterpriseUserApply, o -> o.setEnterpriseId(null)));
+        // 测试 postId 不匹配
+        enterpriseUserApplyMapper.insert(cloneIgnoreId(dbEnterpriseUserApply, o -> o.setPostId(null)));
+        // 测试 name 不匹配
+        enterpriseUserApplyMapper.insert(cloneIgnoreId(dbEnterpriseUserApply, o -> o.setName(null)));
+        // 测试 status 不匹配
+        enterpriseUserApplyMapper.insert(cloneIgnoreId(dbEnterpriseUserApply, o -> o.setStatus(null)));
+        // 测试 createTime 不匹配
+        enterpriseUserApplyMapper.insert(cloneIgnoreId(dbEnterpriseUserApply, o -> o.setCreateTime(null)));
+        // 准备参数
+        EnterpriseUserApplyPageReqVO reqVO = new EnterpriseUserApplyPageReqVO();
+        reqVO.setUserId(null);
+        reqVO.setEnterpriseId(null);
+        reqVO.setPostId(null);
+        reqVO.setName(null);
+        reqVO.setStatus(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<EnterpriseUserApplyDO> pageResult = enterpriseUserApplyService.getEnterpriseUserApplyPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbEnterpriseUserApply, pageResult.getList().get(0));
+    }
+
+}

+ 141 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseUserBindServiceImplTest.java

@@ -0,0 +1,141 @@
+package com.citu.module.menduner.system.service.enterprise;
+
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.enterprise.bind.EnterpriseUserBindPageReqVO;
+import com.citu.module.menduner.system.controller.base.enterprise.bind.EnterpriseUserBindSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.enterprise.EnterpriseUserBindDO;
+import com.citu.module.menduner.system.dal.mysql.enterprise.EnterpriseUserBindMapper;
+import com.citu.module.menduner.system.service.enterprise.bind.EnterpriseUserBindServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_ENTERPRISE_USER_BIND_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link EnterpriseUserBindServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(EnterpriseUserBindServiceImpl.class)
+public class EnterpriseUserBindServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private EnterpriseUserBindServiceImpl enterpriseUserBindService;
+
+    @Resource
+    private EnterpriseUserBindMapper enterpriseUserBindMapper;
+
+    @Test
+    public void testCreateEnterpriseUserBind_success() {
+        // 准备参数
+        EnterpriseUserBindSaveReqVO createReqVO = randomPojo(EnterpriseUserBindSaveReqVO.class).setId(null);
+
+        // 调用
+        Long enterpriseUserBindId = enterpriseUserBindService.createEnterpriseUserBind(createReqVO);
+        // 断言
+        assertNotNull(enterpriseUserBindId);
+        // 校验记录的属性是否正确
+        EnterpriseUserBindDO enterpriseUserBind = enterpriseUserBindMapper.selectById(enterpriseUserBindId);
+        assertPojoEquals(createReqVO, enterpriseUserBind, "id");
+    }
+
+    @Test
+    public void testUpdateEnterpriseUserBind_success() {
+        // mock 数据
+        EnterpriseUserBindDO dbEnterpriseUserBind = randomPojo(EnterpriseUserBindDO.class);
+        enterpriseUserBindMapper.insert(dbEnterpriseUserBind);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        EnterpriseUserBindSaveReqVO updateReqVO = randomPojo(EnterpriseUserBindSaveReqVO.class, o -> {
+            o.setId(dbEnterpriseUserBind.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        enterpriseUserBindService.updateEnterpriseUserBind(updateReqVO);
+        // 校验是否更新正确
+        EnterpriseUserBindDO enterpriseUserBind = enterpriseUserBindMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, enterpriseUserBind);
+    }
+
+    @Test
+    public void testUpdateEnterpriseUserBind_notExists() {
+        // 准备参数
+        EnterpriseUserBindSaveReqVO updateReqVO = randomPojo(EnterpriseUserBindSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseUserBindService.updateEnterpriseUserBind(updateReqVO), MDE_ENTERPRISE_USER_BIND_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteEnterpriseUserBind_success() {
+        // mock 数据
+        EnterpriseUserBindDO dbEnterpriseUserBind = randomPojo(EnterpriseUserBindDO.class);
+        enterpriseUserBindMapper.insert(dbEnterpriseUserBind);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbEnterpriseUserBind.getId();
+
+        // 调用
+        enterpriseUserBindService.deleteEnterpriseUserBind(id);
+        // 校验数据不存在了
+        assertNull(enterpriseUserBindMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteEnterpriseUserBind_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseUserBindService.deleteEnterpriseUserBind(id), MDE_ENTERPRISE_USER_BIND_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetEnterpriseUserBindPage() {
+        // mock 数据
+        EnterpriseUserBindDO dbEnterpriseUserBind = randomPojo(EnterpriseUserBindDO.class, o -> { // 等会查询到
+            o.setEnterpriseId(null);
+            o.setUserId(null);
+            o.setStatus(null);
+            o.setLoginIp(null);
+            o.setCreateTime(null);
+        });
+        enterpriseUserBindMapper.insert(dbEnterpriseUserBind);
+        // 测试 enterpriseId 不匹配
+        enterpriseUserBindMapper.insert(cloneIgnoreId(dbEnterpriseUserBind, o -> o.setEnterpriseId(null)));
+        // 测试 userId 不匹配
+        enterpriseUserBindMapper.insert(cloneIgnoreId(dbEnterpriseUserBind, o -> o.setUserId(null)));
+        // 测试 status 不匹配
+        enterpriseUserBindMapper.insert(cloneIgnoreId(dbEnterpriseUserBind, o -> o.setStatus(null)));
+        // 测试 loginIp 不匹配
+        enterpriseUserBindMapper.insert(cloneIgnoreId(dbEnterpriseUserBind, o -> o.setLoginIp(null)));
+        // 测试 createTime 不匹配
+        enterpriseUserBindMapper.insert(cloneIgnoreId(dbEnterpriseUserBind, o -> o.setCreateTime(null)));
+        // 准备参数
+        EnterpriseUserBindPageReqVO reqVO = new EnterpriseUserBindPageReqVO();
+        reqVO.setEnterpriseId(null);
+        reqVO.setUserId(null);
+        reqVO.setStatus(null);
+        reqVO.setLoginIp(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<EnterpriseUserBindDO> pageResult = enterpriseUserBindService.getEnterpriseUserBindPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbEnterpriseUserBind, pageResult.getList().get(0));
+    }
+
+}

+ 144 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/enterprise/EnterpriseUserLookServiceImplTest.java

@@ -0,0 +1,144 @@
+package com.citu.module.menduner.system.service.enterprise;
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.enterprise.look.EnterpriseUserLookPageReqVO;
+import com.citu.module.menduner.system.controller.base.enterprise.look.EnterpriseUserLookSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.enterprise.EnterpriseUserLookDO;
+import com.citu.module.menduner.system.dal.mysql.enterprise.EnterpriseUserLookMapper;
+import com.citu.module.menduner.system.service.enterprise.look.EnterpriseUserLookServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.ENTERPRISE_USER_LOOK_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link EnterpriseUserLookServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(EnterpriseUserLookServiceImpl.class)
+public class EnterpriseUserLookServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private EnterpriseUserLookServiceImpl enterpriseUserLookService;
+
+    @Resource
+    private EnterpriseUserLookMapper enterpriseUserLookMapper;
+
+    @Test
+    public void testCreateEnterpriseUserLook_success() {
+        // 准备参数
+        EnterpriseUserLookSaveReqVO createReqVO = randomPojo(EnterpriseUserLookSaveReqVO.class).setId(null);
+
+        // 调用
+        Long enterpriseUserLookId = enterpriseUserLookService.createEnterpriseUserLook(createReqVO);
+        // 断言
+        assertNotNull(enterpriseUserLookId);
+        // 校验记录的属性是否正确
+        EnterpriseUserLookDO enterpriseUserLook = enterpriseUserLookMapper.selectById(enterpriseUserLookId);
+        assertPojoEquals(createReqVO, enterpriseUserLook, "id");
+    }
+
+    @Test
+    public void testUpdateEnterpriseUserLook_success() {
+        // mock 数据
+        EnterpriseUserLookDO dbEnterpriseUserLook = randomPojo(EnterpriseUserLookDO.class);
+        enterpriseUserLookMapper.insert(dbEnterpriseUserLook);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        EnterpriseUserLookSaveReqVO updateReqVO = randomPojo(EnterpriseUserLookSaveReqVO.class, o -> {
+            o.setId(dbEnterpriseUserLook.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        enterpriseUserLookService.updateEnterpriseUserLook(updateReqVO);
+        // 校验是否更新正确
+        EnterpriseUserLookDO enterpriseUserLook = enterpriseUserLookMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, enterpriseUserLook);
+    }
+
+    @Test
+    public void testUpdateEnterpriseUserLook_notExists() {
+        // 准备参数
+        EnterpriseUserLookSaveReqVO updateReqVO = randomPojo(EnterpriseUserLookSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseUserLookService.updateEnterpriseUserLook(updateReqVO), ENTERPRISE_USER_LOOK_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteEnterpriseUserLook_success() {
+        // mock 数据
+        EnterpriseUserLookDO dbEnterpriseUserLook = randomPojo(EnterpriseUserLookDO.class);
+        enterpriseUserLookMapper.insert(dbEnterpriseUserLook);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbEnterpriseUserLook.getId();
+
+        // 调用
+        enterpriseUserLookService.deleteEnterpriseUserLook(id);
+        // 校验数据不存在了
+        assertNull(enterpriseUserLookMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteEnterpriseUserLook_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> enterpriseUserLookService.deleteEnterpriseUserLook(id), ENTERPRISE_USER_LOOK_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetEnterpriseUserLookPage() {
+        // mock 数据
+        EnterpriseUserLookDO dbEnterpriseUserLook = randomPojo(EnterpriseUserLookDO.class, o -> { // 等会查询到
+            o.setEnterpriseId(null);
+            o.setUserId(null);
+            o.setUserName(null);
+            o.setUserPostName(null);
+            o.setLookUserId(null);
+            o.setCreateTime(null);
+        });
+        enterpriseUserLookMapper.insert(dbEnterpriseUserLook);
+        // 测试 enterpriseId 不匹配
+        enterpriseUserLookMapper.insert(cloneIgnoreId(dbEnterpriseUserLook, o -> o.setEnterpriseId(null)));
+        // 测试 userId 不匹配
+        enterpriseUserLookMapper.insert(cloneIgnoreId(dbEnterpriseUserLook, o -> o.setUserId(null)));
+        // 测试 userName 不匹配
+        enterpriseUserLookMapper.insert(cloneIgnoreId(dbEnterpriseUserLook, o -> o.setUserName(null)));
+        // 测试 userPostName 不匹配
+        enterpriseUserLookMapper.insert(cloneIgnoreId(dbEnterpriseUserLook, o -> o.setUserPostName(null)));
+        // 测试 lookUserId 不匹配
+        enterpriseUserLookMapper.insert(cloneIgnoreId(dbEnterpriseUserLook, o -> o.setLookUserId(null)));
+        // 测试 createTime 不匹配
+        enterpriseUserLookMapper.insert(cloneIgnoreId(dbEnterpriseUserLook, o -> o.setCreateTime(null)));
+        // 准备参数
+        EnterpriseUserLookPageReqVO reqVO = new EnterpriseUserLookPageReqVO();
+        reqVO.setEnterpriseId(null);
+        reqVO.setUserId(null);
+        reqVO.setUserName(null);
+        reqVO.setUserPostName(null);
+        reqVO.setLookUserId(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<EnterpriseUserLookDO> pageResult = enterpriseUserLookService.getEnterpriseUserLookPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbEnterpriseUserLook, pageResult.getList().get(0));
+    }
+
+}

+ 132 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/hire/HireCommissionRatioServiceImplTest.java

@@ -0,0 +1,132 @@
+package com.citu.module.menduner.system.service.hire;
+
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.hire.HireCommissionRatioPageReqVO;
+import com.citu.module.menduner.system.controller.base.hire.HireCommissionRatioSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.hire.HireCommissionRatioDO;
+import com.citu.module.menduner.system.dal.mysql.hire.HireCommissionRatioMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.HIRE_COMMISSION_RATIO_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link HireCommissionRatioServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(HireCommissionRatioServiceImpl.class)
+public class HireCommissionRatioServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private HireCommissionRatioServiceImpl hireCommissionRatioService;
+
+    @Resource
+    private HireCommissionRatioMapper hireCommissionRatioMapper;
+
+    @Test
+    public void testCreateHireCommissionRatio_success() {
+        // 准备参数
+        HireCommissionRatioSaveReqVO createReqVO = randomPojo(HireCommissionRatioSaveReqVO.class).setId(null);
+
+        // 调用
+        Long hireCommissionRatioId = hireCommissionRatioService.createHireCommissionRatio(createReqVO);
+        // 断言
+        assertNotNull(hireCommissionRatioId);
+        // 校验记录的属性是否正确
+        HireCommissionRatioDO hireCommissionRatio = hireCommissionRatioMapper.selectById(hireCommissionRatioId);
+        assertPojoEquals(createReqVO, hireCommissionRatio, "id");
+    }
+
+    @Test
+    public void testUpdateHireCommissionRatio_success() {
+        // mock 数据
+        HireCommissionRatioDO dbHireCommissionRatio = randomPojo(HireCommissionRatioDO.class);
+        hireCommissionRatioMapper.insert(dbHireCommissionRatio);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        HireCommissionRatioSaveReqVO updateReqVO = randomPojo(HireCommissionRatioSaveReqVO.class, o -> {
+            o.setId(dbHireCommissionRatio.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        hireCommissionRatioService.updateHireCommissionRatio(updateReqVO);
+        // 校验是否更新正确
+        HireCommissionRatioDO hireCommissionRatio = hireCommissionRatioMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, hireCommissionRatio);
+    }
+
+    @Test
+    public void testUpdateHireCommissionRatio_notExists() {
+        // 准备参数
+        HireCommissionRatioSaveReqVO updateReqVO = randomPojo(HireCommissionRatioSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> hireCommissionRatioService.updateHireCommissionRatio(updateReqVO), HIRE_COMMISSION_RATIO_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteHireCommissionRatio_success() {
+        // mock 数据
+        HireCommissionRatioDO dbHireCommissionRatio = randomPojo(HireCommissionRatioDO.class);
+        hireCommissionRatioMapper.insert(dbHireCommissionRatio);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbHireCommissionRatio.getId();
+
+        // 调用
+        hireCommissionRatioService.deleteHireCommissionRatio(id);
+        // 校验数据不存在了
+        assertNull(hireCommissionRatioMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteHireCommissionRatio_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> hireCommissionRatioService.deleteHireCommissionRatio(id), HIRE_COMMISSION_RATIO_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetHireCommissionRatioPage() {
+        // mock 数据
+        HireCommissionRatioDO dbHireCommissionRatio = randomPojo(HireCommissionRatioDO.class, o -> { // 等会查询到
+            o.setEnterpriseId(null);
+            o.setUserId(null);
+            o.setCreateTime(null);
+        });
+        hireCommissionRatioMapper.insert(dbHireCommissionRatio);
+        // 测试 enterpriseId 不匹配
+        hireCommissionRatioMapper.insert(cloneIgnoreId(dbHireCommissionRatio, o -> o.setEnterpriseId(null)));
+        // 测试 userId 不匹配
+        hireCommissionRatioMapper.insert(cloneIgnoreId(dbHireCommissionRatio, o -> o.setUserId(null)));
+        // 测试 createTime 不匹配
+        hireCommissionRatioMapper.insert(cloneIgnoreId(dbHireCommissionRatio, o -> o.setCreateTime(null)));
+        // 准备参数
+        HireCommissionRatioPageReqVO reqVO = new HireCommissionRatioPageReqVO();
+        reqVO.setEnterpriseId(null);
+        reqVO.setUserId(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<HireCommissionRatioDO> pageResult = hireCommissionRatioService.getHireCommissionRatioPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbHireCommissionRatio, pageResult.getList().get(0));
+    }
+
+}

+ 160 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/hire/HireJobCvRelServiceImplTest.java

@@ -0,0 +1,160 @@
+//package com.citu.module.menduner.system.service.hire;
+//
+//
+//import com.citu.framework.common.pojo.PageResult;
+//import com.citu.framework.test.core.ut.BaseDbUnitTest;
+//import com.citu.module.menduner.system.controller.base.hire.HireJobCvRelPageReqVO;
+//import com.citu.module.menduner.system.controller.base.hire.HireJobCvRelSaveReqVO;
+//import com.citu.module.menduner.system.dal.dataobject.hire.HireJobCvRelDO;
+//import com.citu.module.menduner.system.dal.mysql.hire.HireJobCvRelMapper;
+//import org.junit.jupiter.api.Disabled;
+//import org.junit.jupiter.api.Test;
+//import org.springframework.context.annotation.Import;
+//
+//import javax.annotation.Resource;
+//
+//import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+//import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+//import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+//import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+//import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+//import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+//import static com.citu.module.menduner.system.enums.ErrorCodeConstants.HIRE_JOB_CV_REL_NOT_EXISTS;
+//import static org.junit.jupiter.api.Assertions.*;
+//
+///**
+// * {@link HireJobCvRelServiceImpl} 的单元测试类
+// *
+// * @author Rayson
+// */
+//@Import(HireJobCvRelServiceImpl.class)
+//public class HireJobCvRelServiceImplTest extends BaseDbUnitTest {
+//
+//    @Resource
+//    private HireJobCvRelServiceImpl hireJobCvRelService;
+//
+//    @Resource
+//    private HireJobCvRelMapper hireJobCvRelMapper;
+//
+//    @Test
+//    public void testCreateHireJobCvRel_success() {
+//        // 准备参数
+//        HireJobCvRelSaveReqVO createReqVO = randomPojo(HireJobCvRelSaveReqVO.class).setId(null);
+//
+//        // 调用
+//        Long hireJobCvRelId = hireJobCvRelService.createHireJobCvRel(createReqVO);
+//        // 断言
+//        assertNotNull(hireJobCvRelId);
+//        // 校验记录的属性是否正确
+//        HireJobCvRelDO hireJobCvRel = hireJobCvRelMapper.selectById(hireJobCvRelId);
+//        assertPojoEquals(createReqVO, hireJobCvRel, "id");
+//    }
+//
+//    @Test
+//    public void testUpdateHireJobCvRel_success() {
+//        // mock 数据
+//        HireJobCvRelDO dbHireJobCvRel = randomPojo(HireJobCvRelDO.class);
+//        hireJobCvRelMapper.insert(dbHireJobCvRel);// @Sql: 先插入出一条存在的数据
+//        // 准备参数
+//        HireJobCvRelSaveReqVO updateReqVO = randomPojo(HireJobCvRelSaveReqVO.class, o -> {
+//            o.setId(dbHireJobCvRel.getId()); // 设置更新的 ID
+//        });
+//
+//        // 调用
+//        hireJobCvRelService.updateHireJobCvRel(updateReqVO);
+//        // 校验是否更新正确
+//        HireJobCvRelDO hireJobCvRel = hireJobCvRelMapper.selectById(updateReqVO.getId()); // 获取最新的
+//        assertPojoEquals(updateReqVO, hireJobCvRel);
+//    }
+//
+//    @Test
+//    public void testUpdateHireJobCvRel_notExists() {
+//        // 准备参数
+//        HireJobCvRelSaveReqVO updateReqVO = randomPojo(HireJobCvRelSaveReqVO.class);
+//
+//        // 调用, 并断言异常
+//        assertServiceException(() -> hireJobCvRelService.updateHireJobCvRel(updateReqVO), HIRE_JOB_CV_REL_NOT_EXISTS);
+//    }
+//
+//    @Test
+//    public void testDeleteHireJobCvRel_success() {
+//        // mock 数据
+//        HireJobCvRelDO dbHireJobCvRel = randomPojo(HireJobCvRelDO.class);
+//        hireJobCvRelMapper.insert(dbHireJobCvRel);// @Sql: 先插入出一条存在的数据
+//        // 准备参数
+//        Long id = dbHireJobCvRel.getId();
+//
+//        // 调用
+//        hireJobCvRelService.deleteHireJobCvRel(id);
+//        // 校验数据不存在了
+//        assertNull(hireJobCvRelMapper.selectById(id));
+//    }
+//
+//    @Test
+//    public void testDeleteHireJobCvRel_notExists() {
+//        // 准备参数
+//        Long id = randomLongId();
+//
+//        // 调用, 并断言异常
+//        assertServiceException(() -> hireJobCvRelService.deleteHireJobCvRel(id), HIRE_JOB_CV_REL_NOT_EXISTS);
+//    }
+//
+//    @Test
+//    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+//    public void testGetHireJobCvRelPage() {
+//        // mock 数据
+//        HireJobCvRelDO dbHireJobCvRel = randomPojo(HireJobCvRelDO.class, o -> { // 等会查询到
+//            o.setEnterpriseId(null);
+//            o.setJobId(null);
+//            o.setPublishUserId(null);
+//            o.setRecommendUserId(null);
+//            o.setUserId(null);
+//            o.setName(null);
+//            o.setPhone(null);
+//            o.setUrl(null);
+//            o.setStatus(null);
+//            o.setCreateTime(null);
+//        });
+//        hireJobCvRelMapper.insert(dbHireJobCvRel);
+//        // 测试 enterpriseId 不匹配
+//        hireJobCvRelMapper.insert(cloneIgnoreId(dbHireJobCvRel, o -> o.setEnterpriseId(null)));
+//        // 测试 jobId 不匹配
+//        hireJobCvRelMapper.insert(cloneIgnoreId(dbHireJobCvRel, o -> o.setJobId(null)));
+//        // 测试 publishUserId 不匹配
+//        hireJobCvRelMapper.insert(cloneIgnoreId(dbHireJobCvRel, o -> o.setPublishUserId(null)));
+//        // 测试 recommendUserId 不匹配
+//        hireJobCvRelMapper.insert(cloneIgnoreId(dbHireJobCvRel, o -> o.setRecommendUserId(null)));
+//        // 测试 userId 不匹配
+//        hireJobCvRelMapper.insert(cloneIgnoreId(dbHireJobCvRel, o -> o.setUserId(null)));
+//        // 测试 name 不匹配
+//        hireJobCvRelMapper.insert(cloneIgnoreId(dbHireJobCvRel, o -> o.setName(null)));
+//        // 测试 phone 不匹配
+//        hireJobCvRelMapper.insert(cloneIgnoreId(dbHireJobCvRel, o -> o.setPhone(null)));
+//        // 测试 url 不匹配
+//        hireJobCvRelMapper.insert(cloneIgnoreId(dbHireJobCvRel, o -> o.setUrl(null)));
+//        // 测试 status 不匹配
+//        hireJobCvRelMapper.insert(cloneIgnoreId(dbHireJobCvRel, o -> o.setStatus(null)));
+//        // 测试 createTime 不匹配
+//        hireJobCvRelMapper.insert(cloneIgnoreId(dbHireJobCvRel, o -> o.setCreateTime(null)));
+//        // 准备参数
+//        HireJobCvRelPageReqVO reqVO = new HireJobCvRelPageReqVO();
+//        reqVO.setEnterpriseId(null);
+//        reqVO.setJobId(null);
+//        reqVO.setPublishUserId(null);
+//        reqVO.setRecommendUserId(null);
+//        reqVO.setUserId(null);
+//        reqVO.setName(null);
+//        reqVO.setPhone(null);
+//        reqVO.setUrl(null);
+//        reqVO.setStatus(null);
+//        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+//
+//        // 调用
+//        PageResult<HireJobCvRelDO> pageResult = hireJobCvRelService.getHireJobCvRelPage(reqVO);
+//        // 断言
+//        assertEquals(1, pageResult.getTotal());
+//        assertEquals(1, pageResult.getList().size());
+//        assertPojoEquals(dbHireJobCvRel, pageResult.getList().get(0));
+//    }
+//
+//}

+ 133 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/industry/IndustryServiceImplTest.java

@@ -0,0 +1,133 @@
+package com.citu.module.menduner.system.service.industry;
+
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.industry.IndustryPageReqVO;
+import com.citu.module.menduner.system.controller.base.industry.IndustrySaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.industry.IndustryDO;
+import com.citu.module.menduner.system.dal.mysql.industry.IndustryMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_INDUSTRY_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+
+/**
+ * {@link IndustryServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(IndustryServiceImpl.class)
+public class IndustryServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private IndustryServiceImpl industryService;
+
+    @Resource
+    private IndustryMapper industryMapper;
+
+    @Test
+    public void testCreateIndustry_success() {
+        // 准备参数
+        IndustrySaveReqVO createReqVO = randomPojo(IndustrySaveReqVO.class).setId(null);
+
+        // 调用
+        Long industryId = industryService.createIndustry(createReqVO);
+        // 断言
+        assertNotNull(industryId);
+        // 校验记录的属性是否正确
+        IndustryDO industry = industryMapper.selectById(industryId);
+        assertPojoEquals(createReqVO, industry, "id");
+    }
+
+    @Test
+    public void testUpdateIndustry_success() {
+        // mock 数据
+        IndustryDO dbIndustry = randomPojo(IndustryDO.class);
+        industryMapper.insert(dbIndustry);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        IndustrySaveReqVO updateReqVO = randomPojo(IndustrySaveReqVO.class, o -> {
+            o.setId(dbIndustry.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        industryService.updateIndustry(updateReqVO);
+        // 校验是否更新正确
+        IndustryDO industry = industryMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, industry);
+    }
+
+    @Test
+    public void testUpdateIndustry_notExists() {
+        // 准备参数
+        IndustrySaveReqVO updateReqVO = randomPojo(IndustrySaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> industryService.updateIndustry(updateReqVO), MDE_INDUSTRY_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteIndustry_success() {
+        // mock 数据
+        IndustryDO dbIndustry = randomPojo(IndustryDO.class);
+        industryMapper.insert(dbIndustry);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbIndustry.getId();
+
+        // 调用
+        industryService.deleteIndustry(id);
+        // 校验数据不存在了
+        assertNull(industryMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteIndustry_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> industryService.deleteIndustry(id), MDE_INDUSTRY_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetIndustryPage() {
+        // mock 数据
+        IndustryDO dbIndustry = randomPojo(IndustryDO.class, o -> { // 等会查询到
+            o.setNameCn(null);
+            o.setNameEn(null);
+            o.setCreateTime(null);
+        });
+        industryMapper.insert(dbIndustry);
+        // 测试 nameCn 不匹配
+        industryMapper.insert(cloneIgnoreId(dbIndustry, o -> o.setNameCn(null)));
+        // 测试 nameEn 不匹配
+        industryMapper.insert(cloneIgnoreId(dbIndustry, o -> o.setNameEn(null)));
+        // 测试 createTime 不匹配
+        industryMapper.insert(cloneIgnoreId(dbIndustry, o -> o.setCreateTime(null)));
+        // 准备参数
+        IndustryPageReqVO reqVO = new IndustryPageReqVO();
+        reqVO.setNameCn(null);
+        reqVO.setNameEn(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<IndustryDO> pageResult = industryService.getIndustryPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbIndustry, pageResult.getList().get(0));
+    }
+
+}

+ 167 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/job/JobAdvertisedServiceImplTest.java

@@ -0,0 +1,167 @@
+package com.citu.module.menduner.system.service.job;
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.job.JobAdvertisedPageReqVO;
+import com.citu.module.menduner.system.controller.base.job.JobAdvertisedSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.job.JobAdvertisedDO;
+import com.citu.module.menduner.system.dal.mysql.job.JobAdvertisedMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_JOB_ADVERTISED_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link JobAdvertisedServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(JobAdvertisedServiceImpl.class)
+public class JobAdvertisedServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private JobAdvertisedServiceImpl jobAdvertisedService;
+
+    @Resource
+    private JobAdvertisedMapper jobAdvertisedMapper;
+
+    @Test
+    public void testCreateJobAdvertised_success() {
+        // 准备参数
+        JobAdvertisedSaveReqVO createReqVO = randomPojo(JobAdvertisedSaveReqVO.class).setId(null);
+
+        // 调用
+        Long jobAdvertisedId = jobAdvertisedService.createJobAdvertised(createReqVO);
+        // 断言
+        assertNotNull(jobAdvertisedId);
+        // 校验记录的属性是否正确
+        JobAdvertisedDO jobAdvertised = jobAdvertisedMapper.selectById(jobAdvertisedId);
+        assertPojoEquals(createReqVO, jobAdvertised, "id");
+    }
+
+    @Test
+    public void testUpdateJobAdvertised_success() {
+        // mock 数据
+        JobAdvertisedDO dbJobAdvertised = randomPojo(JobAdvertisedDO.class);
+        jobAdvertisedMapper.insert(dbJobAdvertised);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        JobAdvertisedSaveReqVO updateReqVO = randomPojo(JobAdvertisedSaveReqVO.class, o -> {
+            o.setId(dbJobAdvertised.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        jobAdvertisedService.updateJobAdvertised(updateReqVO);
+        // 校验是否更新正确
+        JobAdvertisedDO jobAdvertised = jobAdvertisedMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, jobAdvertised);
+    }
+
+    @Test
+    public void testUpdateJobAdvertised_notExists() {
+        // 准备参数
+        JobAdvertisedSaveReqVO updateReqVO = randomPojo(JobAdvertisedSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> jobAdvertisedService.updateJobAdvertised(updateReqVO), MDE_JOB_ADVERTISED_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteJobAdvertised_success() {
+        // mock 数据
+        JobAdvertisedDO dbJobAdvertised = randomPojo(JobAdvertisedDO.class);
+        jobAdvertisedMapper.insert(dbJobAdvertised);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbJobAdvertised.getId();
+
+        // 调用
+        jobAdvertisedService.deleteJobAdvertised(id);
+        // 校验数据不存在了
+        assertNull(jobAdvertisedMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteJobAdvertised_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> jobAdvertisedService.deleteJobAdvertised(id), MDE_JOB_ADVERTISED_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetJobAdvertisedPage() {
+        // mock 数据
+        JobAdvertisedDO dbJobAdvertised = randomPojo(JobAdvertisedDO.class, o -> { // 等会查询到
+            o.setEnterpriseId(null);
+            o.setUserId(null);
+            o.setAreaId(null);
+            o.setName(null);
+            o.setPositionId(null);
+            o.setType(null);
+            o.setExpType(null);
+            o.setEduType(null);
+            o.setContent(null);
+            o.setRequirement(null);
+            o.setStatus(null);
+            o.setCreateTime(null);
+        });
+        jobAdvertisedMapper.insert(dbJobAdvertised);
+        // 测试 enterpriseId 不匹配
+        jobAdvertisedMapper.insert(cloneIgnoreId(dbJobAdvertised, o -> o.setEnterpriseId(null)));
+        // 测试 userId 不匹配
+        jobAdvertisedMapper.insert(cloneIgnoreId(dbJobAdvertised, o -> o.setUserId(null)));
+        // 测试 areaId 不匹配
+        jobAdvertisedMapper.insert(cloneIgnoreId(dbJobAdvertised, o -> o.setAreaId(null)));
+        // 测试 name 不匹配
+        jobAdvertisedMapper.insert(cloneIgnoreId(dbJobAdvertised, o -> o.setName(null)));
+        // 测试 positionId 不匹配
+        jobAdvertisedMapper.insert(cloneIgnoreId(dbJobAdvertised, o -> o.setPositionId(null)));
+        // 测试 type 不匹配
+        jobAdvertisedMapper.insert(cloneIgnoreId(dbJobAdvertised, o -> o.setType(null)));
+        // 测试 expType 不匹配
+        jobAdvertisedMapper.insert(cloneIgnoreId(dbJobAdvertised, o -> o.setExpType(null)));
+        // 测试 eduType 不匹配
+        jobAdvertisedMapper.insert(cloneIgnoreId(dbJobAdvertised, o -> o.setEduType(null)));
+        // 测试 content 不匹配
+        jobAdvertisedMapper.insert(cloneIgnoreId(dbJobAdvertised, o -> o.setContent(null)));
+        // 测试 requirement 不匹配
+        jobAdvertisedMapper.insert(cloneIgnoreId(dbJobAdvertised, o -> o.setRequirement(null)));
+        // 测试 status 不匹配
+        jobAdvertisedMapper.insert(cloneIgnoreId(dbJobAdvertised, o -> o.setStatus(null)));
+        // 测试 createTime 不匹配
+        jobAdvertisedMapper.insert(cloneIgnoreId(dbJobAdvertised, o -> o.setCreateTime(null)));
+        // 准备参数
+        JobAdvertisedPageReqVO reqVO = new JobAdvertisedPageReqVO();
+        reqVO.setEnterpriseId(null);
+        reqVO.setUserId(null);
+        reqVO.setAreaId(null);
+        reqVO.setName(null);
+        reqVO.setPositionId(null);
+        reqVO.setType(null);
+        reqVO.setExpType(null);
+        reqVO.setEduType(null);
+//        reqVO.setContent(null);
+//        reqVO.setRequirement(null);
+        reqVO.setStatus(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<JobAdvertisedDO> pageResult = jobAdvertisedService.getJobAdvertisedPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbJobAdvertised, pageResult.getList().get(0));
+    }
+
+}

+ 143 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/job/JobCvRelServiceImplTest.java

@@ -0,0 +1,143 @@
+package com.citu.module.menduner.system.service.job;
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.job.JobCvRelPageReqVO;
+import com.citu.module.menduner.system.controller.base.job.JobCvRelSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.job.JobCvRelDO;
+import com.citu.module.menduner.system.dal.mysql.job.JobCvRelMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.JOB_CV_REL_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link JobCvRelServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(JobCvRelServiceImpl.class)
+public class JobCvRelServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private JobCvRelServiceImpl jobCvRelService;
+
+    @Resource
+    private JobCvRelMapper jobCvRelMapper;
+
+    @Test
+    public void testCreateJobCvRel_success() {
+        // 准备参数
+        JobCvRelSaveReqVO createReqVO = randomPojo(JobCvRelSaveReqVO.class).setId(null);
+
+        // 调用
+        Long jobCvRelId = jobCvRelService.createJobCvRel(createReqVO);
+        // 断言
+        assertNotNull(jobCvRelId);
+        // 校验记录的属性是否正确
+        JobCvRelDO jobCvRel = jobCvRelMapper.selectById(jobCvRelId);
+        assertPojoEquals(createReqVO, jobCvRel, "id");
+    }
+
+    @Test
+    public void testUpdateJobCvRel_success() {
+        // mock 数据
+        JobCvRelDO dbJobCvRel = randomPojo(JobCvRelDO.class);
+        jobCvRelMapper.insert(dbJobCvRel);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        JobCvRelSaveReqVO updateReqVO = randomPojo(JobCvRelSaveReqVO.class, o -> {
+            o.setId(dbJobCvRel.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        jobCvRelService.updateJobCvRel(updateReqVO);
+        // 校验是否更新正确
+        JobCvRelDO jobCvRel = jobCvRelMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, jobCvRel);
+    }
+
+    @Test
+    public void testUpdateJobCvRel_notExists() {
+        // 准备参数
+        JobCvRelSaveReqVO updateReqVO = randomPojo(JobCvRelSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> jobCvRelService.updateJobCvRel(updateReqVO), JOB_CV_REL_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteJobCvRel_success() {
+        // mock 数据
+        JobCvRelDO dbJobCvRel = randomPojo(JobCvRelDO.class);
+        jobCvRelMapper.insert(dbJobCvRel);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbJobCvRel.getId();
+
+        // 调用
+        jobCvRelService.deleteJobCvRel(id);
+        // 校验数据不存在了
+        assertNull(jobCvRelMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteJobCvRel_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> jobCvRelService.deleteJobCvRel(id), JOB_CV_REL_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetJobCvRelPage() {
+        // mock 数据
+        JobCvRelDO dbJobCvRel = randomPojo(JobCvRelDO.class, o -> { // 等会查询到
+            o.setEnterpriseId(null);
+            o.setJobId(null);
+            o.setPublishUserId(null);
+            o.setUserId(null);
+            o.setStatus(null);
+            o.setCreateTime(null);
+        });
+        jobCvRelMapper.insert(dbJobCvRel);
+        // 测试 enterpriseId 不匹配
+        jobCvRelMapper.insert(cloneIgnoreId(dbJobCvRel, o -> o.setEnterpriseId(null)));
+        // 测试 jobId 不匹配
+        jobCvRelMapper.insert(cloneIgnoreId(dbJobCvRel, o -> o.setJobId(null)));
+        // 测试 publishUserId 不匹配
+        jobCvRelMapper.insert(cloneIgnoreId(dbJobCvRel, o -> o.setPublishUserId(null)));
+        // 测试 userId 不匹配
+        jobCvRelMapper.insert(cloneIgnoreId(dbJobCvRel, o -> o.setUserId(null)));
+        // 测试 status 不匹配
+        jobCvRelMapper.insert(cloneIgnoreId(dbJobCvRel, o -> o.setStatus(null)));
+        // 测试 createTime 不匹配
+        jobCvRelMapper.insert(cloneIgnoreId(dbJobCvRel, o -> o.setCreateTime(null)));
+        // 准备参数
+        JobCvRelPageReqVO reqVO = new JobCvRelPageReqVO();
+        reqVO.setEnterpriseId(null);
+        reqVO.setJobId(null);
+        reqVO.setPublishUserId(null);
+        reqVO.setUserId(null);
+        reqVO.setStatus(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<JobCvRelDO> pageResult = jobCvRelService.getJobCvRelPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbJobCvRel, pageResult.getList().get(0));
+    }
+
+}

+ 156 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/job/JobInterestedServiceImplTest.java

@@ -0,0 +1,156 @@
+package com.citu.module.menduner.system.service.job;
+
+import com.citu.module.menduner.system.controller.base.job.JobInterestedPageReqVO;
+import com.citu.module.menduner.system.controller.base.job.JobInterestedSaveReqVO;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.job.JobInterestedDO;
+import com.citu.module.menduner.system.dal.mysql.job.JobInterestedMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_JOB_INTERESTED_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link JobInterestedServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(JobInterestedServiceImpl.class)
+public class JobInterestedServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private JobInterestedServiceImpl jobInterestedService;
+
+    @Resource
+    private JobInterestedMapper jobInterestedMapper;
+
+    @Test
+    public void testCreateJobInterested_success() {
+        // 准备参数
+        JobInterestedSaveReqVO createReqVO = randomPojo(JobInterestedSaveReqVO.class).setId(null);
+
+        // 调用
+        Long jobInterestedId = jobInterestedService.createJobInterested(createReqVO);
+        // 断言
+        assertNotNull(jobInterestedId);
+        // 校验记录的属性是否正确
+        JobInterestedDO jobInterested = jobInterestedMapper.selectById(jobInterestedId);
+        assertPojoEquals(createReqVO, jobInterested, "id");
+    }
+
+    @Test
+    public void testUpdateJobInterested_success() {
+        // mock 数据
+        JobInterestedDO dbJobInterested = randomPojo(JobInterestedDO.class);
+        jobInterestedMapper.insert(dbJobInterested);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        JobInterestedSaveReqVO updateReqVO = randomPojo(JobInterestedSaveReqVO.class, o -> {
+            o.setId(dbJobInterested.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        jobInterestedService.updateJobInterested(updateReqVO);
+        // 校验是否更新正确
+        JobInterestedDO jobInterested = jobInterestedMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, jobInterested);
+    }
+
+    @Test
+    public void testUpdateJobInterested_notExists() {
+        // 准备参数
+        JobInterestedSaveReqVO updateReqVO = randomPojo(JobInterestedSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> jobInterestedService.updateJobInterested(updateReqVO), MDE_JOB_INTERESTED_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteJobInterested_success() {
+        // mock 数据
+        JobInterestedDO dbJobInterested = randomPojo(JobInterestedDO.class);
+        jobInterestedMapper.insert(dbJobInterested);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbJobInterested.getId();
+
+        // 调用
+        jobInterestedService.deleteJobInterested(id);
+       // 校验数据不存在了
+       assertNull(jobInterestedMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteJobInterested_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> jobInterestedService.deleteJobInterested(id), MDE_JOB_INTERESTED_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetJobInterestedPage() {
+       // mock 数据
+       JobInterestedDO dbJobInterested = randomPojo(JobInterestedDO.class, o -> { // 等会查询到
+           o.setUserId(null);
+           o.setJobType(null);
+           o.setPositionId(null);
+           o.setIndustryIdList(null);
+           o.setPayFrom(null);
+           o.setPayTo(null);
+           o.setWorkAreaId(null);
+           o.setInterestedAreaIdList(null);
+           o.setCreateTime(null);
+       });
+       jobInterestedMapper.insert(dbJobInterested);
+       // 测试 userId 不匹配
+       jobInterestedMapper.insert(cloneIgnoreId(dbJobInterested, o -> o.setUserId(null)));
+       // 测试 jobType 不匹配
+       jobInterestedMapper.insert(cloneIgnoreId(dbJobInterested, o -> o.setJobType(null)));
+       // 测试 positionId 不匹配
+       jobInterestedMapper.insert(cloneIgnoreId(dbJobInterested, o -> o.setPositionId(null)));
+       // 测试 industryIdList 不匹配
+       jobInterestedMapper.insert(cloneIgnoreId(dbJobInterested, o -> o.setIndustryIdList(null)));
+       // 测试 payFrom 不匹配
+       jobInterestedMapper.insert(cloneIgnoreId(dbJobInterested, o -> o.setPayFrom(null)));
+       // 测试 payTo 不匹配
+       jobInterestedMapper.insert(cloneIgnoreId(dbJobInterested, o -> o.setPayTo(null)));
+       // 测试 workAreaId 不匹配
+       jobInterestedMapper.insert(cloneIgnoreId(dbJobInterested, o -> o.setWorkAreaId(null)));
+       // 测试 interestedAreaIdList 不匹配
+       jobInterestedMapper.insert(cloneIgnoreId(dbJobInterested, o -> o.setInterestedAreaIdList(null)));
+       // 测试 createTime 不匹配
+       jobInterestedMapper.insert(cloneIgnoreId(dbJobInterested, o -> o.setCreateTime(null)));
+       // 准备参数
+       JobInterestedPageReqVO reqVO = new JobInterestedPageReqVO();
+       reqVO.setUserId(null);
+       reqVO.setJobType(null);
+       reqVO.setPositionId(null);
+       reqVO.setIndustryIdList(null);
+       reqVO.setPayFrom(null);
+       reqVO.setPayTo(null);
+       reqVO.setWorkAreaId(null);
+       reqVO.setInterestedAreaIdList(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<JobInterestedDO> pageResult = jobInterestedService.getJobInterestedPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbJobInterested, pageResult.getList().get(0));
+    }
+
+}

+ 136 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/major/MajorServiceImplTest.java

@@ -0,0 +1,136 @@
+package com.citu.module.menduner.system.service.major;
+
+import com.citu.module.menduner.system.controller.base.major.MajorPageReqVO;
+import com.citu.module.menduner.system.controller.base.major.MajorSaveReqVO;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.major.MajorDO;
+import com.citu.module.menduner.system.dal.mysql.major.MajorMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_MAJOR_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link MajorServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(MajorServiceImpl.class)
+public class MajorServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private MajorServiceImpl majorService;
+
+    @Resource
+    private MajorMapper majorMapper;
+
+    @Test
+    public void testCreateMajor_success() {
+        // 准备参数
+        MajorSaveReqVO createReqVO = randomPojo(MajorSaveReqVO.class).setId(null);
+
+        // 调用
+        Long majorId = majorService.createMajor(createReqVO);
+        // 断言
+        assertNotNull(majorId);
+        // 校验记录的属性是否正确
+        MajorDO major = majorMapper.selectById(majorId);
+        assertPojoEquals(createReqVO, major, "id");
+    }
+
+    @Test
+    public void testUpdateMajor_success() {
+        // mock 数据
+        MajorDO dbMajor = randomPojo(MajorDO.class);
+        majorMapper.insert(dbMajor);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        MajorSaveReqVO updateReqVO = randomPojo(MajorSaveReqVO.class, o -> {
+            o.setId(dbMajor.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        majorService.updateMajor(updateReqVO);
+        // 校验是否更新正确
+        MajorDO major = majorMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, major);
+    }
+
+    @Test
+    public void testUpdateMajor_notExists() {
+        // 准备参数
+        MajorSaveReqVO updateReqVO = randomPojo(MajorSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> majorService.updateMajor(updateReqVO), MDE_MAJOR_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteMajor_success() {
+        // mock 数据
+        MajorDO dbMajor = randomPojo(MajorDO.class);
+        majorMapper.insert(dbMajor);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbMajor.getId();
+
+        // 调用
+        majorService.deleteMajor(id);
+       // 校验数据不存在了
+       assertNull(majorMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteMajor_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> majorService.deleteMajor(id), MDE_MAJOR_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetMajorPage() {
+       // mock 数据
+       MajorDO dbMajor = randomPojo(MajorDO.class, o -> { // 等会查询到
+           o.setNameCn(null);
+           o.setNameEn(null);
+           o.setStatus(null);
+           o.setCreateTime(null);
+       });
+       majorMapper.insert(dbMajor);
+       // 测试 nameCn 不匹配
+       majorMapper.insert(cloneIgnoreId(dbMajor, o -> o.setNameCn(null)));
+       // 测试 nameEn 不匹配
+       majorMapper.insert(cloneIgnoreId(dbMajor, o -> o.setNameEn(null)));
+       // 测试 status 不匹配
+       majorMapper.insert(cloneIgnoreId(dbMajor, o -> o.setStatus(null)));
+       // 测试 createTime 不匹配
+       majorMapper.insert(cloneIgnoreId(dbMajor, o -> o.setCreateTime(null)));
+       // 准备参数
+       MajorPageReqVO reqVO = new MajorPageReqVO();
+       reqVO.setNameCn(null);
+       reqVO.setNameEn(null);
+       reqVO.setStatus(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<MajorDO> pageResult = majorService.getMajorPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbMajor, pageResult.getList().get(0));
+    }
+
+}

+ 208 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/person/PersonBrowseRecordServiceImplTest.java

@@ -0,0 +1,208 @@
+package com.citu.module.menduner.system.service.person;
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.person.record.PersonBrowseRecordPageReqVO;
+import com.citu.module.menduner.system.controller.base.person.record.PersonBrowseRecordSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.person.PersonBrowseRecordDO;
+import com.citu.module.menduner.system.dal.mysql.person.PersonBrowseRecordMapper;
+import com.citu.module.menduner.system.service.person.record.PersonBrowseRecordServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.PERSON_BROWSE_RECORD_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link PersonBrowseRecordServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(PersonBrowseRecordServiceImpl.class)
+public class PersonBrowseRecordServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private PersonBrowseRecordServiceImpl personBrowseRecordService;
+
+    @Resource
+    private PersonBrowseRecordMapper personBrowseRecordMapper;
+
+    @Test
+    public void testCreatePersonBrowseRecord_success() {
+        // 准备参数
+        PersonBrowseRecordSaveReqVO createReqVO = randomPojo(PersonBrowseRecordSaveReqVO.class).setId(null);
+
+        // 调用
+        Long personBrowseRecordId = personBrowseRecordService.createPersonBrowseRecord(createReqVO);
+        // 断言
+        assertNotNull(personBrowseRecordId);
+        // 校验记录的属性是否正确
+        PersonBrowseRecordDO personBrowseRecord = personBrowseRecordMapper.selectById(personBrowseRecordId);
+        assertPojoEquals(createReqVO, personBrowseRecord, "id");
+    }
+
+    @Test
+    public void testUpdatePersonBrowseRecord_success() {
+        // mock 数据
+        PersonBrowseRecordDO dbPersonBrowseRecord = randomPojo(PersonBrowseRecordDO.class);
+        personBrowseRecordMapper.insert(dbPersonBrowseRecord);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        PersonBrowseRecordSaveReqVO updateReqVO = randomPojo(PersonBrowseRecordSaveReqVO.class, o -> {
+            o.setId(dbPersonBrowseRecord.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        personBrowseRecordService.updatePersonBrowseRecord(updateReqVO);
+        // 校验是否更新正确
+        PersonBrowseRecordDO personBrowseRecord = personBrowseRecordMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, personBrowseRecord);
+    }
+
+    @Test
+    public void testUpdatePersonBrowseRecord_notExists() {
+        // 准备参数
+        PersonBrowseRecordSaveReqVO updateReqVO = randomPojo(PersonBrowseRecordSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> personBrowseRecordService.updatePersonBrowseRecord(updateReqVO), PERSON_BROWSE_RECORD_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeletePersonBrowseRecord_success() {
+        // mock 数据
+        PersonBrowseRecordDO dbPersonBrowseRecord = randomPojo(PersonBrowseRecordDO.class);
+        personBrowseRecordMapper.insert(dbPersonBrowseRecord);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbPersonBrowseRecord.getId();
+
+        // 调用
+        personBrowseRecordService.deletePersonBrowseRecord(id);
+        // 校验数据不存在了
+        assertNull(personBrowseRecordMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeletePersonBrowseRecord_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> personBrowseRecordService.deletePersonBrowseRecord(id), PERSON_BROWSE_RECORD_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetPersonBrowseRecordPage() {
+        // mock 数据
+        PersonBrowseRecordDO dbPersonBrowseRecord = randomPojo(PersonBrowseRecordDO.class, o -> { // 等会查询到
+            o.setUserId(null);
+            o.setJobId(null);
+            o.setAreaId(null);
+            o.setPositionId(null);
+            o.setEnterpriseId(null);
+            o.setAreaName(null);
+            o.setJobName(null);
+            o.setType(null);
+            o.setExpType(null);
+            o.setEduType(null);
+            o.setPayFrom(null);
+            o.setPayTo(null);
+            o.setPayUnit(null);
+            o.setCurrencyType(null);
+            o.setContactUserId(null);
+            o.setContactUserName(null);
+            o.setContactUserPostName(null);
+            o.setEnterpriseName(null);
+            o.setIndustryId(null);
+            o.setFinancingStatus(null);
+            o.setScale(null);
+            o.setCreateTime(null);
+        });
+        personBrowseRecordMapper.insert(dbPersonBrowseRecord);
+        // 测试 userId 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setUserId(null)));
+        // 测试 jobId 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setJobId(null)));
+        // 测试 areaId 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setAreaId(null)));
+        // 测试 positionId 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setPositionId(null)));
+        // 测试 enterpriseId 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setEnterpriseId(null)));
+        // 测试 areaName 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setAreaName(null)));
+        // 测试 jobName 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setJobName(null)));
+        // 测试 type 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setType(null)));
+        // 测试 expType 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setExpType(null)));
+        // 测试 eduType 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setEduType(null)));
+        // 测试 payFrom 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setPayFrom(null)));
+        // 测试 payTo 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setPayTo(null)));
+        // 测试 payUnit 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setPayUnit(null)));
+        // 测试 currencyType 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setCurrencyType(null)));
+        // 测试 contactUserId 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setContactUserId(null)));
+        // 测试 contactUserName 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setContactUserName(null)));
+        // 测试 contactUserPostName 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setContactUserPostName(null)));
+        // 测试 enterpriseName 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setEnterpriseName(null)));
+        // 测试 industryId 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setIndustryId(null)));
+        // 测试 financingStatus 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setFinancingStatus(null)));
+        // 测试 scale 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setScale(null)));
+        // 测试 createTime 不匹配
+        personBrowseRecordMapper.insert(cloneIgnoreId(dbPersonBrowseRecord, o -> o.setCreateTime(null)));
+        // 准备参数
+        PersonBrowseRecordPageReqVO reqVO = new PersonBrowseRecordPageReqVO();
+        reqVO.setUserId(null);
+        reqVO.setJobId(null);
+        reqVO.setAreaId(null);
+        reqVO.setPositionId(null);
+        reqVO.setEnterpriseId(null);
+        reqVO.setAreaName(null);
+        reqVO.setJobName(null);
+        reqVO.setType(null);
+        reqVO.setExpType(null);
+        reqVO.setEduType(null);
+        reqVO.setPayFrom(null);
+        reqVO.setPayTo(null);
+        reqVO.setPayUnit(null);
+        reqVO.setCurrencyType(null);
+        reqVO.setContactUserId(null);
+        reqVO.setContactUserName(null);
+        reqVO.setContactUserPostName(null);
+        reqVO.setEnterpriseName(null);
+        reqVO.setIndustryId(null);
+        reqVO.setFinancingStatus(null);
+        reqVO.setScale(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<PersonBrowseRecordDO> pageResult = personBrowseRecordService.getPersonBrowseRecordPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbPersonBrowseRecord, pageResult.getList().get(0));
+    }
+
+}

+ 133 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/person/PersonCertificateServiceImplTest.java

@@ -0,0 +1,133 @@
+package com.citu.module.menduner.system.service.person;
+
+import com.citu.module.menduner.system.controller.base.person.certificate.PersonCertificatePageReqVO;
+import com.citu.module.menduner.system.controller.base.person.certificate.PersonCertificateSaveReqVO;
+import com.citu.module.menduner.system.service.person.certificate.PersonCertificateServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.person.PersonCertificateDO;
+import com.citu.module.menduner.system.dal.mysql.person.PersonCertificateMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_USER_CERTIFICATE_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link PersonCertificateServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(PersonCertificateServiceImpl.class)
+public class PersonCertificateServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private PersonCertificateServiceImpl userCertificateService;
+
+    @Resource
+    private PersonCertificateMapper personCertificateMapper;
+
+    @Test
+    public void testCreateUserCertificate_success() {
+        // 准备参数
+        PersonCertificateSaveReqVO createReqVO = randomPojo(PersonCertificateSaveReqVO.class).setId(null);
+
+        // 调用
+        Long userCertificateId = userCertificateService.createUserCertificate(createReqVO);
+        // 断言
+        assertNotNull(userCertificateId);
+        // 校验记录的属性是否正确
+        PersonCertificateDO userCertificate = personCertificateMapper.selectById(userCertificateId);
+        assertPojoEquals(createReqVO, userCertificate, "id");
+    }
+
+    @Test
+    public void testUpdateUserCertificate_success() {
+        // mock 数据
+        PersonCertificateDO dbUserCertificate = randomPojo(PersonCertificateDO.class);
+        personCertificateMapper.insert(dbUserCertificate);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        PersonCertificateSaveReqVO updateReqVO = randomPojo(PersonCertificateSaveReqVO.class, o -> {
+            o.setId(dbUserCertificate.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        userCertificateService.updateUserCertificate(updateReqVO);
+        // 校验是否更新正确
+        PersonCertificateDO userCertificate = personCertificateMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, userCertificate);
+    }
+
+    @Test
+    public void testUpdateUserCertificate_notExists() {
+        // 准备参数
+        PersonCertificateSaveReqVO updateReqVO = randomPojo(PersonCertificateSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> userCertificateService.updateUserCertificate(updateReqVO), MDE_USER_CERTIFICATE_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteUserCertificate_success() {
+        // mock 数据
+        PersonCertificateDO dbUserCertificate = randomPojo(PersonCertificateDO.class);
+        personCertificateMapper.insert(dbUserCertificate);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbUserCertificate.getId();
+
+        // 调用
+        userCertificateService.deleteUserCertificate(id);
+       // 校验数据不存在了
+       assertNull(personCertificateMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteUserCertificate_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> userCertificateService.deleteUserCertificate(id), MDE_USER_CERTIFICATE_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetUserCertificatePage() {
+       // mock 数据
+       PersonCertificateDO dbUserCertificate = randomPojo(PersonCertificateDO.class, o -> { // 等会查询到
+           o.setUserId(null);
+           o.setCertificateId(null);
+           o.setCreateTime(null);
+       });
+       personCertificateMapper.insert(dbUserCertificate);
+       // 测试 userId 不匹配
+       personCertificateMapper.insert(cloneIgnoreId(dbUserCertificate, o -> o.setUserId(null)));
+       // 测试 certificateId 不匹配
+       personCertificateMapper.insert(cloneIgnoreId(dbUserCertificate, o -> o.setCertificateId(null)));
+       // 测试 createTime 不匹配
+       personCertificateMapper.insert(cloneIgnoreId(dbUserCertificate, o -> o.setCreateTime(null)));
+       // 准备参数
+       PersonCertificatePageReqVO reqVO = new PersonCertificatePageReqVO();
+       reqVO.setUserId(null);
+       reqVO.setCertificateId(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<PersonCertificateDO> pageResult = userCertificateService.getUserCertificatePage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbUserCertificate, pageResult.getList().get(0));
+    }
+
+}

+ 132 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/person/PersonEnterpriseSubscribeServiceImplTest.java

@@ -0,0 +1,132 @@
+package com.citu.module.menduner.system.service.person;
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.person.subscribe.PersonEnterpriseSubscribePageReqVO;
+import com.citu.module.menduner.system.controller.base.person.subscribe.PersonEnterpriseSubscribeSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.person.PersonEnterpriseSubscribeDO;
+import com.citu.module.menduner.system.dal.mysql.person.PersonEnterpriseSubscribeMapper;
+import com.citu.module.menduner.system.service.person.subscribe.PersonEnterpriseSubscribeServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.PERSON_ENTERPRISE_ATTENTION_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link PersonEnterpriseSubscribeServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(PersonEnterpriseSubscribeServiceImpl.class)
+public class PersonEnterpriseSubscribeServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private PersonEnterpriseSubscribeServiceImpl personEnterpriseSubscribeService;
+
+    @Resource
+    private PersonEnterpriseSubscribeMapper personEnterpriseSubscribeMapper;
+
+    @Test
+    public void testCreatePersonEnterpriseSubscribe_success() {
+        // 准备参数
+        PersonEnterpriseSubscribeSaveReqVO createReqVO = randomPojo(PersonEnterpriseSubscribeSaveReqVO.class).setId(null);
+
+        // 调用
+        Long personEnterpriseSubscribeId = personEnterpriseSubscribeService.createPersonEnterpriseSubscribe(createReqVO);
+        // 断言
+        assertNotNull(personEnterpriseSubscribeId);
+        // 校验记录的属性是否正确
+        PersonEnterpriseSubscribeDO personEnterpriseSubscribe = personEnterpriseSubscribeMapper.selectById(personEnterpriseSubscribeId);
+        assertPojoEquals(createReqVO, personEnterpriseSubscribe, "id");
+    }
+
+    @Test
+    public void testUpdatePersonEnterpriseSubscribe_success() {
+        // mock 数据
+        PersonEnterpriseSubscribeDO dbPersonEnterpriseSubscribe = randomPojo(PersonEnterpriseSubscribeDO.class);
+        personEnterpriseSubscribeMapper.insert(dbPersonEnterpriseSubscribe);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        PersonEnterpriseSubscribeSaveReqVO updateReqVO = randomPojo(PersonEnterpriseSubscribeSaveReqVO.class, o -> {
+            o.setId(dbPersonEnterpriseSubscribe.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        personEnterpriseSubscribeService.updatePersonEnterpriseSubscribe(updateReqVO);
+        // 校验是否更新正确
+        PersonEnterpriseSubscribeDO personEnterpriseSubscribe = personEnterpriseSubscribeMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, personEnterpriseSubscribe);
+    }
+
+    @Test
+    public void testUpdatePersonEnterpriseSubscribe_notExists() {
+        // 准备参数
+        PersonEnterpriseSubscribeSaveReqVO updateReqVO = randomPojo(PersonEnterpriseSubscribeSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> personEnterpriseSubscribeService.updatePersonEnterpriseSubscribe(updateReqVO), PERSON_ENTERPRISE_ATTENTION_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeletePersonEnterpriseSubscribe_success() {
+        // mock 数据
+        PersonEnterpriseSubscribeDO dbPersonEnterpriseSubscribe = randomPojo(PersonEnterpriseSubscribeDO.class);
+        personEnterpriseSubscribeMapper.insert(dbPersonEnterpriseSubscribe);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbPersonEnterpriseSubscribe.getId();
+
+        // 调用
+        personEnterpriseSubscribeService.deletePersonEnterpriseSubscribe(id);
+        // 校验数据不存在了
+        assertNull(personEnterpriseSubscribeMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeletePersonEnterpriseSubscribe_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> personEnterpriseSubscribeService.deletePersonEnterpriseSubscribe(id), PERSON_ENTERPRISE_ATTENTION_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetPersonEnterpriseSubscribePage() {
+        // mock 数据
+        PersonEnterpriseSubscribeDO dbPersonEnterpriseSubscribe = randomPojo(PersonEnterpriseSubscribeDO.class, o -> { // 等会查询到
+            o.setUserId(null);
+            o.setEnterpriseId(null);
+            o.setCreateTime(null);
+        });
+        personEnterpriseSubscribeMapper.insert(dbPersonEnterpriseSubscribe);
+        // 测试 userId 不匹配
+        personEnterpriseSubscribeMapper.insert(cloneIgnoreId(dbPersonEnterpriseSubscribe, o -> o.setUserId(null)));
+        // 测试 enterpriseId 不匹配
+        personEnterpriseSubscribeMapper.insert(cloneIgnoreId(dbPersonEnterpriseSubscribe, o -> o.setEnterpriseId(null)));
+        // 测试 createTime 不匹配
+        personEnterpriseSubscribeMapper.insert(cloneIgnoreId(dbPersonEnterpriseSubscribe, o -> o.setCreateTime(null)));
+        // 准备参数
+        PersonEnterpriseSubscribePageReqVO reqVO = new PersonEnterpriseSubscribePageReqVO();
+        reqVO.setUserId(null);
+        reqVO.setEnterpriseId(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<PersonEnterpriseSubscribeDO> pageResult = personEnterpriseSubscribeService.getPersonEnterpriseSubscribePage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbPersonEnterpriseSubscribe, pageResult.getList().get(0));
+    }
+
+}

+ 173 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/person/PersonInfoServiceImplTest.java

@@ -0,0 +1,173 @@
+package com.citu.module.menduner.system.service.person;
+
+import com.citu.module.menduner.system.controller.base.person.info.PersonInfoPageReqVO;
+import com.citu.module.menduner.system.controller.base.person.info.PersonInfoSaveReqVO;
+import com.citu.module.menduner.system.service.person.info.PersonInfoServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.person.PersonInfoDO;
+import com.citu.module.menduner.system.dal.mysql.person.PersonInfoMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.*;
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link PersonInfoServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(PersonInfoServiceImpl.class)
+public class PersonInfoServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private PersonInfoServiceImpl userInfoService;
+
+    @Resource
+    private PersonInfoMapper personInfoMapper;
+
+    @Test
+    public void testCreateUserInfo_success() {
+        // 准备参数
+        PersonInfoSaveReqVO createReqVO = randomPojo(PersonInfoSaveReqVO.class).setId(null);
+
+        // 调用
+        Long userInfoId = userInfoService.createUserInfo(createReqVO);
+        // 断言
+        assertNotNull(userInfoId);
+        // 校验记录的属性是否正确
+        PersonInfoDO userInfo = personInfoMapper.selectById(userInfoId);
+        assertPojoEquals(createReqVO, userInfo, "id");
+    }
+
+    @Test
+    public void testUpdateUserInfo_success() {
+        // mock 数据
+        PersonInfoDO dbUserInfo = randomPojo(PersonInfoDO.class);
+        personInfoMapper.insert(dbUserInfo);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        PersonInfoSaveReqVO updateReqVO = randomPojo(PersonInfoSaveReqVO.class, o -> {
+            o.setId(dbUserInfo.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        userInfoService.updateUserInfo(updateReqVO);
+        // 校验是否更新正确
+        PersonInfoDO userInfo = personInfoMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, userInfo);
+    }
+
+    @Test
+    public void testUpdateUserInfo_notExists() {
+        // 准备参数
+        PersonInfoSaveReqVO updateReqVO = randomPojo(PersonInfoSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> userInfoService.updateUserInfo(updateReqVO), MDE_USER_INFO_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteUserInfo_success() {
+        // mock 数据
+        PersonInfoDO dbUserInfo = randomPojo(PersonInfoDO.class);
+        personInfoMapper.insert(dbUserInfo);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbUserInfo.getId();
+
+        // 调用
+        userInfoService.deleteUserInfo(id);
+       // 校验数据不存在了
+       assertNull(personInfoMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteUserInfo_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> userInfoService.deleteUserInfo(id), MDE_USER_INFO_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetUserInfoPage() {
+       // mock 数据
+       PersonInfoDO dbUserInfo = randomPojo(PersonInfoDO.class, o -> { // 等会查询到
+           o.setUserId(null);
+           o.setName(null);
+           o.setSex(null);
+           o.setAvatar(null);
+           o.setPhone(null);
+           o.setEmail(null);
+           o.setWxCode(null);
+           o.setBirthday(null);
+           o.setMaritalStatus(null);
+           o.setAreaId(null);
+           o.setJobType(null);
+           o.setJobStatus(null);
+           o.setCreateTime(null);
+       });
+       personInfoMapper.insert(dbUserInfo);
+       // 测试 userId 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setUserId(null)));
+       // 测试 name 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setName(null)));
+       // 测试 sex 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setSex(null)));
+       // 测试 avatar 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setAvatar(null)));
+       // 测试 phone 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setPhone(null)));
+       // 测试 email 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setEmail(null)));
+       // 测试 wxCode 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setWxCode(null)));
+       // 测试 birthday 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setBirthday(null)));
+       // 测试 maritalStatus 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setMaritalStatus(null)));
+       // 测试 areaId 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setAreaId(null)));
+       // 测试 jobType 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setJobType(null)));
+       // 测试 jobStatus 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setJobStatus(null)));
+       // 测试 createTime 不匹配
+       personInfoMapper.insert(cloneIgnoreId(dbUserInfo, o -> o.setCreateTime(null)));
+       // 准备参数
+       PersonInfoPageReqVO reqVO = new PersonInfoPageReqVO();
+       reqVO.setUserId(null);
+       reqVO.setName(null);
+       reqVO.setSex(null);
+       reqVO.setAvatar(null);
+       reqVO.setPhone(null);
+       reqVO.setEmail(null);
+       reqVO.setWxCode(null);
+       reqVO.setBirthday(null);
+       reqVO.setMaritalStatus(null);
+       reqVO.setAreaId(null);
+       reqVO.setJobType(null);
+       reqVO.setJobStatus(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<PersonInfoDO> pageResult = userInfoService.getUserInfoPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbUserInfo, pageResult.getList().get(0));
+    }
+
+}

+ 132 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/person/PersonJobFavoriteServiceImplTest.java

@@ -0,0 +1,132 @@
+package com.citu.module.menduner.system.service.person;
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.person.favorite.PersonJobFavoritePageReqVO;
+import com.citu.module.menduner.system.controller.base.person.favorite.PersonJobFavoriteSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.person.PersonJobFavoriteDO;
+import com.citu.module.menduner.system.dal.mysql.person.PersonJobFavoriteMapper;
+import com.citu.module.menduner.system.service.person.favorite.PersonJobFavoriteServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.PERSON_JOB_COLLECTION_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link PersonJobFavoriteServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(PersonJobFavoriteServiceImpl.class)
+public class PersonJobFavoriteServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private PersonJobFavoriteServiceImpl personJobFavoriteService;
+
+    @Resource
+    private PersonJobFavoriteMapper personJobFavoriteMapper;
+
+    @Test
+    public void testCreatePersonJobFavorite_success() {
+        // 准备参数
+        PersonJobFavoriteSaveReqVO createReqVO = randomPojo(PersonJobFavoriteSaveReqVO.class).setId(null);
+
+        // 调用
+        Long personJobFavoriteId = personJobFavoriteService.createPersonJobFavorite(createReqVO);
+        // 断言
+        assertNotNull(personJobFavoriteId);
+        // 校验记录的属性是否正确
+        PersonJobFavoriteDO personJobFavorite = personJobFavoriteMapper.selectById(personJobFavoriteId);
+        assertPojoEquals(createReqVO, personJobFavorite, "id");
+    }
+
+    @Test
+    public void testUpdatePersonJobFavorite_success() {
+        // mock 数据
+        PersonJobFavoriteDO dbPersonJobFavorite = randomPojo(PersonJobFavoriteDO.class);
+        personJobFavoriteMapper.insert(dbPersonJobFavorite);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        PersonJobFavoriteSaveReqVO updateReqVO = randomPojo(PersonJobFavoriteSaveReqVO.class, o -> {
+            o.setId(dbPersonJobFavorite.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        personJobFavoriteService.updatePersonJobFavorite(updateReqVO);
+        // 校验是否更新正确
+        PersonJobFavoriteDO personJobFavorite = personJobFavoriteMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, personJobFavorite);
+    }
+
+    @Test
+    public void testUpdatePersonJobFavorite_notExists() {
+        // 准备参数
+        PersonJobFavoriteSaveReqVO updateReqVO = randomPojo(PersonJobFavoriteSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> personJobFavoriteService.updatePersonJobFavorite(updateReqVO), PERSON_JOB_COLLECTION_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeletePersonJobFavorite_success() {
+        // mock 数据
+        PersonJobFavoriteDO dbPersonJobFavorite = randomPojo(PersonJobFavoriteDO.class);
+        personJobFavoriteMapper.insert(dbPersonJobFavorite);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbPersonJobFavorite.getId();
+
+        // 调用
+        personJobFavoriteService.deletePersonJobFavorite(id);
+        // 校验数据不存在了
+        assertNull(personJobFavoriteMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeletePersonJobFavorite_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> personJobFavoriteService.deletePersonJobFavorite(id), PERSON_JOB_COLLECTION_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetPersonJobFavoritePage() {
+        // mock 数据
+        PersonJobFavoriteDO dbPersonJobFavorite = randomPojo(PersonJobFavoriteDO.class, o -> { // 等会查询到
+            o.setUserId(null);
+            o.setJobId(null);
+            o.setCreateTime(null);
+        });
+        personJobFavoriteMapper.insert(dbPersonJobFavorite);
+        // 测试 userId 不匹配
+        personJobFavoriteMapper.insert(cloneIgnoreId(dbPersonJobFavorite, o -> o.setUserId(null)));
+        // 测试 jobId 不匹配
+        personJobFavoriteMapper.insert(cloneIgnoreId(dbPersonJobFavorite, o -> o.setJobId(null)));
+        // 测试 createTime 不匹配
+        personJobFavoriteMapper.insert(cloneIgnoreId(dbPersonJobFavorite, o -> o.setCreateTime(null)));
+        // 准备参数
+        PersonJobFavoritePageReqVO reqVO = new PersonJobFavoritePageReqVO();
+        reqVO.setUserId(null);
+        reqVO.setJobId(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<PersonJobFavoriteDO> pageResult = personJobFavoriteService.getPersonJobFavoritePage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbPersonJobFavorite, pageResult.getList().get(0));
+    }
+
+}

+ 137 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/person/PersonSkillServiceImplTest.java

@@ -0,0 +1,137 @@
+package com.citu.module.menduner.system.service.person;
+
+import com.citu.module.menduner.system.controller.base.person.skill.PersonSkillPageReqVO;
+import com.citu.module.menduner.system.controller.base.person.skill.PersonSkillSaveReqVO;
+import com.citu.module.menduner.system.service.person.skill.PersonSkillServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.person.PersonSkillDO;
+import com.citu.module.menduner.system.dal.mysql.person.PersonSkillMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_USER_SKILL_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link PersonSkillServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(PersonSkillServiceImpl.class)
+public class PersonSkillServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private PersonSkillServiceImpl userSkillService;
+
+    @Resource
+    private PersonSkillMapper personSkillMapper;
+
+    @Test
+    public void testCreateUserSkill_success() {
+        // 准备参数
+        PersonSkillSaveReqVO createReqVO = randomPojo(PersonSkillSaveReqVO.class).setId(null);
+
+        // 调用
+        Long userSkillId = userSkillService.createUserSkill(createReqVO);
+        // 断言
+        assertNotNull(userSkillId);
+        // 校验记录的属性是否正确
+        PersonSkillDO userSkill = personSkillMapper.selectById(userSkillId);
+        assertPojoEquals(createReqVO, userSkill, "id");
+    }
+
+    @Test
+    public void testUpdateUserSkill_success() {
+        // mock 数据
+        PersonSkillDO dbUserSkill = randomPojo(PersonSkillDO.class);
+        personSkillMapper.insert(dbUserSkill);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        PersonSkillSaveReqVO updateReqVO = randomPojo(PersonSkillSaveReqVO.class, o -> {
+            o.setId(dbUserSkill.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        userSkillService.updateUserSkill(updateReqVO);
+        // 校验是否更新正确
+        PersonSkillDO userSkill = personSkillMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, userSkill);
+    }
+
+    @Test
+    public void testUpdateUserSkill_notExists() {
+        // 准备参数
+        PersonSkillSaveReqVO updateReqVO = randomPojo(PersonSkillSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> userSkillService.updateUserSkill(updateReqVO), MDE_USER_SKILL_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteUserSkill_success() {
+        // mock 数据
+        PersonSkillDO dbUserSkill = randomPojo(PersonSkillDO.class);
+        personSkillMapper.insert(dbUserSkill);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbUserSkill.getId();
+
+        // 调用
+        userSkillService.deleteUserSkill(id);
+       // 校验数据不存在了
+       assertNull(personSkillMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteUserSkill_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> userSkillService.deleteUserSkill(id), MDE_USER_SKILL_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetUserSkillPage() {
+       // mock 数据
+       PersonSkillDO dbUserSkill = randomPojo(PersonSkillDO.class, o -> { // 等会查询到
+           o.setUserId(null);
+           o.setSkillId(null);
+           o.setLevel(null);
+           o.setCreateTime(null);
+       });
+       personSkillMapper.insert(dbUserSkill);
+       // 测试 userId 不匹配
+       personSkillMapper.insert(cloneIgnoreId(dbUserSkill, o -> o.setUserId(null)));
+       // 测试 name 不匹配
+       personSkillMapper.insert(cloneIgnoreId(dbUserSkill, o -> o.setSkillId(null)));
+       // 测试 level 不匹配
+       personSkillMapper.insert(cloneIgnoreId(dbUserSkill, o -> o.setLevel(null)));
+       // 测试 createTime 不匹配
+       personSkillMapper.insert(cloneIgnoreId(dbUserSkill, o -> o.setCreateTime(null)));
+       // 准备参数
+       PersonSkillPageReqVO reqVO = new PersonSkillPageReqVO();
+       reqVO.setUserId(null);
+       reqVO.setSkillId(null);
+       reqVO.setLevel(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<PersonSkillDO> pageResult = userSkillService.getUserSkillPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbUserSkill, pageResult.getList().get(0));
+    }
+
+}

+ 140 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/position/PositionServiceImplTest.java

@@ -0,0 +1,140 @@
+package com.citu.module.menduner.system.service.position;
+
+import com.citu.module.menduner.system.controller.base.position.PositionPageReqVO;
+import com.citu.module.menduner.system.controller.base.position.PositionSaveReqVO;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.position.PositionDO;
+import com.citu.module.menduner.system.dal.mysql.position.PositionMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.*;
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link PositionServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(PositionServiceImpl.class)
+public class PositionServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private PositionServiceImpl positionService;
+
+    @Resource
+    private PositionMapper positionMapper;
+
+    @Test
+    public void testCreatePosition_success() {
+        // 准备参数
+        PositionSaveReqVO createReqVO = randomPojo(PositionSaveReqVO.class).setId(null);
+
+        // 调用
+        Long positionId = positionService.createPosition(createReqVO);
+        // 断言
+        assertNotNull(positionId);
+        // 校验记录的属性是否正确
+        PositionDO position = positionMapper.selectById(positionId);
+        assertPojoEquals(createReqVO, position, "id");
+    }
+
+    @Test
+    public void testUpdatePosition_success() {
+        // mock 数据
+        PositionDO dbPosition = randomPojo(PositionDO.class);
+        positionMapper.insert(dbPosition);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        PositionSaveReqVO updateReqVO = randomPojo(PositionSaveReqVO.class, o -> {
+            o.setId(dbPosition.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        positionService.updatePosition(updateReqVO);
+        // 校验是否更新正确
+        PositionDO position = positionMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, position);
+    }
+
+    @Test
+    public void testUpdatePosition_notExists() {
+        // 准备参数
+        PositionSaveReqVO updateReqVO = randomPojo(PositionSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> positionService.updatePosition(updateReqVO), MDE_POSITION_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeletePosition_success() {
+        // mock 数据
+        PositionDO dbPosition = randomPojo(PositionDO.class);
+        positionMapper.insert(dbPosition);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbPosition.getId();
+
+        // 调用
+        positionService.deletePosition(id);
+        // 校验数据不存在了
+        assertNull(positionMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeletePosition_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> positionService.deletePosition(id), MDE_POSITION_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetPositionPage() {
+        // mock 数据
+        PositionDO dbPosition = randomPojo(PositionDO.class, o -> { // 等会查询到
+            o.setNameCn(null);
+            o.setNameEn(null);
+            o.setParentId(null);
+            o.setLevel(null);
+            o.setCreateTime(null);
+        });
+        positionMapper.insert(dbPosition);
+        // 测试 nameCn 不匹配
+        positionMapper.insert(cloneIgnoreId(dbPosition, o -> o.setNameCn(null)));
+        // 测试 nameEn 不匹配
+        positionMapper.insert(cloneIgnoreId(dbPosition, o -> o.setNameEn(null)));
+        // 测试 parentId 不匹配
+        positionMapper.insert(cloneIgnoreId(dbPosition, o -> o.setParentId(null)));
+        // 测试 level 不匹配
+        positionMapper.insert(cloneIgnoreId(dbPosition, o -> o.setLevel(null)));
+        // 测试 createTime 不匹配
+        positionMapper.insert(cloneIgnoreId(dbPosition, o -> o.setCreateTime(null)));
+        // 准备参数
+        PositionPageReqVO reqVO = new PositionPageReqVO();
+        reqVO.setNameCn(null);
+        reqVO.setNameEn(null);
+        reqVO.setParentId(null);
+        reqVO.setLevel(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<PositionDO> pageResult = positionService.getPositionPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbPosition, pageResult.getList().get(0));
+    }
+
+}

+ 133 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/positiontag/PositionTagServiceImplTest.java

@@ -0,0 +1,133 @@
+package com.citu.module.menduner.system.service.positiontag;
+
+import com.citu.module.menduner.system.controller.base.position.PositionTagPageReqVO;
+import com.citu.module.menduner.system.controller.base.position.PositionTagSaveReqVO;
+import com.citu.module.menduner.system.service.position.PositionTagServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.position.PositionTagDO;
+import com.citu.module.menduner.system.dal.mysql.position.PositionTagMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_POSITION_TAG_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link PositionTagServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(PositionTagServiceImpl.class)
+public class PositionTagServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private PositionTagServiceImpl positionTagService;
+
+    @Resource
+    private PositionTagMapper positionTagMapper;
+
+    @Test
+    public void testCreatePositionTag_success() {
+        // 准备参数
+        PositionTagSaveReqVO createReqVO = randomPojo(PositionTagSaveReqVO.class).setId(null);
+
+        // 调用
+        Long positionTagId = positionTagService.createPositionTag(createReqVO);
+        // 断言
+        assertNotNull(positionTagId);
+        // 校验记录的属性是否正确
+        PositionTagDO positionTag = positionTagMapper.selectById(positionTagId);
+        assertPojoEquals(createReqVO, positionTag, "id");
+    }
+
+    @Test
+    public void testUpdatePositionTag_success() {
+        // mock 数据
+        PositionTagDO dbPositionTag = randomPojo(PositionTagDO.class);
+        positionTagMapper.insert(dbPositionTag);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        PositionTagSaveReqVO updateReqVO = randomPojo(PositionTagSaveReqVO.class, o -> {
+            o.setId(dbPositionTag.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        positionTagService.updatePositionTag(updateReqVO);
+        // 校验是否更新正确
+        PositionTagDO positionTag = positionTagMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, positionTag);
+    }
+
+    @Test
+    public void testUpdatePositionTag_notExists() {
+        // 准备参数
+        PositionTagSaveReqVO updateReqVO = randomPojo(PositionTagSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> positionTagService.updatePositionTag(updateReqVO), MDE_POSITION_TAG_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeletePositionTag_success() {
+        // mock 数据
+        PositionTagDO dbPositionTag = randomPojo(PositionTagDO.class);
+        positionTagMapper.insert(dbPositionTag);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbPositionTag.getId();
+
+        // 调用
+        positionTagService.deletePositionTag(id);
+       // 校验数据不存在了
+       assertNull(positionTagMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeletePositionTag_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> positionTagService.deletePositionTag(id), MDE_POSITION_TAG_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetPositionTagPage() {
+       // mock 数据
+       PositionTagDO dbPositionTag = randomPojo(PositionTagDO.class, o -> { // 等会查询到
+           o.setPositionId(null);
+           o.setLabel(null);
+           o.setCreateTime(null);
+       });
+       positionTagMapper.insert(dbPositionTag);
+       // 测试 positionId 不匹配
+       positionTagMapper.insert(cloneIgnoreId(dbPositionTag, o -> o.setPositionId(null)));
+       // 测试 label 不匹配
+       positionTagMapper.insert(cloneIgnoreId(dbPositionTag, o -> o.setLabel(null)));
+       // 测试 createTime 不匹配
+       positionTagMapper.insert(cloneIgnoreId(dbPositionTag, o -> o.setCreateTime(null)));
+       // 准备参数
+       PositionTagPageReqVO reqVO = new PositionTagPageReqVO();
+       reqVO.setPositionId(null);
+       reqVO.setLabel(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<PositionTagDO> pageResult = positionTagService.getPositionTagPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbPositionTag, pageResult.getList().get(0));
+    }
+
+}

+ 145 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/projectexp/ProjectExpServiceImplTest.java

@@ -0,0 +1,145 @@
+package com.citu.module.menduner.system.service.projectexp;
+
+import com.citu.module.menduner.system.controller.base.projectexp.ProjectExpPageReqVO;
+import com.citu.module.menduner.system.controller.base.projectexp.ProjectExpSaveReqVO;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.projectexp.ProjectExpDO;
+import com.citu.module.menduner.system.dal.mysql.projectexp.ProjectExpMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import java.time.LocalDate;
+
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.*;
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link ProjectExpServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(ProjectExpServiceImpl.class)
+public class ProjectExpServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private ProjectExpServiceImpl projectExpService;
+
+    @Resource
+    private ProjectExpMapper projectExpMapper;
+
+    @Test
+    public void testCreateProjectExp_success() {
+        // 准备参数
+        ProjectExpSaveReqVO createReqVO = randomPojo(ProjectExpSaveReqVO.class).setId(null);
+
+        // 调用
+        Long projectExpId = projectExpService.createProjectExp(createReqVO);
+        // 断言
+        assertNotNull(projectExpId);
+        // 校验记录的属性是否正确
+        ProjectExpDO projectExp = projectExpMapper.selectById(projectExpId);
+        assertPojoEquals(createReqVO, projectExp, "id");
+    }
+
+    @Test
+    public void testUpdateProjectExp_success() {
+        // mock 数据
+        ProjectExpDO dbProjectExp = randomPojo(ProjectExpDO.class);
+        projectExpMapper.insert(dbProjectExp);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        ProjectExpSaveReqVO updateReqVO = randomPojo(ProjectExpSaveReqVO.class, o -> {
+            o.setId(dbProjectExp.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        projectExpService.updateProjectExp(updateReqVO);
+        // 校验是否更新正确
+        ProjectExpDO projectExp = projectExpMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, projectExp);
+    }
+
+    @Test
+    public void testUpdateProjectExp_notExists() {
+        // 准备参数
+        ProjectExpSaveReqVO updateReqVO = randomPojo(ProjectExpSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> projectExpService.updateProjectExp(updateReqVO), PROJECT_EXP_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteProjectExp_success() {
+        // mock 数据
+        ProjectExpDO dbProjectExp = randomPojo(ProjectExpDO.class);
+        projectExpMapper.insert(dbProjectExp);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbProjectExp.getId();
+
+        // 调用
+        projectExpService.deleteProjectExp(id);
+        // 校验数据不存在了
+        assertNull(projectExpMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteProjectExp_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> projectExpService.deleteProjectExp(id), PROJECT_EXP_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetProjectExpPage() {
+        // mock 数据
+        ProjectExpDO dbProjectExp = randomPojo(ProjectExpDO.class, o -> { // 等会查询到
+            o.setUserId(null);
+            o.setName(null);
+            o.setStartTime(null);
+            o.setEndTime(null);
+            o.setCreateTime(null);
+        });
+        projectExpMapper.insert(dbProjectExp);
+        // 测试 userId 不匹配
+        projectExpMapper.insert(cloneIgnoreId(dbProjectExp, o -> o.setUserId(null)));
+        // 测试 name 不匹配
+        projectExpMapper.insert(cloneIgnoreId(dbProjectExp, o -> o.setName(null)));
+        // 测试 startTime 不匹配
+        projectExpMapper.insert(cloneIgnoreId(dbProjectExp, o -> o.setStartTime(null)));
+        // 测试 endTime 不匹配
+        projectExpMapper.insert(cloneIgnoreId(dbProjectExp, o -> o.setEndTime(null)));
+        // 测试 createTime 不匹配
+        projectExpMapper.insert(cloneIgnoreId(dbProjectExp, o -> o.setCreateTime(null)));
+        // 准备参数
+        ProjectExpPageReqVO reqVO = new ProjectExpPageReqVO();
+        reqVO.setUserId(null);
+        reqVO.setName(null);
+        reqVO.setStartTime(buildBetweenDate(2023, 2, 1, 2023, 2, 28));
+        reqVO.setEndTime(buildBetweenDate(2023, 2, 1, 2023, 2, 28));
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<ProjectExpDO> pageResult = projectExpService.getProjectExpPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbProjectExp, pageResult.getList().get(0));
+    }
+    public static LocalDate[] buildBetweenDate(int year1, int mouth1, int day1,
+                                               int year2, int mouth2, int day2) {
+        return new LocalDate[]{LocalDate.of(year1, mouth1, day1), LocalDate.of(year2, mouth2, day2)};
+    }
+}

+ 148 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/school/SchoolServiceImplTest.java

@@ -0,0 +1,148 @@
+package com.citu.module.menduner.system.service.school;
+
+import com.citu.module.menduner.system.controller.base.school.SchoolPageReqVO;
+import com.citu.module.menduner.system.controller.base.school.SchoolSaveReqVO;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.school.SchoolDO;
+import com.citu.module.menduner.system.dal.mysql.school.SchoolMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_SCHOOL_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link SchoolServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(SchoolServiceImpl.class)
+public class SchoolServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private SchoolServiceImpl schoolService;
+
+    @Resource
+    private SchoolMapper schoolMapper;
+
+    @Test
+    public void testCreateSchool_success() {
+        // 准备参数
+        SchoolSaveReqVO createReqVO = randomPojo(SchoolSaveReqVO.class).setId(null);
+
+        // 调用
+        Long schoolId = schoolService.createSchool(createReqVO);
+        // 断言
+        assertNotNull(schoolId);
+        // 校验记录的属性是否正确
+        SchoolDO school = schoolMapper.selectById(schoolId);
+        assertPojoEquals(createReqVO, school, "id");
+    }
+
+    @Test
+    public void testUpdateSchool_success() {
+        // mock 数据
+        SchoolDO dbSchool = randomPojo(SchoolDO.class);
+        schoolMapper.insert(dbSchool);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        SchoolSaveReqVO updateReqVO = randomPojo(SchoolSaveReqVO.class, o -> {
+            o.setId(dbSchool.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        schoolService.updateSchool(updateReqVO);
+        // 校验是否更新正确
+        SchoolDO school = schoolMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, school);
+    }
+
+    @Test
+    public void testUpdateSchool_notExists() {
+        // 准备参数
+        SchoolSaveReqVO updateReqVO = randomPojo(SchoolSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> schoolService.updateSchool(updateReqVO), MDE_SCHOOL_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteSchool_success() {
+        // mock 数据
+        SchoolDO dbSchool = randomPojo(SchoolDO.class);
+        schoolMapper.insert(dbSchool);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbSchool.getId();
+
+        // 调用
+        schoolService.deleteSchool(id);
+       // 校验数据不存在了
+       assertNull(schoolMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteSchool_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> schoolService.deleteSchool(id), MDE_SCHOOL_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetSchoolPage() {
+       // mock 数据
+       SchoolDO dbSchool = randomPojo(SchoolDO.class, o -> { // 等会查询到
+           o.setName(null);
+           o.setAreaId(null);
+           o.setAddress(null);
+           o.setLongitude(null);
+           o.setLatitude(null);
+           o.setStatus(null);
+           o.setCreateTime(null);
+       });
+       schoolMapper.insert(dbSchool);
+       // 测试 name 不匹配
+       schoolMapper.insert(cloneIgnoreId(dbSchool, o -> o.setName(null)));
+       // 测试 areaId 不匹配
+       schoolMapper.insert(cloneIgnoreId(dbSchool, o -> o.setAreaId(null)));
+       // 测试 address 不匹配
+       schoolMapper.insert(cloneIgnoreId(dbSchool, o -> o.setAddress(null)));
+       // 测试 longitude 不匹配
+       schoolMapper.insert(cloneIgnoreId(dbSchool, o -> o.setLongitude(null)));
+       // 测试 latitude 不匹配
+       schoolMapper.insert(cloneIgnoreId(dbSchool, o -> o.setLatitude(null)));
+       // 测试 status 不匹配
+       schoolMapper.insert(cloneIgnoreId(dbSchool, o -> o.setStatus(null)));
+       // 测试 createTime 不匹配
+       schoolMapper.insert(cloneIgnoreId(dbSchool, o -> o.setCreateTime(null)));
+       // 准备参数
+       SchoolPageReqVO reqVO = new SchoolPageReqVO();
+       reqVO.setName(null);
+       reqVO.setAreaId(null);
+       reqVO.setAddress(null);
+       reqVO.setLongitude(null);
+       reqVO.setLatitude(null);
+       reqVO.setStatus(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<SchoolDO> pageResult = schoolService.getSchoolPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbSchool, pageResult.getList().get(0));
+    }
+
+}

+ 138 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/skill/SkillServiceImplTest.java

@@ -0,0 +1,138 @@
+package com.citu.module.menduner.system.service.skill;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.skill.SkillListReqVO;
+import com.citu.module.menduner.system.controller.base.skill.SkillSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.skill.SkillDO;
+import com.citu.module.menduner.system.dal.mysql.skill.SkillMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.SKILL_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link SkillServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(SkillServiceImpl.class)
+public class SkillServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private SkillServiceImpl skillService;
+
+    @Resource
+    private SkillMapper skillMapper;
+
+    @Test
+    public void testCreateSkill_success() {
+        // 准备参数
+        SkillSaveReqVO createReqVO = randomPojo(SkillSaveReqVO.class).setId(null);
+
+        // 调用
+        Long skillId = skillService.createSkill(createReqVO);
+        // 断言
+        assertNotNull(skillId);
+        // 校验记录的属性是否正确
+        SkillDO skill = skillMapper.selectById(skillId);
+        assertPojoEquals(createReqVO, skill, "id");
+    }
+
+    @Test
+    public void testUpdateSkill_success() {
+        // mock 数据
+        SkillDO dbSkill = randomPojo(SkillDO.class);
+        skillMapper.insert(dbSkill);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        SkillSaveReqVO updateReqVO = randomPojo(SkillSaveReqVO.class, o -> {
+            o.setId(dbSkill.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        skillService.updateSkill(updateReqVO);
+        // 校验是否更新正确
+        SkillDO skill = skillMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, skill);
+    }
+
+    @Test
+    public void testUpdateSkill_notExists() {
+        // 准备参数
+        SkillSaveReqVO updateReqVO = randomPojo(SkillSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> skillService.updateSkill(updateReqVO), SKILL_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteSkill_success() {
+        // mock 数据
+        SkillDO dbSkill = randomPojo(SkillDO.class);
+        skillMapper.insert(dbSkill);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbSkill.getId();
+
+        // 调用
+        skillService.deleteSkill(id);
+        // 校验数据不存在了
+        assertNull(skillMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteSkill_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> skillService.deleteSkill(id), SKILL_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetSkillList() {
+        // mock 数据
+        SkillDO dbSkill = randomPojo(SkillDO.class, o -> { // 等会查询到
+            o.setNameCn(null);
+            o.setNameEn(null);
+            o.setParentId(null);
+            o.setLevel(null);
+            o.setCreateTime(null);
+        });
+        skillMapper.insert(dbSkill);
+        // 测试 nameCn 不匹配
+        skillMapper.insert(cloneIgnoreId(dbSkill, o -> o.setNameCn(null)));
+        // 测试 nameEn 不匹配
+        skillMapper.insert(cloneIgnoreId(dbSkill, o -> o.setNameEn(null)));
+        // 测试 parentId 不匹配
+        skillMapper.insert(cloneIgnoreId(dbSkill, o -> o.setParentId(null)));
+        // 测试 level 不匹配
+        skillMapper.insert(cloneIgnoreId(dbSkill, o -> o.setLevel(null)));
+        // 测试 createTime 不匹配
+        skillMapper.insert(cloneIgnoreId(dbSkill, o -> o.setCreateTime(null)));
+        // 准备参数
+        SkillListReqVO reqVO = new SkillListReqVO();
+        reqVO.setNameCn(null);
+        reqVO.setNameEn(null);
+        reqVO.setParentId(null);
+        reqVO.setLevel(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        List<SkillDO> list = skillService.getSkillList(reqVO);
+        // 断言
+        assertEquals(1, list.size());
+        assertPojoEquals(dbSkill, list.get(0));
+    }
+
+}

+ 149 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/trainexp/TrainExpServiceImplTest.java

@@ -0,0 +1,149 @@
+package com.citu.module.menduner.system.service.trainexp;
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.trainexp.TrainExpPageReqVO;
+import com.citu.module.menduner.system.controller.base.trainexp.TrainExpSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.trainexp.TrainExpDO;
+import com.citu.module.menduner.system.dal.mysql.trainexp.TrainExpMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+import java.time.LocalDate;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.TRAIN_EXP_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link TrainExpServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(TrainExpServiceImpl.class)
+public class TrainExpServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private TrainExpServiceImpl trainExpService;
+
+    @Resource
+    private TrainExpMapper trainExpMapper;
+
+    public static LocalDate[] buildBetweenDate(int year1, int mouth1, int day1,
+                                               int year2, int mouth2, int day2) {
+        return new LocalDate[]{LocalDate.of(year1, mouth1, day1), LocalDate.of(year2, mouth2, day2)};
+    }
+
+    @Test
+    public void testCreateTrainExp_success() {
+        // 准备参数
+        TrainExpSaveReqVO createReqVO = randomPojo(TrainExpSaveReqVO.class).setId(null);
+
+        // 调用
+        Long trainExpId = trainExpService.createTrainExp(createReqVO);
+        // 断言
+        assertNotNull(trainExpId);
+        // 校验记录的属性是否正确
+        TrainExpDO trainExp = trainExpMapper.selectById(trainExpId);
+        assertPojoEquals(createReqVO, trainExp, "id");
+    }
+
+    @Test
+    public void testUpdateTrainExp_success() {
+        // mock 数据
+        TrainExpDO dbTrainExp = randomPojo(TrainExpDO.class);
+        trainExpMapper.insert(dbTrainExp);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        TrainExpSaveReqVO updateReqVO = randomPojo(TrainExpSaveReqVO.class, o -> {
+            o.setId(dbTrainExp.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        trainExpService.updateTrainExp(updateReqVO);
+        // 校验是否更新正确
+        TrainExpDO trainExp = trainExpMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, trainExp);
+    }
+
+    @Test
+    public void testUpdateTrainExp_notExists() {
+        // 准备参数
+        TrainExpSaveReqVO updateReqVO = randomPojo(TrainExpSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> trainExpService.updateTrainExp(updateReqVO), TRAIN_EXP_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteTrainExp_success() {
+        // mock 数据
+        TrainExpDO dbTrainExp = randomPojo(TrainExpDO.class);
+        trainExpMapper.insert(dbTrainExp);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbTrainExp.getId();
+
+        // 调用
+        trainExpService.deleteTrainExp(id);
+        // 校验数据不存在了
+        assertNull(trainExpMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteTrainExp_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> trainExpService.deleteTrainExp(id), TRAIN_EXP_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetTrainExpPage() {
+        // mock 数据
+        TrainExpDO dbTrainExp = randomPojo(TrainExpDO.class, o -> { // 等会查询到
+            o.setUserId(null);
+            o.setStartTime(null);
+            o.setEndTime(null);
+            o.setOrgName(null);
+            o.setCourse(null);
+            o.setCreateTime(null);
+        });
+        trainExpMapper.insert(dbTrainExp);
+        // 测试 userId 不匹配
+        trainExpMapper.insert(cloneIgnoreId(dbTrainExp, o -> o.setUserId(null)));
+        // 测试 startTime 不匹配
+        trainExpMapper.insert(cloneIgnoreId(dbTrainExp, o -> o.setStartTime(null)));
+        // 测试 endTime 不匹配
+        trainExpMapper.insert(cloneIgnoreId(dbTrainExp, o -> o.setEndTime(null)));
+        // 测试 orgName 不匹配
+        trainExpMapper.insert(cloneIgnoreId(dbTrainExp, o -> o.setOrgName(null)));
+        // 测试 course 不匹配
+        trainExpMapper.insert(cloneIgnoreId(dbTrainExp, o -> o.setCourse(null)));
+        // 测试 createTime 不匹配
+        trainExpMapper.insert(cloneIgnoreId(dbTrainExp, o -> o.setCreateTime(null)));
+        // 准备参数
+        TrainExpPageReqVO reqVO = new TrainExpPageReqVO();
+        reqVO.setUserId(null);
+        reqVO.setStartTime(buildBetweenDate(2023, 2, 1, 2023, 2, 28));
+        reqVO.setEndTime(buildBetweenDate(2023, 2, 1, 2023, 2, 28));
+        reqVO.setOrgName(null);
+        reqVO.setCourse(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<TrainExpDO> pageResult = trainExpService.getTrainExpPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbTrainExp, pageResult.getList().get(0));
+    }
+
+}

+ 144 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/user/MdeUserServiceImplTest.java

@@ -0,0 +1,144 @@
+package com.citu.module.menduner.system.service.user;
+
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.user.MdeUserPageReqVO;
+import com.citu.module.menduner.system.controller.base.user.MdeUserSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.user.MdeUserDO;
+import com.citu.module.menduner.system.dal.mysql.user.MdeUserMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_USER_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link MdeUserServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(MdeUserServiceImpl.class)
+public class MdeUserServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private MdeUserServiceImpl mdeUserService;
+
+    @Resource
+    private MdeUserMapper mdeUserMapper;
+
+    @Test
+    public void testCreateMdeUser_success() {
+        // 准备参数
+        MdeUserSaveReqVO createReqVO = randomPojo(MdeUserSaveReqVO.class).setId(null);
+
+        // 调用
+        Long mdeUserId = mdeUserService.createMdeUser(createReqVO);
+        // 断言
+        assertNotNull(mdeUserId);
+        // 校验记录的属性是否正确
+        MdeUserDO mdeUser = mdeUserMapper.selectById(mdeUserId);
+        assertPojoEquals(createReqVO, mdeUser, "id");
+    }
+
+    @Test
+    public void testUpdateMdeUser_success() {
+        // mock 数据
+        MdeUserDO dbMdeUser = randomPojo(MdeUserDO.class);
+        mdeUserMapper.insert(dbMdeUser);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        MdeUserSaveReqVO updateReqVO = randomPojo(MdeUserSaveReqVO.class, o -> {
+            o.setId(dbMdeUser.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        mdeUserService.updateMdeUser(updateReqVO);
+        // 校验是否更新正确
+        MdeUserDO mdeUser = mdeUserMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, mdeUser);
+    }
+
+    @Test
+    public void testUpdateMdeUser_notExists() {
+        // 准备参数
+        MdeUserSaveReqVO updateReqVO = randomPojo(MdeUserSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> mdeUserService.updateMdeUser(updateReqVO), MDE_USER_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteMdeUser_success() {
+        // mock 数据
+        MdeUserDO dbMdeUser = randomPojo(MdeUserDO.class);
+        mdeUserMapper.insert(dbMdeUser);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbMdeUser.getId();
+
+        // 调用
+        mdeUserService.deleteMdeUser(id);
+        // 校验数据不存在了
+        assertNull(mdeUserMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteMdeUser_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> mdeUserService.deleteMdeUser(id), MDE_USER_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetMdeUserPage() {
+        // mock 数据
+        MdeUserDO dbMdeUser = randomPojo(MdeUserDO.class, o -> { // 等会查询到
+            o.setUsername(null);
+            o.setEmail(null);
+            o.setPhone(null);
+            o.setStatus(null);
+            o.setLoginIp(null);
+            o.setCreateTime(null);
+        });
+        mdeUserMapper.insert(dbMdeUser);
+        // 测试 username 不匹配
+        mdeUserMapper.insert(cloneIgnoreId(dbMdeUser, o -> o.setUsername(null)));
+        // 测试 email 不匹配
+        mdeUserMapper.insert(cloneIgnoreId(dbMdeUser, o -> o.setEmail(null)));
+        // 测试 phone 不匹配
+        mdeUserMapper.insert(cloneIgnoreId(dbMdeUser, o -> o.setPhone(null)));
+        // 测试 status 不匹配
+        mdeUserMapper.insert(cloneIgnoreId(dbMdeUser, o -> o.setStatus(null)));
+        // 测试 loginIp 不匹配
+        mdeUserMapper.insert(cloneIgnoreId(dbMdeUser, o -> o.setLoginIp(null)));
+        // 测试 createTime 不匹配
+        mdeUserMapper.insert(cloneIgnoreId(dbMdeUser, o -> o.setCreateTime(null)));
+        // 准备参数
+        MdeUserPageReqVO reqVO = new MdeUserPageReqVO();
+        reqVO.setUsername(null);
+        reqVO.setEmail(null);
+        reqVO.setPhone(null);
+        reqVO.setStatus(null);
+        reqVO.setLoginIp(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<MdeUserDO> pageResult = mdeUserService.getMdeUserPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbMdeUser, pageResult.getList().get(0));
+    }
+
+}

+ 132 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/user/UserAccountServiceImplTest.java

@@ -0,0 +1,132 @@
+package com.citu.module.menduner.system.service.user;
+
+
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+import com.citu.module.menduner.system.controller.base.user.account.UserAccountPageReqVO;
+import com.citu.module.menduner.system.controller.base.user.account.UserAccountSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.user.UserAccountDO;
+import com.citu.module.menduner.system.dal.mysql.user.UserAccountMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
+import static com.citu.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static com.citu.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static com.citu.framework.test.core.util.AssertUtils.assertServiceException;
+import static com.citu.framework.test.core.util.RandomUtils.randomLongId;
+import static com.citu.framework.test.core.util.RandomUtils.randomPojo;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.USER_ACCOUNT_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link UserAccountServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(UserAccountServiceImpl.class)
+public class UserAccountServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private UserAccountServiceImpl userAccountService;
+
+    @Resource
+    private UserAccountMapper userAccountMapper;
+
+    @Test
+    public void testCreateUserAccount_success() {
+        // 准备参数
+        UserAccountSaveReqVO createReqVO = randomPojo(UserAccountSaveReqVO.class).setId(null);
+
+        // 调用
+        Long userAccountId = userAccountService.createUserAccount(createReqVO);
+        // 断言
+        assertNotNull(userAccountId);
+        // 校验记录的属性是否正确
+        UserAccountDO userAccount = userAccountMapper.selectById(userAccountId);
+        assertPojoEquals(createReqVO, userAccount, "id");
+    }
+
+    @Test
+    public void testUpdateUserAccount_success() {
+        // mock 数据
+        UserAccountDO dbUserAccount = randomPojo(UserAccountDO.class);
+        userAccountMapper.insert(dbUserAccount);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        UserAccountSaveReqVO updateReqVO = randomPojo(UserAccountSaveReqVO.class, o -> {
+            o.setId(dbUserAccount.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        userAccountService.updateUserAccount(updateReqVO);
+        // 校验是否更新正确
+        UserAccountDO userAccount = userAccountMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, userAccount);
+    }
+
+    @Test
+    public void testUpdateUserAccount_notExists() {
+        // 准备参数
+        UserAccountSaveReqVO updateReqVO = randomPojo(UserAccountSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> userAccountService.updateUserAccount(updateReqVO), USER_ACCOUNT_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteUserAccount_success() {
+        // mock 数据
+        UserAccountDO dbUserAccount = randomPojo(UserAccountDO.class);
+        userAccountMapper.insert(dbUserAccount);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbUserAccount.getId();
+
+        // 调用
+        userAccountService.deleteUserAccount(id);
+        // 校验数据不存在了
+        assertNull(userAccountMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteUserAccount_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> userAccountService.deleteUserAccount(id), USER_ACCOUNT_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetUserAccountPage() {
+        // mock 数据
+        UserAccountDO dbUserAccount = randomPojo(UserAccountDO.class, o -> { // 等会查询到
+            o.setUserId(null);
+            o.setBalance(null);
+            o.setCreateTime(null);
+        });
+        userAccountMapper.insert(dbUserAccount);
+        // 测试 userId 不匹配
+        userAccountMapper.insert(cloneIgnoreId(dbUserAccount, o -> o.setUserId(null)));
+        // 测试 balance 不匹配
+        userAccountMapper.insert(cloneIgnoreId(dbUserAccount, o -> o.setBalance(null)));
+        // 测试 createTime 不匹配
+        userAccountMapper.insert(cloneIgnoreId(dbUserAccount, o -> o.setCreateTime(null)));
+        // 准备参数
+        UserAccountPageReqVO reqVO = new UserAccountPageReqVO();
+        reqVO.setUserId(null);
+        reqVO.setBalance(null);
+        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+        // 调用
+        PageResult<UserAccountDO> pageResult = userAccountService.getUserAccountPage(reqVO);
+        // 断言
+        assertEquals(1, pageResult.getTotal());
+        assertEquals(1, pageResult.getList().size());
+        assertPojoEquals(dbUserAccount, pageResult.getList().get(0));
+    }
+
+}

+ 170 - 0
menduner/menduner-im-biz/src/test/java/com/citu/module/menduner/system/service/workexp/WorkExpServiceImplTest.java

@@ -0,0 +1,170 @@
+package com.citu.module.menduner.system.service.workexp;
+
+import com.citu.module.menduner.system.controller.base.workexp.WorkExpPageReqVO;
+import com.citu.module.menduner.system.controller.base.workexp.WorkExpSaveReqVO;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import com.citu.framework.test.core.ut.BaseDbUnitTest;
+
+import com.citu.module.menduner.system.dal.dataobject.workexp.WorkExpDO;
+import com.citu.module.menduner.system.dal.mysql.workexp.WorkExpMapper;
+import com.citu.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import java.time.LocalDateTime;
+
+import static com.citu.framework.test.core.util.AssertUtils.*;
+import static com.citu.framework.test.core.util.RandomUtils.*;
+import static com.citu.framework.common.util.date.LocalDateTimeUtils.*;
+import static com.citu.framework.common.util.object.ObjectUtils.*;
+import static com.citu.module.menduner.system.enums.ErrorCodeConstants.MDE_WORK_EXP_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link WorkExpServiceImpl} 的单元测试类
+ *
+ * @author Rayson
+ */
+@Import(WorkExpServiceImpl.class)
+public class WorkExpServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private WorkExpServiceImpl workExpService;
+
+    @Resource
+    private WorkExpMapper workExpMapper;
+
+    @Test
+    public void testCreateWorkExp_success() {
+        // 准备参数
+        WorkExpSaveReqVO createReqVO = randomPojo(WorkExpSaveReqVO.class).setId(null);
+
+        // 调用
+        Long workExpId = workExpService.createWorkExp(createReqVO);
+        // 断言
+        assertNotNull(workExpId);
+        // 校验记录的属性是否正确
+        WorkExpDO workExp = workExpMapper.selectById(workExpId);
+        assertPojoEquals(createReqVO, workExp, "id");
+    }
+
+    @Test
+    public void testUpdateWorkExp_success() {
+        // mock 数据
+        WorkExpDO dbWorkExp = randomPojo(WorkExpDO.class);
+        workExpMapper.insert(dbWorkExp);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        WorkExpSaveReqVO updateReqVO = randomPojo(WorkExpSaveReqVO.class, o -> {
+            o.setId(dbWorkExp.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        workExpService.updateWorkExp(updateReqVO);
+        // 校验是否更新正确
+        WorkExpDO workExp = workExpMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, workExp);
+    }
+
+    @Test
+    public void testUpdateWorkExp_notExists() {
+        // 准备参数
+        WorkExpSaveReqVO updateReqVO = randomPojo(WorkExpSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> workExpService.updateWorkExp(updateReqVO), MDE_WORK_EXP_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteWorkExp_success() {
+        // mock 数据
+        WorkExpDO dbWorkExp = randomPojo(WorkExpDO.class);
+        workExpMapper.insert(dbWorkExp);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbWorkExp.getId();
+
+        // 调用
+        workExpService.deleteWorkExp(id);
+       // 校验数据不存在了
+       assertNull(workExpMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteWorkExp_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> workExpService.deleteWorkExp(id), MDE_WORK_EXP_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetWorkExpPage() {
+       // mock 数据
+       WorkExpDO dbWorkExp = randomPojo(WorkExpDO.class, o -> { // 等会查询到
+           o.setUserId(null);
+           o.setEnterpriseId(null);
+           o.setEnterpriseName(null);
+           o.setIndustryId(null);
+           o.setDeptName(null);
+           o.setPositionId(null);
+           o.setPositionName(null);
+           o.setStartTime(null);
+           o.setEndTime(null);
+           o.setPayUnit(null);
+           o.setCurrencyType(null);
+           o.setCreateTime(null);
+       });
+       workExpMapper.insert(dbWorkExp);
+       // 测试 userId 不匹配
+       workExpMapper.insert(cloneIgnoreId(dbWorkExp, o -> o.setUserId(null)));
+       // 测试 enterpriseId 不匹配
+       workExpMapper.insert(cloneIgnoreId(dbWorkExp, o -> o.setEnterpriseId(null)));
+       // 测试 enterpriseName 不匹配
+       workExpMapper.insert(cloneIgnoreId(dbWorkExp, o -> o.setEnterpriseName(null)));
+       // 测试 industryId 不匹配
+       workExpMapper.insert(cloneIgnoreId(dbWorkExp, o -> o.setIndustryId(null)));
+       // 测试 deptName 不匹配
+       workExpMapper.insert(cloneIgnoreId(dbWorkExp, o -> o.setDeptName(null)));
+       // 测试 positionId 不匹配
+       workExpMapper.insert(cloneIgnoreId(dbWorkExp, o -> o.setPositionId(null)));
+       // 测试 positionName 不匹配
+       workExpMapper.insert(cloneIgnoreId(dbWorkExp, o -> o.setPositionName(null)));
+       // 测试 startTime 不匹配
+       workExpMapper.insert(cloneIgnoreId(dbWorkExp, o -> o.setStartTime(null)));
+       // 测试 endTime 不匹配
+       workExpMapper.insert(cloneIgnoreId(dbWorkExp, o -> o.setEndTime(null)));
+       // 测试 payUnit 不匹配
+       workExpMapper.insert(cloneIgnoreId(dbWorkExp, o -> o.setPayUnit(null)));
+       // 测试 currencyType 不匹配
+       workExpMapper.insert(cloneIgnoreId(dbWorkExp, o -> o.setCurrencyType(null)));
+       // 测试 createTime 不匹配
+       workExpMapper.insert(cloneIgnoreId(dbWorkExp, o -> o.setCreateTime(null)));
+       // 准备参数
+       WorkExpPageReqVO reqVO = new WorkExpPageReqVO();
+       reqVO.setUserId(null);
+       reqVO.setEnterpriseId(null);
+       reqVO.setEnterpriseName(null);
+       reqVO.setIndustryId(null);
+       reqVO.setDeptName(null);
+       reqVO.setPositionId(null);
+       reqVO.setPositionName(null);
+       reqVO.setStartTime(LocalDateTime.now());
+       reqVO.setEndTime(LocalDateTime.now());
+       reqVO.setPayUnit(null);
+       reqVO.setCurrencyType(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<WorkExpDO> pageResult = workExpService.getWorkExpPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbWorkExp, pageResult.getList().get(0));
+    }
+
+}

+ 60 - 0
menduner/menduner-im-biz/src/test/resources/application-unit-test.yaml

@@ -0,0 +1,60 @@
+spring:
+  main:
+    lazy-initialization: true # 开启懒加载,加快速度
+    banner-mode: off # 单元测试,禁用 Banner
+
+--- #################### 数据库相关配置 ####################
+
+spring:
+  # 数据源配置项
+  datasource:
+    name: ruoyi-vue-pro
+    url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
+    driver-class-name: org.h2.Driver
+    username: sa
+    password:
+    druid:
+      async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
+      initial-size: 1 # 单元测试,配置为 1,提升启动速度
+  sql:
+    init:
+      schema-locations: classpath:/sql/create_tables.sql
+
+  # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
+  redis:
+    host: 127.0.0.1 # 地址
+    port: 16379 # 端口(单元测试,使用 16379 端口)
+    database: 0 # 数据库索引
+  # elasticsearch 配置项
+  elasticsearch:
+    uris: http://192.168.3.80:9200
+
+mybatis:
+  lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
+
+mybatis-plus:
+  global-config:
+    db-config:
+      id-type: AUTO # H2 主键递增
+
+--- #################### 定时任务相关配置 ####################
+
+--- #################### 配置中心相关配置 ####################
+
+--- #################### 服务保障相关配置 ####################
+
+# Lock4j 配置项(单元测试,禁用 Lock4j)
+
+--- #################### 监控相关配置 ####################
+
+--- #################### 芋道相关配置 ####################
+
+# 芋道配置项,设置当前项目所有自定义的配置
+citu:
+  info:
+    base-package: com.citu.module
+  captcha:
+    timeout: 5m
+    width: 160
+    height: 60
+    enable: true

+ 4 - 0
menduner/menduner-im-biz/src/test/resources/logback.xml

@@ -0,0 +1,4 @@
+<configuration>
+    <!-- 引用 Spring Boot 的 logback 基础配置 -->
+    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
+</configuration>

+ 32 - 0
menduner/menduner-im-biz/src/test/resources/sql/clean.sql

@@ -0,0 +1,32 @@
+DELETE FROM "mde_area";
+DELETE FROM "mde_certificate";
+DELETE FROM "mde_user_skill";
+DELETE FROM "mde_work_exp";
+DELETE FROM "mde_user_certificate";
+DELETE FROM "mde_school";
+DELETE FROM "mde_position_tag";
+DELETE FROM "mde_major";
+DELETE FROM "mde_job_interested";
+DELETE FROM "mde_edu_exp";
+DELETE FROM "mde_cv_attachment";
+
+DELETE FROM "mde_user";
+DELETE FROM "mde_job_advertised";
+DELETE FROM "mde_enterprise";
+DELETE FROM "mde_enterprise_business";
+DELETE FROM "mde_enterprise_register";
+DELETE FROM "mde_enterprise_user_bind";
+DELETE FROM "mde_enterprise_post";
+DELETE FROM "mde_enterprise_user_look";
+DELETE FROM "mde_person_enterprise_attention";
+DELETE FROM "mde_person_browse_record";
+DELETE FROM "mde_person_job_collection";
+DELETE FROM "mde_train_exp";
+DELETE FROM "mde_project_exp";
+DELETE FROM "mde_skill";
+DELETE FROM "mde_enterprise_user_apply";
+DELETE FROM "mde_job_cv_rel";
+
+DELETE FROM "mde_hire_job_cv_rel";
+DELETE FROM "mde_hire_commission_ratio";
+DELETE FROM "mde_user_account";

+ 556 - 0
menduner/menduner-im-biz/src/test/resources/sql/create_tables.sql

@@ -0,0 +1,556 @@
+CREATE TABLE IF NOT EXISTS "mde_area" (
+                                          "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                          "name" varchar,
+                                          "type" int,
+                                          "parent_id" bigint,
+                                          "creator" varchar DEFAULT '',
+                                          "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                          "updater" varchar DEFAULT '',
+                                          "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                          "deleted" bit NOT NULL DEFAULT FALSE,
+                                          "tenant_id" bigint NOT NULL DEFAULT 0,
+                                          PRIMARY KEY ("id")
+) COMMENT '地区表';
+  
+   CREATE TABLE IF NOT EXISTS "mde_certificate" (
+                                                    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                    "name_cn" varchar NOT NULL,
+                                                    "name_en" varchar,
+                                                    "parent_id" bigint NOT NULL,
+                                                    "level" bit,
+                                                    "creator" varchar DEFAULT '',
+                                                    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                    "updater" varchar DEFAULT '',
+                                                    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                    "deleted" bit NOT NULL DEFAULT FALSE,
+                                                    "tenant_id" bigint NOT NULL DEFAULT 0,
+                                                    PRIMARY KEY ("id")
+   ) COMMENT '证书表';
+  
+   CREATE TABLE IF NOT EXISTS "mde_user_skill" (
+                                                   "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                   "user_id" bigint NOT NULL,
+                                                   "name" varchar,
+                                                   "level" int,
+                                                   "creator" varchar DEFAULT '',
+                                                   "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                   "updater" varchar DEFAULT '',
+                                                   "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                   "deleted" bit NOT NULL DEFAULT FALSE,
+                                                   "tenant_id" bigint NOT NULL DEFAULT 0,
+                                                   PRIMARY KEY ("id")
+   ) COMMENT '人才技能表';
+  
+   CREATE TABLE IF NOT EXISTS "mde_work_exp" (
+                                                 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                 "user_id" bigint NOT NULL,
+                                                 "enterprise_id" bigint,
+                                                 "enterprise_name" varchar,
+                                                 "industry_id" bigint,
+                                                 "dept_name" varchar,
+                                                 "position_id" bigint,
+                                                 "position_name" varchar,
+                                                 "start_time" varchar,
+                                                 "end_time" varchar,
+                                                 "content" varchar,
+                                                 "achievement" varchar,
+                                                 "pay" varchar,
+                                                 "pay_type" int,
+                                                 "currency_type" int,
+                                                 "skill_list" varchar,
+                                                 "creator" varchar DEFAULT '',
+                                                 "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                 "updater" varchar DEFAULT '',
+                                                 "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                 "deleted" bit NOT NULL DEFAULT FALSE,
+                                                 "tenant_id" bigint NOT NULL DEFAULT 0,
+                                                 PRIMARY KEY ("id")
+   ) COMMENT '工作经历表';
+  
+   CREATE TABLE IF NOT EXISTS "mde_user_certificate" (
+                                                         "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                         "user_id" bigint NOT NULL,
+                                                         "certificate_id" bigint,
+                                                         "creator" varchar DEFAULT '',
+                                                         "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                         "updater" varchar DEFAULT '',
+                                                         "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                         "deleted" bit NOT NULL DEFAULT FALSE,
+                                                         "tenant_id" bigint NOT NULL DEFAULT 0,
+                                                         PRIMARY KEY ("id")
+   ) COMMENT '人才证书表';
+  
+   CREATE TABLE IF NOT EXISTS "mde_school" (
+                                               "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                               "name" varchar,
+                                               "area_id" bigint,
+                                               "address" varchar,
+                                               "longitude" varchar,
+                                               "latitude" varchar,
+                                               "status" int NOT NULL,
+                                               "creator" varchar DEFAULT '',
+                                               "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                               "updater" varchar DEFAULT '',
+                                               "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                               "deleted" bit NOT NULL DEFAULT FALSE,
+                                               "tenant_id" bigint NOT NULL DEFAULT 0,
+                                               PRIMARY KEY ("id")
+   ) COMMENT '学校表';
+  
+   CREATE TABLE IF NOT EXISTS "mde_position_tag" (
+                                                     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                     "position_id" bigint,
+                                                     "label" varchar,
+                                                     "creator" varchar DEFAULT '',
+                                                     "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                     "updater" varchar DEFAULT '',
+                                                     "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                     "deleted" bit NOT NULL DEFAULT FALSE,
+                                                     "tenant_id" bigint NOT NULL DEFAULT 0,
+                                                     PRIMARY KEY ("id")
+   ) COMMENT '职位标签表';
+  
+   CREATE TABLE IF NOT EXISTS "mde_major" (
+                                              "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                              "name_cn" varchar,
+                                              "name__en" varchar,
+                                              "status" int NOT NULL,
+                                              "creator" varchar DEFAULT '',
+                                              "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                              "updater" varchar DEFAULT '',
+                                              "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                              "deleted" bit NOT NULL DEFAULT FALSE,
+                                              "tenant_id" bigint NOT NULL DEFAULT 0,
+                                              PRIMARY KEY ("id")
+   ) COMMENT '专业表';
+  
+   CREATE TABLE IF NOT EXISTS "mde_job_interested" (
+                                                       "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                       "user_id" bigint,
+                                                       "job_type" int,
+                                                       "position_id" bigint,
+                                                       "industry_id_list" varchar,
+                                                       "pay_min" varchar,
+                                                       "pay_max" varchar,
+                                                       "work_area_id" bigint,
+                                                       "interested_area_id_list" varchar,
+                                                       "creator" varchar DEFAULT '',
+                                                       "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                       "updater" varchar DEFAULT '',
+                                                       "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                       "deleted" bit NOT NULL DEFAULT FALSE,
+                                                       "tenant_id" bigint NOT NULL DEFAULT 0,
+                                                       PRIMARY KEY ("id")
+   ) COMMENT '求职意向表';
+  
+   CREATE TABLE IF NOT EXISTS "mde_edu_exp" (
+                                                "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                "user_id" bigint,
+                                                "school_id" bigint,
+                                                "school_name" varchar,
+                                                "education_type" int,
+                                                "education_system_type" int,
+                                                "major_id" bigint,
+                                                "major" varchar,
+                                                "start_time" varchar,
+                                                "end_time" varchar,
+                                                "content" varchar,
+                                                "creator" varchar DEFAULT '',
+                                                "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                "updater" varchar DEFAULT '',
+                                                "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                "deleted" bit NOT NULL DEFAULT FALSE,
+                                                "tenant_id" bigint NOT NULL DEFAULT 0,
+                                                PRIMARY KEY ("id")
+   ) COMMENT '教育经历表';
+  
+   CREATE TABLE IF NOT EXISTS "mde_cv_attachment" (
+                                                      "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                      "user_id" bigint,
+                                                      "title" varchar,
+                                                      "url" varchar,
+                                                      "creator" varchar DEFAULT '',
+                                                      "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                      "updater" varchar DEFAULT '',
+                                                      "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                      "deleted" bit NOT NULL DEFAULT FALSE,
+                                                      "tenant_id" bigint NOT NULL DEFAULT 0,
+                                                      PRIMARY KEY ("id")
+   ) COMMENT '简历附件表';
+
+CREATE TABLE IF NOT EXISTS "mde_user" (
+                                          "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                          "username" varchar NOT NULL,
+                                          "password" varchar NOT NULL,
+                                          "email" varchar,
+                                          "phone" varchar,
+                                          "avatar" varchar,
+                                          "status" int NOT NULL,
+                                          "register_ip" varchar,
+                                          "register_terminal" varchar,
+                                          "login_ip" varchar,
+                                          "login_date" varchar,
+                                          "creator" varchar DEFAULT '',
+                                          "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                          "updater" varchar DEFAULT '',
+                                          "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                          "deleted" bit NOT NULL DEFAULT FALSE,
+                                          "tenant_id" bigint NOT NULL,
+                                          PRIMARY KEY ("id")
+) COMMENT '用户登录表';
+
+CREATE TABLE IF NOT EXISTS "mde_job_advertised" (
+                                                    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                    "enterprise_id" bigint NOT NULL,
+                                                    "user_id" bigint NOT NULL,
+                                                    "area_id" bigint NOT NULL,
+                                                    "name" varchar NOT NULL,
+                                                    "position_id" bigint NOT NULL,
+                                                    "type" int NOT NULL,
+                                                    "exp_type" int NOT NULL,
+                                                    "edu_type" int NOT NULL,
+                                                    "pay_from" int,
+                                                    "pay_to" int,
+                                                    "tag_list" varchar,
+                                                    "content" varchar,
+                                                    "requirement" varchar,
+                                                    "address" varchar,
+                                                    "longitude" varchar,
+                                                    "latitude" varchar,
+                                                    "status" int,
+                                                    "creator" varchar DEFAULT '',
+                                                    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                    "updater" varchar DEFAULT '',
+                                                    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                    "deleted" bit NOT NULL DEFAULT FALSE,
+                                                    "tenant_id" bigint NOT NULL,
+                                                    PRIMARY KEY ("id")
+) COMMENT '招聘职位表';
+
+CREATE TABLE IF NOT EXISTS "mde_enterprise" (
+                                                "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                "name" varchar,
+                                                "another_name" varchar,
+                                                "website" varchar,
+                                                "introduce" varchar,
+                                                "contact" varchar,
+                                                "phone" varchar,
+                                                "logo_url" varchar,
+                                                "industry_id" bigint,
+                                                "financing_status" int,
+                                                "scale" int,
+                                                "welfare_list" varchar,
+                                                "album_list" varchar,
+                                                "work_time" varchar,
+                                                "develop_history" varchar,
+                                                "received_honors" varchar,
+                                                "creator" varchar DEFAULT '',
+                                                "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                "updater" varchar DEFAULT '',
+                                                "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                "deleted" bit NOT NULL DEFAULT FALSE,
+                                                "tenant_id" bigint NOT NULL,
+                                                PRIMARY KEY ("id")
+) COMMENT '企业信息表';
+
+CREATE TABLE IF NOT EXISTS "mde_enterprise_business" (
+                                                         "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                         "enterprise_id" bigint,
+                                                         "code" varchar,
+                                                         "name" varchar,
+                                                         "type" varchar,
+                                                         "area" varchar,
+                                                         "address" varchar,
+                                                         "representative" varchar,
+                                                         "establishment_time" varchar,
+                                                         "registered_capital" varchar,
+                                                         "approval_time" varchar,
+                                                         "former_name" varchar,
+                                                         "industry" varchar,
+                                                         "registration_authority" varchar,
+                                                         "business_status" varchar,
+                                                         "business_term" varchar,
+                                                         "business_scope" varchar,
+                                                         "creator" varchar DEFAULT '',
+                                                         "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                         "updater" varchar DEFAULT '',
+                                                         "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                         "deleted" bit NOT NULL DEFAULT FALSE,
+                                                         "tenant_id" bigint NOT NULL,
+                                                         PRIMARY KEY ("id")
+) COMMENT '企业工商信息表';
+
+CREATE TABLE IF NOT EXISTS "mde_enterprise_register" (
+                                                         "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                         "user_id" bigint,
+                                                         "name" varchar,
+                                                         "phone" varchar,
+                                                         "email" varchar,
+                                                         "business_license_url" varchar,
+                                                         "status" int NOT NULL,
+                                                         "reason" varchar,
+                                                         "remark" varchar,
+                                                         "creator" varchar DEFAULT '',
+                                                         "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                         "updater" varchar DEFAULT '',
+                                                         "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                         "deleted" bit NOT NULL DEFAULT FALSE,
+                                                         "tenant_id" bigint NOT NULL,
+                                                         PRIMARY KEY ("id")
+) COMMENT '企业注册申请表';
+
+CREATE TABLE IF NOT EXISTS "mde_enterprise_address" (
+                                                        "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+                                                        "enterprise_id" bigint,
+                                                        "address" varchar,
+                                                        "longitude" varchar,
+                                                        "latitude" varchar,
+                                                        "creator" varchar DEFAULT '',
+                                                        "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+                                                        "updater" varchar DEFAULT '',
+                                                        "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+                                                        "deleted" bit NOT NULL DEFAULT FALSE,
+                                                        "tenant_id" bigint NOT NULL,
+                                                        PRIMARY KEY ("id")
+) COMMENT '企业地址表';
+
+CREATE TABLE IF NOT EXISTS "mde_enterprise_user_bind" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "enterprise_id" bigint NOT NULL,
+    "user_id" varchar NOT NULL,
+    "status" int NOT NULL,
+    "login_ip" varchar,
+    "login_date" varchar,
+    "user_type" int,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '企业登录用户表';
+
+-- 将该建表 SQL 语句,添加到 citu-module-menduner.system-biz 模块的 test/resources/sql/create_tables.sql 文件里
+CREATE TABLE IF NOT EXISTS "mde_enterprise_post" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "enterprise_id" bigint,
+    "code" varchar,
+    "name_cn" varchar NOT NULL,
+    "name_en" varchar NOT NULL,
+    "sort" int NOT NULL,
+    "status" int NOT NULL,
+    "remark" varchar,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '企业岗位信息表';
+
+-- 将该建表 SQL 语句,添加到 citu-module-menduner.system-biz 模块的 test/resources/sql/create_tables.sql 文件里
+CREATE TABLE IF NOT EXISTS "mde_enterprise_user_look" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "enterprise_id" bigint,
+    "user_id" bigint NOT NULL,
+    "user_name" varchar,
+    "user_post_name" varchar,
+    "look_user_id" bigint,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '门墩儿 - 企业招聘者浏览表';
+
+CREATE TABLE IF NOT EXISTS "mde_person_enterprise_attention" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "user_id" bigint,
+    "enterprise_id" bigint,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '人才-企业关注订阅表';
+
+CREATE TABLE IF NOT EXISTS "mde_person_browse_record" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "user_id" bigint NOT NULL,
+    "job_id" bigint NOT NULL,
+    "area_id" bigint,
+    "position_id" bigint,
+    "enterprise_id" bigint,
+    "area_name" varchar,
+    "job_name" varchar,
+    "type" int,
+    "exp_type" int,
+    "edu_type" int,
+    "pay_from" varchar,
+    "pay_to" varchar,
+    "pay_unit" int,
+    "currency_type" int,
+    "tag_list" varchar NOT NULL,
+    "contact_user_id" bigint,
+    "contact_user_name" varchar,
+    "contact_user_post_name" varchar,
+    "enterprise_name" varchar,
+    "industry_id" bigint,
+    "financing_status" int,
+    "scale" int,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '人才-浏览记录';
+
+CREATE TABLE IF NOT EXISTS "mde_person_job_collection" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "user_id" bigint,
+    "job_id" bigint,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '人才-招聘职位收藏表';
+
+CREATE TABLE IF NOT EXISTS "mde_train_exp" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "user_id" bigint,
+    "start_time" varchar,
+    "end_time" varchar,
+    "org_name" varchar,
+    "course" varchar,
+    "content" varchar,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    PRIMARY KEY ("id")
+) COMMENT '培训经历表';
+
+CREATE TABLE IF NOT EXISTS "mde_project_exp" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "user_id" bigint,
+    "name" varchar,
+    "start_time" varchar,
+    "end_time" varchar,
+    "content" varchar,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    PRIMARY KEY ("id")
+) COMMENT '项目经历表';
+
+CREATE TABLE IF NOT EXISTS "mde_skill" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "name_cn" varchar,
+    "name_en" varchar,
+    "parent_id" bigint,
+    "level" bit,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '职业技能表';
+
+CREATE TABLE IF NOT EXISTS "mde_enterprise_user_apply" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "user_id" bigint NOT NULL,
+    "enterprise_id" bigint NOT NULL,
+    "post_id" bigint,
+    "name" varchar,
+    "status" varchar NOT NULL,
+    "reason" varchar,
+    "remark" varchar,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '企业用户申请';
+
+CREATE TABLE IF NOT EXISTS "mde_job_cv_rel" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "enterprise_id" bigint NOT NULL,
+    "job_id" bigint NOT NULL,
+    "publish_user_id" bigint NOT NULL,
+    "user_id" bigint NOT NULL,
+    "title" varchar NOT NULL,
+    "url" varchar NOT NULL,
+    "status" varchar NOT NULL,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '招聘职位简历投递表';
+
+CREATE TABLE IF NOT EXISTS "mde_hire_job_cv_rel" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "enterprise_id" bigint NOT NULL,
+    "job_id" bigint NOT NULL,
+    "publish_user_id" bigint NOT NULL,
+    "recommend_user_id" bigint NOT NULL,
+    "user_id" bigint NOT NULL,
+    "name" varchar,
+    "phone" varchar,
+    "url" varchar NOT NULL,
+    "status" varchar NOT NULL,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '门墩儿-众聘-简历投递表';
+
+CREATE TABLE IF NOT EXISTS "mde_hire_commission_ratio" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "enterprise_id" bigint NOT NULL,
+    "user_id" varchar NOT NULL,
+    "rate" varchar,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '门墩儿-众聘-佣金比例表';
+
+CREATE TABLE IF NOT EXISTS "mde_user_account" (
+    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "user_id" varchar NOT NULL,
+    "balance" varchar,
+    "creator" varchar DEFAULT '',
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "updater" varchar DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "tenant_id" bigint NOT NULL,
+    PRIMARY KEY ("id")
+) COMMENT '门墩儿-用户账户表';

+ 0 - 22
menduner/menduner-system-biz/src/main/resources/mapper/enterprise/EnterpriseMapper.xml

@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.citu.module.menduner.system.dal.mysql.enterprise.EnterpriseMapper">
-    <!--
-        一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
-        无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
-        代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
-        文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
-     -->
-
-    <resultMap id="appCommonRespVO" type="com.citu.module.menduner.system.controller.base.CommonRespVO">
-        <id property="key" column="id"/>
-        <result property="value" column="name"/>
-        <!-- 其他属性映射 -->
-    </resultMap>
-
-    <select id="searchByName" resultMap="appCommonRespVO">
-        select id, name
-        from mde_enterprise
-        where name like concat(#{name}, '%') and status = 0
-    </select>
-</mapper>

+ 0 - 22
menduner/menduner-system-biz/src/main/resources/mapper/school/SchoolMapper.xml

@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.citu.module.menduner.system.dal.mysql.school.SchoolMapper">
-    <!--
-        一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
-        无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
-        代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
-        文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
-     -->
-
-    <resultMap id="appCommonRespVO" type="com.citu.module.menduner.system.controller.base.CommonRespVO">
-        <id property="key" column="id"/>
-        <result property="value" column="name"/>
-        <!-- 其他属性映射 -->
-    </resultMap>
-
-    <select id="searchByName" resultMap="appCommonRespVO">
-        select id, name
-        from mde_school
-        where name like concat(#{name}, '%') and status = 0
-    </select>
-</mapper>

+ 2 - 0
menduner/pom.xml

@@ -18,6 +18,8 @@
         <module>menduner-system-biz</module>
         <module>menduner-reward-api</module>
         <module>menduner-reward-biz</module>
+        <module>menduner-im-api</module>
+        <module>menduner-im-biz</module>
     </modules>
     <description>
         门墩儿相关