Преглед на файлове

【feat】【v3】
1.完善打卡结算逻辑

ChenYL преди 11 месеца
родител
ревизия
06cb906e1f

+ 29 - 7
doc/sql/update-v3.sql

@@ -87,6 +87,29 @@ CREATE TABLE `reward_history` (
   KEY `idx_RewardHis_CreationTime` (`creation_time`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='奖励兑换记录';
 
+CREATE TABLE `punch_in_stats_week` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `user_id` bigint NOT NULL COMMENT '用户id',
+  `task_unique_id` bigint NOT NULL COMMENT '任务唯一ID',
+  `stats_time` char(7) NOT NULL COMMENT '统计时间(格式:yyyy-周数)',
+  `punch_in_total_count` int NOT NULL COMMENT '本周需打卡数',
+  `punch_in_count` int NOT NULL COMMENT '本周已打卡数',
+  `punch_in_done_count` int NOT NULL COMMENT '本周完成打卡数',
+  `punch_in_rate` decimal(5,2) NOT NULL COMMENT '本周打卡率',
+  `punch_in_done_rate` decimal(5,2) NOT NULL COMMENT '本周打卡完成率',
+  `points` int NOT NULL COMMENT '本周获取积分数',
+  `created_by` bigint NOT NULL COMMENT '创建人',
+  `creation_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `last_updated_by` bigint NOT NULL COMMENT '最后更新人',
+  `last_update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
+  `version` bigint NOT NULL DEFAULT '1' COMMENT '版本号',
+  `delete_flag` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+  PRIMARY KEY (`id`),
+  KEY `idx_PIStatsMonth_UserId` (`user_id`),
+  KEY `idx_PIStatsMonth_TaskUniqueId` (`task_unique_id`),
+  KEY `idx_PIStatsMonth_StatsTime` (`stats_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='打卡任务周数据统计';
+
 CREATE TABLE `punch_in_stats_month` (
   `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
   `user_id` bigint NOT NULL COMMENT '用户id',
@@ -208,10 +231,11 @@ CREATE TABLE `punch_in_task_ext` (
 
 CREATE TABLE `punch_in_multi_task` (
   `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `user_id` bigint NOT NULL COMMENT '用户ID',
   `multi_task_unique_id` bigint NOT NULL COMMENT '打卡多任务唯一ID',
   `multi_task_status` varchar(10) NOT NULL COMMENT '任务状态(活跃-ACTIVE,归档-ARCHIVE)',
   `multi_task_version` int NOT NULL COMMENT '任务修改版本',
-  `user_id` bigint NOT NULL COMMENT '用户ID',
+  `task_points_status` varchar(10) NOT NULL COMMENT '是否启用多任务积分计算(ENABLED-启用,DISABLED-关闭)',
   `punch_in_method` varchar(10) NOT NULL COMMENT '打卡方式(次数-COUNT,比率-RATE)',
   `punch_in_done_count` int DEFAULT NULL COMMENT '打卡完成次数',
   `punch_in_done_rate` decimal(5,2) DEFAULT NULL COMMENT '打卡完成率',
@@ -327,7 +351,7 @@ CREATE TABLE `punch_in_status` (
   `user_id` bigint NOT NULL COMMENT '用户ID',
   `punch_in_multi_task_unique_id` bigint DEFAULT NULL COMMENT '打卡多任务唯一ID',
   `punch_in_task_unique_id` bigint DEFAULT NULL COMMENT '打卡任务唯一ID',
-  `consecutive_status` varchar(20) NOT NULL COMMENT '连续打卡状态(正常打卡-NORMAL、中断--INTERRUPTED)',
+  `consecutive_status` varchar(20) NOT NULL COMMENT '连续打卡状态(连续打卡-CONSECUTIVE、中断--INTERRUPTED)',
   `consecutive_day` int NOT NULL COMMENT '连续天数,第一天开始就等于1',
   `start_date` date NOT NULL COMMENT '开始日期',
   `end_date` date DEFAULT NULL COMMENT '结束日期',
@@ -480,7 +504,8 @@ ADD COLUMN `grace_status` varchar(10) DEFAULT NULL COMMENT '是否启用宽限
 ADD COLUMN `grace_day` int DEFAULT NULL COMMENT '宽限期(单位:天)',
 ADD COLUMN `interrupted_day` int DEFAULT NULL COMMENT '打卡中断天数(单位:天)',
 ADD COLUMN `penalty_day` int DEFAULT NULL COMMENT '惩罚天数(单位:天)',
-ADD COLUMN `auto_status` varchar(10) DEFAULT NULL COMMENT '是否启用自动打卡(ENABLED-启用,DISABLED-关闭)';
+ADD COLUMN `auto_status` varchar(10) DEFAULT NULL COMMENT '是否启用自动打卡(ENABLED-启用,DISABLED-关闭)',
+ADD COLUMN `task_points_status` varchar(10) DEFAULT NULL COMMENT '是否启用任务积分计算(ENABLED-启用,DISABLED-关闭)';
 
 ALTER TABLE punch_settle.punch_in_task CHANGE created_by created_by bigint NOT NULL COMMENT '创建人' AFTER auto_status;
 ALTER TABLE punch_settle.punch_in_task CHANGE creation_time creation_time timestamp DEFAULT CURRENT_TIMESTAMP  NOT NULL COMMENT '创建时间' AFTER created_by;
@@ -645,7 +670,4 @@ update punch_settle.sys_dict_item set item_code = 'TIMING' where id = 11;
 update punch_settle.sys_dict_item set item_code = 'GTE' where id = 12;
 update punch_settle.sys_dict_item set item_code = 'LET' where id = 13;
 
-
-
-
-
+ALTER TABLE punch_settle.punch_in_task CHANGE task_points_status task_points_status varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '是否启用任务积分计算(ENABLED-启用,DISABLED-关闭)' AFTER auto_status;

+ 8 - 0
src/main/java/com/punchsettle/server/atomic/entity/PunchInMultiTask.java

@@ -14,6 +14,7 @@ import jakarta.persistence.Column;
 import jakarta.persistence.Table;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import org.checkerframework.checker.units.qual.N;
 
 /**
  * @author tyuio
@@ -54,6 +55,13 @@ public class PunchInMultiTask extends BaseEntity implements Serializable {
     @Column(name = "user_id")
     private Long userId;
 
+    /**
+     * 是否启用多任务积分计算(ENABLED-启用,DISABLED-关闭)
+     * @see CommonEnableStatusEnum
+     */
+    @Column(name = "task_points_status")
+    private CommonEnableStatusEnum taskPointsStatus;
+
     /**
      * 打卡方式(次数-COUNT,比率-RATE)
      * @see PunchInMethodMultiEnum

+ 81 - 0
src/main/java/com/punchsettle/server/atomic/entity/PunchInStatsWeek.java

@@ -0,0 +1,81 @@
+package com.punchsettle.server.atomic.entity;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+import com.punchsettle.server.common.pojo.BaseEntity;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Table;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 打卡任务周数据统计
+ * @date 2025/04/08 09:49
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Table(name = "punch_in_stats_week")
+public class PunchInStatsWeek extends BaseEntity implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = -590856736982272243L;
+
+    /**
+     * 用户id
+     */
+    @Column(name = "user_id")
+    private Long userId;
+
+    /**
+     * 任务唯一ID
+     */
+    @Column(name = "task_unique_id")
+    private Long taskUniqueId;
+
+    /**
+     * 统计时间(格式:yyyy-周数)
+     */
+    @Column(name = "stats_time")
+    private String statsTime;
+
+    /**
+     * 本周需打卡数
+     */
+    @Column(name = "punch_in_total_count")
+    private Integer punchInTotalCount;
+
+    /**
+     * 本周已打卡数
+     */
+    @Column(name = "punch_in_count")
+    private Integer punchInCount;
+
+    /**
+     * 本周完成打卡数
+     */
+    @Column(name = "punch_in_done_count")
+    private Integer punchInDoneCount;
+
+    /**
+     * 本周打卡率
+     */
+    @Column(name = "punch_in_rate")
+    private BigDecimal punchInRate;
+
+    /**
+     * 本周打卡完成率
+     */
+    @Column(name = "punch_in_done_rate")
+    private BigDecimal punchInDoneRate;
+
+    /**
+     * 本周获取积分数
+     */
+    @Column(name = "points")
+    private Integer points;
+}

+ 7 - 4
src/main/java/com/punchsettle/server/atomic/entity/PunchInStatus.java

@@ -2,9 +2,11 @@ package com.punchsettle.server.atomic.entity;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.time.LocalDate;
 import java.util.Date;
 
 import com.punchsettle.server.common.pojo.BaseEntity;
+import com.punchsettle.server.constant.ConsecutiveStatusEnum;
 import jakarta.persistence.Column;
 import jakarta.persistence.Table;
 import lombok.Data;
@@ -43,10 +45,11 @@ public class PunchInStatus extends BaseEntity implements Serializable {
     private Long punchInTaskUniqueId;
 
     /**
-     * 连续打卡状态(正常打卡-NORMAL、中断--INTERRUPTED)
+     * 连续打卡状态(连续打卡-CONSECUTIVE、中断--INTERRUPTED)
+     * @see ConsecutiveStatusEnum
      */
     @Column(name = "consecutive_status")
-    private String consecutiveStatus;
+    private ConsecutiveStatusEnum consecutiveStatus;
 
     /**
      * 连续天数,第一天开始就等于1
@@ -58,12 +61,12 @@ public class PunchInStatus extends BaseEntity implements Serializable {
      * 开始日期
      */
     @Column(name = "start_date")
-    private Date startDate;
+    private LocalDate startDate;
 
     /**
      * 结束日期
      */
     @Column(name = "end_date")
-    private Date endDate;
+    private LocalDate endDate;
 
 }

+ 7 - 0
src/main/java/com/punchsettle/server/atomic/entity/PunchInTask.java

@@ -223,4 +223,11 @@ public class PunchInTask extends BaseEntity implements Serializable {
     @Column(name = "auto_status")
     private CommonEnableStatusEnum autoStatus;
 
+    /**
+     * 是否启用任务积分计算(ENABLED-启用,DISABLED-关闭)
+     * @see CommonEnableStatusEnum
+     */
+    @Column(name = "task_points_status")
+    private CommonEnableStatusEnum taskPointsStatus;
+
 }

+ 3 - 1
src/main/java/com/punchsettle/server/atomic/entity/PunchInTaskExt.java

@@ -5,6 +5,7 @@ import java.io.Serializable;
 
 import com.punchsettle.server.common.pojo.BaseEntity;
 
+import com.punchsettle.server.constant.PunchInDimensionEnum;
 import jakarta.persistence.Column;
 import jakarta.persistence.Table;
 import lombok.Data;
@@ -38,9 +39,10 @@ public class PunchInTaskExt extends BaseEntity implements Serializable {
 
     /**
      * 使用维度(一天-ONE_DAY,多天-MULTI_DAY)
+     * @see PunchInDimensionEnum
      */
     @Column(name = "dimension")
-    private String dimension;
+    private PunchInDimensionEnum dimension;
 
     /**
      * 起始值(单位:次)

+ 2 - 2
src/main/java/com/punchsettle/server/constant/ConsecutiveStatusEnum.java

@@ -7,13 +7,13 @@ import lombok.Getter;
  * @author tyuio
  * @version 1.0.0
  * @date 2025/4/9 12:24
- * @description 连续打卡状态枚举(正常打卡-NORMAL、中断--INTERRUPTED)
+ * @description 连续打卡状态枚举(连续打卡-NORMAL、中断--INTERRUPTED)
  */
 @Getter
 @AllArgsConstructor
 public enum ConsecutiveStatusEnum {
 
-    NORMAL("正常打卡"),
+    CONSECUTIVE("连续打卡"),
     INTERRUPTED("中断打卡");
 
     /**

+ 23 - 0
src/main/java/com/punchsettle/server/constant/PunchInDimensionEnum.java

@@ -0,0 +1,23 @@
+package com.punchsettle.server.constant;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @date 2025/4/15 9:06
+ * @description 打卡拓展信息使用维度(一天-ONE_DAY,多天-MULTI_DAY)
+ */
+@Getter
+@AllArgsConstructor
+public enum PunchInDimensionEnum {
+
+    ONE_DAY("一天"),
+    MULTI_DAY("多天");
+
+    /**
+     * 名称
+     */
+    private String name;
+}

+ 17 - 2
src/main/java/com/punchsettle/server/service/manager/IPunchInManager.java

@@ -3,6 +3,11 @@ package com.punchsettle.server.service.manager;
 import java.util.List;
 
 import com.punchsettle.server.atomic.entity.PunchInMultiTask;
+import com.punchsettle.server.atomic.entity.PunchInMultiTaskExt;
+import com.punchsettle.server.atomic.entity.PunchInMultiTaskHistory;
+import com.punchsettle.server.atomic.entity.PunchInStatsMonth;
+import com.punchsettle.server.atomic.entity.PunchInStatsWeek;
+import com.punchsettle.server.atomic.entity.PunchInStatus;
 import com.punchsettle.server.atomic.entity.PunchInTask;
 import com.punchsettle.server.atomic.entity.PunchInTaskExt;
 import com.punchsettle.server.atomic.entity.PunchInTaskHistory;
@@ -103,11 +108,21 @@ public interface IPunchInManager {
     PunchInStatusEnum judgePunchInStatusInMultiTask(PunchInMultiTask punchInMultiTask, List<PunchInTaskHistory> punchInTaskHistoryList, int multiTaskNum);
 
     /**
-     * 计算积分
+     * 计算任务积分
      * @param punchInTask 打卡任务
      * @param punchInTaskExtList 打卡任务拓展信息列表
      * @param punchInTaskHistory 打卡记录
      * @return
      */
-    int calculatePointsInTask(PunchInTask punchInTask, List<PunchInTaskExt> punchInTaskExtList, PunchInTaskHistory punchInTaskHistory);
+    int calculatePointsInTask(PunchInTask punchInTask, List<PunchInTaskExt> punchInTaskExtList, PunchInTaskHistory punchInTaskHistory, PunchInStatsWeek punchInStatsWeek, PunchInStatsMonth punchInStatsMonth, PunchInStatus punchInStatus);
+
+
+    /**
+     * 计算多任务积分
+     * @param punchInMultiTask
+     * @param punchInMultiTaskExts
+     * @param punchInMultiTaskHistory
+     * @return
+     */
+    int calculatePointsInMultiTask(PunchInMultiTask punchInMultiTask, List<PunchInMultiTaskExt> punchInMultiTaskExts, PunchInMultiTaskHistory punchInMultiTaskHistory, PunchInStatus punchInStatus);
 }

+ 144 - 11
src/main/java/com/punchsettle/server/service/manager/impl/PunchInManagerImpl.java

@@ -9,6 +9,7 @@ import java.time.YearMonth;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -17,8 +18,15 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import com.punchsettle.server.atomic.entity.PunchInMultiTask;
+import com.punchsettle.server.atomic.entity.PunchInMultiTaskExt;
+import com.punchsettle.server.atomic.entity.PunchInMultiTaskHistory;
+import com.punchsettle.server.atomic.entity.PunchInStatsMonth;
+import com.punchsettle.server.atomic.entity.PunchInStatsWeek;
+import com.punchsettle.server.atomic.entity.PunchInStatus;
 import com.punchsettle.server.atomic.entity.PunchInTaskExt;
+import com.punchsettle.server.constant.ConsecutiveStatusEnum;
 import com.punchsettle.server.constant.FullAttendancePeriodEnum;
+import com.punchsettle.server.constant.PunchInDimensionEnum;
 import com.punchsettle.server.constant.PunchInExtraMethodEnum;
 import com.punchsettle.server.constant.PunchInMethodMultiEnum;
 import com.punchsettle.server.constant.RepeatCategoryEnum;
@@ -558,34 +566,44 @@ public class PunchInManagerImpl implements IPunchInManager {
         return PunchInStatusEnum.UNDONE;
     }
 
-    // TODO 考虑把结算过程记录下来(每个一个积分项、是否双倍结算、是否全勤结算
     @Override
-    public int calculatePointsInTask(PunchInTask punchInTask, List<PunchInTaskExt> punchInTaskExtList, PunchInTaskHistory punchInTaskHistory) {
+    public int calculatePointsInTask(PunchInTask punchInTask, List<PunchInTaskExt> punchInTaskExts, PunchInTaskHistory punchInTaskHistory, PunchInStatsWeek punchInStatsWeek, PunchInStatsMonth punchInStatsMonth, PunchInStatus punchInStatus) {
         // 未完成打卡,积分为0
         if (PunchInStatusEnum.UNDONE.equals(punchInTaskHistory.getPunchInStatus())) {
             return 0;
         }
 
+        // 单次打卡中使用的拓展信息
+        List<PunchInTaskExt> punchInExtList = punchInTaskExts.stream().filter(v -> PunchInDimensionEnum.ONE_DAY.equals(v.getDimension())).collect(Collectors.toList());
+        // 打卡任务使用的拓展信息
+        List<PunchInTaskExt> punchInTaskExtList = punchInTaskExts.stream().filter(v -> PunchInDimensionEnum.MULTI_DAY.equals(v.getDimension())).collect(Collectors.toList());
+
         // 单个任务积分=基本积分+(可选)额外积分+(可选)连续完成额外积分+法定节假日(含周末)双倍奖励+全勤双倍奖励
         // 基本积分
         int basicPoints = Optional.ofNullable(punchInTask.getPoints()).orElse(0);
-        basicPoints += calculateExtraPointsInTask(punchInTask, punchInTaskExtList, punchInTaskHistory, calendarManager.judgeHoliday(punchInTaskHistory.getPunchInDate()));
+
+        // 额外积分
+        basicPoints += calculateExtraPointsInTask(punchInTask, punchInExtList, punchInTaskHistory, calendarManager.judgeHoliday(punchInTaskHistory.getPunchInDate()));
 
         // 启用了全勤奖励
         int fullAttendancePoints = 0;
         if (CommonEnableStatusEnum.ENABLED.equals(punchInTask.getFullAttendanceStatus())) {
             LocalDate punchInDate = LocalDate.parse(punchInTaskHistory.getPunchInDate());
-            // 结算周期:周,并且结算日是周末,则双倍奖励
+            // 结算周期:周,并且结算日是周末,则双倍奖励
             if (FullAttendancePeriodEnum.WEEK.equals(punchInTask.getFullAttendancePeriod()) && punchInDate.getDayOfWeek().getValue() == 7) {
-                fullAttendancePoints = basicPoints;
+                int undoneCount = punchInStatsWeek.getPunchInTotalCount() - punchInStatsWeek.getPunchInDoneCount();
+                if (undoneCount <= punchInTask.getFullAttendanceFaultToleranceCnt()) {
+                    fullAttendancePoints = basicPoints;
+                }
             }
 
-            // 结算周期:月,结算日是当月最后一天,则双倍奖励
+            // 结算周期:月,结算日是当月最后一天,则双倍奖励
             if (FullAttendancePeriodEnum.MONTH.equals(punchInTask.getFullAttendancePeriod()) && punchInDate.lengthOfMonth() == punchInDate.getDayOfMonth()) {
-                fullAttendancePoints = basicPoints;
+                int undoneCount = punchInStatsMonth.getPunchInTotalCount() - punchInStatsMonth.getPunchInDoneCount();
+                if (undoneCount <= punchInTask.getFullAttendanceFaultToleranceCnt()) {
+                    fullAttendancePoints = basicPoints;
+                }
             }
-
-            // TODO 无法判断容错率,要加一个专门的统计表才行,每周、每月的统计
         }
 
         // 启用了法定节假日(含周末)双倍奖励
@@ -594,7 +612,37 @@ public class PunchInManagerImpl implements IPunchInManager {
             holidayPoints = basicPoints;
         }
 
-        return basicPoints + fullAttendancePoints + holidayPoints;
+        // 连续完成额外积分
+        int taskPoints = 0;
+        if (CommonEnableStatusEnum.ENABLED.equals(punchInTask.getTaskPointsStatus()) && ConsecutiveStatusEnum.CONSECUTIVE.equals(punchInStatus.getConsecutiveStatus())) {
+            // punchInTaskExtList 根据InitialValue倒序排列
+            punchInTaskExtList.sort(Comparator.comparing(PunchInTaskExt::getInitialValue).reversed());
+            // 第二轮标志
+            boolean secondRound = false;
+            // 上一轮的值
+            int prevInitialValue = 0;
+            for (PunchInTaskExt punchInTaskExt : punchInTaskExtList) {
+                // 比较结果
+                int compareValue = punchInStatus.getConsecutiveDay().compareTo(punchInTaskExt.getInitialValue());
+                // 如果连续日期小于initialValue,则跳过
+                if (compareValue == -1) {
+                    continue;
+                }
+                // 额外奖励数
+                int extraCount = 0;
+                // 如果连续日期大于等于initialValue,则进行第一次计算,并把第二轮标志位设置为true,第二轮/后续轮只需要用上一轮的值进行计算
+                if (secondRound) {
+                    extraCount = prevInitialValue - punchInTaskExt.getInitialValue();
+                } else if (compareValue >= 0) {
+                    extraCount = punchInStatus.getConsecutiveDay() - punchInTaskExt.getInitialValue() + 1;
+                    secondRound = true;
+                }
+                taskPoints +=  punchInTaskExt.getExtraPoints() * extraCount;
+                prevInitialValue = punchInTaskExt.getInitialValue();
+            }
+        }
+
+        return basicPoints + fullAttendancePoints + holidayPoints + taskPoints;
     }
 
     /**
@@ -660,5 +708,90 @@ public class PunchInManagerImpl implements IPunchInManager {
         return 0;
     }
 
-    // TODO 多任务结算
+    @Override
+    public int calculatePointsInMultiTask(PunchInMultiTask punchInMultiTask, List<PunchInMultiTaskExt> punchInMultiTaskExts, PunchInMultiTaskHistory punchInMultiTaskHistory, PunchInStatus punchInStatus) {
+        // 未完成打卡或没有启用多任务积分计算,积分为0
+        if (PunchInStatusEnum.UNDONE.equals(punchInMultiTaskHistory.getPunchInStatus())
+            || CommonEnableStatusEnum.ENABLED.equals(punchInMultiTask.getTaskPointsStatus())) {
+            return 0;
+        }
+
+        // 单次打卡中使用的拓展信息
+        List<PunchInMultiTaskExt> punchInExtList = punchInMultiTaskExts.stream().filter(v -> PunchInDimensionEnum.ONE_DAY.equals(v.getDimension())).collect(Collectors.toList());
+        // 打卡任务使用的拓展信息
+        List<PunchInMultiTaskExt> punchInTaskExtList = punchInMultiTaskExts.stream().filter(v -> PunchInDimensionEnum.MULTI_DAY.equals(v.getDimension())).collect(Collectors.toList());
+
+        // 多个任务积分=多任务基本积分+多任务额外积分+连续完成额外积分
+        // 基本积分
+        int basicPoints = punchInMultiTask.getPoints();
+
+        // 额外积分
+        int extraPoints = 0;
+        // 额外次数
+        int extractCount = punchInMultiTaskHistory.getPunchInDoneCount() - punchInMultiTask.getPunchInDoneCount();
+        // 固定计算
+        if (PunchInExtraMethodEnum.FIXED.equals(punchInMultiTask.getExtraMethod())) {
+            extraPoints = extractCount * punchInMultiTask.getExtraPoints();
+        }
+        // 区间计算
+        if (PunchInExtraMethodEnum.INTERVAL.equals(punchInMultiTask.getExtraMethod()) && !CollectionUtils.isEmpty(punchInMultiTaskExts)) {
+            // punchInTaskExtList 根据InitialValue倒序排列
+            punchInExtList.sort(Comparator.comparing(PunchInMultiTaskExt::getInitialValue).reversed());
+            // 第二轮标志
+            boolean secondRound = false;
+            // 上一轮的值
+            int prevInitialValue = 0;
+            for (PunchInMultiTaskExt punchInTaskExt : punchInTaskExtList) {
+                // 比较结果
+                int compareValue = Integer.compare(extractCount, punchInTaskExt.getInitialValue());
+                // 如果连续日期小于initialValue,则跳过
+                if (compareValue == -1) {
+                    continue;
+                }
+                // 额外奖励数
+                int tempExtraCount = 0;
+                // 如果连续日期大于等于initialValue,则进行第一次计算,并把第二轮标志位设置为true,第二轮/后续轮只需要用上一轮的值进行计算
+                if (secondRound) {
+                    tempExtraCount = prevInitialValue - punchInTaskExt.getInitialValue();
+                } else if (compareValue >= 0) {
+                    tempExtraCount = extractCount - punchInTaskExt.getInitialValue() + 1;
+                    secondRound = true;
+                }
+                extraPoints +=  punchInTaskExt.getExtraPoints() * tempExtraCount;
+                prevInitialValue = punchInTaskExt.getInitialValue();
+            }
+        }
+
+        // 连续完成额外积分
+        int taskPoints = 0;
+        if (CommonEnableStatusEnum.ENABLED.equals(punchInMultiTask.getTaskPointsStatus()) && ConsecutiveStatusEnum.CONSECUTIVE.equals(punchInStatus.getConsecutiveStatus())) {
+            // punchInTaskExtList 根据InitialValue倒序排列
+            punchInTaskExtList.sort(Comparator.comparing(PunchInMultiTaskExt::getInitialValue).reversed());
+            // 第二轮标志
+            boolean secondRound = false;
+            // 上一轮的值
+            int prevInitialValue = 0;
+            for (PunchInMultiTaskExt punchInTaskExt : punchInTaskExtList) {
+                // 比较结果
+                int compareValue = punchInStatus.getConsecutiveDay().compareTo(punchInTaskExt.getInitialValue());
+                // 如果连续日期小于initialValue,则跳过
+                if (compareValue == -1) {
+                    continue;
+                }
+                // 额外奖励数
+                int tempExtraCount = 0;
+                // 如果连续日期大于等于initialValue,则进行第一次计算,并把第二轮标志位设置为true,第二轮/后续轮只需要用上一轮的值进行计算
+                if (secondRound) {
+                    tempExtraCount = prevInitialValue - punchInTaskExt.getInitialValue();
+                } else if (compareValue >= 0) {
+                    tempExtraCount = punchInStatus.getConsecutiveDay() - punchInTaskExt.getInitialValue() + 1;
+                    secondRound = true;
+                }
+                taskPoints +=  punchInTaskExt.getExtraPoints() * tempExtraCount;
+                prevInitialValue = punchInTaskExt.getInitialValue();
+            }
+        }
+
+        return basicPoints + extraPoints + taskPoints;
+    }
 }