Merge branch 'dev' into 'test'

Dev

See merge request jygk/dsc!12
This commit is contained in:
wencai he
2025-12-22 03:05:45 +00:00
20 changed files with 1037 additions and 44 deletions

View File

@@ -1,56 +1,101 @@
package com.zt.plat.framework.databus.client.handler.userdept;
import com.zt.plat.module.databus.api.data.DatabusUserDeptData;
import com.zt.plat.module.system.api.userdept.UserDeptApi;
import com.zt.plat.module.system.api.userdept.dto.UserDeptSaveReqDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
/**
* 用户-部门关系同步服务实现
* 用户-部门关系同步服务实现(通过 Feign API 调用远程服务)
* <p>
* 使用条件:
* 1. zt.databus.sync.client.enabled=true
* 2. 系统中存在 UserDeptApi 接口Feign 客户端)
* <p>
* 注意:由于用户-部门关系通常集成在用户管理中,此实现为占位符。
* 分公司可以根据实际情况:
* 1. 自定义实现此接口,直接操作本地数据库
* 2. 或者通过用户管理 API 间接处理关联关系
* 如果分公司需要自定义实现,可以创建自己的 UserDeptSyncService Bean
* 此默认实现会自动失效(@ConditionalOnMissingBean
*
* @author ZT
*/
@Slf4j
@Service
@ConditionalOnProperty(prefix = "zt.databus.sync.client", name = "enabled", havingValue = "true")
@ConditionalOnClass(name = "com.zt.plat.module.system.api.userdept.UserDeptApi")
public class UserDeptSyncServiceImpl implements UserDeptSyncService {
@Autowired(required = false)
private UserDeptApi userDeptApi; // Feign 远程调用接口
@Override
public void create(DatabusUserDeptData data) {
log.info("[UserDeptSync] 收到创建用户-部门关系请求, userId={}, deptId={}",
data.getUserId(), data.getDeptId());
log.warn("[UserDeptSync] 用户-部门关系同步服务需要分公司自定义实现,当前为占位符实现");
// TODO: 分公司需要实现此方法,通过本地 API 或直接数据库操作完成同步
if (userDeptApi == null) {
log.warn("[UserDeptSync] UserDeptApi未注入跳过创建用户-部门关系, userId={}", data.getUserId());
return;
}
UserDeptSaveReqDTO dto = buildUserDeptDTO(data);
userDeptApi.createUserDept(dto).checkError();
log.info("[UserDeptSync] 用户-部门关系创建成功, userId={}, deptId={}", data.getUserId(), data.getDeptId());
}
@Override
public void update(DatabusUserDeptData data) {
log.info("[UserDeptSync] 收到更新用户-部门关系请求, userId={}, deptId={}",
data.getUserId(), data.getDeptId());
log.warn("[UserDeptSync] 用户-部门关系同步服务需要分公司自定义实现,当前为占位符实现");
// TODO: 分公司需要实现此方法
if (userDeptApi == null) {
log.warn("[UserDeptSync] UserDeptApi未注入跳过更新用户-部门关系, userId={}", data.getUserId());
return;
}
UserDeptSaveReqDTO dto = buildUserDeptDTO(data);
userDeptApi.updateUserDept(dto).checkError();
log.info("[UserDeptSync] 用户-部门关系更新成功, userId={}, deptId={}", data.getUserId(), data.getDeptId());
}
@Override
public void delete(Long id) {
log.info("[UserDeptSync] 收到删除用户-部门关系请求, id={}", id);
log.warn("[UserDeptSync] 用户-部门关系同步服务需要分公司自定义实现,当前为占位符实现");
// TODO: 分公司需要实现此方法
if (userDeptApi == null) {
log.warn("[UserDeptSync] UserDeptApi未注入跳过删除用户-部门关系, id={}", id);
return;
}
userDeptApi.deleteUserDept(id).checkError();
log.info("[UserDeptSync] 用户-部门关系删除成功, id={}", id);
}
@Override
public void fullSync(DatabusUserDeptData data) {
log.info("[UserDeptSync] 收到全量同步用户-部门关系请求, userId={}, deptId={}",
data.getUserId(), data.getDeptId());
log.warn("[UserDeptSync] 用户-部门关系同步服务需要分公司自定义实现,当前为占位符实现");
// TODO: 分公司需要实现此方法,逻辑:存在则更新,不存在则插入
if (userDeptApi == null) {
log.warn("[UserDeptSync] UserDeptApi未注入跳过全量同步用户-部门关系, userId={}", data.getUserId());
return;
}
UserDeptSaveReqDTO dto = buildUserDeptDTO(data);
try {
// 尝试获取,存在则更新,不存在则创建
var existing = userDeptApi.getUserDept(dto.getId());
if (existing.isSuccess() && existing.getData() != null) {
userDeptApi.updateUserDept(dto).checkError();
log.info("[UserDeptSync] 用户-部门关系全量同步-更新成功, id={}", dto.getId());
} else {
userDeptApi.createUserDept(dto).checkError();
log.info("[UserDeptSync] 用户-部门关系全量同步-创建成功, id={}", dto.getId());
}
} catch (Exception e) {
// 获取失败,尝试创建
log.warn("[UserDeptSync] 用户-部门关系获取失败,尝试创建, id={}", dto.getId());
userDeptApi.createUserDept(dto).checkError();
log.info("[UserDeptSync] 用户-部门关系全量同步-创建成功, id={}", dto.getId());
}
}
/**
* 构建用户部门关系 DTO用于 Feign 调用)
*/
private UserDeptSaveReqDTO buildUserDeptDTO(DatabusUserDeptData data) {
UserDeptSaveReqDTO dto = new UserDeptSaveReqDTO();
dto.setId(data.getId());
dto.setUserId(data.getUserId());
dto.setDeptId(data.getDeptId());
dto.setRemark(data.getRemark());
return dto;
}
}

View File

@@ -1,56 +1,100 @@
package com.zt.plat.framework.databus.client.handler.userpost;
import com.zt.plat.module.databus.api.data.DatabusUserPostData;
import com.zt.plat.module.system.api.userpost.UserPostApi;
import com.zt.plat.module.system.api.userpost.dto.UserPostSaveReqDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
/**
* 用户-岗位关系同步服务实现
* 用户-岗位关系同步服务实现(通过 Feign API 调用远程服务)
* <p>
* 使用条件:
* 1. zt.databus.sync.client.enabled=true
* 2. 系统中存在 UserPostApi 接口Feign 客户端)
* <p>
* 注意:由于用户-岗位关系通常集成在用户管理中,此实现为占位符。
* 分公司可以根据实际情况:
* 1. 自定义实现此接口,直接操作本地数据库
* 2. 或者通过用户管理 API 间接处理关联关系
* 如果分公司需要自定义实现,可以创建自己的 UserPostSyncService Bean
* 此默认实现会自动失效(@ConditionalOnMissingBean
*
* @author ZT
*/
@Slf4j
@Service
@ConditionalOnProperty(prefix = "zt.databus.sync.client", name = "enabled", havingValue = "true")
@ConditionalOnClass(name = "com.zt.plat.module.system.api.userpost.UserPostApi")
public class UserPostSyncServiceImpl implements UserPostSyncService {
@Autowired(required = false)
private UserPostApi userPostApi; // Feign 远程调用接口
@Override
public void create(DatabusUserPostData data) {
log.info("[UserPostSync] 收到创建用户-岗位关系请求, userId={}, postId={}",
data.getUserId(), data.getPostId());
log.warn("[UserPostSync] 用户-岗位关系同步服务需要分公司自定义实现,当前为占位符实现");
// TODO: 分公司需要实现此方法,通过本地 API 或直接数据库操作完成同步
if (userPostApi == null) {
log.warn("[UserPostSync] UserPostApi未注入跳过创建用户-岗位关系, userId={}", data.getUserId());
return;
}
UserPostSaveReqDTO dto = buildUserPostDTO(data);
userPostApi.createUserPost(dto).checkError();
log.info("[UserPostSync] 用户-岗位关系创建成功, userId={}, postId={}", data.getUserId(), data.getPostId());
}
@Override
public void update(DatabusUserPostData data) {
log.info("[UserPostSync] 收到更新用户-岗位关系请求, userId={}, postId={}",
data.getUserId(), data.getPostId());
log.warn("[UserPostSync] 用户-岗位关系同步服务需要分公司自定义实现,当前为占位符实现");
// TODO: 分公司需要实现此方法
if (userPostApi == null) {
log.warn("[UserPostSync] UserPostApi未注入跳过更新用户-岗位关系, userId={}", data.getUserId());
return;
}
UserPostSaveReqDTO dto = buildUserPostDTO(data);
userPostApi.updateUserPost(dto).checkError();
log.info("[UserPostSync] 用户-岗位关系更新成功, userId={}, postId={}", data.getUserId(), data.getPostId());
}
@Override
public void delete(Long id) {
log.info("[UserPostSync] 收到删除用户-岗位关系请求, id={}", id);
log.warn("[UserPostSync] 用户-岗位关系同步服务需要分公司自定义实现,当前为占位符实现");
// TODO: 分公司需要实现此方法
if (userPostApi == null) {
log.warn("[UserPostSync] UserPostApi未注入跳过删除用户-岗位关系, id={}", id);
return;
}
userPostApi.deleteUserPost(id).checkError();
log.info("[UserPostSync] 用户-岗位关系删除成功, id={}", id);
}
@Override
public void fullSync(DatabusUserPostData data) {
log.info("[UserPostSync] 收到全量同步用户-岗位关系请求, userId={}, postId={}",
data.getUserId(), data.getPostId());
log.warn("[UserPostSync] 用户-岗位关系同步服务需要分公司自定义实现,当前为占位符实现");
// TODO: 分公司需要实现此方法,逻辑:存在则更新,不存在则插入
if (userPostApi == null) {
log.warn("[UserPostSync] UserPostApi未注入跳过全量同步用户-岗位关系, userId={}", data.getUserId());
return;
}
UserPostSaveReqDTO dto = buildUserPostDTO(data);
try {
// 尝试获取,存在则更新,不存在则创建
var existing = userPostApi.getUserPost(dto.getId());
if (existing.isSuccess() && existing.getData() != null) {
userPostApi.updateUserPost(dto).checkError();
log.info("[UserPostSync] 用户-岗位关系全量同步-更新成功, id={}", dto.getId());
} else {
userPostApi.createUserPost(dto).checkError();
log.info("[UserPostSync] 用户-岗位关系全量同步-创建成功, id={}", dto.getId());
}
} catch (Exception e) {
// 获取失败,尝试创建
log.warn("[UserPostSync] 用户-岗位关系获取失败,尝试创建, id={}", dto.getId());
userPostApi.createUserPost(dto).checkError();
log.info("[UserPostSync] 用户-岗位关系全量同步-创建成功, id={}", dto.getId());
}
}
/**
* 构建用户岗位关系 DTO用于 Feign 调用)
*/
private UserPostSaveReqDTO buildUserPostDTO(DatabusUserPostData data) {
UserPostSaveReqDTO dto = new UserPostSaveReqDTO();
dto.setId(data.getId());
dto.setUserId(data.getUserId());
dto.setPostId(data.getPostId());
return dto;
}
}

View File

@@ -34,7 +34,15 @@ public class DatabusUserChangeConsumer implements RocketMQListener<DatabusUserCh
@Override
public void onMessage(DatabusUserChangeMessage message) {
log.info("[Databus] 收到用户变更消息, action={}, userId={}", message.getAction(), message.getUserId());
log.info("[Databus] 收到用户变更消息, action={}, userId={}, userSource={}",
message.getAction(), message.getUserId(), message.getUserSource());
// ⚠️ 只处理 userSource = 2 的用户
if (message.getUserSource() == null || message.getUserSource() != 2) {
log.info("[Databus] 跳过非集团用户的变更消息, userId={}, userSource={}",
message.getUserId(), message.getUserSource());
return;
}
try {
Map<String, Object> dataMap = new HashMap<>();

View File

@@ -0,0 +1,81 @@
package com.zt.plat.framework.databus.server.provider;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.databus.server.core.provider.DataProvider;
import com.zt.plat.framework.databus.server.core.provider.DataProviderRegistry;
import com.zt.plat.module.databus.api.dto.CursorPageReqDTO;
import com.zt.plat.module.databus.api.dto.CursorPageResult;
import com.zt.plat.module.databus.api.data.DatabusUserDeptData;
import com.zt.plat.module.databus.api.provider.DatabusUserDeptProviderApi;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 用户-部门关系数据提供者
*/
@Slf4j
@Component
public class UserDeptDataFeignProvider implements DataProvider<DatabusUserDeptData> {
public static final String PROVIDER_TYPE = "USER_DEPT";
@Resource
private DatabusUserDeptProviderApi userDeptProviderApi;
@Resource
private DataProviderRegistry dataProviderRegistry;
@PostConstruct
public void init() {
dataProviderRegistry.register(this);
}
@Override
public String getProviderType() {
return PROVIDER_TYPE;
}
@Override
public CursorPageData<DatabusUserDeptData> getPageByCursor(LocalDateTime cursorTime, Long cursorId,
int batchSize, Long tenantId) {
CursorPageReqDTO reqDTO = CursorPageReqDTO.builder()
.cursorTime(cursorTime)
.cursorId(cursorId)
.batchSize(batchSize)
.tenantId(tenantId)
.build();
CommonResult<CursorPageResult<DatabusUserDeptData>> result = userDeptProviderApi.getPageByCursor(reqDTO);
if (!result.isSuccess()) {
throw new RuntimeException("获取用户-部门关系数据失败: " + result.getMsg());
}
CursorPageResult<DatabusUserDeptData> pageResult = result.getData();
return CursorPageData.of(
pageResult.getList(),
pageResult.getNextCursorTime(),
pageResult.getNextCursorId(),
pageResult.getCount(),
Boolean.TRUE.equals(pageResult.getHasMore()),
(pageResult.getTotal() != null ? pageResult.getTotal() : 0L)
);
}
@Override
public long count(Long tenantId) {
CommonResult<Long> result = userDeptProviderApi.count(tenantId);
if (!result.isSuccess()) {
throw new RuntimeException("获取用户-部门关系总数失败: " + result.getMsg());
}
return result.getData();
}
@Override
public Long extractUid(DatabusUserDeptData data) {
return data.getId();
}
}

View File

@@ -0,0 +1,81 @@
package com.zt.plat.framework.databus.server.provider;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.databus.server.core.provider.DataProvider;
import com.zt.plat.framework.databus.server.core.provider.DataProviderRegistry;
import com.zt.plat.module.databus.api.dto.CursorPageReqDTO;
import com.zt.plat.module.databus.api.dto.CursorPageResult;
import com.zt.plat.module.databus.api.data.DatabusUserPostData;
import com.zt.plat.module.databus.api.provider.DatabusUserPostProviderApi;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 用户-岗位关系数据提供者
*/
@Slf4j
@Component
public class UserPostDataFeignProvider implements DataProvider<DatabusUserPostData> {
public static final String PROVIDER_TYPE = "USER_POST";
@Resource
private DatabusUserPostProviderApi userPostProviderApi;
@Resource
private DataProviderRegistry dataProviderRegistry;
@PostConstruct
public void init() {
dataProviderRegistry.register(this);
}
@Override
public String getProviderType() {
return PROVIDER_TYPE;
}
@Override
public CursorPageData<DatabusUserPostData> getPageByCursor(LocalDateTime cursorTime, Long cursorId,
int batchSize, Long tenantId) {
CursorPageReqDTO reqDTO = CursorPageReqDTO.builder()
.cursorTime(cursorTime)
.cursorId(cursorId)
.batchSize(batchSize)
.tenantId(tenantId)
.build();
CommonResult<CursorPageResult<DatabusUserPostData>> result = userPostProviderApi.getPageByCursor(reqDTO);
if (!result.isSuccess()) {
throw new RuntimeException("获取用户-岗位关系数据失败: " + result.getMsg());
}
CursorPageResult<DatabusUserPostData> pageResult = result.getData();
return CursorPageData.of(
pageResult.getList(),
pageResult.getNextCursorTime(),
pageResult.getNextCursorId(),
pageResult.getCount(),
Boolean.TRUE.equals(pageResult.getHasMore()),
(pageResult.getTotal() != null ? pageResult.getTotal() : 0L)
);
}
@Override
public long count(Long tenantId) {
CommonResult<Long> result = userPostProviderApi.count(tenantId);
if (!result.isSuccess()) {
throw new RuntimeException("获取用户-岗位关系总数失败: " + result.getMsg());
}
return result.getData();
}
@Override
public Long extractUid(DatabusUserPostData data) {
return data.getId();
}
}

View File

@@ -0,0 +1,71 @@
package com.zt.plat.module.databus.api.provider;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.databus.api.data.DatabusUserDeptData;
import com.zt.plat.module.databus.api.dto.CursorPageReqDTO;
import com.zt.plat.module.databus.api.dto.CursorPageResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* Databus 用户-部门关系数据提供者 API
* <p>
* 供 Databus 调用,获取用户-部门关联数据用于全量/增量同步
*
* @author ZT
*/
@FeignClient(name = "${databus.provider.user-dept.service:system-server}")
@Tag(name = "RPC 服务 - Databus 用户-部门关系数据提供者")
public interface DatabusUserDeptProviderApi {
String PREFIX = "/rpc/databus/user-dept";
/**
* 游标分页查询用户-部门关系数据(用于全量同步)
*
* @param reqDTO 游标分页请求
* @return 用户-部门关系数据分页结果
*/
@PostMapping(PREFIX + "/page-by-cursor")
@Operation(summary = "游标分页查询用户-部门关系数据")
CommonResult<CursorPageResult<DatabusUserDeptData>> getPageByCursor(@RequestBody CursorPageReqDTO reqDTO);
/**
* 根据ID查询用户-部门关系详情(用于增量同步)
*
* @param id 关系ID
* @return 用户-部门关系数据
*/
@GetMapping(PREFIX + "/get")
@Operation(summary = "查询用户-部门关系详情")
@Parameter(name = "id", description = "关系ID", required = true, example = "1001")
CommonResult<DatabusUserDeptData> getById(@RequestParam("id") Long id);
/**
* 批量查询用户-部门关系详情(用于增量同步批量获取)
*
* @param ids 关系ID列表
* @return 用户-部门关系数据列表
*/
@GetMapping(PREFIX + "/list")
@Operation(summary = "批量查询用户-部门关系详情")
@Parameter(name = "ids", description = "关系ID列表", required = true, example = "1001,1002,1003")
CommonResult<List<DatabusUserDeptData>> getListByIds(@RequestParam("ids") List<Long> ids);
/**
* 统计用户-部门关系总数(用于全量同步进度计算)
*
* @param tenantId 租户ID可选
* @return 用户-部门关系总数
*/
@GetMapping(PREFIX + "/count")
@Operation(summary = "统计用户-部门关系总数")
@Parameter(name = "tenantId", description = "租户ID", example = "1")
CommonResult<Long> count(@RequestParam(value = "tenantId", required = false) Long tenantId);
}

View File

@@ -0,0 +1,71 @@
package com.zt.plat.module.databus.api.provider;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.databus.api.data.DatabusUserPostData;
import com.zt.plat.module.databus.api.dto.CursorPageReqDTO;
import com.zt.plat.module.databus.api.dto.CursorPageResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* Databus 用户-岗位关系数据提供者 API
* <p>
* 供 Databus 调用,获取用户-岗位关联数据用于全量/增量同步
*
* @author ZT
*/
@FeignClient(name = "${databus.provider.user-post.service:system-server}")
@Tag(name = "RPC 服务 - Databus 用户-岗位关系数据提供者")
public interface DatabusUserPostProviderApi {
String PREFIX = "/rpc/databus/user-post";
/**
* 游标分页查询用户-岗位关系数据(用于全量同步)
*
* @param reqDTO 游标分页请求
* @return 用户-岗位关系数据分页结果
*/
@PostMapping(PREFIX + "/page-by-cursor")
@Operation(summary = "游标分页查询用户-岗位关系数据")
CommonResult<CursorPageResult<DatabusUserPostData>> getPageByCursor(@RequestBody CursorPageReqDTO reqDTO);
/**
* 根据ID查询用户-岗位关系详情(用于增量同步)
*
* @param id 关系ID
* @return 用户-岗位关系数据
*/
@GetMapping(PREFIX + "/get")
@Operation(summary = "查询用户-岗位关系详情")
@Parameter(name = "id", description = "关系ID", required = true, example = "1001")
CommonResult<DatabusUserPostData> getById(@RequestParam("id") Long id);
/**
* 批量查询用户-岗位关系详情(用于增量同步批量获取)
*
* @param ids 关系ID列表
* @return 用户-岗位关系数据列表
*/
@GetMapping(PREFIX + "/list")
@Operation(summary = "批量查询用户-岗位关系详情")
@Parameter(name = "ids", description = "关系ID列表", required = true, example = "1001,1002,1003")
CommonResult<List<DatabusUserPostData>> getListByIds(@RequestParam("ids") List<Long> ids);
/**
* 统计用户-岗位关系总数(用于全量同步进度计算)
*
* @param tenantId 租户ID可选
* @return 用户-岗位关系总数
*/
@GetMapping(PREFIX + "/count")
@Operation(summary = "统计用户-岗位关系总数")
@Parameter(name = "tenantId", description = "租户ID", example = "1")
CommonResult<Long> count(@RequestParam(value = "tenantId", required = false) Long tenantId);
}

View File

@@ -4,9 +4,13 @@ import com.zt.plat.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import com.zt.plat.module.databus.api.provider.DatabusDeptProviderApi;
import com.zt.plat.module.databus.api.provider.DatabusPostProviderApi;
import com.zt.plat.module.databus.api.provider.DatabusUserProviderApi;
import com.zt.plat.module.databus.api.provider.DatabusUserDeptProviderApi;
import com.zt.plat.module.databus.api.provider.DatabusUserPostProviderApi;
import com.zt.plat.module.system.api.dept.DeptApi;
import com.zt.plat.module.system.api.dept.PostApi;
import com.zt.plat.module.system.api.user.AdminUserApi;
import com.zt.plat.module.system.api.userdept.UserDeptApi;
import com.zt.plat.module.system.api.userpost.UserPostApi;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
@@ -21,9 +25,12 @@ import org.springframework.context.annotation.Configuration;
DatabusDeptProviderApi.class,
DatabusUserProviderApi.class,
DatabusPostProviderApi.class,
DatabusUserDeptProviderApi.class,
DatabusUserPostProviderApi.class,
PostApi.class,
DeptApi.class,
AdminUserApi.class,
UserDeptApi.class,
UserPostApi.class,
})
public class RpcConfiguration {
}

View File

@@ -0,0 +1,64 @@
package com.zt.plat.module.system.api.userdept;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.system.api.userdept.dto.UserDeptRespDTO;
import com.zt.plat.module.system.api.userdept.dto.UserDeptSaveReqDTO;
import com.zt.plat.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
import java.util.List;
/**
* 用户-部门关系 Feign API
*
* @author ZT
*/
@FeignClient(name = ApiConstants.NAME)
@Tag(name = "RPC 服务 - 用户部门关系")
public interface UserDeptApi {
String PREFIX = ApiConstants.PREFIX + "/user-dept";
@PostMapping(PREFIX + "/create")
@Operation(summary = "新增用户部门关系")
CommonResult<Long> createUserDept(@RequestBody UserDeptSaveReqDTO reqVO);
@PutMapping(PREFIX + "/update")
@Operation(summary = "修改用户部门关系")
CommonResult<Boolean> updateUserDept(@RequestBody UserDeptSaveReqDTO reqVO);
@DeleteMapping(PREFIX + "/delete")
@Operation(summary = "删除用户部门关系")
@Parameter(name = "id", description = "关系编号", example = "1", required = true)
CommonResult<Boolean> deleteUserDept(@RequestParam("id") Long id);
@GetMapping(PREFIX + "/get")
@Operation(summary = "通过ID查询用户部门关系")
@Parameter(name = "id", description = "关系编号", example = "1", required = true)
CommonResult<UserDeptRespDTO> getUserDept(@RequestParam("id") Long id);
@GetMapping(PREFIX + "/list-by-user-id")
@Operation(summary = "通过用户ID查询用户部门关系列表")
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
CommonResult<List<UserDeptRespDTO>> getUserDeptListByUserId(@RequestParam("userId") Long userId);
@GetMapping(PREFIX + "/list-by-dept-id")
@Operation(summary = "通过部门ID查询用户部门关系列表")
@Parameter(name = "deptId", description = "部门编号", example = "1", required = true)
CommonResult<List<UserDeptRespDTO>> getUserDeptListByDeptId(@RequestParam("deptId") Long deptId);
@DeleteMapping(PREFIX + "/delete-by-user-id")
@Operation(summary = "通过用户ID删除用户部门关系")
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
CommonResult<Boolean> deleteUserDeptByUserId(@RequestParam("userId") Long userId);
@DeleteMapping(PREFIX + "/delete-by-dept-id")
@Operation(summary = "通过部门ID删除用户部门关系")
@Parameter(name = "deptId", description = "部门编号", example = "1", required = true)
CommonResult<Boolean> deleteUserDeptByDeptId(@RequestParam("deptId") Long deptId);
}

View File

@@ -0,0 +1,31 @@
package com.zt.plat.module.system.api.userdept.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 用户部门关系 Response DTO
*
* @author ZT
*/
@Schema(description = "RPC 服务 - 用户部门关系 Response DTO")
@Data
public class UserDeptRespDTO {
@Schema(description = "关系编号", example = "1024")
private Long id;
@Schema(description = "用户编号", example = "1")
private Long userId;
@Schema(description = "部门编号", example = "100")
private Long deptId;
@Schema(description = "备注", example = "主部门")
private String remark;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,26 @@
package com.zt.plat.module.system.api.userdept.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 用户部门关系创建/修改 Request DTO
*
* @author ZT
*/
@Schema(description = "RPC 服务 - 用户部门关系创建/修改 Request DTO")
@Data
public class UserDeptSaveReqDTO {
@Schema(description = "关系编号", example = "1024")
private Long id;
@Schema(description = "用户编号", example = "1", required = true)
private Long userId;
@Schema(description = "部门编号", example = "100", required = true)
private Long deptId;
@Schema(description = "备注", example = "主部门")
private String remark;
}

View File

@@ -0,0 +1,64 @@
package com.zt.plat.module.system.api.userpost;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.system.api.userpost.dto.UserPostRespDTO;
import com.zt.plat.module.system.api.userpost.dto.UserPostSaveReqDTO;
import com.zt.plat.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
import java.util.List;
/**
* 用户-岗位关系 Feign API
*
* @author ZT
*/
@FeignClient(name = ApiConstants.NAME)
@Tag(name = "RPC 服务 - 用户岗位关系")
public interface UserPostApi {
String PREFIX = ApiConstants.PREFIX + "/user-post";
@PostMapping(PREFIX + "/create")
@Operation(summary = "新增用户岗位关系")
CommonResult<Long> createUserPost(@RequestBody UserPostSaveReqDTO reqVO);
@PutMapping(PREFIX + "/update")
@Operation(summary = "修改用户岗位关系")
CommonResult<Boolean> updateUserPost(@RequestBody UserPostSaveReqDTO reqVO);
@DeleteMapping(PREFIX + "/delete")
@Operation(summary = "删除用户岗位关系")
@Parameter(name = "id", description = "关系编号", example = "1", required = true)
CommonResult<Boolean> deleteUserPost(@RequestParam("id") Long id);
@GetMapping(PREFIX + "/get")
@Operation(summary = "通过ID查询用户岗位关系")
@Parameter(name = "id", description = "关系编号", example = "1", required = true)
CommonResult<UserPostRespDTO> getUserPost(@RequestParam("id") Long id);
@GetMapping(PREFIX + "/list-by-user-id")
@Operation(summary = "通过用户ID查询用户岗位关系列表")
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
CommonResult<List<UserPostRespDTO>> getUserPostListByUserId(@RequestParam("userId") Long userId);
@GetMapping(PREFIX + "/list-by-post-id")
@Operation(summary = "通过岗位ID查询用户岗位关系列表")
@Parameter(name = "postId", description = "岗位编号", example = "1", required = true)
CommonResult<List<UserPostRespDTO>> getUserPostListByPostId(@RequestParam("postId") Long postId);
@DeleteMapping(PREFIX + "/delete-by-user-id")
@Operation(summary = "通过用户ID删除用户岗位关系")
@Parameter(name = "userId", description = "用户编号", example = "1", required = true)
CommonResult<Boolean> deleteUserPostByUserId(@RequestParam("userId") Long userId);
@DeleteMapping(PREFIX + "/delete-by-post-id")
@Operation(summary = "通过岗位ID删除用户岗位关系")
@Parameter(name = "postId", description = "岗位编号", example = "1", required = true)
CommonResult<Boolean> deleteUserPostByPostId(@RequestParam("postId") Long postId);
}

View File

@@ -0,0 +1,28 @@
package com.zt.plat.module.system.api.userpost.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 用户岗位关系 Response DTO
*
* @author ZT
*/
@Schema(description = "RPC 服务 - 用户岗位关系 Response DTO")
@Data
public class UserPostRespDTO {
@Schema(description = "关系编号", example = "1024")
private Long id;
@Schema(description = "用户编号", example = "1")
private Long userId;
@Schema(description = "岗位编号", example = "100")
private Long postId;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,23 @@
package com.zt.plat.module.system.api.userpost.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 用户岗位关系创建/修改 Request DTO
*
* @author ZT
*/
@Schema(description = "RPC 服务 - 用户岗位关系创建/修改 Request DTO")
@Data
public class UserPostSaveReqDTO {
@Schema(description = "关系编号", example = "1024")
private Long id;
@Schema(description = "用户编号", example = "1", required = true)
private Long userId;
@Schema(description = "岗位编号", example = "100", required = true)
private Long postId;
}

View File

@@ -0,0 +1,127 @@
package com.zt.plat.module.system.api.databus;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.databus.api.data.DatabusUserDeptData;
import com.zt.plat.module.databus.api.dto.CursorPageReqDTO;
import com.zt.plat.module.databus.api.dto.CursorPageResult;
import com.zt.plat.module.databus.api.provider.DatabusUserDeptProviderApi;
import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO;
import com.zt.plat.module.system.dal.mysql.userdept.UserDeptMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
/**
* Databus 用户-部门关系数据提供者 API 实现
*
* @author ZT
*/
@Slf4j
@RestController
@Validated
public class DatabusUserDeptProviderApiImpl implements DatabusUserDeptProviderApi {
@Resource
private UserDeptMapper userDeptMapper;
@Override
public CommonResult<CursorPageResult<DatabusUserDeptData>> getPageByCursor(CursorPageReqDTO reqDTO) {
// 多查一条判断是否有更多数据
int limit = reqDTO.getBatchSize() != null ? reqDTO.getBatchSize() : 100;
// ⚠️ 使用关联查询,只查询 userSource = 2 的用户的部门关系
List<UserDeptDO> list = userDeptMapper.selectPageByCursorWithUserSource(
reqDTO.isFirstPage() ? null : reqDTO.getCursorTime(),
reqDTO.isFirstPage() ? null : reqDTO.getCursorId(),
reqDTO.getTenantId(),
limit + 1
);
// 判断是否有更多
boolean hasMore = list.size() > limit;
if (hasMore) {
list = list.subList(0, limit);
}
if (CollUtil.isEmpty(list)) {
return success(CursorPageResult.empty());
}
// 转换为同步数据
List<DatabusUserDeptData> dataList = list.stream()
.map(this::convertToData)
.collect(Collectors.toList());
// 获取最后一条数据的游标
UserDeptDO last = list.get(list.size() - 1);
// 首次查询时返<E697B6><E8BF94><EFBFBD>总数
Long total = null;
if (reqDTO.isFirstPage()) {
// ⚠️ 只统计 userSource = 2 的用户的部门关系
total = userDeptMapper.countWithUserSource(reqDTO.getTenantId());
}
return success(CursorPageResult.of(
dataList,
last.getCreateTime(),
last.getId(),
hasMore,
total
));
}
@Override
public CommonResult<DatabusUserDeptData> getById(Long id) {
UserDeptDO userDept = userDeptMapper.selectById(id);
if (userDept == null) {
return success(null);
}
return success(convertToData(userDept));
}
@Override
public CommonResult<List<DatabusUserDeptData>> getListByIds(List<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return success(Collections.emptyList());
}
List<UserDeptDO> list = userDeptMapper.selectBatchIds(ids);
if (CollUtil.isEmpty(list)) {
return success(Collections.emptyList());
}
return success(list.stream()
.map(this::convertToData)
.collect(Collectors.toList()));
}
@Override
public CommonResult<Long> count(Long tenantId) {
// ⚠️ 只统计 userSource = 2 的用户的部门关系
return success(userDeptMapper.countWithUserSource(tenantId));
}
/**
* 将 UserDeptDO 转换为 DatabusUserDeptData
*/
private DatabusUserDeptData convertToData(UserDeptDO userDept) {
return DatabusUserDeptData.builder()
.id(userDept.getId())
.userId(userDept.getUserId())
.deptId(userDept.getDeptId())
.tenantId(userDept.getTenantId())
.remark(userDept.getRemark())
.build();
}
}

View File

@@ -0,0 +1,125 @@
package com.zt.plat.module.system.api.databus;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.databus.api.data.DatabusUserPostData;
import com.zt.plat.module.databus.api.dto.CursorPageReqDTO;
import com.zt.plat.module.databus.api.dto.CursorPageResult;
import com.zt.plat.module.databus.api.provider.DatabusUserPostProviderApi;
import com.zt.plat.module.system.dal.dataobject.dept.UserPostDO;
import com.zt.plat.module.system.dal.mysql.dept.UserPostMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
/**
* Databus 用户-岗位关系数据提供者 API 实现
*
* @author ZT
*/
@Slf4j
@RestController
@Validated
public class DatabusUserPostProviderApiImpl implements DatabusUserPostProviderApi {
@Resource
private UserPostMapper userPostMapper;
@Override
public CommonResult<CursorPageResult<DatabusUserPostData>> getPageByCursor(CursorPageReqDTO reqDTO) {
// 多查一条判断是否有更多数据
int limit = reqDTO.getBatchSize() != null ? reqDTO.getBatchSize() : 100;
// ⚠️ 使用关联查询,只查询 userSource = 2 的用户的岗位关系
List<UserPostDO> list = userPostMapper.selectPageByCursorWithUserSource(
reqDTO.isFirstPage() ? null : reqDTO.getCursorTime(),
reqDTO.isFirstPage() ? null : reqDTO.getCursorId(),
reqDTO.getTenantId(),
limit + 1
);
// 判断是否有更多
boolean hasMore = list.size() > limit;
if (hasMore) {
list = list.subList(0, limit);
}
if (CollUtil.isEmpty(list)) {
return success(CursorPageResult.empty());
}
// 转换为同步数据
List<DatabusUserPostData> dataList = list.stream()
.map(this::convertToData)
.collect(Collectors.toList());
// 获取最后一条数据的游标
UserPostDO last = list.get(list.size() - 1);
// 首次查询时返回总数
Long total = null;
if (reqDTO.isFirstPage()) {
// ⚠️ 只统计 userSource = 2 的用户的岗位关系
total = userPostMapper.countWithUserSource(reqDTO.getTenantId());
}
return success(CursorPageResult.of(
dataList,
last.getCreateTime(),
last.getId(),
hasMore,
total
));
}
@Override
public CommonResult<DatabusUserPostData> getById(Long id) {
UserPostDO userPost = userPostMapper.selectById(id);
if (userPost == null) {
return success(null);
}
return success(convertToData(userPost));
}
@Override
public CommonResult<List<DatabusUserPostData>> getListByIds(List<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return success(Collections.emptyList());
}
List<UserPostDO> list = userPostMapper.selectBatchIds(ids);
if (CollUtil.isEmpty(list)) {
return success(Collections.emptyList());
}
return success(list.stream()
.map(this::convertToData)
.collect(Collectors.toList()));
}
@Override
public CommonResult<Long> count(Long tenantId) {
// ⚠️ 只统计 userSource = 2 的用户的岗位关系
return success(userPostMapper.countWithUserSource(tenantId));
}
/**
* 将 UserPostDO 转换为 DatabusUserPostData
*/
private DatabusUserPostData convertToData(UserPostDO userPost) {
return DatabusUserPostData.builder()
.id(userPost.getId())
.userId(userPost.getUserId())
.postId(userPost.getPostId())
.build();
}
}

View File

@@ -54,6 +54,9 @@ public class DatabusUserProviderApiImpl implements DatabusUserProviderApi {
// 构建游标查询条件
LambdaQueryWrapper<AdminUserDO> queryWrapper = new LambdaQueryWrapper<>();
// ⚠️ 只同步 userSource = 2 的用户
queryWrapper.eq(AdminUserDO::getUserSource, 2);
// 游标条件create_time > cursorTime OR (create_time = cursorTime AND id > cursorId)
if (!reqDTO.isFirstPage()) {
queryWrapper.and(w -> w
@@ -100,6 +103,8 @@ public class DatabusUserProviderApiImpl implements DatabusUserProviderApi {
Long total = null;
if (reqDTO.isFirstPage()) {
LambdaQueryWrapper<AdminUserDO> countWrapper = new LambdaQueryWrapper<>();
// ⚠️ 只统计 userSource = 2 的用户
countWrapper.eq(AdminUserDO::getUserSource, 2);
if (reqDTO.getTenantId() != null) {
countWrapper.eq(AdminUserDO::getTenantId, reqDTO.getTenantId());
}
@@ -143,6 +148,8 @@ public class DatabusUserProviderApiImpl implements DatabusUserProviderApi {
@Override
public CommonResult<Long> count(Long tenantId) {
LambdaQueryWrapper<AdminUserDO> queryWrapper = new LambdaQueryWrapper<>();
// ⚠️ 只统计 userSource = 2 的用户
queryWrapper.eq(AdminUserDO::getUserSource, 2);
if (tenantId != null) {
queryWrapper.eq(AdminUserDO::getTenantId, tenantId);
}

View File

@@ -10,6 +10,7 @@ import com.zt.plat.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.dept.PostDO;
import com.zt.plat.module.system.service.dept.PostService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
@@ -20,6 +21,7 @@ import java.util.List;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@Slf4j
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class PostApiImpl implements PostApi {
@@ -36,6 +38,7 @@ public class PostApiImpl implements PostApi {
@Override
public CommonResult<Boolean> updatePost(PostSaveReqDTO updateReqVO) {
log.error("ssssssssss");
PostSaveReqVO reqVO = BeanUtils.toBean(updateReqVO, PostSaveReqVO.class);
postService.updatePost(reqVO);
return success(true);
@@ -49,6 +52,7 @@ public class PostApiImpl implements PostApi {
@Override
public CommonResult<PostRespDTO> getPost(Long id) {
log.error("cccccccc"+id);
PostDO post = postService.getPost(id);
return success(BeanUtils.toBean(post, PostRespDTO.class));
}

View File

@@ -5,7 +5,10 @@ import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.zt.plat.module.system.dal.dataobject.dept.UserPostDO;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
@@ -29,4 +32,44 @@ public interface UserPostMapper extends BaseMapperX<UserPostDO> {
default void deleteByUserId(Long userId) {
delete(Wrappers.lambdaUpdate(UserPostDO.class).eq(UserPostDO::getUserId, userId));
}
/**
* 游标分页查询用户-岗位关系(只查询 userSource = 2 的用户)
* @param cursorTime 游标时间
* @param cursorId 游标ID
* @param tenantId 租户ID可选
* @param limit 限制数量
* @return 用户岗位关系列表
*/
@Select("<script>" +
"SELECT up.* FROM system_user_post up " +
"INNER JOIN system_users u ON up.user_id = u.id " +
"WHERE u.user_source = 2 " +
"AND up.deleted = 0 " +
"<if test='tenantId != null'> AND up.tenant_id = #{tenantId} </if>" +
"<if test='cursorTime != null'>" +
" AND (up.create_time > #{cursorTime} " +
" OR (up.create_time = #{cursorTime} AND up.id > #{cursorId}))" +
"</if>" +
"ORDER BY up.create_time ASC, up.id ASC " +
"LIMIT #{limit}" +
"</script>")
List<UserPostDO> selectPageByCursorWithUserSource(@Param("cursorTime") LocalDateTime cursorTime,
@Param("cursorId") Long cursorId,
@Param("tenantId") Long tenantId,
@Param("limit") Integer limit);
/**
* 统计用户-岗位关系数量(只统计 userSource = 2 的用户)
* @param tenantId 租户ID可选
* @return 数量
*/
@Select("<script>" +
"SELECT COUNT(*) FROM system_user_post up " +
"INNER JOIN system_users u ON up.user_id = u.id " +
"WHERE u.user_source = 2 " +
"AND up.deleted = 0 " +
"<if test='tenantId != null'> AND up.tenant_id = #{tenantId} </if>" +
"</script>")
Long countWithUserSource(@Param("tenantId") Long tenantId);
}

View File

@@ -4,7 +4,10 @@ import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.zt.plat.module.system.dal.dataobject.userdept.UserDeptDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -45,4 +48,44 @@ public interface UserDeptMapper extends BaseMapperX<UserDeptDO> {
);
}
/**
* 游标分页查询用户-部门关系(只查询 userSource = 2 的用户)
* @param cursorTime 游标时间
* @param cursorId 游标ID
* @param tenantId 租户ID可选
* @param limit 限制数量
* @return 用户部门关系列表
*/
@Select("<script>" +
"SELECT ud.* FROM system_user_dept ud " +
"INNER JOIN system_users u ON ud.user_id = u.id " +
"WHERE u.user_source = 2 " +
"AND ud.deleted = 0 " +
"<if test='tenantId != null'> AND ud.tenant_id = #{tenantId} </if>" +
"<if test='cursorTime != null'>" +
" AND (ud.create_time > #{cursorTime} " +
" OR (ud.create_time = #{cursorTime} AND ud.id > #{cursorId}))" +
"</if>" +
"ORDER BY ud.create_time ASC, ud.id ASC " +
"LIMIT #{limit}" +
"</script>")
List<UserDeptDO> selectPageByCursorWithUserSource(@Param("cursorTime") LocalDateTime cursorTime,
@Param("cursorId") Long cursorId,
@Param("tenantId") Long tenantId,
@Param("limit") Integer limit);
/**
* 统计用户-部门关系数量(只统计 userSource = 2 的用户)
* @param tenantId 租户ID可选
* @return 数量
*/
@Select("<script>" +
"SELECT COUNT(*) FROM system_user_dept ud " +
"INNER JOIN system_users u ON ud.user_id = u.id " +
"WHERE u.user_source = 2 " +
"AND ud.deleted = 0 " +
"<if test='tenantId != null'> AND ud.tenant_id = #{tenantId} </if>" +
"</script>")
Long countWithUserSource(@Param("tenantId") Long tenantId);
}