浙政钉集成
浙政钉集成 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) | authCode | AppUserResponseData - 用户信息 |
opsForMozi() | 无 | DingTalkGovMoziOperations - 墨子操作 |
opsForMessage() | 无 | DingTalkGovMessageOperations - 消息操作 |
publishEvent(headers, params) | headers, params | CallbackResult - 事件发布结果 |
execute(request) | request | Response - 执行请求 |
DingTalkGovMoziOperations 主要方法
| 方法名 | 参数 | 返回值 |
|---|---|---|
getEmployee(employeeCode) | employeeCode | EmployeeResponseData - 员工信息 |
getEmployees(employeeCodes) | employeeCodes | List<EmployeeResponseData> - 员工列表 |
getEmployees(organizationCode) | organizationCode | List<EmployeeResponseData> - 组织员工列表 |
getEmployeeCodes(organizationCode) | organizationCode | List<String> - 员工编码列表 |
getEmployeePositions(employeeCode) | employeeCode | List<EmployeePositionResponseData> - 职位列表 |
getAccount(employeeCode) | employeeCode | AccountResponseData - 账号信息 |
getAccounts(employeeCodes) | employeeCodes | List<AccountResponseData> - 账号列表 |
getOrganization(organizationCode) | organizationCode | OrganizationResponseData - 组织信息 |
getOrganizations(organizationCodes) | organizationCodes | List<OrganizationResponseData> - 组织列表 |
getOrganizationPath(organizationCode) | organizationCode | List<OrganizationPathResponseData> - 组织路径 |
getSubOrganizationCodes(organizationCode) | organizationCode | List<String> - 子组织编码列表 |
getAuthScopes() | 无 | AuthScopesV2ResponseData - 授权范围 |
getPropertyCode(name) | name | Optional<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);
}
}
}