接入考勤系统接口说明
1.生成签名
简要描述
- 将商户号,秘钥,员工工号等参数生成签名
参与签名参数
| 参数名 | 必选 | 类型 | 说明 |
|---|---|---|---|
| staffCode | 是 | string | 员工工号 |
| neckName | 是 | string | 昵称 |
| merchantNo | 是 | string | 商户号 |
| signTime | 是 | string | 签名时间戳 |
签名示例代码
HashMap<String, String> params = new HashMap<>();
params.put("staffCode", "12400");
params.put("neckName", "nicky");
params.put("merchantId", "234319900");
params.put("signTime", "12560999199");
String sign = C2cScanSignatureUtil.generateSignature(params, oaFaceAccessReqVO.getMerchantNo(), oaFaceAccessReqVO.getMerchantSecret());
/**
* 签名工具类
*/
@Slf4j
public class OaSignatureUtil {
/**
* SHA1 加密
*
* @param input 待加密字符串
* @return SHA1 加密结果(小写)
*/
public static String sha1(String input) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] messageDigest = md.digest(input.getBytes("UTF-8"));
StringBuilder hexString = new StringBuilder();
for (byte b : messageDigest) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-1 algorithm not found", e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* MD5 加密
*
* @param input 待加密字符串
* @return MD5 加密结果(小写)
*/
public static String md5(String input) {
return CommonSignatureUtil.md5(input);
}
/**
* C2C_SCAN 通用签名方法
*
* @param params 待签名参数(包含 signTime,毫秒为单位)
* @param secret1 商户秘钥一
* @param secret2 商户秘钥二
* @return 签名结果
*/
public static String generateSignature(Map<String, String> params, String secret1, String secret2) {
// 1. 排除 sign 参数并按参数名 ASCII 自然排序
TreeMap<String, String> sortedParams = new TreeMap<>();
for (Map.Entry<String, String> entry : params.entrySet()) {
if (!"sign".equals(entry.getKey()) && StrUtil.isNotEmpty(entry.getValue())) {
sortedParams.put(entry.getKey(), entry.getValue());
}
}
// 2. 拼接待签名字符串:name=value&name=value
StringBuilder signString = new StringBuilder();
for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
signString.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
// 3. 参数拼接后拼接商户秘钥一:name=value&name=value&秘钥一
String step1 = signString + secret1;
log.info("C2C_SCAN signature step1 (before SHA1): {}", step1);
// 4. 将拼接结果通过 SHA1 加密获得摘要
String sha1Result = sha1(step1);
log.info("C2C_SCAN signature SHA1 result: {}", sha1Result);
// 5. 将加密后的字符串再拼接商户秘钥二:ABCDEFG&秘钥二
String step2 = sha1Result + "&" + secret2;
log.info("C2C_SCAN signature step2 (before MD5): {}", step2);
// 6. 将拼接结果通过 MD5 加密获得摘要
String finalSign = md5(step2);
log.info("C2C_SCAN signature final result: {}", finalSign);
return finalSign;
}
}
/**
* MD5加密
*
* @param input 待加密字符串
* @return MD5加密结果(小写)
*/
public static String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes("UTF-8"));
StringBuilder hexString = new StringBuilder();
for (byte b : messageDigest) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5 algorithm not found", e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}2.获取员工考勤页(接入方->考勤系统)
接口地址
- 商户域名+/api/app-api/oa/face/check-access
简要描述
- 获取员工考勤页
请求头参数
| 参数名 | 必选 | 类型 | 说明 | 参数值 |
|---|---|---|---|---|
| grant-type | 是 | Integer | 授权类型 | 2 |
| Referer | 是 | string | 商户域名 | 如'https://234.com' |
参数
| 参数名 | 必选 | 类型 | 说明 | 参数值 |
|---|---|---|---|---|
| source | 是 | Integer | 访问来源 0--内部访问 1--外部api访问 | 1 |
| staffCode | 是 | string | 员工工号 | 如'123444' |
| neckName | 是 | string | 员工昵称 | 如'nocky' |
| merchantNo | 是 | string | 商户号 | 如'7632141237' |
| merchantSecret | 是 | string | 商户秘钥 | 如'ru2873bdfasi83271jfda987jdsa' |
| sign | 是 | string | 签名 | 见@1'签名生成' |
| signTime | 是 | string | 签名时间戳 | 1234441111 |
| callbackUrl | 是 | string | 回调地址 | http://2412.com |
返回示例
{
"success": true,
"code": 0,
"data": {"url":"http://2342.com/h5/#face?staffCode=10000&neckName=nicky&source=1"}
}返回参数说明
| 参数名 | 类型 | 说明 |
|---|---|---|
| success | Boolean | 成功与否 |
| code | Integer | 错误码 |
| data | JsonOjbect | 返回信息 |
备注
- code 错误码 0 成功 非0 不成功 如1040203 员工工号已经存在
3.接收考勤系统返回在岗状态事件(考勤系统->接入方)
接口地址
- 接入时传入的回调地址
简要描述
- 接收考勤状态更新等消息
返回参数说明
| 参数名 | 类型 | 说明 |
|---|---|---|
| success | Boolean | 成功与否 |
| code | Integer | 错误码 |
| data | JsonObject | 返回信息 |
data参数
| 参数名 | 必选 | 类型 | 说明 |
|---|---|---|---|
| type | 是 | string | 事件名 |
| status | 是 | string | 员工上班状态 0-离线 1-在线 2-上厕所中 3-吃饭中 4-外出中 |
| staffCode | 是 | string | 员工工号 |
返回示例
{
"success": true,
"code": 0,
"data": {"type":"event.oa.status",staffCode":"12300","status":2}
}备注
- code 错误码 0 成功 非0 不成功 如1040206 工号不存在
4.通知关闭考勤页面(接入方->考勤系统)
接口地址
- 商户域名+/api/app-api/oa/face/exit-oa
简要描述
- 关闭考勤页时通知结束考勤
请求头参数
| 参数名 | 必选 | 类型 | 说明 | 参数值 |
|---|---|---|---|---|
| grant-type | 是 | Integer | 授权类型 | 2 |
| Referer | 是 | string | 商户域名 | 如'https://234.com' |
参数
| 参数名 | 必选 | 类型 | 说明 | 参数值 |
|---|---|---|---|---|
| source | 是 | Integer | 访问来源 0--内部访问 1--外部api访问 | 1 |
| staffCode | 是 | string | 员工工号 | 如'123444' |
| neckName | 是 | string | 员工昵称 | 如'nocky' |
| merchantNo | 是 | string | 商户号 | 如'7632141237' |
| merchantSecret | 是 | string | 商户秘钥 | 如'ru2873bdfasi83271jfda987jdsa' |
| sign | 是 | string | 签名 | 见@1'签名生成' |
| signTime | 是 | string | 签名时间戳 | 1234441111 |
返回示例
{
"success": true,
"code": 0
}返回参数说明
| 参数名 | 类型 | 说明 |
|---|---|---|
| success | Boolean | 成功与否 |
| code | Integer | 错误码 |
| data | JsonOjbect | 返回信息 |
备注
- code 错误码 0 成功 非0 不成功 如1040203 员工工号已经存在