# 附录三:接入代码示例
# 调用勤策OpenAPI接口
# 前提条件
开始之前先联系企业专属客户经理申请企业OpenAPI调用授权信息openid和appkey,确认允许调用接口的授权范围和ip白名单等信息,确保账号可用。 表格中附件为调用勤策OpenAPI示例代码,包括java,python,C#,Go,NodeJS,php等语言。 openapi_tools-1.3.2.jar和openapi_tools-1.1.9.jar分别适配不同JDK版本。 demo-openapi.zip 为各种语言调用示例代码。
| 项目 | 名称 | 大小 |
|---|---|---|
| JDK1.8 OpenAPI SDK | openapi_tools-1.3.2.jar (opens new window) | 43 KB |
| JDK1.7 OpenAPI SDK | openapi_tools-1.1.9.jar (opens new window) | 34 KB |
| JAVA开发工具包 | demo-openapi.zip (opens new window) | 1.61 MB |
# 快速开始
// 引用JAVA开发工具包openapi_tools.jar
import com.alibaba.fastjson.JSONObject;
import com.waiqin365.openapi.tools.WQApiHandler;
import com.waiqin365.openapi.vo.WQOpenApi;
import com.waiqin365.openapi.vo.WQRequest;
import com.waiqin365.openapi.vo.WQResponse;
public class TestSample
{
public static void main(String[] args)
{
// 企业所在数据中心服务地址
String region = "https://openapi.qince.com";
// 企业唯一授权令牌OPENID
String openid = "847740919664405****";
// 企业授权加密秘钥
String appkey = "hgm*************";
WQOpenApi wqOpenApi = new WQOpenApi(openid, appkey);
wqOpenApi.setOpenurl(String.format("%s/api", region));
WQRequest request = new WQRequest();
request.setWqOpenApi(wqOpenApi);
request.setModule("organization");
request.setVersion("v1");
request.setOperation("queryOrganization");
JSONObject body = new JSONObject();
request.setRequestdata(body.toJSONString());
try
{
WQResponse res = WQApiHandler.handleOpenApi(request);
log.info(request.getRequestUrlV2());
log.info("return_code:" + res.getReturn_code());
log.info("return_message:" + res.getReturn_msg());
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
/** 引用代码 demo-openapi.zip */
String domain = "https://openapi.qince.com";
String openid = "547416644250459****";
String appkey = "ipA1CBM7RoXJb6****";
Long msgId = java.util.UUID.randomUUID().getLeastSignificantBits() * -1;
String timestamp = OpenAPIUtil.getTimestamp();
String jsonData = "{}";
String nonce = OpenAPIUtil.randomString();
String digest = OpenAPIUtil.makeDigest(jsonData ,timestamp, nonce, appkey);
String url = domain + "/api/organization/v1/queryOrganization/" + openid + "/" + timestamp + "/" + nonce + "/" + msgId;
String resp = OpenAPIUtil.postJsonString(url, jsonData, digest);
System.out.println("勤策响应结果:" + resp);
# 引用代码demo-openapi.zip test_waiqin_openapi.py
domain = "https://openapi.qince.com"
openid = "547416644250459****"
appkey = "ipA1CBM7R********"
msg_id = uuid.uuid4().int & 0x7FFFFFFFFFFFFFFF # least significant bits, positive
timestamp = get_timestamp()
json_data = "{}"
nonce = random_string()
digest = make_digest(json_data, timestamp, nonce, appkey)
url = f"{domain}/api/organization/v1/queryOrganization/{openid}/{timestamp}/{nonce}/{msg_id}"
resp = requests.post(url, data=json_data, headers={
"Content-Type": "application/json; charset=UTF-8",
"encrypt-version": "v2",
"digest": digest
}, timeout=5)
print(f"勤策响应结果:{resp.text}")
/** 引用代码demo-openapi.zip test_waiqin_openapi.cs */
static async Task Main(string[] args)
{
var msgId = Math.Abs(BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0));
var timestamp = GetTimestamp();
var jsonData = "{}";
var nonce = RandomString();
var digest = MakeDigest(jsonData, timestamp, nonce, Appkey);
var url = $"{Domain}/api/organization/v1/queryOrganization/{Openid}/{timestamp}/{nonce}/{msgId}";
using var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(5);
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
var request = new HttpRequestMessage(HttpMethod.Post, url) { Content = content };
request.Headers.Add("encrypt-version", "v2");
request.Headers.Add("digest", digest);
var response = await client.SendAsync(request);
var resp = await response.Content.ReadAsStringAsync();
Console.WriteLine($"勤策响应结果:{resp}");
}
// 引用代码demo-openapi.zip test_waiqin_openapi.go
const (
domain = "https://openapi.qince.com"
openid = "547416644250459****"
appkey = "ipA1CBM7R********"
codeChars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz_0123456789"
)
func main() {
msgId := uuid.New().ID()
timestamp := getTimestamp()
jsonData := "{}"
nonce := randomString()
digest := makeDigest(jsonData, timestamp, nonce, appkey)
url := fmt.Sprintf("%s/api/organization/v1/queryOrganization/%s/%s/%s/%d", domain, openid, timestamp, nonce, msgId)
req, _ := http.NewRequest("POST", url, strings.NewReader(jsonData))
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
req.Header.Set("encrypt-version", "v2")
req.Header.Set("digest", digest)
client := &http.Client{Timeout: 5 * time.Second}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("勤策响应结果:%s\n", string(body))
}
/** 引用代码demo-openapi.zip test_waiqin_openapi.js */
const domain = "https://openapi.qince.com";
const openid = "547416644250459****";
const appkey = "ipA1CBM7R********";
const msgId = BigInt.asUintN(64, BigInt('0x' + uuidV4().replace(/-/g, '')));
const timestamp = getTimestamp();
const jsonData = "{}";
const nonce = randomString();
const digest = makeDigest(jsonData, timestamp, nonce, appkey);
const path = `/api/organization/v1/queryOrganization/${openid}/${timestamp}/${nonce}/${msgId}`;
const url = new URL(path, domain);
const options = {
hostname: url.hostname,
port: url.port,
path: url.pathname,
method: 'POST',
timeout: 5000,
headers: {
'Content-Type': 'application/json; charset=UTF-8',
'encrypt-version': 'v2',
'digest': digest,
'Content-Length': Buffer.byteLength(jsonData)
}
};
const req = http.request(options, (res) => {
let body = '';
res.on('data', (chunk) => body += chunk);
res.on('end', () => {
console.log(`勤策响应结果:${body}`);
});
});
req.on('error', (e) => {
console.error(`发送请求失败:${e.message}`);
});
req.write(jsonData);
req.end();
# 接收勤策推送
# 快速开始
| 项目 | 名称 | 大小 |
|---|---|---|
| 接收勤策推送示例 | springboot-waiqin365.zip (opens new window) | 112 KB |
1.将示例代码导入IDEA之后启动运行。
2.在浏览器中输入,开始模拟请求数据:
http://127.0.0.1:8080/waiqin365/mock
3.在ProcessFactory中加入同步保存数据处理代码。建议接收到推送数据后保存入库完成即可同步返回,后面启动新线程ProcessSubThread处理业务逻辑
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.security.MessageDigest;
@Controller
@RequestMapping(value = "/")
public class ReceiverController
{
private static final Logger logger = LoggerFactory.getLogger(ReceiverController.class);
public static String PUSH_SECRET = "xxxxxxx";
@RequestMapping(value = "/receivce")
@ResponseBody
public JSONObject receivce(String msgId, String dataType, String dataId, String dataVersion, String dataFormat, String dataSource, String data, long timestamp,String digest, String status, @RequestParam(required = false) String statusTime, String tenantId)
{
logger.info("[NEW]===================================================");
logger.info("msgId: {}", msgId);
logger.info("dataType: {}", dataType);
logger.info("dataId: {}", dataId);
logger.info("dataVersion:{}", dataVersion);
logger.info("dataFormat: {}", dataFormat);
logger.info("dataSource: {}", dataSource);
logger.info("data: {}", data);
logger.info("timestamp: {}", timestamp);
logger.info("digest: {}", digest);
logger.info("status: {}", status);
logger.info("statusTime: {}", statusTime);
logger.info("tenantId: {}", tenantId);
StringBuffer mdata = new StringBuffer();
mdata.append(data).append("|").append(PUSH_SECRET).append("|").append(timestamp);
String ndigest = encodeMd5(mdata.toString());
logger.info("Calcdigest: {}", ndigest);
if(!ndigest.equals(digest))
{
JSONObject json = new JSONObject();
json.put("return_code", "1");
json.put("return_msg", "数据签名校验失败,请求不合法!");
return json;
}
// TODO: add proccess code
JSONObject json = new JSONObject();
json.put("return_code", "0");
return json;
}
public static synchronized String encodeMd5(String data)
{
MessageDigest digest = null;
if (digest == null)
{
try
{
digest = MessageDigest.getInstance("MD5");
digest.update(data.getBytes("UTF-8"));
byte[] hash = digest.digest();
StringBuffer buf = new StringBuffer(hash.length * 2);
int i;
for (i = 0; i < hash.length; i++)
{
if ((hash[i] & 0xff) < 0x10)
{
buf.append("0");
}
buf.append(Long.toString(hash[i] & 0xff, 16));
}
return buf.toString();
}
catch (Exception nsae)
{
System.err.println("Failed to load the MD5 MessageDigest. ");
}
}
return null;
}
}
const crypto = require('crypto');
const http = require('http');
const url = require('url');
const PUSH_SECRET = 'xxxxxx';
const PORT = 8080;
function md5(str) {
return crypto.createHash('md5').update(str, 'utf8').digest('hex');
}
const server = http.createServer((req, res) => {
const query = url.parse(req.url, true).query;
const msgId = query.msgId || '';
const dataType = query.dataType || '';
const dataId = query.dataId || '';
const dataVersion = query.dataVersion || '';
const dataFormat = query.dataFormat || '';
const dataSource = query.dataSource || '';
const data = query.data || '';
const timestamp = query.timestamp || '';
const digest = query.digest || '';
const status = query.status || '';
const statusTime = query.statusTime || '';
const tenantId = query.tenantId || '';
console.log('[NEW]=======================================================');
console.log('msgId: ' + msgId);
console.log('dataType: ' + dataType);
console.log('dataId: ' + dataId);
console.log('dataVersion:' + dataVersion);
console.log('dataFormat: ' + dataFormat);
console.log('dataSource: ' + dataSource);
console.log('data: ' + data);
console.log('timestamp: ' + timestamp);
console.log('digest: ' + digest);
console.log('status: ' + status);
console.log('statusTime: ' + statusTime);
console.log('tenantId: ' + tenantId);
const mdata = data + '|' + PUSH_SECRET + '|' + timestamp;
const ndigest = md5(mdata);
console.log('Calcdigest: ' + ndigest);
res.setHeader('Content-Type', 'application/json; charset=UTF-8');
if (ndigest !== digest) {
res.end(JSON.stringify({
return_code: '1',
return_msg: '数据签名校验失败,请求不合法!'
}));
return;
}
res.end(JSON.stringify({ return_code: '0' }));
});
server.listen(PORT, () => {
console.log(`Push message server running on port ${PORT}`);
});
← 系统字典表 aPaaS操作符说明 →