1、完善项目

2、完善合同
3、完善费用项
4、司机管理对接OCR
5、完善运输计划
6、完善临时额度
This commit is contained in:
2026-08-04 02:49:59 +08:00
387 changed files with 22482 additions and 110 deletions

View File

@@ -13,6 +13,13 @@
<name>${project.artifactId}</name>
<packaging>jar</packaging>
<properties>
<!-- 其他依赖版本 -->
<lombok.version>1.18.30</lombok.version>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
<junrar.version>7.5.5</junrar.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springblade</groupId>
@@ -38,6 +45,35 @@
<groupId>org.springblade</groupId>
<artifactId>blade-user-api</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-oa-api</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-mk-api</artifactId>
</dependency>
<!-- 其他依赖 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-powerjob</artifactId>
</dependency>
<!--安全模块-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-oauth2</artifactId>
</dependency>
</dependencies>
<build>
@@ -53,6 +89,34 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>

View File

@@ -0,0 +1,115 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.mp.support.Query;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.pojo.entity.AppVersion;
import org.springblade.system.service.AppVersionService;
import org.springframework.web.bind.annotation.*;
@RestController
@AllArgsConstructor
@RequestMapping("appVersion")
@Tag(name = "APP版本管理", description = "APP版本管理")
public class AppVersionController extends BladeController {
private static final Logger logger = LoggerFactory.getLogger(AppVersionController.class);
private final AppVersionService appVersionService;
/**
* 分页
*/
@GetMapping("/page")
@ApiOperationSupport(order = 1)
@Operation(summary = "分页")
public R<IPage<AppVersion>> page(AppVersion modelVo, Query query) {
try {
return R.data(appVersionService.page(modelVo,query));
} catch (Exception e) {
logger.error("appVersion/page" + e.getMessage());
return R.fail(e.getMessage());
}
}
/**
* 详情
*/
@GetMapping("/detail/{id}")
@ApiOperationSupport(order = 2)
@Operation(summary = "详情")
public R<AppVersion> detail(@PathVariable Long id) {
try {
return R.data(appVersionService.detail(id));
} catch (Exception e) {
logger.error("appVersion/detail" + e.getMessage());
return R.fail(e.getMessage());
}
}
/**
* 发布版本/编辑
*/
@PostMapping("/publishOrEdit")
@ApiOperationSupport(order = 3)
@Operation(summary = "发布版本/编辑")
public R<AppVersion> publishOrEdit(@Valid @RequestBody AppVersion modelVo) {
try {
return R.data(appVersionService.publishOrEdit(modelVo));
} catch (Exception e) {
logger.error("appVersion/publishOrEdit" + e.getMessage());
return R.fail(e.getMessage());
}
}
/**
* 删除
*/
@PostMapping("/remove")
@ApiOperationSupport(order = 4)
@Operation(summary = "删除")
public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
try {
return R.status(appVersionService.removeByIds(Func.toLongList(ids)));
} catch (Exception e) {
logger.error("appVersion/remove" + e.getMessage());
return R.fail(e.getMessage());
}
}
}

View File

@@ -25,7 +25,6 @@
*/
package org.springblade.system.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -183,10 +182,11 @@ public class DeptController extends BladeController {
public R<List<Dept>> select(Long userId, String deptId) {
if (Func.isNotEmpty(userId)) {
User user = UserCache.getUser(userId);
deptId = user.getDeptId();
if (Func.isNotEmpty(user)) {
deptId = user.getDeptId();
}
}
List<Dept> list = deptService.list(Wrappers.<Dept>lambdaQuery().in(Dept::getId, Func.toLongList(deptId)));
return R.data(list);
return R.data(deptService.selectDept(deptId));
}
/**

View File

@@ -25,20 +25,28 @@
*/
package org.springblade.system.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.springblade.common.excel.ImportFailureExcelUtil;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.excel.util.ExcelUtil;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.excel.FeeItemExcel;
import org.springblade.system.excel.FeeItemImportFailureExcel;
import org.springblade.system.pojo.entity.FeeItem;
import org.springblade.system.pojo.vo.FeeItemVO;
import org.springblade.system.service.IFeeItemService;
@@ -49,6 +57,10 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
/**
* 费用项 控制器
@@ -118,4 +130,69 @@ public class FeeItemController extends BladeController {
return R.status(feeItemService.changeStatus(id, status));
}
/**
* 导入费用项
*/
@PostMapping("/import-fee-item")
@ApiOperationSupport(order = 6)
@Operation(summary = "导入费用项", description = "传入excel")
public R importFeeItem(MultipartFile file, HttpServletResponse response) {
List<FeeItemExcel> data = ExcelUtil.read(file, FeeItemExcel.class);
List<FeeItemImportFailureExcel> failureList = feeItemService.importFeeItem(data);
if (Func.isNotEmpty(failureList)) {
ImportFailureExcelUtil.export(response, "费用项导入失败明细" + DateUtil.time(), "导入失败明细", failureList, FeeItemExcel.class);
return null;
}
return R.success("导入数据成功");
}
/**
* 导出费用项
*/
@GetMapping("/export-fee-item")
@ApiOperationSupport(order = 7)
@Operation(summary = "导出费用项")
public void exportFeeItem(FeeItemVO feeItem,
@RequestParam(required = false) String ids,
HttpServletResponse response) {
List<FeeItemExcel> list = feeItemService.exportFeeItem(buildExportQuery(feeItem, ids));
ExcelUtil.export(response, "费用项" + DateUtil.time(), "费用项表", list, FeeItemExcel.class);
}
/**
* 导出模板
*/
@GetMapping("/export-template")
@ApiOperationSupport(order = 8)
@Operation(summary = "导出模板")
public void exportTemplate(HttpServletResponse response) {
List<FeeItemExcel> list = new ArrayList<>();
ExcelUtil.export(response, "费用项模板", "费用项导入模板", list, FeeItemExcel.class);
}
private LambdaQueryWrapper<FeeItem> buildExportQuery(FeeItemVO feeItem, String ids) {
LambdaQueryWrapper<FeeItem> queryWrapper = Wrappers.<FeeItem>lambdaQuery()
.eq(FeeItem::getIsDeleted, 0)
.orderByDesc(FeeItem::getCreateTime);
if (Func.isNotEmpty(ids)) {
queryWrapper.in(FeeItem::getId, Func.toLongList(ids));
}
if (Func.isNotEmpty(feeItem.getFeeCategory())) {
queryWrapper.eq(FeeItem::getFeeCategory, feeItem.getFeeCategory());
}
if (Func.isNotEmpty(feeItem.getName())) {
queryWrapper.like(FeeItem::getName, feeItem.getName());
}
if (Func.isNotEmpty(feeItem.getEnglishName())) {
queryWrapper.like(FeeItem::getEnglishName, feeItem.getEnglishName());
}
if (Func.isNotEmpty(feeItem.getStatus())) {
queryWrapper.eq(FeeItem::getStatus, feeItem.getStatus());
}
if (Func.isNotEmpty(feeItem.getCreateDept())) {
queryWrapper.eq(FeeItem::getCreateDept, feeItem.getCreateDept());
}
return queryWrapper;
}
}

View File

@@ -0,0 +1,50 @@
package org.springblade.system.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.constant.RoleConstant;
import org.springblade.system.service.IMKPushService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* mk数据推送
* @author bfhuange
* @date 2024/9/24
*/
@Tag(name = "mk数据推送", description = "mk数据推送")
@RequestMapping("/mk")
@RestController
@RequiredArgsConstructor
public class MKPushController {
private final IMKPushService mkPushService;
/**
* 推送公司和部门
* @return
*/
@Operation(summary = "推送公司和部门")
@PostMapping("/pushCompanyAndDept")
@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
public R<Boolean> pushCompanyAndDept() {
mkPushService.pushOrgAndRecord(null);
return R.data(true);
}
/**
* 推送人员
* @return
*/
@Operation(summary = "推送人员")
@PostMapping("/pushPerson")
@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
public R<Boolean> pushPerson() {
mkPushService.pushPersonAndRecord(null);
return R.data(true);
}
}

View File

@@ -0,0 +1,78 @@
package org.springblade.system.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springblade.core.log.annotation.ApiLog;
import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.tool.api.R;
import org.springblade.system.service.IOASyncService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* oa数据同步
* @author bfhuange
* @date 2024/8/23
*/
@Tag(name = "oa数据同步", description = "oa数据同步")
@RequestMapping("/oa")
@RestController
@RequiredArgsConstructor
public class OAController {
private final IOASyncService oaService;
/**
* 增量同步公司和部门并推送MK
* @return
*/
@ApiLog("系统管理-机构管理-增量同步")
@Operation(summary = "增量同步公司和部门并推送MK")
@PostMapping("/syncCompanyAndDeptAndPushMK")
@PreAuth("hasPermission('oasync:org:inc')")
public R<Boolean> syncCompanyAndDeptAndPushMK() {
oaService.syncCompanyAndDeptAndPushMK(false);
return R.data(true);
}
/**
* 全量同步公司和部门并推送MK
* @return
*/
@ApiLog("系统管理-机构管理-全量同步")
@Operation(summary = "全量同步公司和部门并推送MK")
@PostMapping("/syncAllCompanyAndDeptAndPushMK")
@PreAuth("hasPermission('oasync:org:all')")
public R<Boolean> syncAllCompanyAndDeptAndPushMK() {
oaService.syncCompanyAndDeptAndPushMK(true);
return R.data(true);
}
/**
* 增量同步人员并推送MK
* @return
*/
@ApiLog("系统管理-用户管理-增量同步")
@Operation(summary = "增量同步人员并推送MK")
@PostMapping("/syncPersonAndPushMK")
@PreAuth("hasPermission('oasync:person:inc')")
public R<Boolean> syncPersonAndPushMK() {
oaService.syncPersonAndPushMK(false);
return R.data(true);
}
/**
* 全量同步人员并推送MK
* @return
*/
@ApiLog("系统管理-用户管理-全量同步")
@Operation(summary = "全量同步人员并推送MK")
@PostMapping("/syncAllPersonAndPushMK")
@PreAuth("hasPermission('oasync:person:all')")
public R<Boolean> syncAllPersonAndPushMK() {
oaService.syncPersonAndPushMK(true);
return R.data(true);
}
}

View File

@@ -0,0 +1,142 @@
package org.springblade.system.convert;
import org.mapstruct.*;
import org.springblade.common.constant.CommonConstant;
import org.springblade.common.constant.DataStatusEnum;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.system.pojo.enums.DeptCategory;
import org.springblade.system.pojo.vo.DeptListVO;
import org.springblade.thirdparty.mk.constant.MKConstant;
import org.springblade.thirdparty.mk.pojo.dto.MKPushOrgDeptDTO;
import org.springblade.thirdparty.oa.constant.OAConstant;
import org.springblade.thirdparty.oa.constant.OAConvertConstant;
import org.springblade.thirdparty.oa.pojo.response.OACompanyResponse;
import org.springblade.thirdparty.oa.pojo.response.OADepartmentResponse;
import org.springblade.thirdparty.oa.util.OAUtils;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* oa公司、部门到本系统部门转换工具
* @author bfhuange
* @date 2024/8/26
*/
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface DeptConvert {
DeptListVO entity2vo(Dept entity);
List<DeptListVO> entities2vos(List<Dept> entities);
@Mapping(source = "deptCode", target = "fdId")
@Mapping(source = "deptName", target = "fdName")
@Mapping(source = "deptCode", target = "fdNo")
@Mapping(source = "sort", target = "fdOrder")
@Mapping(source = "status", target = "fdIsAvailable", qualifiedByName = "fdIsAvailable")
@Mapping(source = "deptCategory", target = "fdOrgType", qualifiedByName = "fdOrgType")
@Mapping(source = "deptCategory", target = "fdSourceSystem", qualifiedByName = "fdSourceSystem")
@Mapping(source = "parentCode", target = "fdParent")
MKPushOrgDeptDTO entity2dto(Dept entity);
List<MKPushOrgDeptDTO> entities2dtos(List<Dept> entities);
@Mapping(source = "subcompanyname", target = "deptName")
@Mapping(source = "subcompanydesc", target = "fullName")
// 父公司id转为父部门编码要拼接前缀
@Mapping(source = "supsubcomid", target = "parentCode")
@Mapping(source = "created", target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Mapping(source = "modified", target = "updateTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
Dept baseConvert(OACompanyResponse company);
@Mapping(source = "departmentname", target = "deptName")
@Mapping(source = "departmentmark", target = "fullName")
// 父部门id转为父部门编码要拼接前缀
@Mapping(source = "supdepid", target = "parentCode")
// 所属子公司id转为所属公司编码要拼接前缀
@Mapping(source = "subcompanyid1", target = "belongCompanyCode")
@Mapping(source = "created", target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Mapping(source = "modified", target = "updateTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
Dept baseConvert(OADepartmentResponse department);
/**
* oa公司转本系统部门
* @param company
* @return
*/
default Dept company2dept(OACompanyResponse company) {
Dept dept = this.baseConvert(company);
// 部门编码添加前缀
dept.setDeptCode(OAConvertConstant.COMPANY_OA_PREFIX + company.getId());
String parentCode = dept.getParentCode();
if (!OAConstant.ROOT_COMPANY_ID.equals(parentCode)) {
// 父部门编码不是0拼接前缀
dept.setParentCode(OAConvertConstant.COMPANY_OA_PREFIX + parentCode);
// 父部门id后续处理
} else {
// 父部门编码是0父id也设置为0
dept.setParentId(OAConvertConstant.ROOT_PARENT_ID);
}
// 公司所属公司编码就是他自己的部门编码
dept.setBelongCompanyCode(dept.getDeptCode());
// 显示顺序处理
dept.setSort(OAUtils.parseInt(company.getShoworder()));
// 部门类型,公司
dept.setDeptCategory(DeptCategory.COMPANY.getCode());
dept.setSyncTime(new Date());
// 是oa的
dept.setIsOa(CommonConstant.YES);
return dept;
}
/**
* oa部门转本系统部门
* @param oaDept
* @param companyDeptMap 所有公司的部门编码与id map
* @return
*/
default Dept dept2dept(OADepartmentResponse oaDept, Map<String, Long> companyDeptMap) {
Dept dept = this.baseConvert(oaDept);
// 部门编码添加前缀
dept.setDeptCode(OAConvertConstant.DEPARTMENT_OA_PREFIX + oaDept.getId());
// 部门所属公司编码就是他所属公司编码加前缀
dept.setBelongCompanyCode(OAConvertConstant.COMPANY_OA_PREFIX + dept.getBelongCompanyCode());
String parentCode = dept.getParentCode();
if (!OAConstant.ROOT_COMPANY_ID.equals(parentCode)) {
// 父部门编码不是0拼接前缀
dept.setParentCode(OAConvertConstant.DEPARTMENT_OA_PREFIX + parentCode);
// 父部门id后续处理
} else {
// 父部门编码是0设置为公司编码
dept.setParentCode(dept.getBelongCompanyCode());
// 根据父部门编码获取对应的id
dept.setParentId(companyDeptMap.get(dept.getParentCode()));
}
// 显示顺序处理
dept.setSort(OAUtils.parseInt(oaDept.getShoworder()));
// 部门类型,部门
dept.setDeptCategory(DeptCategory.DEPT.getCode());
dept.setSyncTime(new Date());
// 是oa的
dept.setIsOa(CommonConstant.YES);
return dept;
}
@Named("fdIsAvailable")
default Boolean fdIsAvailable(Integer status) {
return DataStatusEnum.ENABLE.getCode().equals(status);
}
@Named("fdOrgType")
default String fdOrgType(Integer deptCategory) {
return DeptCategory.COMPANY.getCode().equals(deptCategory) ? MKConstant.ORG_TYPE_ORG : MKConstant.ORG_TYPE_DEPT;
}
@Named("fdSourceSystem")
default String fdSourceSystem(Integer deptCategory) {
return CommonConstant.SYSTEM_CODE;
}
}

View File

@@ -0,0 +1,200 @@
package org.springblade.system.convert;
import org.apache.commons.lang3.StringUtils;
import org.mapstruct.*;
import org.springblade.common.constant.CommonConstant;
import org.springblade.common.constant.DataStatusEnum;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.pojo.entity.User;
import org.springblade.system.pojo.entity.UserDept;
import org.springblade.system.pojo.enums.UserSex;
import org.springblade.system.pojo.enums.UserType;
import org.springblade.system.pojo.vo.UserListVO;
import org.springblade.thirdparty.mk.constant.MKConstant;
import org.springblade.thirdparty.mk.pojo.dto.MKAuxiliaryDeptDTO;
import org.springblade.thirdparty.mk.pojo.dto.MKPushPersonDTO;
import org.springblade.thirdparty.oa.constant.OAConvertConstant;
import org.springblade.thirdparty.oa.pojo.response.OAPersonResponse;
import org.springblade.thirdparty.oa.util.OAUtils;
import java.util.*;
/**
* @author bfhuange
* @date 2024/8/26
*/
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface UserConvert {
List<UserListVO> entities2vos(List<User> list);
default List<MKPushPersonDTO> entities2dtos(List<User> records, String defaultPassword) {
if (CollectionUtil.isEmpty(records)) {
return Collections.emptyList();
}
return records.stream()
.map(user -> {
MKPushPersonDTO personDTO = entity2dto(user);
if (personDTO == null) {
return null;
}
if (CommonConstant.NO.equals(user.getIsPushMk())) {
// 未推送mk的才设置默认密码
personDTO.setFdPassword(defaultPassword);
}
return personDTO;
}).toList();
}
@Mapping(source = "account", target = "fdId")
@Mapping(source = "account", target = "fdLoginName")
@Mapping(source = "name", target = "fdName")
@Mapping(source = "phone", target = "fdMobileNo")
@Mapping(source = "code", target = "fdNo")
@Mapping(source = "sex", target = "fdSex", qualifiedByName = "fdSex")
@Mapping(source = "status", target = "fdIsAvailable", qualifiedByName = "fdIsAvailable")
@Mapping(source = "account", target = "fdSourceSystem", qualifiedByName = "fdSourceSystem")
@Mapping(source = "deptCodes", target = "fdParent", qualifiedByName = "fdParent")
@Mapping(source = "deptCodes", target = "fdAuxiliaryDept", qualifiedByName = "fdAuxiliaryDept")
MKPushPersonDTO entity2dto(User entity);
@Mapping(target = "id", ignore = true)
@Mapping(target = "status", ignore = true)
@Mapping(target = "password", ignore = true)
@Mapping(target = "birthday", ignore = true)
@Mapping(target = "sex", ignore = true)
@Mapping(source = "workcode", target = "code")
@Mapping(source = "lastname", target = "name")
@Mapping(source = "lastname", target = "realName")
@Mapping(source = "mobile", target = "phone")
@Mapping(source = "mobile", target = "account")
@Mapping(source = "email", target = "email")
User baseConvert(OAPersonResponse person);
@Mapping(source = "subcompanyname", target = "companyName")
@Mapping(source = "departmentname", target = "deptName")
@Mapping(source = "id", target = "oaId")
@Mapping(source = "loginid", target = "oaLoginId")
UserDept userDeptConvert(OAPersonResponse person);
/**
* oa人员转本系统用户
* @param person
* @param defaultPassword 默认密码
* @return
*/
default User person2user(OAPersonResponse person, String defaultPassword) {
User user = baseConvert(person);
// 密码
user.setPassword(defaultPassword);
// 性别
user.setSex(UserSex.getCodeByName(person.getSex()));
// 状态转换
Integer status = getStatus(person);
user.setStatus(status);
// web平台
user.setUserType(UserType.WEB.getCategory());
user.setSyncTime(new Date());
// 从oa同步的
user.setIsOa(CommonConstant.YES);
return user;
}
/**
* oa人员转本系统用户部门
* @param person
* @param userMap 所有用户手机号与id map
* @return
*/
default UserDept person2userDept(OAPersonResponse person, Map<String, Long> userMap) {
UserDept userDept = userDeptConvert(person);
// 公司编码添加前缀
userDept.setCompanyCode(OAConvertConstant.COMPANY_OA_PREFIX + person.getSubcompanyid1());
// 部门编码添加前缀
userDept.setDeptCode(OAConvertConstant.DEPARTMENT_OA_PREFIX + person.getDepartmentid());
// 状态转换
Integer status = getStatus(person);
userDept.setStatus(status);
// 排序
userDept.setSort(OAUtils.parseInt(person.getDsporder()));
// 用户id
userDept.setUserId(userMap.get(person.getMobile()));
userDept.setSyncTime(new Date());
return userDept;
}
/**
* 转换状态
* @param person
* @return
*/
private Integer getStatus(OAPersonResponse person) {
// 过滤状态 0-试用 1-正式 2-临时 3-试用延期 4-解聘 5-离职 6-退休 7-无效
return switch (person.getStatus()) {
case "0", "1", "2", "3" -> DataStatusEnum.ENABLE.getCode();
default -> DataStatusEnum.DISABLE.getCode();
};
}
@Named("fdIsAvailable")
default Boolean fdIsAvailable(Integer status) {
return DataStatusEnum.ENABLE.getCode().equals(status);
}
@Named("fdSourceSystem")
default String fdSourceSystem(String account) {
return CommonConstant.SYSTEM_CODE;
}
@Named("fdSex")
default String fdSex(Integer sex) {
if (sex == null) {
return null;
}
if (UserSex.MAN.getCode().equals(sex)) {
return MKConstant.SEX_MAN;
}
if (UserSex.WOMAN.getCode().equals(sex)) {
return MKConstant.SEX_WOMAN;
}
return MKConstant.SEX_UNKONW;
}
@Named("fdParent")
default String fdParent(String deptCodes) {
if (StringUtils.isBlank(deptCodes)) {
return null;
}
// 第一个部门编码作为主部门id
return Func.toStrList(deptCodes).stream()
// 非外部组织编码
.filter(deptCode -> !deptCode.startsWith(OAConvertConstant.COMPANY_EX_PREFIX) && !deptCode.startsWith(OAConvertConstant.DEPARTMENT_EX_PREFIX))
.findFirst()
.orElse(null);
}
@Named("fdAuxiliaryDept")
default List<MKAuxiliaryDeptDTO> fdAuxiliaryDept(String deptCodes) {
if (StringUtils.isBlank(deptCodes)) {
return null;
}
List<String> deptCodeList = Func.toStrList(deptCodes).stream()
// 非外部组织编码
.filter(deptCode -> !deptCode.startsWith(OAConvertConstant.COMPANY_EX_PREFIX) && !deptCode.startsWith(OAConvertConstant.DEPARTMENT_EX_PREFIX))
.toList();
if (deptCodeList.size() <= 1) {
return null;
}
deptCodeList = new ArrayList<>(deptCodeList);
// 移除第一个部门编码,其他的作为辅部门
deptCodeList.remove(0);
return deptCodeList.stream()
.map(deptCode -> {
MKAuxiliaryDeptDTO deptDTO = new MKAuxiliaryDeptDTO();
deptDTO.setFdId(deptCode);
return deptDTO;
}).toList();
}
}

View File

@@ -0,0 +1,66 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.excel;
import cn.idev.excel.annotation.ExcelIgnore;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.write.style.ColumnWidth;
import cn.idev.excel.annotation.write.style.ContentRowHeight;
import cn.idev.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 费用项 Excel
*
* @author Chill
*/
@Data
@ColumnWidth(22)
@HeadRowHeight(20)
@ContentRowHeight(18)
public class FeeItemExcel implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ExcelIgnore
private Long id;
@ExcelProperty("*费用类型")
private String feeCategory;
@ExcelProperty("*费用项代码")
private String englishName;
@ExcelProperty("*费用项")
private String name;
@ExcelIgnore
private String errorMessage;
}

View File

@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.excel;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.write.style.ColumnWidth;
import cn.idev.excel.annotation.write.style.ContentRowHeight;
import cn.idev.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 费用项导入失败 Excel
*
* @author Chill
*/
@Data
@ColumnWidth(22)
@HeadRowHeight(20)
@ContentRowHeight(18)
public class FeeItemImportFailureExcel implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ExcelProperty("*费用类型")
private String feeCategory;
@ExcelProperty("*费用项代码")
private String englishName;
@ExcelProperty("*费用项")
private String name;
@ExcelProperty("导入失败原因")
private String failureReason;
}

View File

@@ -0,0 +1,89 @@
package org.springblade.system.log;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import tech.powerjob.worker.log.OmsLogger;
import java.util.LinkedList;
/**
* 日志工具类
* @author bfhuange
* @date 2024/9/25
*/
@Slf4j
public class ComposeLogUtil {
/**
* oms logger
*/
private static final ThreadLocal<OmsLogger> OMS_LOGGER_CONTEXT = new ThreadLocal<>();
/**
* 组合 logger
*/
private static final ThreadLocal<LinkedList<ComposeLogger>> CONTEXT = new ThreadLocal<>();
/**
* 默认日志记录器
*/
private static final ComposeLogger DEFAULT = new ComposeLogger(log, null);
private ComposeLogUtil() {}
/**
* 设置oms logger
* @param omsLogger
*/
public static void setOmsLogger(OmsLogger omsLogger) {
OMS_LOGGER_CONTEXT.set(omsLogger);
}
/**
* 获取oms logger
* @return
*/
public static OmsLogger getOmsLogger() {
return OMS_LOGGER_CONTEXT.get();
}
/**
* 移除oms logger
*/
public static void removeOmsLogger() {
OMS_LOGGER_CONTEXT.remove();
}
/**
* 添加日志记录器
* @param log
*/
public static void addLog(Logger log) {
LinkedList<ComposeLogger> list = CONTEXT.get();
if (list == null) {
list = new LinkedList<>();
CONTEXT.set(list);
}
ComposeLogger omsLog = new ComposeLogger(log, ComposeLogUtil.getOmsLogger());
list.addLast(omsLog);
}
/**
* 获取最后一个日志记录器
* @return
*/
public static ComposeLogger getLastLog() {
LinkedList<ComposeLogger> list = CONTEXT.get();
if (list == null || list.isEmpty()) {
return DEFAULT;
}
return list.getLast();
}
/**
* 清除最后一个日志记录器
*/
public static void removeLastLog() {
LinkedList<ComposeLogger> list = CONTEXT.get();
if (list == null || list.isEmpty()) {
return;
}
list.removeLast();
}
}

View File

@@ -0,0 +1,48 @@
package org.springblade.system.log;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import tech.powerjob.worker.log.OmsLogger;
/**
* 组合日志,兼容 powerjob 日志
* @author bfhuange
* @date 2024/8/30
*/
@RequiredArgsConstructor
public class ComposeLogger implements OmsLogger {
private final Logger log;
private final OmsLogger omsLog;
@Override
public void debug(String s, Object... objects) {
log.debug(s, objects);
if (omsLog != null) {
omsLog.debug(s, objects);
}
}
@Override
public void info(String s, Object... objects) {
log.info(s, objects);
if (omsLog != null) {
omsLog.info(s, objects);
}
}
@Override
public void warn(String s, Object... objects) {
log.warn(s, objects);
if (omsLog != null) {
omsLog.warn(s, objects);
}
}
@Override
public void error(String s, Object... objects) {
log.error(s, objects);
if (omsLog != null) {
omsLog.error(s, objects);
}
}
}

View File

@@ -0,0 +1,36 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springblade.system.pojo.entity.AppVersion;
public interface AppVersionMapper extends BaseMapper<AppVersion> {
}

View File

@@ -0,0 +1,13 @@
package org.springblade.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springblade.system.pojo.entity.DataSyncRecord;
/**
* 数据同步记录表 Mapper 接口
*
* @author BladeX
* @since 2024-09-27
*/
public interface DataSyncRecordMapper extends BaseMapper<DataSyncRecord> {
}

View File

@@ -0,0 +1,18 @@
<?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="org.springblade.system.mapper.DataSyncRecordMapper">
<!-- 通用查询映射结果 -->
<resultMap id="dataSyncRecordResultMap" type="org.springblade.system.pojo.entity.DataSyncRecord">
<result column="id" property="id"/>
<result column="system_code" property="systemCode"/>
<result column="data_type" property="dataType"/>
<result column="mode" property="mode"/>
<result column="result" property="result"/>
<result column="sync_time" property="syncTime"/>
<result column="tenant_id" property="tenantId"/>
<result column="create_time" property="createTime"/>
<result column="is_deleted" property="isDeleted"/>
</resultMap>
</mapper>

View File

@@ -37,6 +37,9 @@
<if test="feeItem.status != null">
AND status = #{feeItem.status}
</if>
<if test="feeItem.createDept != null">
AND create_dept = #{feeItem.createDept}
</if>
ORDER BY create_time DESC
</select>

View File

@@ -26,7 +26,12 @@
package org.springblade.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springblade.system.pojo.entity.UserDept;
import org.springblade.system.pojo.vo.UserDeptIdsVO;
import java.util.Collection;
import java.util.List;
/**
* Mapper 接口
@@ -35,5 +40,11 @@ import org.springblade.system.pojo.entity.UserDept;
*/
public interface UserDeptMapper extends BaseMapper<UserDept> {
/**
* 根据用户id列表查询对应的部门id列表拼接的字符串
* @param userIds
* @return
*/
List<UserDeptIdsVO> queryUserDeptIds(@Param("userIds") Collection<Long> userIds);
}

View File

@@ -2,4 +2,19 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.springblade.system.mapper.UserDeptMapper">
<select id="queryUserDeptIds" resultType="org.springblade.system.pojo.vo.UserDeptIdsVO">
select
group_concat(distinct bd.id) deptIds,
group_concat(distinct bd.dept_code) deptCodes,
bud.user_id
from
blade_user_dept bud
left join blade_dept bd on bd.dept_code = bud.dept_code
where
bd.id is not null
and bud.is_deleted = 0
and bud.user_id in <foreach collection="userIds" item="item" separator="," open="(" close=")">#{item}</foreach>
group by bud.user_id
</select>
</mapper>

View File

@@ -0,0 +1,56 @@
package org.springblade.system.service;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.system.mapper.AppVersionMapper;
import org.springblade.system.pojo.entity.AppVersion;
import org.springframework.stereotype.Service;
@Service
@AllArgsConstructor
public class AppVersionService extends ServiceImpl<AppVersionMapper, AppVersion> {
/**
* 分页
*/
public IPage<AppVersion> page(AppVersion modelVo, Query query){
query.setDescs("create_time");
IPage<AppVersion> page = this.page(Condition.getPage(query), Condition.getQueryWrapper(modelVo));
return page;
}
/**
* 详情
*/
public AppVersion detail(Long id){
if (ObjectUtil.isEmpty(id)) {
throw new ServiceException("id不能为空");
}
AppVersion model = this.getById(id);
return model;
}
/**
* 发布版本/编辑
*/
public AppVersion publishOrEdit(AppVersion modelVo) {
//版本号没有重复校验
AppVersion appVersion=this.getOne(new LambdaQueryWrapper<AppVersion>().eq(AppVersion::getUpdateType, modelVo.getUpdateType())
.eq(AppVersion::getVersion, modelVo.getVersion())
.eq(AppVersion::getSubVersion, modelVo.getSubVersion())
);
if(ObjectUtil.isNotEmpty(appVersion)){
if(ObjectUtil.isEmpty(modelVo.getId())||(ObjectUtil.isNotEmpty(modelVo.getId())&&!modelVo.getId().equals(appVersion.getId()))){
throw new ServiceException("该更新类型的版本号重复");
}
}
this.saveOrUpdate(modelVo);
return modelVo;
}
}

View File

@@ -0,0 +1,26 @@
package org.springblade.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springblade.system.pojo.entity.DataSyncRecord;
/**
* 数据同步记录表 服务类
*
* @author BladeX
* @since 2024-09-27
*/
public interface IDataSyncRecordService extends IService<DataSyncRecord> {
/**
* 使用新事物提交
* @param entity
*/
void saveWithNewTransaction(DataSyncRecord entity);
/**
* 查询最后一次同步成功的记录
* @param param
* @return
*/
DataSyncRecord getLastSuccessRecord(DataSyncRecord param);
}

View File

@@ -30,6 +30,7 @@ import org.springblade.system.pojo.entity.Dept;
import org.springblade.system.pojo.vo.DeptVO;
import org.springblade.system.pojo.vo.UserVO;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -88,6 +89,14 @@ public interface IDeptService extends IService<Dept> {
*/
List<DeptVO> lazyTree(String tenantId, Long parentId);
/**
* 部门下拉数据源
*
* @param deptId 部门ID集合逗号分隔为空时返回当前租户可选部门
* @return 部门列表
*/
List<Dept> selectDept(String deptId);
/**
* 根据部门名称精确匹配获取部门ID集合
*
@@ -157,4 +166,11 @@ public interface IDeptService extends IService<Dept> {
*/
List<UserVO> deptLeaderInfo(Long deptId);
/**
* 更新部门祖级列表
* @param startTime 要更新的部门的同步时间或更新时间起始
*/
void updateAncestors(Date startTime);
}

View File

@@ -25,11 +25,16 @@
*/
package org.springblade.system.service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.core.mp.base.BaseService;
import org.springblade.system.excel.FeeItemExcel;
import org.springblade.system.excel.FeeItemImportFailureExcel;
import org.springblade.system.pojo.entity.FeeItem;
import org.springblade.system.pojo.vo.FeeItemVO;
import java.util.List;
/**
* 费用项 服务类
*
@@ -63,4 +68,20 @@ public interface IFeeItemService extends BaseService<FeeItem> {
*/
boolean changeStatus(Long id, Integer status);
/**
* 导入费用项
*
* @param data 导入数据
* @return 导入失败明细
*/
List<FeeItemImportFailureExcel> importFeeItem(List<FeeItemExcel> data);
/**
* 导出费用项
*
* @param queryWrapper 查询条件
* @return 导出数据
*/
List<FeeItemExcel> exportFeeItem(Wrapper<FeeItem> queryWrapper);
}

View File

@@ -0,0 +1,24 @@
package org.springblade.system.service;
import java.util.Date;
/**
* mk 推送接口
* @author bfhuange
* @date 2024/9/24
*/
public interface IMKPushService {
/**
* 推送所有公司和部门并记录
*
* @param oaStartTime oa同步查询开始时间
*/
void pushOrgAndRecord(Date oaStartTime);
/**
* 推送人员并记录
*
* @param oaStartTime oa同步查询开始时间
*/
void pushPersonAndRecord(Date oaStartTime);
}

View File

@@ -0,0 +1,21 @@
package org.springblade.system.service;
/**
* oa同步接口
* @author bfhuange
* @date 2024/8/23
*/
public interface IOASyncService {
/**
* 从oa同步公司和部门并且推送到mk
* @param syncAll 是否同步所有
*/
void syncCompanyAndDeptAndPushMK(boolean syncAll);
/**
* 从oa同步人员并且推送到mk
* @param syncAll 是否同步所有
*/
void syncPersonAndPushMK(boolean syncAll);
}

View File

@@ -28,6 +28,10 @@ package org.springblade.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springblade.system.pojo.entity.UserDept;
import org.springblade.system.pojo.vo.UserDeptIdsVO;
import java.util.Collection;
import java.util.List;
/**
* 服务类
@@ -36,4 +40,11 @@ import org.springblade.system.pojo.entity.UserDept;
*/
public interface IUserDeptService extends IService<UserDept> {
/**
* 根据用户id列表查询对应的部门id列表拼接的字符串
* @param userIds
* @return
*/
List<UserDeptIdsVO> queryUserDeptIds(Collection<Long> userIds);
}

View File

@@ -30,6 +30,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.core.mp.base.BaseService;
import org.springblade.core.mp.support.Query;
import org.springblade.system.pojo.context.IPhone;
import org.springblade.system.pojo.entity.User;
import org.springblade.system.pojo.entity.UserInfo;
import org.springblade.system.pojo.entity.UserOauth;
@@ -346,4 +347,18 @@ public interface IUserService extends BaseService<User> {
* @return 用户分页UserVO 包含角色、部门、岗位名称)
*/
IPage<UserVO> userPage(IPage<User> page, String account, String name);
/**
* 解密手机号
*
* @param user 用户对象
*/
void decryptPhone(IPhone user);
/**
* 加密手机号
*
* @param user 用户对象
*/
void encryptPhone(IPhone user);
}

View File

@@ -0,0 +1,42 @@
package org.springblade.system.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springblade.system.mapper.DataSyncRecordMapper;
import org.springblade.system.pojo.entity.DataSyncRecord;
import org.springblade.system.pojo.enums.DataSync;
import org.springblade.system.service.IDataSyncRecordService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* 数据同步记录表 服务实现类
*
* @author BladeX
* @since 2024-09-27
*/
@Service
public class DataSyncRecordServiceImpl extends ServiceImpl<DataSyncRecordMapper, DataSyncRecord> implements IDataSyncRecordService {
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
@Override
public void saveWithNewTransaction(DataSyncRecord entity) {
this.save(entity);
}
@Override
public DataSyncRecord getLastSuccessRecord(DataSyncRecord param) {
// 根据系统编码,数据类型,模式 查询最新一条成功的记录
return this.getOne(Wrappers.<DataSyncRecord>lambdaQuery()
.eq(DataSyncRecord::getSystemCode, param.getSystemCode())
.eq(DataSyncRecord::getDataType, param.getDataType())
.eq(DataSyncRecord::getMode, param.getMode())
// 只查成功的
.eq(DataSyncRecord::getResult, DataSync.Result.SUCCESS.name())
// 同步时间倒序排列
.orderByDesc(DataSyncRecord::getSyncTime)
.last("limit 1")
);
}
}

View File

@@ -30,15 +30,14 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tenant.TenantGuard;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.node.ForestNodeMerger;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.*;
import org.springblade.system.cache.SysCache;
import org.springblade.system.mapper.DeptMapper;
import org.springblade.system.pojo.entity.Dept;
@@ -49,12 +48,12 @@ import org.springblade.system.service.IDeptService;
import org.springblade.system.service.IUserService;
import org.springblade.system.wrapper.DeptWrapper;
import org.springblade.system.wrapper.UserWrapper;
import org.springblade.thirdparty.oa.constant.OAConvertConstant;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.springblade.core.tenant.TenantGuard.EntityType.DEPT;
@@ -67,6 +66,7 @@ import static org.springblade.core.tenant.TenantGuard.EntityType.DEPT_PARENT;
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements IDeptService {
private static final String TENANT_ID = "tenantId";
@@ -135,6 +135,21 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
return ForestNodeMerger.merge(baseMapper.lazyTree(resolvedTenantId, parentId));
}
@Override
public List<Dept> selectDept(String deptId) {
LambdaQueryWrapper<Dept> queryWrapper = Wrappers.<Dept>lambdaQuery()
.orderByAsc(Dept::getSort)
.orderByAsc(Dept::getId);
if (!AuthUtil.isAdministrator()) {
queryWrapper.eq(Dept::getTenantId, AuthUtil.getTenantId());
}
List<Long> deptIds = Func.toLongList(deptId);
if (CollectionUtil.isNotEmpty(deptIds)) {
queryWrapper.in(Dept::getId, deptIds);
}
return baseMapper.selectList(queryWrapper);
}
@Override
public String getDeptIds(String tenantId, String deptNames) {
List<Dept> deptList = baseMapper.selectList(Wrappers.<Dept>query().lambda().eq(Dept::getTenantId, tenantId).in(Dept::getDeptName, Func.toStrList(deptNames)));
@@ -265,4 +280,121 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateAncestors(Date startTime) {
// 1. 查询需要更新祖级列表的部门
List<Dept> list = this.list(Wrappers.<Dept>lambdaQuery()
// 祖级列表为空
.isNull(Dept::getAncestors)
// 开始时间不为空,查询同步时间大于开始时间或修改时间大于开始时间的数据
.or(startTime != null, wrapper -> wrapper.ge(Dept::getSyncTime, startTime))
.or(startTime != null, wrapper -> wrapper.ge(Dept::getUpdateTime, startTime))
);
if (CollectionUtil.isEmpty(list)) {
log.info("需要更新祖级列表的部门为空");
return;
}
// 所有公司部门列表
List<DeptVO> allList = this.list().stream()
.map(dept -> BeanUtil.copyProperties(dept, DeptVO.class))
.toList();
// id对应的部门map
Map<Long, DeptVO> idDeptMap = allList.stream()
.collect(Collectors.toMap(DeptVO::getId, Function.identity(), (a, b) -> b));
// 获取根节点列表
List<DeptVO> rootList = getRootList(allList, idDeptMap);
// 计算祖级列表
setAncestors(rootList);
// 3. 更新祖级列表
List<Dept> updateParams = list.stream()
.filter(dept -> {
DeptVO vo = idDeptMap.get(dept.getId());
if (vo == null) {
return false;
}
if (vo.getAncestors() == null) {
return false;
}
// 祖级列表和数据库的不一样的才需要更新
return !StringUtil.equals(vo.getAncestors(), dept.getAncestors());
})
.map(dept -> {
Dept updateParam = new Dept();
updateParam.setId(dept.getId());
DeptVO vo = idDeptMap.get(dept.getId());
updateParam.setAncestors(vo.getAncestors());
return updateParam;
}).toList();
if (CollectionUtil.isNotEmpty(updateParams)) {
this.updateBatchById(updateParams);
}
}
/**
* 获取根节点列表
* @param allList
* @param idDeptMap
* @return
*/
private List<DeptVO> getRootList(List<DeptVO> allList, Map<Long, DeptVO> idDeptMap) {
if (CollectionUtil.isEmpty(allList)) {
return Collections.emptyList();
}
// 根节点列表
List<DeptVO> rootList = new ArrayList<>();
for (DeptVO vo : allList) {
Long parentId = vo.getParentId();
if (OAConvertConstant.ROOT_PARENT_ID.equals(parentId)) {
// 父节点是根节点,添加到根节点列表
rootList.add(vo);
continue;
}
DeptVO parent = idDeptMap.get(parentId);
if (parent != null) {
// 父节点存在,放入父节点的子节点列表中
if (parent.getChildren() == null) {
parent.setChildren(new ArrayList<>());
}
parent.getChildren().add(vo);
}
}
return rootList;
}
/**
* 计算祖级列表
* @param rootList 根节点列表
*/
private void setAncestors(List<DeptVO> rootList) {
if (CollectionUtil.isEmpty(rootList)) {
return;
}
// 根节点的祖级列表是 0, 最后拼接一个逗号,防止查询的时候有包含的
String ancestors = OAConvertConstant.ROOT_PARENT_ID + ",";
setAncestors(rootList, ancestors);
}
/**
* 设置祖级列表
* @param list 节点列表
* @param ancestors 祖级列表
*/
private void setAncestors(List<DeptVO> list, String ancestors) {
if (CollectionUtil.isEmpty(list)) {
return;
}
for (DeptVO vo : list) {
vo.setAncestors(ancestors);
if (CollectionUtil.isNotEmpty(vo.getChildren())) {
// 子节点列表不为空,设置子节点的祖级列表
String newAncestors = ancestors + vo.getId() + ",";
setAncestors(vo.getChildren(), newAncestors);
}
}
}
}

View File

@@ -25,19 +25,27 @@
*/
package org.springblade.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.DictBizCache;
import org.springblade.system.excel.FeeItemExcel;
import org.springblade.system.excel.FeeItemImportFailureExcel;
import org.springblade.system.mapper.FeeItemMapper;
import org.springblade.system.pojo.entity.DictBiz;
import org.springblade.system.pojo.entity.FeeItem;
import org.springblade.system.pojo.vo.FeeItemVO;
import org.springblade.system.service.IFeeItemService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
@@ -48,6 +56,7 @@ import java.util.Objects;
@Service
public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem> implements IFeeItemService {
private static final String FEE_CATEGORY_CODE = "fee_category";
private static final int STATUS_ENABLED = 1;
private static final int STATUS_DISABLED = 2;
private static final int FEE_CATEGORY_MAX_LENGTH = 50;
@@ -86,10 +95,38 @@ public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem>
return updateById(update);
}
@Override
@Transactional(rollbackFor = Exception.class)
public List<FeeItemImportFailureExcel> importFeeItem(List<FeeItemExcel> data) {
if (Func.isEmpty(data)) {
throw new ServiceException("导入数据不能为空");
}
List<FeeItemImportFailureExcel> failureList = new ArrayList<>();
for (int index = 0; index < data.size(); index++) {
FeeItemExcel excel = data.get(index);
try {
FeeItem feeItem = buildImportFeeItem(excel);
prepare(feeItem);
validate(feeItem);
save(feeItem);
} catch (Exception exception) {
String message = exception instanceof ServiceException ? exception.getMessage() : "导入失败";
failureList.add(toImportFailureExcel(excel, "" + (index + 2) + "行:" + message));
}
}
return failureList;
}
@Override
public List<FeeItemExcel> exportFeeItem(Wrapper<FeeItem> queryWrapper) {
return list(queryWrapper).stream().map(this::toExcel).toList();
}
private void prepare(FeeItem feeItem) {
feeItem.setFeeCategory(trimToEmpty(feeItem.getFeeCategory()));
feeItem.setName(trimToEmpty(feeItem.getName()));
feeItem.setEnglishName(trimToNull(feeItem.getEnglishName()));
appendFeeCategoryPrefix(feeItem);
if (Func.isEmpty(feeItem.getStatus())) {
feeItem.setStatus(STATUS_ENABLED);
}
@@ -99,6 +136,9 @@ public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem>
if (Func.isEmpty(feeItem.getFeeCategory())) {
throw new ServiceException("费用类型不能为空");
}
if (!isValidFeeCategory(feeItem.getFeeCategory())) {
throw new ServiceException("费用类型不正确");
}
if (feeItem.getFeeCategory().length() > FEE_CATEGORY_MAX_LENGTH) {
throw new ServiceException("费用类型不能超过50字");
}
@@ -126,6 +166,70 @@ public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem>
}
}
private FeeItem buildImportFeeItem(FeeItemExcel excel) {
FeeItem feeItem = new FeeItem();
feeItem.setFeeCategory(resolveFeeCategory(excel.getFeeCategory()));
feeItem.setEnglishName(trimToNull(excel.getEnglishName()));
feeItem.setName(trimToEmpty(excel.getName()));
feeItem.setStatus(STATUS_ENABLED);
return feeItem;
}
private FeeItemExcel toExcel(FeeItem feeItem) {
FeeItemExcel excel = Objects.requireNonNull(BeanUtil.copyProperties(feeItem, FeeItemExcel.class));
excel.setFeeCategory(formatFeeCategory(feeItem.getFeeCategory()));
return excel;
}
private FeeItemImportFailureExcel toImportFailureExcel(FeeItemExcel excel, String failureReason) {
FeeItemImportFailureExcel failureExcel = new FeeItemImportFailureExcel();
failureExcel.setFeeCategory(excel.getFeeCategory());
failureExcel.setEnglishName(excel.getEnglishName());
failureExcel.setName(excel.getName());
failureExcel.setFailureReason(failureReason);
return failureExcel;
}
private String resolveFeeCategory(String value) {
String category = trimToEmpty(value);
if (Func.isEmpty(category)) {
return category;
}
return feeCategoryList().stream()
.filter(dict -> Objects.equals(category, trimToEmpty(dict.getDictKey())) || Objects.equals(category, trimToEmpty(dict.getDictValue())))
.findFirst()
.map(DictBiz::getDictKey)
.orElse(category);
}
private String formatFeeCategory(String value) {
String category = trimToEmpty(value);
return feeCategoryList().stream()
.filter(dict -> Objects.equals(category, trimToEmpty(dict.getDictKey())))
.findFirst()
.map(DictBiz::getDictValue)
.orElse(category);
}
private boolean isValidFeeCategory(String value) {
String category = trimToEmpty(value);
return feeCategoryList().stream().anyMatch(dict -> Objects.equals(category, trimToEmpty(dict.getDictKey())));
}
private List<DictBiz> feeCategoryList() {
List<DictBiz> dictBizList = DictBizCache.getList(FEE_CATEGORY_CODE);
return dictBizList == null ? List.of() : dictBizList;
}
private void appendFeeCategoryPrefix(FeeItem feeItem) {
if (Func.isEmpty(feeItem.getEnglishName()) || Func.isEmpty(feeItem.getFeeCategory())) {
return;
}
if (!feeItem.getEnglishName().startsWith(feeItem.getFeeCategory())) {
feeItem.setEnglishName(feeItem.getFeeCategory() + feeItem.getEnglishName());
}
}
private String trimToEmpty(String value) {
return value == null ? "" : value.trim();
}

View File

@@ -0,0 +1,280 @@
package org.springblade.system.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.constant.CommonConstant;
import org.springblade.core.tool.utils.DigestUtil;
import org.springblade.system.cache.ParamCache;
import org.springblade.system.convert.DeptConvert;
import org.springblade.system.convert.UserConvert;
import org.springblade.system.log.ComposeLogUtil;
import org.springblade.system.pojo.entity.DataSyncRecord;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.system.pojo.entity.User;
import org.springblade.system.pojo.enums.DataSync;
import org.springblade.system.pojo.enums.DeptCategory;
import org.springblade.system.service.IDataSyncRecordService;
import org.springblade.system.service.IDeptService;
import org.springblade.system.service.IMKPushService;
import org.springblade.system.service.IUserService;
import org.springblade.system.util.DataSyncRecordUtils;
import org.springblade.thirdparty.mk.pojo.dto.MKPushDTO;
import org.springblade.thirdparty.mk.pojo.dto.MKPushOrgDeptDTO;
import org.springblade.thirdparty.mk.pojo.dto.MKPushPersonDTO;
import org.springblade.thirdparty.mk.service.IMKService;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import static org.springblade.common.constant.CommonConstant.DEFAULT_PARAM_PASSWORD;
/**
* mk 推送实现
* @author bfhuange
* @date 2024/9/24
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class MKPushServiceImpl implements IMKPushService {
/**
* 分页查询每页最大大小
*/
private static final long MAX_SIZE_PER_PAGE = 1000;
private final IDeptService deptService;
private final IUserService userService;
private final IMKService mkService;
private final DeptConvert deptConvert;
private final UserConvert userConvert;
private final IDataSyncRecordService dataSyncRecordService;
@Override
public void pushOrgAndRecord(Date oaStartTime) {
try {
ComposeLogUtil.addLog(log);
// 推送所有公司和部门并记录
this.pushAndRecord(oaStartTime, DataSyncRecordUtils::createMKOrgPush, (startTime, endTime) -> {
// 1. 推送公司
pushDept(DeptCategory.COMPANY, startTime, endTime);
// 2. 推送部门
pushDept(DeptCategory.DEPT, startTime, endTime);
});
} finally {
ComposeLogUtil.removeLastLog();
}
}
@Override
public void pushPersonAndRecord(Date oaStartTime) {
try {
ComposeLogUtil.addLog(log);
// 推送人员并记录
this.pushAndRecord(oaStartTime, DataSyncRecordUtils::createMKPersonPush, (startTime, endTime) -> {
// 分页查询函数
UnaryOperator<IPage<User>> queryUser = this.getQueryUser(startTime, endTime);
// 分页推送函数
BiConsumer<IPage<User>, String> pushConsumer = this::pushUser;
// 公共推送
pushCommon(queryUser, pushConsumer);
});
} finally {
ComposeLogUtil.removeLastLog();
}
}
/**
* 推送并记录
* @param oaStartTime
* @param recordSupplier
* @param pushHandler
*/
private void pushAndRecord(Date oaStartTime, Supplier<DataSyncRecord> recordSupplier, BiConsumer<Date, Date> pushHandler) {
// 创建记录
DataSyncRecord record = recordSupplier.get();
// 查询最新一条成功记录
DataSyncRecord lastSuccessRecord = dataSyncRecordService.getLastSuccessRecord(record);
// 获取推送数据的开始时间
Date startTime = getStartTime(lastSuccessRecord, oaStartTime);
// 推送开始时间作为截止时间,只同步这个时间段的更新的数据
Date endTime = new Date();
record.setSyncTime(endTime);
try {
pushHandler.accept(startTime, endTime);
} catch (Exception e) {
// 有异常,结果改成失败
record.setResult(DataSync.Result.FAIL.name());
throw e;
} finally {
// 新事务保存同步记录
dataSyncRecordService.saveWithNewTransaction(record);
}
}
/**
* 获取推送开始时间
* @param lastSuccessRecord 最后一条同步成功的记录
* @param oaStartTime oa同步查询开始时间
* @return
*/
private Date getStartTime(DataSyncRecord lastSuccessRecord, Date oaStartTime) {
if (lastSuccessRecord == null || lastSuccessRecord.getSyncTime() == null) {
// 最后一条推送成功记录为空,返回空推送全部
return null;
}
if (oaStartTime == null) {
// oa 同步查询开始时间为空,且最后一条推送成功记录不为空
return lastSuccessRecord.getSyncTime();
}
// 否则取2个时间较小的那个
Date lastSyncTime = lastSuccessRecord.getSyncTime();
return oaStartTime.before(lastSyncTime) ? oaStartTime : lastSyncTime;
}
/**
* 同步公司/部门
*
* @param deptCategory 类型
* @param startTime 数据更新开始时间
* @param endTime 数据更新截止时间
*/
private void pushDept(DeptCategory deptCategory, Date startTime, Date endTime) {
// 分页查询函数
UnaryOperator<IPage<Dept>> queryDept = getQueryDept(deptCategory, startTime, endTime);
// 推送函数
BiConsumer<IPage<Dept>, String> pushDept = getPushDept(deptCategory);
// 公共推送
pushCommon(queryDept, pushDept);
}
/**
* 获取查询部门的方法
*
* @param deptCategory
* @param startTime
* @param endTime
* @return
*/
private UnaryOperator<IPage<Dept>> getQueryDept(DeptCategory deptCategory, Date startTime, Date endTime) {
return page -> deptService.page(page, Wrappers.<Dept>lambdaQuery()
.eq(Dept::getDeptCategory, deptCategory.getCode())
.eq(Dept::getIsOa, CommonConstant.YES)
// 查询同步时间小于结束时间且修改时间小于结束时间的部门
.lt(Dept::getSyncTime, endTime)
.lt(Dept::getUpdateTime, endTime)
// 开始时间不为空,查询同步时间大于开始时间或修改时间大于开始时间的数据
.and(startTime != null, wrapper -> wrapper.ge(Dept::getSyncTime, startTime)
.or(w -> w.ge(Dept::getUpdateTime, startTime)))
);
}
/**
* 获取推送部门的方法
* @param deptCategory
* @return
*/
private BiConsumer<IPage<Dept>, String> getPushDept(DeptCategory deptCategory) {
return (page, batchId) -> {
List<Dept> records = page.getRecords();
if (CollectionUtil.isEmpty(records)) {
return;
}
// 当前页是否最后一页
boolean lastPage = page.getCurrent() == page.getPages();
MKPushDTO<MKPushOrgDeptDTO> param = new MKPushDTO<>();
param.setBatchId(batchId);
param.setFinish(lastPage);
List<MKPushOrgDeptDTO> list = deptConvert.entities2dtos(records);
param.setMessage(list);
// 推送公司
boolean result = mkService.pushOrgDept(param);
ComposeLogUtil.getLastLog().info("mk推送{} 批次号:{} 当前页:{} 当前页数量:{} 结果:{}", deptCategory.getName(), batchId, page.getCurrent(), page.getSize(), result);
};
}
/**
* 获取查询人员的方法
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return
*/
private UnaryOperator<IPage<User>> getQueryUser(Date startTime, Date endTime) {
return page -> userService.page(page, Wrappers.<User>lambdaQuery()
.eq(User::getIsOa, CommonConstant.YES)
// 查询同步时间小于结束时间且修改时间小于结束时间的用户
.lt(User::getSyncTime, endTime)
.lt(User::getUpdateTime, endTime)
// 开始时间不为空,查询同步时间大于更新时间或修改时间大于更新时间的数据
.and(startTime != null, wrapper -> wrapper.ge(User::getSyncTime, startTime)
.or(w -> w.ge(User::getUpdateTime, startTime)))
);
}
/**
* 推送人员
* @param page
* @param batchId
*/
private void pushUser(IPage<User> page, String batchId) {
List<User> records = page.getRecords();
if (CollectionUtil.isEmpty(records)) {
return;
}
// 当前页是否最后一页
boolean lastPage = page.getCurrent() == page.getPages();
MKPushDTO<MKPushPersonDTO> param = new MKPushDTO<>();
param.setBatchId(batchId);
param.setFinish(lastPage);
// 默认密码及参数转换
String defaultPassword = ParamCache.getValue(DEFAULT_PARAM_PASSWORD);
// 解密手机号
records.forEach(userService::decryptPhone);
List<MKPushPersonDTO> list = userConvert.entities2dtos(records, DigestUtil.md5Hex(defaultPassword));
param.setMessage(list);
// 推送人员
boolean result = mkService.pushPerson(param);
ComposeLogUtil.getLastLog().info("mk推送人员 批次号:{} 当前页:{} 当前页数量:{} 结果:{}", batchId, page.getCurrent(), page.getSize(), result);
if (result) {
// 推送成功,修改用户推送状态
List<Long> updateIds = records.stream()
.map(User::getId)
.toList();
userService.lambdaUpdate()
// 设置是否推送mk为是
.set(User::getIsPushMk, CommonConstant.YES)
.in(User::getId, updateIds)
// 筛选未推送mk的
.eq(User::getIsPushMk, CommonConstant.NO)
.update();
}
}
/**
* 公共推送
* @param queryOperator 分页查询
* @param pushConsumer 数据推送
* @param <T>
*/
private <T> void pushCommon(UnaryOperator<IPage<T>> queryOperator, BiConsumer<IPage<T>, String> pushConsumer) {
IPage<T> page = new Page<>(1, MAX_SIZE_PER_PAGE);
String batchId = UUID.fastUUID().toString(true);
do {
// 分页查询数据
page = queryOperator.apply(page);
// 推送数据
pushConsumer.accept(page, batchId);
page.setCurrent(page.getCurrent() + 1);
} while (page.getCurrent() <= page.getPages());
}
}

View File

@@ -0,0 +1,649 @@
package org.springblade.system.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springblade.common.constant.DictTypeEnum;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.DigestUtil;
import org.springblade.system.cache.DictCache;
import org.springblade.system.cache.ParamCache;
import org.springblade.system.convert.DeptConvert;
import org.springblade.system.convert.UserConvert;
import org.springblade.system.log.ComposeLogUtil;
import org.springblade.system.pojo.entity.*;
import org.springblade.system.pojo.enums.DataSync;
import org.springblade.system.pojo.enums.DeptCategory;
import org.springblade.system.pojo.vo.UserDeptIdsVO;
import org.springblade.system.service.*;
import org.springblade.system.util.DataSyncRecordUtils;
import org.springblade.thirdparty.oa.constant.OAConstant;
import org.springblade.thirdparty.oa.constant.OAConvertConstant;
import org.springblade.thirdparty.oa.feign.IOAClient;
import org.springblade.thirdparty.oa.pojo.response.OACompanyResponse;
import org.springblade.thirdparty.oa.pojo.response.OADepartmentResponse;
import org.springblade.thirdparty.oa.pojo.response.OAPersonResponse;
import org.springblade.thirdparty.oa.pojo.search.OACompanySearch;
import org.springblade.thirdparty.oa.pojo.search.OADepartmentSearch;
import org.springblade.thirdparty.oa.pojo.search.OAPersonSearch;
import org.springblade.thirdparty.oa.pojo.search.OASearch;
import org.springblade.thirdparty.oa.util.OAUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import static org.springblade.common.constant.CommonConstant.*;
import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;
import static org.springblade.core.cache.constant.CacheConstant.USER_CACHE;
/**
* @author bfhuange
* @date 2024/8/23
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class OASyncServiceImpl implements IOASyncService {
/**
* 用户表部门id最大长度
*/
private static final int MAX_DEPT_ID_LENGTH = 2000;
private final IOAClient oaClient;
private final DeptConvert deptConvert;
private final IDeptService deptService;
private final IUserService userService;
private final UserConvert userConvert;
private final IUserDeptService userDeptService;
private final IRoleService roleService;
private final IMKPushService mkPushService;
private final IDataSyncRecordService dataSyncRecordService;
@Transactional(rollbackFor = Exception.class)
@Override
public void syncCompanyAndDeptAndPushMK(boolean syncAll) {
try {
ComposeLogUtil.addLog(log);
// 1. 同步公司和部门并记录
Date startTime = this.syncAndRecord(DataSyncRecordUtils::createOAOrgFetch, queryStartTime -> {
// 1. 同步公司
this.syncCompany(queryStartTime);
// 2. 同步部门
this.syncDept(queryStartTime);
// 2. 更新部门祖级列表
deptService.updateAncestors(queryStartTime);
}, syncAll);
// 2. 推送mk
mkPushService.pushOrgAndRecord(startTime);
} finally {
ComposeLogUtil.removeLastLog();
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void syncPersonAndPushMK(boolean syncAll) {
try {
ComposeLogUtil.addLog(log);
// 1. 同步人员并记录
Date startTime = this.syncAndRecord(DataSyncRecordUtils::createOAPersonFetch, this::syncPerson, syncAll);
// 2. 推送mk
mkPushService.pushPersonAndRecord(startTime);
} finally {
ComposeLogUtil.removeLastLog();
}
}
/**
* 同步并记录
*
* @param recordSupplier 同步记录提供
* @param syncHandler 同步处理器
* @param syncAll 是否同步所有
* @return
*/
private Date syncAndRecord(Supplier<DataSyncRecord> recordSupplier, Consumer<Date> syncHandler, boolean syncAll) {
// 创建记录
DataSyncRecord record = recordSupplier.get();
// 查询最新一条成功记录
DataSyncRecord lastSuccessRecord = syncAll ? null : dataSyncRecordService.getLastSuccessRecord(record);
// 同步所有开始时间设置为null否则最新成功记录的同步时间作为查询开始时间第一次为null同步所有
Date startTime = syncAll ? null : Optional.ofNullable(lastSuccessRecord)
.map(DataSyncRecord::getSyncTime)
.orElse(null);
// 同步时间设置早一些,可能下次增量同步会多同步一些数据
// 设置晚了,增量同步可能会漏同步数据
record.setSyncTime(new Date());
try {
// 1. 同步处理
syncHandler.accept(startTime);
} catch (Exception e) {
// 有异常,结果改成失败
record.setResult(DataSync.Result.FAIL.name());
throw e;
} finally {
// 新事务保存同步记录
dataSyncRecordService.saveWithNewTransaction(record);
}
return startTime;
}
/**
* 同步公司
* @param startTime 查询开始时间
*/
private void syncCompany(Date startTime) {
// 未处理的数据
List<Dept> notHandleList = new ArrayList<>();
// 1. 设置查询参数
OACompanySearch companySearch = new OACompanySearch();
companySearch.setCurPage(1);
if (startTime != null) {
// 开始时间不为空,设置修改时间参数
companySearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
}
// 2. 分页查询并处理数据
OAUtils.pageSyncHandler(companySearch, param -> oaClient.queryCompanyPage(new OASearch<>(param)), response -> {
ComposeLogUtil.getLastLog().error("调用OA接口查询公司信息失败 {}", JSON.toJSONString(response));
return new ServiceException("调用OA接口查询公司信息失败");
}, 10000, ComposeLogUtil.getLastLog()::info).accept(list -> {
// 处理数据
List<Dept> deptList = handleCompany(list);
notHandleList.addAll(deptList);
});
// 3. 未处理的数据
if (CollectionUtil.isNotEmpty(notHandleList)) {
ComposeLogUtil.getLastLog().warn("同步公司,还有未处理的数据:{}", JSON.toJSONString(notHandleList));
}
// 清除部门缓存
CacheUtil.clear(SYS_CACHE);
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
}
/**
* 同步部门
* @param startTime 查询开始时间
*/
private void syncDept(Date startTime) {
String subCompanyIds = getSubCompanyIds();
if (StringUtils.isEmpty(subCompanyIds)) {
ComposeLogUtil.getLastLog().warn("未查询到子公司查询参数");
return;
}
// 未处理的数据
List<Dept> notHandleList = new ArrayList<>();
// 1. 设置查询参数
OADepartmentSearch departmentSearch = new OADepartmentSearch();
departmentSearch.setCurPage(1);
departmentSearch.setSubcompanyid1(subCompanyIds);
if (startTime != null) {
// 开始时间不为空,设置修改时间参数
departmentSearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
}
// 2. 分页查询并处理数据
OAUtils.pageSyncHandler(departmentSearch, param -> oaClient.queryDepartmentPage(new OASearch<>(param)), response -> {
ComposeLogUtil.getLastLog().error("调用OA接口查询部门信息失败 {}", JSON.toJSONString(response));
return new ServiceException("调用OA接口查询部门信息失败");
}, 10000, ComposeLogUtil.getLastLog()::info).accept(list -> {
// 处理数据
List<Dept> deptList = handleDept(list);
notHandleList.addAll(deptList);
});
// 3. 未处理的数据
if (CollectionUtil.isNotEmpty(notHandleList)) {
ComposeLogUtil.getLastLog().warn("同步部门,还有未处理的数据:{}", JSON.toJSONString(notHandleList));
}
// 清除部门缓存
CacheUtil.clear(SYS_CACHE);
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
}
/**
* 同步人员
* @param startTime 查询开始时间
*/
private void syncPerson(Date startTime) {
String subCompanyIds = getSubCompanyIds();
if (StringUtils.isEmpty(subCompanyIds)) {
ComposeLogUtil.getLastLog().warn("未查询到子公司查询参数");
return;
}
// 1. 设置查询参数
OAPersonSearch personSearch = new OAPersonSearch();
personSearch.setCurPage(1);
personSearch.setSubcompanyid1(subCompanyIds);
if (startTime != null) {
// 开始时间不为空,设置修改时间参数
personSearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
}
// 2. 分页查询并处理数据
OAUtils.pageSyncHandler(personSearch, param -> oaClient.queryPersonPage(new OASearch<>(param)), response -> {
ComposeLogUtil.getLastLog().error("调用OA接口查询人员信息失败 {}", JSON.toJSONString(response));
return new ServiceException("调用OA接口查询人员信息失败");
}, 10000, ComposeLogUtil.getLastLog()::info).accept(this::handlePerson);
// 清除用户缓存
CacheUtil.clear(USER_CACHE);
}
/**
* 处理oa人员
* @param oaPersons
*/
private void handlePerson(List<OAPersonResponse> oaPersons) {
if (CollectionUtil.isEmpty(oaPersons)) {
// 数据为空,直接返回
return;
}
// 根公司id
String rootCompanyId = getRootCompanyId();
if (rootCompanyId == null) {
return;
}
// 根公司下要同步的部门id
Set<String> rootCompanyDeptIds = getRootCompanyDeptIds(rootCompanyId);
oaPersons = oaPersons.stream()
// 公司不是根公司,或者部门在根公司下要同步的部门列表中
.filter(oaPerson -> !rootCompanyId.equals(oaPerson.getSubcompanyid1()) || rootCompanyDeptIds.contains(oaPerson.getDepartmentid()))
.toList();
// 根据手机号转成set
TreeSet<OAPersonResponse> oaPersonSet = CollectionUtil.toTreeSet(oaPersons, Comparator.comparing(OAPersonResponse::getMobile));
// 默认密码
String defaultPassword = DigestUtil.encrypt(ParamCache.getValue(DEFAULT_PARAM_PASSWORD));
// 转换数据
List<User> users = oaPersonSet.stream()
// 只需要手机不为空的
.filter(person -> StringUtils.isNotBlank(person.getMobile()))
.map(person -> userConvert.person2user(person, defaultPassword))
.toList();
List<String> phones = users.stream()
.map(User::getPhone)
.filter(StringUtils::isNotBlank)
.distinct()
.toList();
// 查询所有用户
Map<String, Long> userMap = userService.list(Wrappers.<User>lambdaQuery()
//.eq(User::getIsDeleted, BladeConstant.DB_NOT_DELETED)
.in(User::getPhone, phones)
).stream()
// 解密手机号
.peek(userService::decryptPhone)
.collect(Collectors.toMap(User::getPhone, User::getId, (a, b) -> b));
// 数据库存在的所有用户id
Set<Long> existsUserIds = new HashSet<>(userMap.values());
users.forEach(user -> {
if (userMap.containsKey(user.getPhone())) {
// 根据手机号获取对应的用户id
user.setId(userMap.get(user.getPhone()));
// 清空密码,不修改密码
user.setPassword(null);
} else {
// 没有就生成一个id
user.setId(DefaultIdentifierGenerator.getInstance().nextId(null));
userService.encryptPhone(user);
userMap.put(user.getPhone(), user.getId());
}
});
// 不存在的新增
List<User> addUsers = users.stream()
.filter(user -> !existsUserIds.contains(user.getId()))
.toList();
if (CollectionUtil.isNotEmpty(addUsers)) {
ComposeLogUtil.getLastLog().info("批量新增用户:{}", addUsers.size());
userService.saveBatch(addUsers);
}
// 存在的修改
List<User> updateUsers = users.stream()
.filter(user -> existsUserIds.contains(user.getId()))
.toList();
if (CollectionUtil.isNotEmpty(updateUsers)) {
ComposeLogUtil.getLastLog().info("批量修改用户:{}", updateUsers.size());
userService.updateBatchById(updateUsers);
}
// 没有手机号的数据 = 手机号为空的数量
long noPhoneNum = oaPersons.stream()
.map(OAPersonResponse::getMobile)
.filter(StringUtils::isBlank)
.count();
ComposeLogUtil.getLastLog().info("没有手机号的数据:{}", noPhoneNum);
Map<String, Long> userDeptMap = userDeptService.list(Wrappers.<UserDept>lambdaQuery()
.in(UserDept::getUserId, userMap.values())
).stream()
.collect(Collectors.toMap(this::getUserDeptKey, UserDept::getId, (a, b) -> b));
// 数据库存在的所有用户部门id
Set<Long> existsUserDeptIds = new HashSet<>(userDeptMap.values());
List<UserDept> userDeptList = oaPersons.stream()
// 只要包含用户手机号的
.filter(oaPerson -> userMap.containsKey(oaPerson.getMobile()))
.map(oaPerson -> userConvert.person2userDept(oaPerson, userMap))
.toList();
userDeptList.forEach(userDept -> {
String userDeptKey = getUserDeptKey(userDept);
if (userDeptMap.containsKey(userDeptKey)) {
// 根据key获取用户部门id
userDept.setId(userDeptMap.get(userDeptKey));
} else {
// 没有就生成一个id
userDept.setId(DefaultIdentifierGenerator.getInstance().nextId(null));
}
});
// 不存在的新增
List<UserDept> addList = userDeptList.stream()
.filter(userDept -> !existsUserDeptIds.contains(userDept.getId()))
.toList();
if (CollectionUtil.isNotEmpty(addList)) {
ComposeLogUtil.getLastLog().info("批量新增用户部门:{}", addList.size());
userDeptService.saveBatch(addList);
}
// 存在的修改
List<UserDept> updateList = userDeptList.stream()
.filter(userDept -> existsUserDeptIds.contains(userDept.getId()))
.toList();
if (CollectionUtil.isNotEmpty(updateList)) {
ComposeLogUtil.getLastLog().info("批量修改用户部门:{}", updateList.size());
userDeptService.updateBatchById(updateList);
}
// 回写部门id到用户表
Collection<Long> userIds = userMap.values();
List<UserDeptIdsVO> list = userDeptService.queryUserDeptIds(userIds);
// 查询没有角色的用户id
Set<Long> noRoleUserIds = userService.list(Wrappers.<User>lambdaQuery()
.in(User::getId, userIds)
.isNull(User::getRoleId)
).stream()
.map(User::getId)
.collect(Collectors.toSet());
// 获取默认角色id
String defaultRoleId = getDefaultRoleId();
List<User> updateUserParams = list.stream()
// 过滤掉空部门id及长度超长的
.filter(userDeptIds -> StringUtils.isNotBlank(userDeptIds.getDeptIds()) && userDeptIds.getDeptIds().length() <= MAX_DEPT_ID_LENGTH)
.map(userDeptIds -> {
User user = new User();
user.setId(userDeptIds.getUserId());
user.setDeptId(userDeptIds.getDeptIds());
user.setDeptCodes(userDeptIds.getDeptCodes());
if (noRoleUserIds.contains(userDeptIds.getUserId())) {
user.setRoleId(defaultRoleId);
}
return user;
}).toList();
userService.updateBatchById(updateUserParams);
}
/**
* 处理oa公司
* @param oaCompanies
* @return 未处理的数据
*/
private List<Dept> handleCompany(List<OACompanyResponse> oaCompanies) {
if (CollectionUtil.isEmpty(oaCompanies)) {
// 数据为空,直接返回
return Collections.emptyList();
}
// 获取需要的公司名称
Set<String> companyNames = getCompanyNames();
// 转换数据
List<Dept> allParam = oaCompanies.stream()
// 筛选只要需要的公司
.filter(company -> companyNames.contains(company.getSubcompanyname()))
.map(deptConvert::company2dept)
.toList();
// 查询数据库的部门转换成map
Map<String, Long> deptMap = getAllCompanyDeptMap();
// 处理部门
return handleDept(allParam, deptMap, DeptCategory.COMPANY);
}
/**
* 处理oa部门
* @param oaDepts
* @return 未处理的数据
*/
private List<Dept> handleDept(List<OADepartmentResponse> oaDepts) {
if (CollectionUtil.isEmpty(oaDepts)) {
// 数据为空,直接返回
return Collections.emptyList();
}
// 查询所有公司的编码和id的map
Map<String, Long> companyDeptMap = getAllCompanyDeptMap();
// 根公司id
String rootCompanyId = getRootCompanyId();
if (rootCompanyId == null) {
return Collections.emptyList();
}
// 根公司下要同步的部门名称
Set<String> rootCompanyDeptNames = getRootCompanyDeptNames();
// 转换数据
List<Dept> allParam = oaDepts.stream()
// 部门的公司不是根公司,或者部门名称在根公司下需要同步的部门列表中,且是根部门
.filter(oaDept -> !rootCompanyId.equals(oaDept.getSubcompanyid1()) || (OAConstant.ROOT_COMPANY_ID.equals(oaDept.getSupdepid()) && rootCompanyDeptNames.contains(oaDept.getDepartmentname())))
.map(dept -> deptConvert.dept2dept(dept, companyDeptMap))
.toList();
if (CollectionUtil.isEmpty(allParam)) {
return Collections.emptyList();
}
Set<String> deptCodes = allParam.stream()
.map(Dept::getDeptCode)
.collect(Collectors.toSet());
// 查询数据库的部门转换成map
Map<String, Long> deptMap = deptService.list(Wrappers.<Dept>lambdaQuery()
.eq(Dept::getDeptCategory, DeptCategory.DEPT.getCode())
.in(Dept::getDeptCode, deptCodes)
).stream()
.filter(dept -> StringUtils.isNotEmpty(dept.getDeptCode()))
.collect(Collectors.toMap(Dept::getDeptCode, Dept::getId, (a, b) -> b));
// 处理部门
return this.handleDept(allParam, deptMap, DeptCategory.DEPT);
}
/**
* 处理部门
* @param allParam 待处理的部门
* @param deptMap 数据库所有部门编码和id map
* @return 未处理的数据
*/
private List<Dept> handleDept(List<Dept> allParam, Map<String, Long> deptMap, DeptCategory deptCategory) {
// 数据库存在的所有部门id
Set<Long> existsDeptIds = new HashSet<>(deptMap.values());
allParam.forEach(dept -> {
if (deptMap.containsKey(dept.getDeptCode())) {
// 根据部门编码获取对应的部门id
dept.setId(deptMap.get(dept.getDeptCode()));
} else {
// 没有就生成一个id
dept.setId(DefaultIdentifierGenerator.getInstance().nextId(null));
deptMap.put(dept.getDeptCode(), dept.getId());
}
});
// 重新把没有父id的数据设置一下父id
allParam.stream()
.filter(dept -> dept.getParentId() == null && deptMap.containsKey(dept.getDeptCode()))
.forEach(dept -> dept.setParentId(deptMap.get(dept.getParentCode())));
// 父id不为空保存数据
List<Dept> list = allParam.stream()
.filter(dept -> dept.getParentId() != null)
.toList();
// 不存在的新增
List<Dept> addList = list.stream()
.filter(dept -> !existsDeptIds.contains(dept.getId()))
.toList();
if (CollectionUtil.isNotEmpty(addList)) {
ComposeLogUtil.getLastLog().info("批量新增{}{}", deptCategory.getName(), addList.size());
deptService.saveBatch(addList);
}
// 存在的修改
List<Dept> updateList = list.stream()
.filter(dept -> existsDeptIds.contains(dept.getId()))
.toList();
if (CollectionUtil.isNotEmpty(updateList)) {
ComposeLogUtil.getLastLog().info("批量修改{}{}", deptCategory.getName(), updateList.size());
deptService.updateBatchById(updateList);
}
// 返回未处理的
return allParam.stream()
.filter(dept -> dept.getParentId() == null)
.toList();
}
/**
* 获取oa查询参数子公司id参数
* @return
*/
private String getSubCompanyIds() {
Set<String> companyNames = this.getCompanyNames();
if (companyNames.isEmpty()) {
return null;
}
return deptService.list(Wrappers.<Dept>lambdaQuery()
// 根据名称查询公司
.in(Dept::getDeptName, companyNames)
.eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode())
).stream()
// 只要部门编码不为空且包含了oa公司前缀的
.filter(dept -> StringUtils.isNotBlank(dept.getDeptCode()) && dept.getDeptCode().contains(OAConvertConstant.COMPANY_OA_PREFIX))
// 去掉oa公司前缀
.map(dept -> dept.getDeptCode().replace(OAConvertConstant.COMPANY_OA_PREFIX, ""))
.collect(Collectors.joining(","));
}
/**
* 获取默认角色id
*/
private String getDefaultRoleId() {
// 从参数管理查询,没有取默认值,作为角色别名,查询角色
String defaultRole = ParamCache.getValue(DEFAULT_PARAM_ROLE);
if (defaultRole == null) {
defaultRole = DEFAULT_ROLE;
}
List<Role> roleList = roleService.list(Wrappers.<Role>lambdaQuery()
.eq(Role::getRoleAlias, defaultRole)
);
if (CollectionUtil.isEmpty(roleList)) {
return null;
}
return roleList.get(0).getId().toString();
}
/**
* 获取所有公司的编码和id的map
* @return
*/
private Map<String, Long> getAllCompanyDeptMap() {
return deptService.list(Wrappers.<Dept>lambdaQuery()
.eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode())
).stream()
.filter(dept -> StringUtils.isNotEmpty(dept.getDeptCode()))
.collect(Collectors.toMap(Dept::getDeptCode, Dept::getId, (a, b) -> b));
}
/**
* 获取需要的公司id
* @return
*/
private Set<String> getCompanyNames() {
List<Dict> list = DictCache.getList(DictTypeEnum.SYNC_COMPANY_NAME.getType());
if (CollectionUtil.isEmpty(list)) {
ComposeLogUtil.getLastLog().warn("未查询到要同步的公司名称");
return Collections.emptySet();
}
return list.stream()
.filter(Objects::nonNull)
.map(Dict::getDictValue)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
}
/**
* 获取根公司下需要同步的部门名称
* @return
*/
private Set<String> getRootCompanyDeptNames() {
List<Dict> list = DictCache.getList(DictTypeEnum.SYNC_ROOT_COMPANY_DEPT_NAME.getType());
if (CollectionUtil.isEmpty(list)) {
ComposeLogUtil.getLastLog().warn("未查询到要同步的根公司下的部门名称");
return Collections.emptySet();
}
return list.stream()
.filter(Objects::nonNull)
.map(Dict::getDictValue)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
}
/**
* 获取根公司下需要同步的部门id
* @param rootCompanyId
* @return
*/
private Set<String> getRootCompanyDeptIds(String rootCompanyId) {
Set<String> rootCompanyDeptNames = this.getRootCompanyDeptNames();
if (CollectionUtil.isEmpty(rootCompanyDeptNames)) {
return Collections.emptySet();
}
// 查询父部门编码是根公司的,部门类型的,根公司下部门名称的部门
return deptService.list(Wrappers.<Dept>lambdaQuery()
.in(Dept::getDeptName, rootCompanyDeptNames)
.eq(Dept::getDeptCategory, DeptCategory.DEPT.getCode())
.eq(Dept::getParentCode, OAConvertConstant.COMPANY_OA_PREFIX + rootCompanyId)
).stream()
.filter(dept -> StringUtils.isNotBlank(dept.getDeptCode()))
.map(dept -> dept.getDeptCode().replace(OAConvertConstant.DEPARTMENT_OA_PREFIX, ""))
.collect(Collectors.toSet());
}
/**
* 根公司id
* @return
*/
private String getRootCompanyId() {
String companyName = DictCache.getValue(DictTypeEnum.SYNC_COMPANY_NAME.getType(), DICT_KEY_SYNC_ROOT_COMPANY_NAME);
if (StringUtils.isBlank(companyName)) {
ComposeLogUtil.getLastLog().warn("未查询到配置的根公司名称");
return null;
}
Dept dept = deptService.getOne(Wrappers.<Dept>lambdaQuery()
.eq(Dept::getDeptName, companyName)
.eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode())
.last("limit 1")
);
if (dept == null) {
ComposeLogUtil.getLastLog().warn("未查询到根公司 名称:{}", companyName);
return null;
}
if (StringUtils.isBlank(dept.getDeptCode())) {
ComposeLogUtil.getLastLog().warn("根公司编码为空:{}", companyName);
return null;
}
// 部门编码去掉前缀就是oa的id
return dept.getDeptCode().replace(OAConvertConstant.COMPANY_OA_PREFIX, "");
}
/**
* 获取用户部门唯一标识用户id+公司编码+部门编码
* @param userDept
* @return
*/
private String getUserDeptKey(UserDept userDept) {
if (userDept == null) {
return null;
}
return userDept.getUserId() + userDept.getCompanyCode() + userDept.getDeptCode();
}
}

View File

@@ -26,12 +26,18 @@
package org.springblade.system.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springblade.system.pojo.entity.UserDept;
import org.springblade.system.mapper.UserDeptMapper;
import org.springblade.system.pojo.vo.UserDeptIdsVO;
import org.springblade.system.service.IUserDeptService;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* 服务实现类
*
@@ -40,4 +46,12 @@ import org.springframework.stereotype.Service;
@Service
public class UserDeptServiceImpl extends ServiceImpl<UserDeptMapper, UserDept> implements IUserDeptService {
@Override
public List<UserDeptIdsVO> queryUserDeptIds(Collection<Long> userIds) {
if (CollectionUtil.isEmpty(userIds)) {
return Collections.emptyList();
}
return baseMapper.queryUserDeptIds(userIds);
}
}

View File

@@ -32,6 +32,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import org.bouncycastle.util.encoders.Hex;
import org.springblade.common.constant.ParamConstant;
import org.springblade.common.constant.TenantConstant;
import org.springblade.core.cache.utils.CacheUtil;
@@ -40,6 +41,7 @@ import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.mp.enums.StatusType;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.oauth2.props.OAuth2Properties;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tenant.BladeTenantProperties;
import org.springblade.core.tenant.TenantGuard;
@@ -54,6 +56,7 @@ import org.springblade.system.cache.SysCache;
import org.springblade.system.cache.UserCache;
import org.springblade.system.excel.UserExcel;
import org.springblade.system.mapper.UserMapper;
import org.springblade.system.pojo.context.IPhone;
import org.springblade.system.pojo.entity.*;
import org.springblade.system.pojo.enums.DictEnum;
import org.springblade.system.pojo.enums.UserType;
@@ -96,6 +99,9 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
private final IRoleService roleService;
private final BladeTenantProperties tenantProperties;
private final OAuth2Properties properties;
@Override
public User getDetail(User user) {
QueryWrapper<User> queryWrapper = Condition.getQueryWrapper(user);
@@ -774,6 +780,27 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
}
}
@Override
public void decryptPhone(IPhone user) {
// if (user == null) {
// return;
// }
// if (user.getPhone() != null && user.getPhone().length() > 20) {
// user.setPhone(SM2Util.decrypt(user.getPhone(), properties.getPublicKey(), properties.getPrivateKey()));
// }
}
@Override
public void encryptPhone(IPhone user) {
// if (user == null) {
// return;
// }
// if(StringUtil.isNotBlank(user.getPhone())) {
// user.setPhone(Hex.toHexString(SM2Util.encrypt(user.getPhone(),properties.getPublicKey())));
// }
}
private String randomPassword() {
StringBuilder password = new StringBuilder(RANDOM_PASSWORD_LENGTH);
for (int index = 0; index < RANDOM_PASSWORD_LENGTH; index++) {

View File

@@ -27,6 +27,9 @@ package org.springblade.system.wrapper;
import org.springblade.core.mp.support.BaseEntityWrapper;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.SysCache;
import org.springblade.system.cache.UserCache;
import org.springblade.system.pojo.entity.FeeItem;
import org.springblade.system.pojo.vo.FeeItemVO;
@@ -45,7 +48,12 @@ public class FeeItemWrapper extends BaseEntityWrapper<FeeItem, FeeItemVO> {
@Override
public FeeItemVO entityVO(FeeItem feeItem) {
return Objects.requireNonNull(BeanUtil.copyProperties(feeItem, FeeItemVO.class));
FeeItemVO feeItemVO = Objects.requireNonNull(BeanUtil.copyProperties(feeItem, FeeItemVO.class));
feeItemVO.setCreateDeptName(
Func.isEmpty(feeItem.getCreateDept()) ? "" : SysCache.getDeptName(feeItem.getCreateDept())
);
feeItemVO.setUpdateUserName(UserCache.getUserRealName(feeItem.getUpdateUser()));
return feeItemVO;
}
}