Skip to content

Fix missing "late_rule" field in WxCpCropCheckinOption deserialization #3671

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,30 @@ public static class CheckinDate implements Serializable {
*/
@SerializedName("flex_off_duty_time")
private Integer flexOffDutyTime;

/**
* 是否允许弹性时间
*/
@SerializedName("allow_flex")
private Boolean allowFlex;

/**
* 迟到规则
*/
@SerializedName("late_rule")
private LateRule lateRule;

/**
* 最早可打卡时间限制
*/
@SerializedName("max_allow_arrive_early")
private Integer maxAllowArriveEarly;

/**
* 最晚可打卡时间限制
*/
@SerializedName("max_allow_arrive_late")
private Integer maxAllowArriveLate;
}

/**
Expand All @@ -160,6 +184,13 @@ public static class CheckinDate implements Serializable {
public static class CheckinTime implements Serializable {

private static final long serialVersionUID = -5507709858609705279L;

/**
* 时段id,为班次中某一堆上下班时间组合的id
*/
@SerializedName("time_id")
private Integer timeId;

/**
* 上班时间,表示为距离当天0点的秒数。
*/
Expand All @@ -183,6 +214,60 @@ public static class CheckinTime implements Serializable {
*/
@SerializedName("remind_off_work_sec")
private Integer remindOffWorkSec;

/**
* 休息开始时间,仅单时段支持,距离0点的秒
*/
@SerializedName("rest_begin_time")
private Integer restBeginTime;

/**
* 休息结束时间,仅单时段支持,距离0点的秒
*/
@SerializedName("rest_end_time")
private Integer restEndTime;

/**
* 是否允许休息
*/
@SerializedName("allow_rest")
private Boolean allowRest;

/**
* 最早可打卡时间,距离0点的秒数
*/
@SerializedName("earliest_work_sec")
private Integer earliestWorkSec;

/**
* 最晚可打卡时间,距离0点的秒数
*/
@SerializedName("latest_work_sec")
private Integer latestWorkSec;

/**
* 最早可下班打卡时间,距离0点的秒数
*/
@SerializedName("earliest_off_work_sec")
private Integer earliestOffWorkSec;

/**
* 最晚可下班打卡时间,距离0点的秒数
*/
@SerializedName("latest_off_work_sec")
private Integer latestOffWorkSec;

/**
* 上班无需打卡
*/
@SerializedName("no_need_checkon")
private Boolean noNeedCheckon;

/**
* 下班无需打卡
*/
@SerializedName("no_need_checkoff")
private Boolean noNeedCheckoff;
}

/**
Expand Down Expand Up @@ -438,6 +523,17 @@ public static class LateRule implements Serializable {

private static final long serialVersionUID = 5604969713950037053L;

/**
* 晚走的时间 距离最晚一个下班的时间单位:秒
*/
@SerializedName("offwork_after_time")
private Integer offWorkAfterTime;

/**
* 第二天第一个班次允许迟到的弹性时间单位:秒
*/
@SerializedName("onwork_flex_time")
private Integer onWorkFlexTime;

/**
* 是否允许超时下班(下班晚走次日晚到)允许时onwork_flex_time,offwork_after_time才有意义
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,206 @@ public void testOtInfoV2Deserialization() {
System.out.println(gson.toJson(option.getOtInfoV2()));
}

/**
* Test late_rule field deserialization in getCropCheckinOption response.
*/
@Test
public void testLateRuleDeserialization() {
// Test JSON with late_rule structure based on the issue #3323
String jsonWithLateRule = "{\n" +
" \"grouptype\": 1,\n" +
" \"groupid\": 1,\n" +
" \"checkindate\": [\n" +
" {\n" +
" \"workdays\": [1, 2, 3, 4, 5],\n" +
" \"checkintime\": [\n" +
" {\n" +
" \"time_id\": 1,\n" +
" \"work_sec\": 32400,\n" +
" \"off_work_sec\": 64800,\n" +
" \"remind_work_sec\": 31800,\n" +
" \"remind_off_work_sec\": 64800,\n" +
" \"rest_begin_time\": 43200,\n" +
" \"rest_end_time\": 48600,\n" +
" \"allow_rest\": true,\n" +
" \"earliest_work_sec\": 21600,\n" +
" \"latest_work_sec\": 64740,\n" +
" \"earliest_off_work_sec\": 32460,\n" +
" \"latest_off_work_sec\": 107940,\n" +
" \"no_need_checkon\": false,\n" +
" \"no_need_checkoff\": false\n" +
" }\n" +
" ],\n" +
" \"noneed_offwork\": false,\n" +
" \"limit_aheadtime\": 0,\n" +
" \"flex_on_duty_time\": 0,\n" +
" \"flex_off_duty_time\": 0,\n" +
" \"allow_flex\": false,\n" +
" \"late_rule\": {\n" +
" \"offwork_after_time\": 3600,\n" +
" \"onwork_flex_time\": 3600,\n" +
" \"allow_offwork_after_time\": true,\n" +
" \"timerules\": [\n" +
" {\n" +
" \"offwork_after_time\": 18000,\n" +
" \"onwork_flex_time\": 3600\n" +
" },\n" +
" {\n" +
" \"offwork_after_time\": 21600,\n" +
" \"onwork_flex_time\": 7200\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"max_allow_arrive_early\": 0,\n" +
" \"max_allow_arrive_late\": 0\n" +
" }\n" +
" ],\n" +
" \"groupname\": \"打卡\",\n" +
" \"need_photo\": false\n" +
"}";

WxCpCropCheckinOption option = WxCpGsonBuilder.create().fromJson(jsonWithLateRule, WxCpCropCheckinOption.class);
assertThat(option).isNotNull();
assertThat(option.getCheckinDate()).isNotNull();
assertThat(option.getCheckinDate().size()).isEqualTo(1);

WxCpCheckinGroupBase.CheckinDate checkinDate = option.getCheckinDate().get(0);
assertThat(checkinDate).isNotNull();
assertThat(checkinDate.getAllowFlex()).isFalse();
assertThat(checkinDate.getMaxAllowArriveEarly()).isEqualTo(0);
assertThat(checkinDate.getMaxAllowArriveLate()).isEqualTo(0);

// Test late_rule field
assertThat(checkinDate.getLateRule()).isNotNull();
assertThat(checkinDate.getLateRule().getOffWorkAfterTime()).isEqualTo(3600);
assertThat(checkinDate.getLateRule().getOnWorkFlexTime()).isEqualTo(3600);
assertThat(checkinDate.getLateRule().getAllowOffWorkAfterTime()).isTrue();
assertThat(checkinDate.getLateRule().getTimerules()).isNotNull();
assertThat(checkinDate.getLateRule().getTimerules().size()).isEqualTo(2);

// Test timerules
WxCpCheckinGroupBase.TimeRule firstRule = checkinDate.getLateRule().getTimerules().get(0);
assertThat(firstRule.getOffWorkAfterTime()).isEqualTo(18000);
assertThat(firstRule.getOnWorkFlexTime()).isEqualTo(3600);

// Test CheckinTime fields
assertThat(checkinDate.getCheckinTime()).isNotNull();
assertThat(checkinDate.getCheckinTime().size()).isEqualTo(1);

WxCpCheckinGroupBase.CheckinTime checkinTime = checkinDate.getCheckinTime().get(0);
assertThat(checkinTime.getTimeId()).isEqualTo(1);
assertThat(checkinTime.getRestBeginTime()).isEqualTo(43200);
assertThat(checkinTime.getRestEndTime()).isEqualTo(48600);
assertThat(checkinTime.getAllowRest()).isTrue();
assertThat(checkinTime.getEarliestWorkSec()).isEqualTo(21600);
assertThat(checkinTime.getLatestWorkSec()).isEqualTo(64740);
assertThat(checkinTime.getEarliestOffWorkSec()).isEqualTo(32460);
assertThat(checkinTime.getLatestOffWorkSec()).isEqualTo(107940);
assertThat(checkinTime.getNoNeedCheckon()).isFalse();
assertThat(checkinTime.getNoNeedCheckoff()).isFalse();

System.out.println("Successfully parsed late_rule and new checkintime fields:");
System.out.println(gson.toJson(option));
}

/**
* Test issue #3323 - full JSON from the issue report.
*/
@Test
public void testIssue3323FullJson() {
// Full JSON from issue #3323
String issueJson = "{\n" +
" \"grouptype\": 1,\n" +
" \"groupid\": 1,\n" +
" \"checkindate\": [\n" +
" {\n" +
" \"workdays\": [\n" +
" 1,\n" +
" 2,\n" +
" 3,\n" +
" 4,\n" +
" 5\n" +
" ],\n" +
" \"checkintime\": [\n" +
" {\n" +
" \"time_id\": 1,\n" +
" \"work_sec\": 32400,\n" +
" \"off_work_sec\": 64800,\n" +
" \"remind_work_sec\": 31800,\n" +
" \"remind_off_work_sec\": 64800,\n" +
" \"rest_begin_time\": 43200,\n" +
" \"rest_end_time\": 48600,\n" +
" \"allow_rest\": true,\n" +
" \"earliest_work_sec\": 21600,\n" +
" \"latest_work_sec\": 64740,\n" +
" \"earliest_off_work_sec\": 32460,\n" +
" \"latest_off_work_sec\": 107940,\n" +
" \"no_need_checkon\": false,\n" +
" \"no_need_checkoff\": false\n" +
" }\n" +
" ],\n" +
" \"noneed_offwork\": false,\n" +
" \"limit_aheadtime\": 0,\n" +
" \"flex_on_duty_time\": 0,\n" +
" \"flex_off_duty_time\": 0,\n" +
" \"allow_flex\": false,\n" +
" \"late_rule\": {\n" +
" \"offwork_after_time\": 3600,\n" +
" \"onwork_flex_time\": 3600,\n" +
" \"allow_offwork_after_time\": true,\n" +
" \"timerules\": [\n" +
" {\n" +
" \"offwork_after_time\": 18000,\n" +
" \"onwork_flex_time\": 3600\n" +
" },\n" +
" {\n" +
" \"offwork_after_time\": 21600,\n" +
" \"onwork_flex_time\": 7200\n" +
" },\n" +
" {\n" +
" \"offwork_after_time\": 28800,\n" +
" \"onwork_flex_time\": 10800\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"max_allow_arrive_early\": 0,\n" +
" \"max_allow_arrive_late\": 0\n" +
" }\n" +
" ],\n" +
" \"spe_workdays\": [],\n" +
" \"spe_offdays\": [],\n" +
" \"sync_holidays\": true,\n" +
" \"groupname\": \"打卡\",\n" +
" \"need_photo\": false,\n" +
" \"wifimac_infos\": [],\n" +
" \"note_can_use_local_pic\": true,\n" +
" \"allow_checkin_offworkday\": false,\n" +
" \"allow_apply_offworkday\": false,\n" +
" \"loc_infos\": []\n" +
" }";

WxCpCropCheckinOption option = WxCpGsonBuilder.create().fromJson(issueJson, WxCpCropCheckinOption.class);
assertThat(option).isNotNull();
assertThat(option.getGroupId()).isEqualTo(1);
assertThat(option.getGroupName()).isEqualTo("打卡");
assertThat(option.getCheckinDate()).isNotNull();
assertThat(option.getCheckinDate().size()).isEqualTo(1);

WxCpCheckinGroupBase.CheckinDate checkinDate = option.getCheckinDate().get(0);
assertThat(checkinDate.getLateRule()).isNotNull();
assertThat(checkinDate.getLateRule().getOffWorkAfterTime()).isEqualTo(3600);
assertThat(checkinDate.getLateRule().getOnWorkFlexTime()).isEqualTo(3600);
assertThat(checkinDate.getLateRule().getAllowOffWorkAfterTime()).isTrue();
assertThat(checkinDate.getLateRule().getTimerules()).isNotNull();
assertThat(checkinDate.getLateRule().getTimerules().size()).isEqualTo(3);

System.out.println("✓ Successfully parsed full JSON from issue #3323");
System.out.println("✓ Late Rule offwork_after_time: " + checkinDate.getLateRule().getOffWorkAfterTime());
System.out.println("✓ Late Rule onwork_flex_time: " + checkinDate.getLateRule().getOnWorkFlexTime());
System.out.println("✓ Late Rule allow_offwork_after_time: " + checkinDate.getLateRule().getAllowOffWorkAfterTime());
System.out.println("✓ Late Rule timerules count: " + checkinDate.getLateRule().getTimerules().size());
}

/**
* Test get approval info.
*
Expand Down