Browse Source

接口自测和修改

1811872455@163.com 2 weeks ago
parent
commit
cc22a4b561
20 changed files with 335 additions and 671 deletions
  1. 0 2
      java/storlead-api/src/main/java/com/storlead/system/controller/FileResourceController.java
  2. 0 310
      java/storlead-api/src/main/java/com/storlead/system/controller/OrganizDataSyncApiController.java
  3. 4 5
      java/storlead-api/src/main/java/com/storlead/system/controller/UserApiController.java
  4. 0 124
      java/storlead-api/src/main/java/com/storlead/system/controller/UserController.java
  5. 4 2
      java/storlead-api/src/main/resources/application-dev.yml
  6. 3 0
      java/storlead-api/src/main/resources/application.yml
  7. 113 113
      java/storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/datasource/ModuleDefaultDataSourceAspect.java
  8. 27 5
      java/storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/interceptor/MybatisPlusConfig.java
  9. 32 0
      java/storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/tenant/TenantProperties.java
  10. 5 1
      java/storlead-framework/storlead-redis/src/main/java/com/storlead/framework/redis/config/RedissonConfig.java
  11. 57 57
      java/storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/filter/AuthRequestFilter.java
  12. 9 1
      java/storlead-system/storlead-system-api/src/main/java/com/storlead/system/controller/MenuApiController.java
  13. 2 2
      java/storlead-system/storlead-system-biz/src/main/java/com/storlead/system/mapper/SysAppMapper.java
  14. 5 16
      java/storlead-system/storlead-system-biz/src/main/java/com/storlead/system/service/impl/SysAppServiceImpl.java
  15. 3 32
      java/storlead-system/storlead-system-core/src/main/java/com/storlead/system/pojo/entity/SysAppEntity.java
  16. 2 1
      java/storlead-system/storlead-system-spi/src/main/java/com/storlead/system/service/SysAppService.java
  17. 1 0
      java/storlead-thirdparty/pom.xml
  18. 4 0
      java/storlead-thirdparty/storlead-thirdparty-biz/pom.xml
  19. 5 0
      java/storlead-thirdparty/storlead-thirdparty-wecom/pom.xml
  20. 59 0
      java/storlead-thirdparty/storlead-thirdparty-wecom/src/main/java/com/storlead/wecom/integration/WecomThirdPartyUserSyncClient.java

+ 0 - 2
java/storlead-api/src/main/java/com/storlead/system/controller/FileResourceController.java

@@ -55,8 +55,6 @@ public class FileResourceController {
     @Resource
     private FileResourceService fileResourceService;
 
-
-
     @ResponseBody
     @PostMapping("/upload")
     @ApiOperation(value = "公共接口-oss上传", notes = "公共接口-oss上传")

+ 0 - 310
java/storlead-api/src/main/java/com/storlead/system/controller/OrganizDataSyncApiController.java

@@ -1,310 +0,0 @@
-package com.storlead.system.controller;
-
-import cn.hutool.core.util.StrUtil;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.storlead.framework.common.ecode.PasswordEncoder;
-import com.storlead.framework.common.result.Result;
-import com.storlead.system.service.IUserRoleService;
-import com.storlead.system.util.SystemConfigItemCacheUtil;
-import com.storlead.user.pojo.entity.*;
-import com.storlead.user.pojo.vo.WxExcelOrgVo;
-import com.storlead.user.service.*;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import javax.annotation.Resource;
-import org.springframework.util.CollectionUtils;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestPart;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.multipart.MultipartHttpServletRequest;
-
-import javax.annotation.Resource;
-import javax.servlet.http.HttpServletRequest;
-import java.io.InputStream;
-import java.util.*;
-import java.util.stream.Collectors;
-
-/**
- * @program: sp-sales
- * @description:
- * @author: chenkq
- * @create: 2022-07-15 15:55
- */
-
-@Api(tags = "平台中心 - 系统: 角色管理")
-@RestController
-@RequestMapping("/sys/data/sync")
-public class OrganizDataSyncApiController {
-
-    @Resource
-    private ICompanyService companyService;
-
-    @Resource
-    private ISubCompanyService subCompanyService;
-
-    @Resource
-    private IDepartService departService;
-
-    @Resource
-    private IUserService userService;
-
-    @Resource
-    private PasswordEncoder passwordEncoder;
-
-    @Resource
-    private IUserRoleService userRoleService;
-
-    @Resource
-    private IJobService jobService;
-
-    @ApiOperation("导入企业微信数据")
-    @RequestMapping(value = "/importWxData",method = {RequestMethod.POST},headers = "content-type=multipart/form-data")
-    public Result importWxData(HttpServletRequest request, @ApiParam(value="文件",required=true)@RequestPart("file") MultipartFile file) throws Exception {
-        if (file == null ) {
-            return Result.error("请上传附件!");
-        }
-        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
-        // MultipartFile files = multipartRequest.getFile("filename");
-        InputStream inputStream = file.getInputStream();
-        List<WxExcelOrgVo> vols = excelWxExcelOrg(inputStream, file.getOriginalFilename());
-        Result result = markData(vols);
-        return result;
-    }
-
-    public List<WxExcelOrgVo> excelWxExcelOrg(InputStream in, String fileName) {
-        List list = new ArrayList<>();
-        try{
-            //创建Excel工作薄
-            Workbook work = this.getWorkbook(in, fileName);
-            if (null == work) {
-                return null;
-            }
-            //获得第一个shell
-            Sheet sheet=work.getSheetAt(0);
-            //获得Excel的行数
-            int totalRows=sheet.getPhysicalNumberOfRows();
-            // 获取总列数
-            // int totalCells=sheet.getRow(0).getPhysicalNumberOfCells();
-            // Cell cell = null;
-            List<WxExcelOrgVo> orgVos = new ArrayList<>();
-            for (int j = 10; j < totalRows; j++) {
-                Row row = sheet.getRow(j);
-                WxExcelOrgVo orgVo = new WxExcelOrgVo();
-                if(row.getCell(0) != null) {
-                    orgVo.setRealName(row.getCell(0).toString());
-                }
-                if(row.getCell(1) != null) {
-                    orgVo.setXworkUserId(row.getCell(1).toString());
-                }
-                if(row.getCell(2) != null) {
-                    orgVo.setNickName(row.getCell(2).toString());
-                }
-                if(row.getCell(3) != null) {
-                    orgVo.setJobName(row.getCell(3).toString());
-                }
-                if(row.getCell(4) != null) {
-                    orgVo.setDeptName(row.getCell(4).toString());
-                }
-                if(row.getCell(5) != null) {
-                    orgVo.setSex(row.getCell(5).toString());
-                }
-
-                if(row.getCell(6) != null) {
-                    orgVo.setMobile(row.getCell(6).toString());
-                }
-                if(row.getCell(10) != null) {
-                    orgVo.setEmail(row.getCell(10).toString());
-                }
-                if(row.getCell(12) != null) {
-                    String  enabledStr = row.getCell(12).toString();
-                    orgVo.setEnabled("已激活".equals(enabledStr) ? true:false);
-                }
-                orgVos.add(orgVo);
-            }
-            work.close();
-            return orgVos;
-        }catch (Exception e){
-            return null;
-        }
-    }
-
-    /**
-     * 判断文件格式
-     *
-     * @param inStr
-     * @param fileName
-     * @return
-     * @throws Exception
-     */
-    public Workbook getWorkbook(InputStream inStr, String fileName) throws Exception {
-        Workbook workbook = null;
-        String fileType = fileName.substring(fileName.lastIndexOf("."));
-        if (".xls".equals(fileType)) {
-            workbook = new HSSFWorkbook(inStr);
-        } else if (".xlsx".equals(fileType)) {
-            workbook = new XSSFWorkbook(inStr);
-        } else {
-            return null;
-        }
-        return workbook;
-    }
-
-    public Result markData(List<WxExcelOrgVo> vos) {
-
-        /**
-         * 岗位处理
-         */
-        Map<String, Long> jobMap = new HashMap<>();
-        List<JobEntity> oldJob = jobService.list(new LambdaUpdateWrapper<JobEntity>().eq(JobEntity::getIsDelete,0));
-        if (!CollectionUtils.isEmpty(oldJob)) {
-            jobMap = oldJob.stream().collect(Collectors.toMap(JobEntity::getName, JobEntity::getId));
-        }
-
-        List<JobEntity> jobs = new ArrayList<>();
-        for (WxExcelOrgVo vo : vos) {
-            JobEntity job = oldJob.stream().filter(e ->vo.getJobName().equals(e.getName())).findFirst().orElse(null);
-            if (Objects.isNull(job)) {
-                job = new JobEntity();
-                job.setName(vo.getJobName());
-                jobs.add(job);
-                if (StrUtil.isNotBlank(vo.getJobName())) {
-                    jobService.saveOrUpdate(job);
-                    oldJob.add(job);
-                    jobMap.put(job.getName(),job.getId());
-                }
-            }
-        }
-
-
-        /**
-         * 人员处理
-         */
-        List<DeptEntity> oldDeptls = departService.list(new LambdaUpdateWrapper<DeptEntity>().eq(DeptEntity::getIsDelete,0));
-
-        Map<String,DeptEntity> deptMap = new HashMap();
-        if (!CollectionUtils.isEmpty(oldDeptls)) {
-            deptMap = oldDeptls.stream().collect(Collectors.toMap(DeptEntity::getDeptNameRoute, a -> a));
-        }
-        CompanyEntity company = new CompanyEntity();
-        SubCompanyEntity subCompany = new SubCompanyEntity();
-
-        /**
-         * 处理组织数据
-         */
-        int i = 1;
-        for (WxExcelOrgVo vo : vos) {
-            String[] ds = vo.getDeptName().split(";");
-            String[] deptstr = ds[0].split("/");
-            if (i == 1) {
-                company.setName(deptstr[0]);
-                company.setRouteCode("100");
-                company.setId(Long.valueOf(1));
-                subCompany.setName(deptstr[0]);
-                subCompany.setCompanyId(company.getId());
-                subCompany.setRouteCode("100,100");
-                subCompany.setId(Long.valueOf(1));
-                companyService.truncateTable();
-                companyService.save(company);
-
-                subCompanyService.truncateTable();
-                subCompanyService.save(subCompany);
-                i++;
-            }
-
-            String routeStr = deptstr[0];
-            for (int j = 1; j < deptstr.length; j++) {
-                routeStr = routeStr + "/" + deptstr[j];
-                String finalRouteStr = routeStr;
-                DeptEntity dept = oldDeptls.stream().filter(e -> e.getDeptNameRoute().equals(finalRouteStr)).findFirst().orElse(null);
-                if (Objects.isNull(dept)) {
-                    String topRoute = finalRouteStr.substring(0, StrUtil.ordinalIndexOf(finalRouteStr, "/", j));
-                    // 上一级
-                    DeptEntity topDept = oldDeptls.stream().filter(e -> e.getDeptNameRoute().equals(topRoute)).findFirst().orElse(null);
-                    DeptEntity newDept = new DeptEntity();
-                    if (Objects.nonNull(topDept)) {
-                        DeptEntity nextD = oldDeptls.stream().filter(e -> e.getDeptNameRoute().contains(topRoute) && !e.getDeptNameRoute().equals(topRoute)).collect(Collectors.toList()).stream().max(Comparator.comparing(s -> s.getRouteCode())).orElse(null);
-                        if (Objects.isNull(nextD)) {
-                            newDept.setRouteCode(topDept.getRouteCode() + ",100");
-                        } else {
-                            String[] routeCodes = nextD.getRouteCode().split(",");
-                            String lastCode = routeCodes[routeCodes.length - 1];
-                            lastCode = String.valueOf(Integer.valueOf(lastCode) + 1);
-                            routeCodes[routeCodes.length - 1] = lastCode;
-                            String routeCode = StringUtils.join(routeCodes, ",");
-                            newDept.setRouteCode(routeCode);
-                        }
-                        newDept.setCompanyId(topDept.getCompanyId());
-                        newDept.setSubCompanyId(topDept.getSubCompanyId());
-                        newDept.setDeptNameRoute(routeStr);
-                        newDept.setPid(topDept.getId());
-                        newDept.setName(deptstr[j]);
-                        departService.saveOrUpdate(newDept);
-                        deptMap.put(newDept.getDeptNameRoute(), newDept);
-                        oldDeptls.add(newDept);
-                    } else {
-                        newDept.setPid(Long.valueOf(0));
-                        newDept.setRouteCode(subCompany.getRouteCode() + ",100");
-                        newDept.setDeptNameRoute(subCompany.getName() + "/"+deptstr[j]);
-                        newDept.setCompanyId(company.getId());
-                        newDept.setSubCompanyId(subCompany.getId());
-                        newDept.setName(deptstr[j]);
-                        departService.saveOrUpdate(newDept);
-                        deptMap.put(newDept.getDeptNameRoute(), newDept);
-                        oldDeptls.add(newDept);
-
-                    }
-
-                }
-            }
-        }
-
-
-
-        List<UserEntity> oldUsers = userService.list(new LambdaQueryWrapper<UserEntity>().eq(UserEntity::getIsDelete,0));
-        List<UserEntity> users = new ArrayList<>();
-        String defaultPassWord = SystemConfigItemCacheUtil.getDefaultPassWord();
-        for (WxExcelOrgVo vo : vos) {
-            UserEntity oldInfo = oldUsers.stream().filter(e ->e.getMobile().equals(vo.getMobile())).findFirst().orElse(null);
-            if (Objects.nonNull(oldInfo)) {
-                continue;
-            }
-            UserEntity user = new UserEntity();
-            user.setUserName(vo.getRealName());
-            user.setSex(vo.getSex());
-            user.setNickName(vo.getNickName());
-            user.setRealName(vo.getRealName());
-            user.setXworkUserId(vo.getXworkUserId());
-            user.setEmail(vo.getEmail());
-            user.setPassword(passwordEncoder.encode(defaultPassWord));
-            user.setJobId(jobMap.get(vo.getJobName()));
-            user.setId(Long.valueOf(i));
-            user.setStatus("1");
-            String[] ds = vo.getDeptName().split(";");
-            String deptRouteName = ds[0];
-            DeptEntity wxVo = deptMap.get(deptRouteName);
-            if (Objects.nonNull(wxVo)) {
-                DeptEntity d = wxVo;
-                user.setSubCompanyId(d.getSubCompanyId());
-                user.setCompanyId(d.getCompanyId());
-                user.setDeptId(d.getId());
-                user.setOrgRouteCode(d.getRouteCode());
-            }
-            user.setMobile(vo.getMobile());
-            users.add(user);
-        }
-        userService.saveBatch(users);
-        userRoleService.initUserRole();
-
-        return Result.ok();
-    }
-}

+ 4 - 5
java/storlead-api/src/main/java/com/storlead/system/controller/UserApiController.java

@@ -346,7 +346,7 @@ public class UserApiController {
         return Result.ok();
     }
 
-    @ApiOperation("删除人员数据")
+    @ApiOperation("修改真是姓名")
     @PostMapping("updateRealName")
     public Result deleteById(Long id,String realName) {
         if (id == null) {
@@ -394,10 +394,6 @@ public class UserApiController {
     @ApiOperation("同步OA数据")
     @PostMapping("syncOa")
     public Result syncOa() {
-        Result r = SystemSettingCacheCheckUtil.checkSyncOaDataMode();
-        if (!r.isSuccess()) {
-            return r;
-        }
        userService.syncOa();
        return Result.ok();
     }
@@ -604,4 +600,7 @@ public class UserApiController {
         }
 
     }
+
+
+
 }

+ 0 - 124
java/storlead-api/src/main/java/com/storlead/system/controller/UserController.java

@@ -1,124 +0,0 @@
-package com.storlead.system.controller;
-
-import com.storlead.framework.auth.vo.LoginUser;
-import com.storlead.framework.common.result.Result;
-import com.storlead.framework.util.LoginUserUtil;
-import com.storlead.user.model.UserInfo;
-import com.storlead.user.model.UserQueryModel;
-import com.storlead.user.pojo.entity.UserEntity;
-import com.storlead.user.pojo.vo.WxUserVO;
-import com.storlead.user.service.IUserService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
-import lombok.extern.slf4j.Slf4j;
-import javax.annotation.Resource;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * <p>
- * 用户表 前端控制器
- * </p>
- *
- * @Author scott
- * @since 2018-12-20
- */
-@Slf4j
-@RestController
-@RequestMapping("/lingcun/user")
-@Api(tags="平台中心 - 系统: 公共接口")
-public class UserController {
-
-	@Resource
-	private IUserService sysUserService;
-
-	/**
-	 * 查询数据 查出所有部门,并以树结构数据格式响应给前端
-	 *
-	 * @return
-	 */
-	@RequestMapping(value = "/list", method = RequestMethod.GET)
-	@ApiOperation(value = "公共接口-用户信息-列表", notes = "公共接口-用户信息-列表")
-	@ApiResponses({
-			@ApiResponse(code = 200, message = "", response = UserInfo.class)
-	})
-	public Result<List<UserInfo>> queryTreeList(UserQueryModel userQueryModel) {
-		Result<List<UserInfo>> result = new Result<>();
-		try {
-			List<UserInfo> list = sysUserService.getUserListBySearch(userQueryModel);
-			result.setResult(list);
-			result.setSuccess(true);
-		} catch (Exception e) {
-			log.error(e.getMessage(),e);
-		}
-		return result;
-	}
-
-	 /***
-	  * @Description: 用户列表,按拼音首字母排序
-	  * @Param:
-	  * @return:
-	  * @Author: YPZ
-	  * @Date: 2023/3/16 11:54
-	  */
-	@RequestMapping(value = "/user-list-pinyin", method = RequestMethod.GET)
-	@ApiOperation(value = "公共接口-用户信息-列表(按拼音首字母排序)", notes = "公共接口-用户信息-列表(按拼音首字母排序)")
-	@ApiResponses({
-			@ApiResponse(code = 200, message = "", response = UserInfo.class)
-	})
-	public Result<List<WxUserVO>> userListPinyin(UserQueryModel userQueryModel) {
-		Result<List<WxUserVO>> result = new Result<>();
-		try {
-			List<WxUserVO> wxUserInfoList =sysUserService.getPingyinUserList(userQueryModel);
-			result.setResult(wxUserInfoList);
-			result.setSuccess(true);
-		} catch (Exception e) {
-			log.error(e.getMessage(),e);
-		}
-		return result;
-	}
-
-	@RequestMapping(value = "/list-subordinate", method = RequestMethod.GET)
-	@ApiOperation(value = "公共接口-下级用户信息-列表", notes = "公共接口-下级用户信息-列表")
-	@ApiResponses({
-			@ApiResponse(code = 200, message = "", response = UserInfo.class)
-	})
-	public Result<List<UserInfo>> listSubordinate(UserQueryModel userQueryModel) {
-		Result<List<UserInfo>> result = new Result<>();
-		try {
-			LoginUser loginUser = LoginUserUtil.getLoginUser();
-			List<UserInfo> list = sysUserService.getUserListBySearch(userQueryModel);
-			List<Long> allSubordinate = sysUserService.getAllSubordinate(loginUser.getId());
-			Set<Long> allSubordinateSet =new HashSet<>(allSubordinate);
-			list = list.stream().filter((u)->allSubordinateSet.contains(u.getId())).collect(Collectors.toList());
-			result.setResult(list);
-			result.setSuccess(true);
-		} catch (Exception e) {
-			log.error(e.getMessage(),e);
-		}
-		return result;
-	}
-
-	/**
-	 * 查询数据 查出所有部门,并以树结构数据格式响应给前端
-	 *
-	 * @return
-	 */
-	@GetMapping(value = "/findManager")
-	@ApiOperation(value = "公共接口-用户信息-查看当前用户领导", notes = "公共接口-用户信息-查看当前用户领导")
-	public Result findManager() {
-		LoginUser loginUser = LoginUserUtil.getLoginUser();
-		UserEntity user = sysUserService.getById(loginUser.getManagerId());
-		return Result.result(user);
-	}
-
-}

+ 4 - 2
java/storlead-api/src/main/resources/application-dev.yml

@@ -47,12 +47,14 @@ spring:
     host: test1.storlead.com #test1.storlead.com
     port: 59394
     database: 10
+    connect-timeout: 10000
     lettuce:
       pool:
-        max-wait: 100000
+        max-wait: 10000
         max-idle: 10
         max-active: 100
-    timeout: 5000
+        min-idle: 2
+    timeout: 10000
     password: bnoCWkyDqYA*ecT7FoL7
 
   mvc:

+ 3 - 0
java/storlead-api/src/main/resources/application.yml

@@ -76,6 +76,9 @@ storlead:
     column-name: tenant_id
     # 需要租户隔离的表白名单(仅 listed 表会加 tenant_id 条件与插入填充)
     include-tables: []
+    # 不参与租户 SQL 解析的 Mapper 语句(多表 JOIN UPDATE 等须排除,默认已含 OA 同步 SQL)
+    # exclude-statement-ids:
+    #   - com.storlead.user.mapper.HrmresourceMapper.updatePermanentDate
     # 示例(按业务启用后取消注释并设 enabled: true):
     # include-tables:
     #   - customer

+ 113 - 113
java/storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/datasource/ModuleDefaultDataSourceAspect.java

@@ -1,113 +1,113 @@
-package com.storlead.framework.mybatis.datasource;
-
-import com.baomidou.dynamic.datasource.annotation.DS;
-import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.aspectj.lang.ProceedingJoinPoint;
-import org.aspectj.lang.annotation.Around;
-import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Pointcut;
-import org.aspectj.lang.reflect.MethodSignature;
-import org.springframework.core.annotation.AnnotatedElementUtils;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
-
-import java.lang.reflect.Method;
-
-/**
- * 子模块默认数据源:按配置包前缀 push 数据源;若目标已标注 {@link DS} 则跳过,由官方 {@code @DS} 切面处理。
- */
-@Slf4j
-@Aspect
-@Component
-@Order(0)
-@RequiredArgsConstructor
-public class ModuleDefaultDataSourceAspect {
-
-    private final ModuleDefaultDataSourceProperties properties;
-
-    @Pointcut("execution(* *(..)) && (within(com.storlead..service..*) || within(com.storlead..mapper..*))")
-    public void moduleServiceOrMapperLayer() {
-    }
-
-    @Around("moduleServiceOrMapperLayer()")
-    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
-        if (!properties.isModuleDefaultEnabled()) {
-            return joinPoint.proceed();
-        }
-        if (hasDsAnnotation(joinPoint)) {
-            return joinPoint.proceed();
-        }
-        String datasource = resolveDatasource(joinPoint);
-        if (!StringUtils.hasText(datasource)) {
-            return joinPoint.proceed();
-        }
-        DynamicDataSourceContextHolder.push(datasource);
-        try {
-            if (log.isDebugEnabled()) {
-                log.debug("module default datasource [{}] -> {}", datasource, joinPoint.getSignature().toShortString());
-            }
-            return joinPoint.proceed();
-        } finally {
-            DynamicDataSourceContextHolder.poll();
-        }
-    }
-
-    private boolean hasDsAnnotation(ProceedingJoinPoint joinPoint) {
-        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
-        Method method = signature.getMethod();
-        Class<?> declaringType = signature.getDeclaringType();
-        if (AnnotatedElementUtils.hasAnnotation(method, DS.class)) {
-            return true;
-        }
-        if (declaringType != null && AnnotatedElementUtils.hasAnnotation(declaringType, DS.class)) {
-            return true;
-        }
-        return AnnotatedElementUtils.hasAnnotation(joinPoint.getTarget().getClass(), DS.class);
-    }
-
-    /**
-     * 最长包前缀匹配
-     */
-    private String resolveDatasource(ProceedingJoinPoint joinPoint) {
-        String className = resolveBusinessClassName(joinPoint);
-        String matchedDs = null;
-        int matchedLen = -1;
-        for (ModuleDefaultDataSourceProperties.ModuleDefaultRule rule : properties.getModuleDefaults()) {
-            if (rule.getPackages() == null || !StringUtils.hasText(rule.getDatasource())) {
-                continue;
-            }
-            for (String pkg : rule.getPackages()) {
-                if (!StringUtils.hasText(pkg)) {
-                    continue;
-                }
-                if (matchesPackage(className, pkg.trim()) && pkg.length() > matchedLen) {
-                    matchedLen = pkg.length();
-                    matchedDs = rule.getDatasource();
-                }
-            }
-        }
-        return matchedDs;
-    }
-
-    private boolean matchesPackage(String className, String packagePrefix) {
-        return className.equals(packagePrefix)
-                || className.startsWith(packagePrefix + ".");
-    }
-
-    /**
-     * Mapper 为 JDK 代理时,用接口全限定名匹配包前缀。
-     */
-    private String resolveBusinessClassName(ProceedingJoinPoint joinPoint) {
-        if (joinPoint.getSignature() instanceof MethodSignature) {
-            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
-            Class<?> declaringType = signature.getDeclaringType();
-            if (declaringType != null && declaringType.getName().startsWith("com.storlead.")) {
-                return declaringType.getName();
-            }
-        }
-        return joinPoint.getTarget().getClass().getName();
-    }
-}
+//package com.storlead.framework.mybatis.datasource;
+//
+//import com.baomidou.dynamic.datasource.annotation.DS;
+//import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
+//import lombok.RequiredArgsConstructor;
+//import lombok.extern.slf4j.Slf4j;
+//import org.aspectj.lang.ProceedingJoinPoint;
+//import org.aspectj.lang.annotation.Around;
+//import org.aspectj.lang.annotation.Aspect;
+//import org.aspectj.lang.annotation.Pointcut;
+//import org.aspectj.lang.reflect.MethodSignature;
+//import org.springframework.core.annotation.AnnotatedElementUtils;
+//import org.springframework.core.annotation.Order;
+//import org.springframework.stereotype.Component;
+//import org.springframework.util.StringUtils;
+//
+//import java.lang.reflect.Method;
+//
+///**
+// * 子模块默认数据源:按配置包前缀 push 数据源;若目标已标注 {@link DS} 则跳过,由官方 {@code @DS} 切面处理。
+// */
+//@Slf4j
+//@Aspect
+//@Component
+//@Order(0)
+//@RequiredArgsConstructor
+//public class ModuleDefaultDataSourceAspect {
+//
+//    private final ModuleDefaultDataSourceProperties properties;
+//
+//    @Pointcut("execution(* *(..)) && (within(com.storlead..service..*) || within(com.storlead..mapper..*))")
+//    public void moduleServiceOrMapperLayer() {
+//    }
+//
+//    @Around("moduleServiceOrMapperLayer()")
+//    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+//        if (!properties.isModuleDefaultEnabled()) {
+//            return joinPoint.proceed();
+//        }
+//        if (hasDsAnnotation(joinPoint)) {
+//            return joinPoint.proceed();
+//        }
+//        String datasource = resolveDatasource(joinPoint);
+//        if (!StringUtils.hasText(datasource)) {
+//            return joinPoint.proceed();
+//        }
+//        DynamicDataSourceContextHolder.push(datasource);
+//        try {
+//            if (log.isDebugEnabled()) {
+//                log.debug("module default datasource [{}] -> {}", datasource, joinPoint.getSignature().toShortString());
+//            }
+//            return joinPoint.proceed();
+//        } finally {
+//            DynamicDataSourceContextHolder.poll();
+//        }
+//    }
+//
+//    private boolean hasDsAnnotation(ProceedingJoinPoint joinPoint) {
+//        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+//        Method method = signature.getMethod();
+//        Class<?> declaringType = signature.getDeclaringType();
+//        if (AnnotatedElementUtils.hasAnnotation(method, DS.class)) {
+//            return true;
+//        }
+//        if (declaringType != null && AnnotatedElementUtils.hasAnnotation(declaringType, DS.class)) {
+//            return true;
+//        }
+//        return AnnotatedElementUtils.hasAnnotation(joinPoint.getTarget().getClass(), DS.class);
+//    }
+//
+//    /**
+//     * 最长包前缀匹配
+//     */
+//    private String resolveDatasource(ProceedingJoinPoint joinPoint) {
+//        String className = resolveBusinessClassName(joinPoint);
+//        String matchedDs = null;
+//        int matchedLen = -1;
+//        for (ModuleDefaultDataSourceProperties.ModuleDefaultRule rule : properties.getModuleDefaults()) {
+//            if (rule.getPackages() == null || !StringUtils.hasText(rule.getDatasource())) {
+//                continue;
+//            }
+//            for (String pkg : rule.getPackages()) {
+//                if (!StringUtils.hasText(pkg)) {
+//                    continue;
+//                }
+//                if (matchesPackage(className, pkg.trim()) && pkg.length() > matchedLen) {
+//                    matchedLen = pkg.length();
+//                    matchedDs = rule.getDatasource();
+//                }
+//            }
+//        }
+//        return matchedDs;
+//    }
+//
+//    private boolean matchesPackage(String className, String packagePrefix) {
+//        return className.equals(packagePrefix)
+//                || className.startsWith(packagePrefix + ".");
+//    }
+//
+//    /**
+//     * Mapper 为 JDK 代理时,用接口全限定名匹配包前缀。
+//     */
+//    private String resolveBusinessClassName(ProceedingJoinPoint joinPoint) {
+//        if (joinPoint.getSignature() instanceof MethodSignature) {
+//            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+//            Class<?> declaringType = signature.getDeclaringType();
+//            if (declaringType != null && declaringType.getName().startsWith("com.storlead.")) {
+//                return declaringType.getName();
+//            }
+//        }
+//        return joinPoint.getTarget().getClass().getName();
+//    }
+//}

+ 27 - 5
java/storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/interceptor/MybatisPlusConfig.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.MybatisConfiguration;
 import com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver;
 import com.baomidou.mybatisplus.core.config.GlobalConfig;
 import com.baomidou.mybatisplus.core.parser.ISqlParser;
+import com.baomidou.mybatisplus.core.parser.ISqlParserFilter;
 import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
 import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
@@ -47,11 +48,32 @@ public class MybatisPlusConfig {
     @Bean
     public PaginationInterceptor paginationInterceptor(TenantProperties tenantProperties) {
         PaginationInterceptor interceptor = new PaginationInterceptor().setLimit(-1);
-        TenantSqlParser tenantSqlParser = new TenantSqlParser();
-        tenantSqlParser.setTenantHandler(new StorleadTenantHandler(tenantProperties));
-        List<ISqlParser> sqlParserList = new ArrayList<>();
-        sqlParserList.add(tenantSqlParser);
-        interceptor.setSqlParserList(sqlParserList);
+
+        if (tenantProperties.isEnabled()) {
+            TenantSqlParser tenantSqlParser = new TenantSqlParser();
+            tenantSqlParser.setTenantHandler(new StorleadTenantHandler(tenantProperties));
+            List<ISqlParser> sqlParserList = new ArrayList<>();
+            sqlParserList.add(tenantSqlParser);
+            interceptor.setSqlParserList(sqlParserList);
+            log.info("MyBatis tenant line enabled, includeTables={}", tenantProperties.getIncludeTables());
+        } else {
+            log.info("MyBatis tenant line disabled (storlead.tenant.enabled=false)");
+        }
+
+        interceptor.setSqlParserFilter(new ISqlParserFilter() {
+            @Override
+            public boolean doFilter(org.apache.ibatis.reflection.MetaObject metaObject) {
+                Object statementId = metaObject.getValue("delegate.mappedStatement.id");
+                if (statementId == null) {
+                    return false;
+                }
+                boolean exclude = tenantProperties.shouldExcludeStatement(statementId.toString());
+                if (exclude) {
+                    log.debug("Skip tenant SQL parser for statement: {}", statementId);
+                }
+                return exclude;
+            }
+        });
         return interceptor;
     }
 //

+ 32 - 0
java/storlead-framework/storlead-mybatis/src/main/java/com/storlead/framework/mybatis/tenant/TenantProperties.java

@@ -38,6 +38,20 @@ public class TenantProperties {
     @Deprecated
     private List<String> ignoreTables = new ArrayList<>();
 
+    /**
+     * 不参与租户 SQL 解析的 Mapper 语句(全限定 statementId,或后缀匹配如 HrmresourceMapper.updatePermanentDate)。
+     * 多表 JOIN 的 UPDATE/复杂 SELECT 无法被 TenantSqlParser 处理,须排除。
+     */
+    private List<String> excludeStatementIds = defaultExcludeStatementIds();
+
+    private static List<String> defaultExcludeStatementIds() {
+        List<String> ids = new ArrayList<>();
+        ids.add("com.storlead.user.mapper.HrmresourceMapper.updatePermanentDate");
+        ids.add("com.storlead.user.mapper.HrmresourceMapper.updateResignDate");
+        ids.add("com.storlead.user.mapper.HrmresourceMapper.getUserList");
+        return ids;
+    }
+
     /**
      * 当前表是否应注入 / 填充 tenant_id。
      */
@@ -55,6 +69,24 @@ public class TenantProperties {
         return false;
     }
 
+    /**
+     * 是否跳过 MyBatis 租户 SQL 解析(复杂 JOIN / 多表 UPDATE 等)。
+     */
+    public boolean shouldExcludeStatement(String statementId) {
+        if (statementId == null || excludeStatementIds == null || excludeStatementIds.isEmpty()) {
+            return false;
+        }
+        for (String pattern : excludeStatementIds) {
+            if (pattern == null || pattern.isEmpty()) {
+                continue;
+            }
+            if (statementId.equals(pattern) || statementId.endsWith("." + pattern)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public static String normalizeTableName(String tableName) {
         return tableName.replace("`", "").toLowerCase(Locale.ROOT);
     }

+ 5 - 1
java/storlead-framework/storlead-redis/src/main/java/com/storlead/framework/redis/config/RedissonConfig.java

@@ -28,7 +28,11 @@ public class RedissonConfig {
     @Bean
     public RedissonClient getRedisson() {
         Config config = new Config();
-        config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
+        config.useSingleServer()
+                .setAddress("redis://" + host + ":" + port)
+                .setPassword(password)
+                .setConnectTimeout(10000)
+                .setTimeout(10000);
         RedissonClient redissonClient = Redisson.create(config);
         return redissonClient;
     }

+ 57 - 57
java/storlead-framework/storlead-web/src/main/java/com/storlead/framework/web/filter/AuthRequestFilter.java

@@ -25,6 +25,7 @@ import com.storlead.framework.web.wrapper.JsonRequestWrapper;
 import lombok.extern.log4j.Log4j2;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.core.Ordered;
+import org.springframework.dao.DataAccessException;
 import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Component;
 import org.springframework.util.CollectionUtils;
@@ -139,72 +140,71 @@ public class AuthRequestFilter implements Filter, Ordered {
         }
 
         if (StringUtils.isNotBlank(token)) {
-            String jsonInfo = redisService.getCacheObject(token);
-            if (StrUtil.isNotBlank(jsonInfo) && jsonInfo.length() < "1efb52db4da5d2b92516a4eba0b3145b34234234".length()) {
-                jsonInfo = redisService.getCacheObject(jsonInfo);
-            }
-            if (StringUtils.isNotBlank(jsonInfo)) {
-                try {
-                    IContext context = Context.getContext();
-                    LoginUser loginUserInfo = JSONUtil.toBean(jsonInfo, LoginUser.class);
+            try {
+                String jsonInfo = redisService.getCacheObject(token);
+                if (StrUtil.isNotBlank(jsonInfo) && jsonInfo.length() < "1efb52db4da5d2b92516a4eba0b3145b34234234".length()) {
+                    jsonInfo = redisService.getCacheObject(jsonInfo);
+                }
+                if (StringUtils.isBlank(jsonInfo)) {
+                    return false;
+                }
+                IContext context = Context.getContext();
+                LoginUser loginUserInfo = JSONUtil.toBean(jsonInfo, LoginUser.class);
 
-                    String  scopeMenuId =  context.getAttribute("scopeMenuId",String.class);
-                    Object  commonScope =  context.getAttribute("commonScope",Object.class);
-                    Object  commonScopeType =  context.getAttribute("commonScopeType",Object.class);
-                    // 当前接口的地址
-                    loginUserInfo.setApiUrl(url);
-                    loginUserInfo.setScopeMenuId(scopeMenuId);
-                    if (Objects.nonNull(commonScope)) {
-                        loginUserInfo.setCommonScope((Integer)commonScope);
-                    }
-                    if (Objects.nonNull(commonScopeType)) {
-                        loginUserInfo.setCommonScopeType(commonScopeType.toString());
-                    }
-                    context.setAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_KEY, loginUserInfo);
-                    context.setAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_TOKEN_KEY, token);
-                    context.setAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_ID_KEY, loginUserInfo.getId());
+                String scopeMenuId = context.getAttribute("scopeMenuId", String.class);
+                Object commonScope = context.getAttribute("commonScope", Object.class);
+                Object commonScopeType = context.getAttribute("commonScopeType", Object.class);
+                loginUserInfo.setApiUrl(url);
+                loginUserInfo.setScopeMenuId(scopeMenuId);
+                if (Objects.nonNull(commonScope)) {
+                    loginUserInfo.setCommonScope((Integer) commonScope);
+                }
+                if (Objects.nonNull(commonScopeType)) {
+                    loginUserInfo.setCommonScopeType(commonScopeType.toString());
+                }
+                context.setAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_KEY, loginUserInfo);
+                context.setAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_TOKEN_KEY, token);
+                context.setAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_ID_KEY, loginUserInfo.getId());
 
-                    Long tenantId = loginUserInfo.getTenantId();
-                    if (tenantId == null) {
-                        tenantId = loginUserInfo.getCompanyId();
-                    }
-                    if (tenantId != null) {
-                        TenantContext.bindTenantId(tenantId);
-                    }
-                    String headerTenant = req.getHeader(TenantConstants.HTTP_HEADER_TENANT_ID);
-                    if (StringUtils.isNotBlank(headerTenant)) {
-                        try {
-                            Long headerTid = Long.valueOf(headerTenant.trim());
-                            if (tenantId != null && !tenantId.equals(headerTid)) {
-                                log.warn("X-Tenant-Id {} 与登录租户 {} 不一致,已以登录信息为准", headerTid, tenantId);
-                            } else if (tenantId == null && Boolean.TRUE.equals(loginUserInfo.getIsAdmin())) {
-                                TenantContext.bindTenantId(headerTid);
-                            }
-                        } catch (NumberFormatException e) {
-                            log.warn("非法 X-Tenant-Id 请求头: {}", headerTenant);
+                Long tenantId = loginUserInfo.getTenantId();
+                if (tenantId == null) {
+                    tenantId = loginUserInfo.getCompanyId();
+                }
+                if (tenantId != null) {
+                    TenantContext.bindTenantId(tenantId);
+                }
+                String headerTenant = req.getHeader(TenantConstants.HTTP_HEADER_TENANT_ID);
+                if (StringUtils.isNotBlank(headerTenant)) {
+                    try {
+                        Long headerTid = Long.valueOf(headerTenant.trim());
+                        if (tenantId != null && !tenantId.equals(headerTid)) {
+                            log.warn("X-Tenant-Id {} 与登录租户 {} 不一致,已以登录信息为准", headerTid, tenantId);
+                        } else if (tenantId == null && Boolean.TRUE.equals(loginUserInfo.getIsAdmin())) {
+                            TenantContext.bindTenantId(headerTid);
                         }
+                    } catch (NumberFormatException e) {
+                        log.warn("非法 X-Tenant-Id 请求头: {}", headerTenant);
                     }
-                    if (TenantContext.getTenantId() == null) {
-                        log.warn("已登录用户未解析到租户 ID(LoginUser.tenantId / companyId 均为空),多租户条件将不生效: userId={}", loginUserInfo.getId());
-                    }
+                }
+                if (TenantContext.getTenantId() == null) {
+                    log.warn("已登录用户未解析到租户 ID(LoginUser.tenantId / companyId 均为空),多租户条件将不生效: userId={}", loginUserInfo.getId());
+                }
 
-                    // 修改失效时间,超过24小时没有使用,
-                    Long expire = redisService.getCacheExpire(token,TimeUnit.SECONDS);
-                    log.debug("getCacheExpire-----------当前用户信息 = "+loginUserInfo);
-                    if (expire < 3600) {
-                        redisService.setCacheExpire(token,60 * 60 * 24L * 7, TimeUnit.SECONDS);
-                    }
-                } catch (Exception e) {
-                    log.error("loginHandle error",e);
-                    return false;
+                Long expire = redisService.getCacheExpire(token, TimeUnit.SECONDS);
+                log.debug("getCacheExpire-----------当前用户信息 = " + loginUserInfo);
+                if (expire != null && expire < 3600) {
+                    redisService.setCacheExpire(token, 60 * 60 * 24L * 7, TimeUnit.SECONDS);
                 }
-            } else {
+                return true;
+            } catch (DataAccessException e) {
+                log.error("loginHandle redis unavailable, url={}", url, e);
+                return false;
+            } catch (Exception e) {
+                log.error("loginHandle error, url={}", url, e);
                 return false;
             }
-        } else {
-            return false;
         }
-        return true;
+        return false;
     }
 
     @Override

+ 9 - 1
java/storlead-system/storlead-system-api/src/main/java/com/storlead/system/controller/MenuApiController.java

@@ -71,6 +71,9 @@ public class MenuApiController {
             queryWrapper.eq(MenuEntity::getIsDelete,0);
             queryWrapper.eq(MenuEntity::getEnabled,1);
             applyMenuScopeFilter(queryWrapper, dto);
+            if (Objects.nonNull(dto.getAppId())) {
+                queryWrapper.eq(MenuEntity::getAppId,dto.getAppId());
+            }
             queryWrapper.orderByAsc(MenuEntity::getSort);
             List<MenuEntity> menus = menuService.list(queryWrapper);
             if(CollectionUtils.isEmpty(menus)) {
@@ -92,6 +95,9 @@ public class MenuApiController {
             LambdaQueryWrapper<MenuEntity> queryWrapper = new LambdaQueryWrapper();
             queryWrapper.eq(MenuEntity::getIsDelete,0);
             queryWrapper.eq(MenuEntity::getEnabled,1);
+            if (Objects.nonNull(dto.getAppId())) {
+                queryWrapper.eq(MenuEntity::getAppId,dto.getAppId());
+            }
             queryWrapper.orderByAsc(MenuEntity::getSort);
             applyMenuScopeFilter(queryWrapper, dto);
             List<MenuEntity> menus = menuService.list(queryWrapper);
@@ -135,7 +141,6 @@ public class MenuApiController {
         return Result.ok();
     }
 
-
     /**
      * 资源树(权限配置等):返回 {@link TreeVo},type=20 表示菜单节点。
      * PC / H5 原各有一套接口,逻辑相同,已合并;旧路径 list_web_h5_res_tree 仍可用。
@@ -155,6 +160,9 @@ public class MenuApiController {
         LambdaQueryWrapper<MenuEntity> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.eq(MenuEntity::getIsDelete, 0);
         queryWrapper.eq(MenuEntity::getEnabled, 1);
+        if (Objects.nonNull(dto.getAppId())) {
+            queryWrapper.eq(MenuEntity::getAppId,dto.getAppId());
+        }
         queryWrapper.orderByAsc(MenuEntity::getSort);
         applyMenuScopeFilter(queryWrapper, dto);
         List<MenuEntity> menus = menuService.list(queryWrapper);

+ 2 - 2
java/storlead-system/storlead-system-biz/src/main/java/com/storlead/system/mapper/SysAppMapper.java

@@ -1,10 +1,10 @@
 package com.storlead.system.mapper;
 
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.storlead.framework.mybatis.mapper.MyBaseMapper;
 import com.storlead.system.pojo.entity.SysAppEntity;
 
 /**
  * 系统应用 Mapper
  */
-public interface SysAppMapper extends BaseMapper<SysAppEntity> {
+public interface SysAppMapper extends MyBaseMapper<SysAppEntity> {
 }

+ 5 - 16
java/storlead-system/storlead-system-biz/src/main/java/com/storlead/system/service/impl/SysAppServiceImpl.java

@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.storlead.framework.common.result.Result;
+import com.storlead.framework.mybatis.service.impl.MyBaseServiceImpl;
 import com.storlead.framework.util.LoginUserUtil;
 import com.storlead.system.mapper.MenuMapper;
 import com.storlead.system.mapper.SysAppMapper;
@@ -25,7 +26,7 @@ import java.util.Objects;
  * 系统应用服务实现
  */
 @Service
-public class SysAppServiceImpl extends ServiceImpl<SysAppMapper, SysAppEntity> implements SysAppService {
+public class SysAppServiceImpl extends MyBaseServiceImpl<SysAppMapper, SysAppEntity> implements SysAppService {
 
     @Resource
     private MenuMapper menuMapper;
@@ -76,25 +77,13 @@ public class SysAppServiceImpl extends ServiceImpl<SysAppMapper, SysAppEntity> i
         if (StrUtil.isNotBlank(body.getAppTitle())) {
             body.setAppTitle(body.getAppTitle().trim());
         }
-        if (StrUtil.isNotBlank(body.getDomainName())) {
-            body.setDomainName(body.getDomainName().trim());
-        }
+//        if (StrUtil.isNotBlank(body.getDomainName())) {
+//            body.setDomainName(body.getDomainName().trim());
+//        }
 
         Long userId = LoginUserUtil.getCurrentUserId();
         Date now = new Date();
         if (body.getId() == null) {
-            if (body.getEnabled() == null) {
-                body.setEnabled(1);
-            }
-            if (body.getIsDelete() == null) {
-                body.setIsDelete(0);
-            }
-            if (body.getSort() == null) {
-                body.setSort(0);
-            }
-            if (body.getOwnerBy() == null) {
-                body.setOwnerBy(userId != null ? userId : 0L);
-            }
             body.setCreateBy(userId);
             body.setCreateTime(now);
         }

+ 3 - 32
java/storlead-system/storlead-system-core/src/main/java/com/storlead/system/pojo/entity/SysAppEntity.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.storlead.framework.mybatis.entity.SysBaseField;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -20,7 +21,7 @@ import java.util.Date;
 @Accessors(chain = true)
 @TableName("sys_app")
 @ApiModel(value = "SysAppEntity", description = "系统应用")
-public class SysAppEntity implements Serializable {
+public class SysAppEntity extends SysBaseField {
 
     private static final long serialVersionUID = 1L;
 
@@ -29,7 +30,7 @@ public class SysAppEntity implements Serializable {
     private Long id;
 
     @ApiModelProperty(value = "应用编码,全局唯一")
-    private Long appCode;
+    private String appCode;
 
     @ApiModelProperty(value = "应用名称")
     private String appName;
@@ -40,37 +41,7 @@ public class SysAppEntity implements Serializable {
     @ApiModelProperty(value = "图标")
     private String icon;
 
-    @ApiModelProperty(value = "域名/端口")
-    private String domainName;
-
     @ApiModelProperty(value = "默认首页路由/入口")
     private String homePath;
 
-    @ApiModelProperty(value = "创建者")
-    private Long createBy;
-
-    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
-    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    @ApiModelProperty(value = "创建时间")
-    private Date createTime;
-
-    @ApiModelProperty(value = "更新者")
-    private Long updateBy;
-
-    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
-    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    @ApiModelProperty(value = "更新时间")
-    private Date updateTime;
-
-    @ApiModelProperty(value = "是否启用:1 启用,0 禁用")
-    private Integer enabled;
-
-    @ApiModelProperty(value = "是否删除:1 已删除,0 未删除")
-    private Integer isDelete;
-
-    @ApiModelProperty(value = "排序")
-    private Integer sort;
-
-    @ApiModelProperty(value = "拥有者")
-    private Long ownerBy;
 }

+ 2 - 1
java/storlead-system/storlead-system-spi/src/main/java/com/storlead/system/service/SysAppService.java

@@ -3,6 +3,7 @@ package com.storlead.system.service;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.storlead.framework.common.result.Result;
+import com.storlead.framework.mybatis.service.MyBaseService;
 import com.storlead.system.pojo.dto.SysAppPageDTO;
 import com.storlead.system.pojo.entity.SysAppEntity;
 
@@ -10,7 +11,7 @@ import java.util.List;
 /**
  * 系统应用服务
  */
-public interface SysAppService extends IService<SysAppEntity> {
+public interface SysAppService extends MyBaseService<SysAppEntity> {
 
     /**
      * 分页查询(未删除)

+ 1 - 0
java/storlead-thirdparty/pom.xml

@@ -19,6 +19,7 @@
         <module>storlead-thirdparty-api</module>
         <module>storlead-thirdparty-spi</module>
         <module>storlead-thirdparty-biz</module>
+        <module>storlead-thirdparty-wecom</module>
         <module>storlead-thirdparty-dingtalk</module>
     </modules>
 </project>

+ 4 - 0
java/storlead-thirdparty/storlead-thirdparty-biz/pom.xml

@@ -25,6 +25,10 @@
             <groupId>com.storlead.boot</groupId>
             <artifactId>storlead-thirdparty-spi</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.storlead.boot</groupId>
+            <artifactId>storlead-thirdparty-wecom</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter</artifactId>

+ 5 - 0
java/storlead-thirdparty/storlead-thirdparty-wecom/pom.xml

@@ -42,6 +42,11 @@
             <artifactId>storlead-thirdparty-spi</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.storlead.boot</groupId>
+            <artifactId>storlead-thirdparty-api</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-validation</artifactId>

+ 59 - 0
java/storlead-thirdparty/storlead-thirdparty-wecom/src/main/java/com/storlead/wecom/integration/WecomThirdPartyUserSyncClient.java

@@ -0,0 +1,59 @@
+package com.storlead.wecom.integration;
+
+import com.storlead.framework.common.vo.user.UserVO;
+import com.storlead.thirdparty.api.dto.ThirdPartyUserSyncDTO;
+import com.storlead.thirdparty.api.enums.ThirdPartyChannel;
+import com.storlead.thirdparty.spi.ThirdPartyUserSyncClient;
+import com.storlead.wecom.service.CorpWeChatService;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 企业微信用户 openId(xworkUserId)/ 头像同步 SPI 实现。
+ */
+@Component
+public class WecomThirdPartyUserSyncClient implements ThirdPartyUserSyncClient {
+
+    @Resource
+    private CorpWeChatService corpWeChatService;
+
+    @Override
+    public ThirdPartyChannel channel() {
+        return ThirdPartyChannel.WECOM;
+    }
+
+    @Override
+    public List<ThirdPartyUserSyncDTO> syncUserOpenId(List<ThirdPartyUserSyncDTO> users) {
+        if (CollectionUtils.isEmpty(users)) {
+            return Collections.emptyList();
+        }
+        List<UserVO> userVoList = new ArrayList<>(users.size());
+        for (ThirdPartyUserSyncDTO user : users) {
+            UserVO vo = new UserVO();
+            vo.setId(user.getId());
+            vo.setMobile(user.getMobile());
+            vo.setXworkUserId(user.getXworkUserId());
+            vo.setAvatar(user.getAvatar());
+            userVoList.add(vo);
+        }
+        List<UserVO> synced = corpWeChatService.syncUserOpenIdFormTencent(userVoList);
+        if (CollectionUtils.isEmpty(synced)) {
+            return Collections.emptyList();
+        }
+        List<ThirdPartyUserSyncDTO> result = new ArrayList<>(synced.size());
+        for (UserVO vo : synced) {
+            ThirdPartyUserSyncDTO dto = new ThirdPartyUserSyncDTO();
+            dto.setId(vo.getId());
+            dto.setMobile(vo.getMobile());
+            dto.setXworkUserId(vo.getXworkUserId());
+            dto.setAvatar(vo.getAvatar());
+            result.add(dto);
+        }
+        return result;
+    }
+}