跳到主要内容

浙政钉集成

浙政钉集成 Starter 提供了专门适配浙江政务钉钉平台的 SDK 封装,支持政务办公场景的各种应用需求。浙政钉是基于钉钉技术打造的政务协同办公平台,具有更高的安全性和合规性要求。

主要特性

  • 组织架构管理: 支持获取组织架构信息、员工信息等
  • 消息推送: 支持工作通知消息推送
  • 用户认证: 支持免登授权和用户信息获取
  • 事件订阅: 支持组织架构变更、员工变更等事件回调
  • 缓存支持: 内置 Token 缓存机制
  • 限流控制: 支持 API 调用频率限制
  • 多环境支持: 支持不同环境的域名配置
  • 安全加密: 支持事件回调消息加解密

安装

Maven 安装

在您的 pom.xml 中添加以下依赖:

<dependency>
<groupId>com.jeeapp.spring.boot</groupId>
<artifactId>dingtalkgov-spring-boot-starter</artifactId>
</dependency>

Gradle 安装

在您的 build.gradle 中添加以下依赖:

implementation 'com.jeeapp.spring.boot:dingtalkgov-spring-boot-starter'

配置

基础配置

application.yml 中添加浙政钉配置:

dingtalkgov:
# 访问密钥(AppKey)
access-key: your-app-key
# 访问密钥(AppSecret)
secret-key: your-app-secret
# 租户 ID
tenant-id: 123456
# 请求超时时间(毫秒)
timeout: 5000

环境配置

浙政钉支持不同环境的域名配置:

dingtalkgov:
# 协议
protocol: https
# 域名配置
# Saas 旧环境: openplatform.dg-work.cn
# Saas 新环境: open.on-premises.dingtalk.com
# 浙政钉环境: openplatform-pro.ding.zj.gov.cn
domain-name: openplatform-pro.ding.zj.gov.cn
access-key: your-app-key
secret-key: your-app-secret
tenant-id: 123456

事件回调配置

如果需要接收浙政钉事件回调:

dingtalkgov:
access-key: your-app-key
secret-key: your-app-secret
tenant-id: 123456
# 事件回调配置
callback:
# 回调地址
url: https://your-domain.com/callback/dingtalkgov
# 数据加密 AES Key
aes-key: your-aes-key
# 回调签名 Token
token: your-callback-token
# 回调版本(v1 或 v2)
version: v2

限流配置

配置 API 限流规则:

dingtalkgov:
access-key: your-app-key
secret-key: your-app-secret
tenant-id: 123456
rate-limits:
- path: "/rpc/oauth2/*"
limit: 100
window: 60
- path: "/mozi/users/*"
limit: 200
window: 60

使用指南

基础使用

注入 DingTalkGovTemplate 即可使用:

import com.jeeapp.dingtalkgov.DingTalkGovTemplate;
import com.jeeapp.dingtalkgov.DingTalkGovApiException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DingTalkGovController {

@Autowired
private DingTalkGovTemplate dingTalkGovTemplate;

@GetMapping("/token")
public String getAccessToken() {
try {
return dingTalkGovTemplate.getAccessToken();
} catch (DingTalkGovApiException e) {
return "Error: " + e.getMessage();
}
}

@GetMapping("/jsapi-token")
public String getJsapiToken() {
try {
return dingTalkGovTemplate.getJsapiToken();
} catch (DingTalkGovApiException e) {
return "Error: " + e.getMessage();
}
}
}

用户认证

获取用户信息:

import com.jeeapp.dingtalkgov.DingTalkGovTemplate;
import com.jeeapp.dingtalkgov.response.data.AppUserResponseData;
import org.springframework.stereotype.Service;

@Service
public class AuthService {

@Autowired
private DingTalkGovTemplate dingTalkGovTemplate;

public AppUserResponseData authenticate(String authCode) throws DingTalkGovApiException {
// 通过授权码获取用户信息
AppUserResponseData appUser = dingTalkGovTemplate.getAppUser(authCode);

System.out.println("员工编码: " + appUser.getEmployeeCode());
System.out.println("用户名: " + appUser.getName());

return appUser;
}
}

组织架构操作

浙政钉使用"墨子"系统管理组织架构和员工信息:

员工操作

import com.jeeapp.dingtalkgov.DingTalkGovTemplate;
import com.jeeapp.dingtalkgov.DingTalkGovMoziOperations;
import com.jeeapp.dingtalkgov.response.data.EmployeeResponseData;
import com.jeeapp.dingtalkgov.response.data.EmployeePositionResponseData;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Arrays;

@Service
public class EmployeeService {

@Autowired
private DingTalkGovTemplate dingTalkGovTemplate;

public EmployeeResponseData getEmployee(String employeeCode) throws DingTalkGovApiException {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();

// 获取单个员工信息
EmployeeResponseData employee = moziOps.getEmployee(employeeCode);

System.out.println("员工姓名: " + employee.getName());
System.out.println("员工编码: " + employee.getEmployeeCode());
System.out.println("手机号: " + employee.getMobile());

return employee;
}

public List<EmployeeResponseData> getEmployeesByOrganization(String organizationCode)
throws DingTalkGovApiException {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();

// 获取组织下的所有员工
List<EmployeeResponseData> employees = moziOps.getEmployees(organizationCode);

return employees;
}

public List<EmployeePositionResponseData> getEmployeePositions(String employeeCode)
throws DingTalkGovApiException {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();

// 获取员工的职位信息
List<EmployeePositionResponseData> positions = moziOps.getEmployeePositions(employeeCode);

return positions;
}

public List<EmployeeResponseData> batchGetEmployees(List<String> employeeCodes)
throws DingTalkGovApiException {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();

// 批量获取员工信息
return moziOps.getEmployees(employeeCodes);
}
}

组织操作

import com.jeeapp.dingtalkgov.DingTalkGovTemplate;
import com.jeeapp.dingtalkgov.DingTalkGovMoziOperations;
import com.jeeapp.dingtalkgov.response.data.OrganizationResponseData;
import com.jeeapp.dingtalkgov.response.data.OrganizationPathResponseData;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class OrganizationService {

@Autowired
private DingTalkGovTemplate dingTalkGovTemplate;

public OrganizationResponseData getOrganization(String organizationCode)
throws DingTalkGovApiException {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();

// 获取单个组织信息
OrganizationResponseData organization = moziOps.getOrganization(organizationCode);

System.out.println("组织名称: " + organization.getName());
System.out.println("组织编码: " + organization.getOrganizationCode());

return organization;
}

public List<String> getSubOrganizations(String organizationCode)
throws DingTalkGovApiException {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();

// 获取子组织编码列表
return moziOps.getSubOrganizationCodes(organizationCode);
}

public List<OrganizationPathResponseData> getOrganizationPath(String organizationCode)
throws DingTalkGovApiException {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();

// 获取组织路径(从根组织到当前组织)
return moziOps.getOrganizationPath(organizationCode);
}

public List<OrganizationResponseData> batchGetOrganizations(List<String> organizationCodes)
throws DingTalkGovApiException {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();

// 批量获取组织信息
return moziOps.getOrganizations(organizationCodes);
}
}

消息操作

发送工作通知:

import com.jeeapp.dingtalkgov.DingTalkGovTemplate;
import com.jeeapp.dingtalkgov.DingTalkGovMessageOperations;
import com.jeeapp.dingtalkgov.request.WorkNotificationBuilder;
import org.springframework.stereotype.Service;

@Service
public class MessageService {

@Autowired
private DingTalkGovTemplate dingTalkGovTemplate;

public void sendTextMessage(String employeeCode, String content) throws DingTalkGovApiException {
DingTalkGovMessageOperations messageOps = dingTalkGovTemplate.opsForMessage();

// 创建工作通知
WorkNotificationBuilder builder = messageOps.createWorkNotification()
.employeeCodes(employeeCode)
.msgType("text")
.text(content);

String messageId = builder.send();
System.out.println("消息发送成功,消息ID: " + messageId);
}

public void sendLinkMessage(String employeeCode, String title, String text,
String messageUrl, String picUrl) throws DingTalkGovApiException {
DingTalkGovMessageOperations messageOps = dingTalkGovTemplate.opsForMessage();

// 发送链接消息
WorkNotificationBuilder builder = messageOps.createWorkNotification()
.employeeCodes(employeeCode)
.msgType("link")
.link(title, text, messageUrl, picUrl);

builder.send();
}

public void sendMarkdownMessage(String employeeCode, String title, String markdown)
throws DingTalkGovApiException {
DingTalkGovMessageOperations messageOps = dingTalkGovTemplate.opsForMessage();

// 发送 Markdown 消息
WorkNotificationBuilder builder = messageOps.createWorkNotification()
.employeeCodes(employeeCode)
.msgType("markdown")
.markdown(title, markdown);

builder.send();
}

public void sendActionCardMessage(String employeeCode, String title, String markdown)
throws DingTalkGovApiException {
DingTalkGovMessageOperations messageOps = dingTalkGovTemplate.opsForMessage();

// 发送卡片消息
WorkNotificationBuilder builder = messageOps.createWorkNotification()
.employeeCodes(employeeCode)
.msgType("action_card")
.actionCard(title, markdown, "查看详情", "https://example.com/detail");

builder.send();
}
}

事件处理

浙政钉支持通过 HTTP 回调接收事件通知。

支持的事件类型

浙政钉支持的事件类型主要分为以下几类:

组织架构事件

事件标签说明事件类
ORGANIZATION_ADD_UPDATE增加/更新组织OrganizationAddUpdateEvent
ORGANIZATION_PARENT_CHANGED组织变更父组织OrganizationParentChangedEvent
ORGANIZATION_REMOVE删除组织OrganizationRemoveEvent
EMPLOYEE_ADD_UPDATE增加/更新员工EmployeeAddUpdateEvent
EMPLOYEE_LEAVE员工离职EmployeeLeaveEvent
ORGANIZATION_ATTACH_EMPLOYEE组织关联员工OrganizationAttachEmployeeEvent
ORGANIZATION_DETACH_EMPLOYEE组织取关员工OrganizationDetachEmployeeEvent

账号管理事件

事件标签说明事件类
change_account_name修改账号名DingTalkGovEvent
frozen冻结账号DingTalkGovEvent
unfrozen解冻账号DingTalkGovEvent

其他事件

事件标签说明事件类
openplatform_app_update_tag开放平台应用通知DingTalkGovEvent
SendSuccessMsg消息发送成功事件DingTalkGovEvent

事件处理器

使用 @EventListener 注解监听浙政钉事件:

import com.jeeapp.dingtalkgov.event.*;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class DingTalkGovEventHandler {

@Autowired
private DingTalkGovTemplate dingTalkGovTemplate;

@Autowired
private OrganizationService organizationService;

@Autowired
private EmployeeService employeeService;

// 处理通用浙政钉事件
@EventListener
public void handleDingTalkGovEvent(DingTalkGovEvent event) {
log.info("收到浙政钉事件: eventTag={}, eventType={}",
event.getEventTag(), event.getEventType());
}

// 处理原始文本事件(可用于调试)
@EventListener
public void handlePlainTextEvent(PlainTextEvent event) {
log.debug("收到原始事件: {}", event.getPlainText());
}

/**
* 处理员工新增或更新事件
*/
@EventListener
public void handleEmployeeAddUpdate(EmployeeAddUpdateEvent event) {
EmployeeAddUpdateEventContent content = event.getContent();
log.info("员工新增或更新: employeeCode={}, name={}, optType={}",
content.getEmployeeCode(), content.getName(), content.getOptType());

try {
String employeeCode = content.getEmployeeCode();
String optType = content.getOptType(); // ADD: 新增, UPDATE: 更新

// 调用墨子 API 获取完整的员工信息
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();
EmployeeResponseData employee = moziOps.getEmployee(employeeCode);

// 保存或更新员工信息到本地
if ("ADD".equals(optType)) {
employeeService.createEmployee(employee);
log.info("新增员工成功: {}", employeeCode);
} else if ("UPDATE".equals(optType)) {
employeeService.updateEmployee(employee);
log.info("更新员工成功: {}", employeeCode);
}
} catch (Exception e) {
log.error("处理员工新增/更新事件失败", e);
}
}

/**
* 处理员工离职事件
*/
@EventListener
public void handleEmployeeLeave(EmployeeLeaveEvent event) {
EmployeeLeaveEventContent content = event.getContent();
log.info("员工离职: employeeCode={}, name={}",
content.getEmployeeCode(), content.getName());

try {
String employeeCode = content.getEmployeeCode();

// 更新员工状态为离职
employeeService.markEmployeeAsLeft(employeeCode);
log.info("员工离职处理成功: {}", employeeCode);
} catch (Exception e) {
log.error("处理员工离职事件失败", e);
}
}

/**
* 处理组织新增或更新事件
*/
@EventListener
public void handleOrganizationAddUpdate(OrganizationAddUpdateEvent event) {
OrganizationAddUpdateEventContent content = event.getContent();
log.info("组织新增或更新: organizationCode={}, name={}, optType={}",
content.getOrganizationCode(), content.getName(), content.getOptType());

try {
String organizationCode = content.getOrganizationCode();
String optType = content.getOptType(); // ADD: 新增, UPDATE: 更新

// 调用墨子 API 获取完整的组织信息
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();
OrganizationResponseData organization = moziOps.getOrganization(organizationCode);

// 保存或更新组织信息到本地
if ("ADD".equals(optType)) {
organizationService.createOrganization(organization);
log.info("新增组织成功: {}", organizationCode);
} else if ("UPDATE".equals(optType)) {
organizationService.updateOrganization(organization);
log.info("更新组织成功: {}", organizationCode);
}
} catch (Exception e) {
log.error("处理组织新增/更新事件失败", e);
}
}

/**
* 处理组织删除事件
*/
@EventListener
public void handleOrganizationRemove(OrganizationRemoveEvent event) {
OrganizationRemoveEventContent content = event.getContent();
log.info("组织删除: organizationCode={}", content.getOrganizationCode());

try {
String organizationCode = content.getOrganizationCode();

// 删除本地组织数据
organizationService.deleteOrganization(organizationCode);
log.info("删除组织成功: {}", organizationCode);
} catch (Exception e) {
log.error("处理组织删除事件失败", e);
}
}

/**
* 处理员工加入组织事件
*/
@EventListener
public void handleOrganizationAttachEmployee(OrganizationAttachEmployeeEvent event) {
OrganizationAttachEmployeeEventContent content = event.getContent();
log.info("员工加入组织: employeeCode={}, organizationCode={}",
content.getEmployeeCode(), content.getOrganizationCode());

try {
String employeeCode = content.getEmployeeCode();
String organizationCode = content.getOrganizationCode();

// 建立员工和组织的关联关系
employeeService.attachToOrganization(employeeCode, organizationCode);
log.info("员工加入组织成功: {} -> {}", employeeCode, organizationCode);
} catch (Exception e) {
log.error("处理员工加入组织事件失败", e);
}
}

/**
* 处理员工离开组织事件
*/
@EventListener
public void handleOrganizationDetachEmployee(OrganizationDetachEmployeeEvent event) {
OrganizationDetachEmployeeEventContent content = event.getContent();
log.info("员工离开组织: employeeCode={}, organizationCode={}",
content.getEmployeeCode(), content.getOrganizationCode());

try {
String employeeCode = content.getEmployeeCode();
String organizationCode = content.getOrganizationCode();

// 解除员工和组织的关联关系
employeeService.detachFromOrganization(employeeCode, organizationCode);
log.info("员工离开组织成功: {} <- {}", employeeCode, organizationCode);
} catch (Exception e) {
log.error("处理员工离开组织事件失败", e);
}
}

/**
* 处理组织父节点变更事件
*/
@EventListener
public void handleOrganizationParentChanged(OrganizationParentChangedEvent event) {
OrganizationParentChangedEventContent content = event.getContent();
log.info("组织父节点变更: organizationCode={}, oldParent={}, newParent={}",
content.getOrganizationCode(),
content.getOldParentOrganizationCode(),
content.getNewParentOrganizationCode());

try {
String organizationCode = content.getOrganizationCode();
String newParentCode = content.getNewParentOrganizationCode();

// 更新组织的父节点
organizationService.updateParentOrganization(organizationCode, newParentCode);
log.info("组织父节点变更成功: {}", organizationCode);
} catch (Exception e) {
log.error("处理组织父节点变更事件失败", e);
}
}
}

异步事件处理

对于耗时较长的业务处理,建议使用异步方式:

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
@EnableAsync
public class AsyncDingTalkGovEventHandler {

@Async
@EventListener
public void handleEmployeeAddUpdateAsync(EmployeeAddUpdateEvent event) {
// 异步处理员工新增/更新事件
try {
EmployeeAddUpdateEventContent content = event.getContent();

// 执行耗时操作
// 1. 同步员工信息到多个系统
// 2. 发送通知
// 3. 更新缓存

Thread.sleep(2000); // 模拟耗时操作
log.info("异步处理员工事件完成: {}", content.getEmployeeCode());
} catch (Exception e) {
log.error("异步处理员工事件失败", e);
}
}
}

回调控制器

创建接收事件回调的控制器:

import com.jeeapp.dingtalkgov.DingTalkGovTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;

@RestController
@RequestMapping("/callback/dingtalkgov")
public class DingTalkGovCallbackController {

@Autowired
private DingTalkGovTemplate dingTalkGovTemplate;

/**
* 浙政钉事件回调接口
* 需要在浙政钉开放平台配置此回调 URL
*/
@PostMapping
public Map<String, String> handleCallback(
@RequestParam(required = false) String signature,
@RequestParam(required = false) String timestamp,
@RequestParam(required = false) String nonce,
@RequestBody(required = false) String encryptMsg) {

try {
// 发布事件到 Spring 事件系统
return dingTalkGovTemplate.publishEvent(signature, timestamp, nonce, encryptMsg);
} catch (Exception e) {
log.error("处理浙政钉回调失败", e);
return Map.of("msg_signature", "error", "encrypt", "");
}
}
}

回调配置步骤

1. 配置应用信息

application.yml 中配置回调参数:

dingtalkgov:
access-key: your-app-key
secret-key: your-app-secret
tenant-id: 123456
callback:
url: https://your-domain.com/callback/dingtalkgov
aes-key: your-aes-key-32-chars-long
token: your-callback-token
version: v2

2. 在开放平台配置回调地址

  • 登录浙政钉开放平台:https://openplatform-portal.dg-work.cn/portal/#/helpdoc
  • 进入应用管理,选择您的应用
  • 找到"事件订阅"或"回调配置"菜单
  • 配置回调 URL:https://your-domain.com/callback/dingtalkgov
  • 配置加密参数:
    • 数据加密密钥(AES Key):32位随机字符串
    • 签名 Token:随机字符串
  • 选择需要订阅的事件类型
  • 点击保存,等待验证通过

3. 测试回调

使用浙政钉开放平台提供的测试工具发送测试事件,验证回调是否配置成功。

事件处理最佳实践

1. 幂等性处理

事件可能会重复推送,需要保证处理的幂等性:

@EventListener
public void handleEmployeeAddUpdate(EmployeeAddUpdateEvent event) {
EmployeeAddUpdateEventContent content = event.getContent();
String employeeCode = content.getEmployeeCode();

// 使用事件ID进行幂等性校验
String eventId = event.getEventId();
if (eventProcessedRepository.exists(eventId)) {
log.info("事件已处理,跳过: {}", eventId);
return;
}

try {
// 处理业务逻辑
employeeService.syncEmployee(employeeCode);

// 记录事件已处理
eventProcessedRepository.save(eventId);
} catch (Exception e) {
log.error("处理员工事件失败", e);
}
}

2. 快速响应

回调接口应该快速返回,避免超时:

@PostMapping
public Map<String, String> handleCallback(...) {
try {
// 快速响应,将事件发布到消息队列或事件总线
Map<String, String> response = dingTalkGovTemplate.publishEvent(
signature, timestamp, nonce, encryptMsg);

// 立即返回成功响应(处理时间 < 3秒)
return response;
} catch (Exception e) {
log.error("处理回调失败", e);
return Map.of("msg_signature", "error", "encrypt", "");
}
}

// 使用异步或消息队列处理业务逻辑
@Async
@EventListener
public void handleEventAsync(DingTalkGovEvent event) {
// 执行耗时的业务处理
}

3. 错误重试

对于处理失败的事件,实现重试机制:

@EventListener
@Retryable(
value = {Exception.class},
maxAttempts = 3,
backoff = @Backoff(delay = 2000, multiplier = 2)
)
public void handleEmployeeLeave(EmployeeLeaveEvent event) {
EmployeeLeaveEventContent content = event.getContent();

try {
// 处理业务逻辑
employeeService.markEmployeeAsLeft(content.getEmployeeCode());
} catch (Exception e) {
log.error("处理员工离职事件失败,将重试", e);
throw e; // 抛出异常触发重试
}
}

@Recover
public void recoverEmployeeLeave(Exception e, EmployeeLeaveEvent event) {
// 重试多次后仍然失败,记录到死信队列或告警
log.error("处理员工离职事件失败,已达到最大重试次数", e);
deadLetterQueue.send(event);
}

4. 日志记录

详细记录事件处理过程,便于问题排查:

@EventListener
public void handleOrganizationAddUpdate(OrganizationAddUpdateEvent event) {
OrganizationAddUpdateEventContent content = event.getContent();

// 记录事件接收
log.info("收到组织变更事件: eventId={}, eventTag={}, organizationCode={}",
event.getEventId(), event.getEventTag(), content.getOrganizationCode());

long startTime = System.currentTimeMillis();
try {
// 处理业务逻辑
organizationService.syncOrganization(content.getOrganizationCode());

// 记录处理成功
long duration = System.currentTimeMillis() - startTime;
log.info("组织变更事件处理成功: organizationCode={}, duration={}ms",
content.getOrganizationCode(), duration);
} catch (Exception e) {
// 记录处理失败
long duration = System.currentTimeMillis() - startTime;
log.error("组织变更事件处理失败: organizationCode={}, duration={}ms",
content.getOrganizationCode(), duration, e);
throw e;
}
}

5. 监控告警

实现事件处理的监控和告警:

@Component
public class DingTalkGovEventMonitor {

@Autowired
private MeterRegistry meterRegistry;

@EventListener
public void monitorEvent(DingTalkGovEvent event) {
// 记录事件接收次数
Counter.builder("dingtalkgov.event.received")
.tag("eventTag", event.getEventTag())
.tag("eventType", event.getEventType())
.register(meterRegistry)
.increment();
}

@EventListener
public void monitorEmployeeEvent(EmployeeAddUpdateEvent event) {
// 记录处理耗时
Timer.builder("dingtalkgov.event.process.duration")
.tag("eventTag", "EMPLOYEE_ADD_UPDATE")
.register(meterRegistry)
.record(() -> {
// 处理业务逻辑
});
}
}

API 参考

DingTalkGovTemplate 主要方法

方法名参数返回值
getAccessToken()String - 访问令牌
getJsapiToken()String - JSAPI 令牌
getAppUser(authCode)authCodeAppUserResponseData - 用户信息
opsForMozi()DingTalkGovMoziOperations - 墨子操作
opsForMessage()DingTalkGovMessageOperations - 消息操作
publishEvent(headers, params)headers, paramsCallbackResult - 事件发布结果
execute(request)requestResponse - 执行请求

DingTalkGovMoziOperations 主要方法

方法名参数返回值
getEmployee(employeeCode)employeeCodeEmployeeResponseData - 员工信息
getEmployees(employeeCodes)employeeCodesList<EmployeeResponseData> - 员工列表
getEmployees(organizationCode)organizationCodeList<EmployeeResponseData> - 组织员工列表
getEmployeeCodes(organizationCode)organizationCodeList<String> - 员工编码列表
getEmployeePositions(employeeCode)employeeCodeList<EmployeePositionResponseData> - 职位列表
getAccount(employeeCode)employeeCodeAccountResponseData - 账号信息
getAccounts(employeeCodes)employeeCodesList<AccountResponseData> - 账号列表
getOrganization(organizationCode)organizationCodeOrganizationResponseData - 组织信息
getOrganizations(organizationCodes)organizationCodesList<OrganizationResponseData> - 组织列表
getOrganizationPath(organizationCode)organizationCodeList<OrganizationPathResponseData> - 组织路径
getSubOrganizationCodes(organizationCode)organizationCodeList<String> - 子组织编码列表
getAuthScopes()AuthScopesV2ResponseData - 授权范围
getPropertyCode(name)nameOptional<String> - 属性编码

DingTalkGovMessageOperations 主要方法

方法名参数返回值
createWorkNotification()WorkNotificationBuilder - 工作通知构建器

完整示例

完整应用示例

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import com.jeeapp.dingtalkgov.DingTalkGovTemplate;
import com.jeeapp.dingtalkgov.DingTalkGovMoziOperations;
import com.jeeapp.dingtalkgov.DingTalkGovMessageOperations;
import com.jeeapp.dingtalkgov.response.data.EmployeeResponseData;
import com.jeeapp.dingtalkgov.response.data.OrganizationResponseData;
import com.jeeapp.dingtalkgov.response.data.AppUserResponseData;
import java.util.List;
import java.util.Map;

@SpringBootApplication
public class DingTalkGovDemoApplication {
public static void main(String[] args) {
SpringApplication.run(DingTalkGovDemoApplication.class, args);
}
}

@RestController
@RequestMapping("/api/dingtalkgov")
public class DingTalkGovApiController {

private final DingTalkGovTemplate dingTalkGovTemplate;

public DingTalkGovApiController(DingTalkGovTemplate dingTalkGovTemplate) {
this.dingTalkGovTemplate = dingTalkGovTemplate;
}

@GetMapping("/auth")
public Object authenticate(@RequestParam String authCode) {
try {
AppUserResponseData appUser = dingTalkGovTemplate.getAppUser(authCode);
return Map.of(
"employeeCode", appUser.getEmployeeCode(),
"name", appUser.getName()
);
} catch (Exception e) {
return Map.of("error", e.getMessage());
}
}

@GetMapping("/employee/{employeeCode}")
public Object getEmployee(@PathVariable String employeeCode) {
try {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();
EmployeeResponseData employee = moziOps.getEmployee(employeeCode);
return employee;
} catch (Exception e) {
return Map.of("error", e.getMessage());
}
}

@GetMapping("/organization/{organizationCode}")
public Object getOrganization(@PathVariable String organizationCode) {
try {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();
OrganizationResponseData organization = moziOps.getOrganization(organizationCode);
return organization;
} catch (Exception e) {
return Map.of("error", e.getMessage());
}
}

@GetMapping("/organization/{organizationCode}/employees")
public Object getOrganizationEmployees(@PathVariable String organizationCode) {
try {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();
List<EmployeeResponseData> employees = moziOps.getEmployees(organizationCode);
return employees;
} catch (Exception e) {
return Map.of("error", e.getMessage());
}
}

@PostMapping("/message/send")
public Object sendMessage(@RequestBody Map<String, String> request) {
try {
DingTalkGovMessageOperations messageOps = dingTalkGovTemplate.opsForMessage();

String messageId = messageOps.createWorkNotification()
.employeeCodes(request.get("employeeCode"))
.msgType("text")
.text(request.get("content"))
.send();

return Map.of("messageId", messageId);
} catch (Exception e) {
return Map.of("error", e.getMessage());
}
}
}

组织架构同步示例

import com.jeeapp.dingtalkgov.DingTalkGovTemplate;
import com.jeeapp.dingtalkgov.DingTalkGovMoziOperations;
import com.jeeapp.dingtalkgov.response.data.EmployeeResponseData;
import com.jeeapp.dingtalkgov.response.data.OrganizationResponseData;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class OrganizationSyncService {

@Autowired
private DingTalkGovTemplate dingTalkGovTemplate;

@Autowired
private OrganizationRepository organizationRepository;

@Autowired
private EmployeeRepository employeeRepository;

/**
* 定时同步组织架构
*/
@Scheduled(cron = "0 0 2 * * ?")
public void syncOrganizations() {
try {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();

// 获取根组织
OrganizationResponseData rootOrg = moziOps.getOrganization("root");
syncOrganizationRecursive(moziOps, rootOrg);

} catch (Exception e) {
log.error("组织架构同步失败", e);
}
}

/**
* 递归同步组织架构
*/
private void syncOrganizationRecursive(DingTalkGovMoziOperations moziOps,
OrganizationResponseData organization) throws Exception {
// 保存组织信息
organizationRepository.saveOrganization(organization);

// 同步组织下的员工
List<EmployeeResponseData> employees = moziOps.getEmployees(organization.getOrganizationCode());
for (EmployeeResponseData employee : employees) {
employeeRepository.saveEmployee(employee);
}

// 递归同步子组织
List<String> subOrgCodes = moziOps.getSubOrganizationCodes(organization.getOrganizationCode());
for (String subOrgCode : subOrgCodes) {
OrganizationResponseData subOrg = moziOps.getOrganization(subOrgCode);
syncOrganizationRecursive(moziOps, subOrg);
}
}

/**
* 增量同步(监听事件)
*/
@EventListener
public void handleOrganizationEvent(OrganizationAddUpdateEvent event) {
try {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();
OrganizationResponseData organization = moziOps.getOrganization(
event.getContent().getOrganizationCode()
);
organizationRepository.saveOrganization(organization);
} catch (Exception e) {
log.error("组织信息同步失败", e);
}
}

@EventListener
public void handleEmployeeEvent(EmployeeAddUpdateEvent event) {
try {
DingTalkGovMoziOperations moziOps = dingTalkGovTemplate.opsForMozi();
EmployeeResponseData employee = moziOps.getEmployee(
event.getContent().getEmployeeCode()
);
employeeRepository.saveEmployee(employee);
} catch (Exception e) {
log.error("员工信息同步失败", e);
}
}
}

相关资源