Explorar o código

【feat】【第二版开发】
1.增加功能代码
2.完善第二版的计数文档
3.完善第二版的ddl语句

ChenYL hai 1 ano
pai
achega
2fa4c86d9d
Modificáronse 39 ficheiros con 1121 adicións e 251 borrados
  1. 6 0
      doc/sql/update-v2.sql
  2. 51 25
      doc/技术文档.md
  3. 3 0
      src/main/java/com/punchsettle/server/atomic/entity/LotteryScratchRecord.java
  4. 10 0
      src/main/java/com/punchsettle/server/atomic/entity/PunchInRecord.java
  5. 16 2
      src/main/java/com/punchsettle/server/atomic/entity/PunchInRecordSettlementRela.java
  6. 3 1
      src/main/java/com/punchsettle/server/atomic/mapper/PunchInRecordMapper.java
  7. 10 0
      src/main/java/com/punchsettle/server/atomic/service/ILotteryScratchRecordService.java
  8. 17 0
      src/main/java/com/punchsettle/server/atomic/service/IPunchInRecordService.java
  9. 8 0
      src/main/java/com/punchsettle/server/atomic/service/IPunchInSettlementService.java
  10. 20 0
      src/main/java/com/punchsettle/server/atomic/service/impl/LotteryScratchRecordServiceImpl.java
  11. 21 3
      src/main/java/com/punchsettle/server/atomic/service/impl/PunchInRecordServiceImpl.java
  12. 4 6
      src/main/java/com/punchsettle/server/atomic/service/impl/PunchInRecordSettlementRelaServiceImpl.java
  13. 3 0
      src/main/java/com/punchsettle/server/atomic/service/impl/PunchInServiceImpl.java
  14. 21 0
      src/main/java/com/punchsettle/server/atomic/service/impl/PunchInSettlementServiceImpl.java
  15. 36 0
      src/main/java/com/punchsettle/server/constant/PunchInSettleTypeEnum.java
  16. 8 7
      src/main/java/com/punchsettle/server/constant/PunchInStatusEnum.java
  17. 33 0
      src/main/java/com/punchsettle/server/constant/PunchInStatusViewEnum.java
  18. 5 0
      src/main/java/com/punchsettle/server/dto/punchin/PunchInQuery.java
  19. 12 3
      src/main/java/com/punchsettle/server/dto/punchin/PunchInRecordDto.java
  20. 2 1
      src/main/java/com/punchsettle/server/dto/punchin/PunchInWithRecordDto.java
  21. 27 0
      src/main/java/com/punchsettle/server/dto/punchin/RemakePunchInDto.java
  22. 5 0
      src/main/java/com/punchsettle/server/dto/scratch/ScratchQuery.java
  23. 47 0
      src/main/java/com/punchsettle/server/dto/settle/SettleDto.java
  24. 55 0
      src/main/java/com/punchsettle/server/dto/settle/SettleInfoDto.java
  25. 27 0
      src/main/java/com/punchsettle/server/dto/settle/SettleQuery.java
  26. 43 0
      src/main/java/com/punchsettle/server/dto/settle/SettleRequest.java
  27. 46 0
      src/main/java/com/punchsettle/server/dto/settle/SettleResultDto.java
  28. 22 0
      src/main/java/com/punchsettle/server/service/controller/PunchInController.java
  29. 53 0
      src/main/java/com/punchsettle/server/service/controller/ScratchController.java
  30. 43 0
      src/main/java/com/punchsettle/server/service/controller/SettleController.java
  31. 0 30
      src/main/java/com/punchsettle/server/service/controller/TaskController.java
  32. 13 0
      src/main/java/com/punchsettle/server/service/manager/IPunchInManager.java
  33. 2 2
      src/main/java/com/punchsettle/server/service/manager/IScratchManager.java
  34. 23 7
      src/main/java/com/punchsettle/server/service/manager/ISettleManager.java
  35. 63 10
      src/main/java/com/punchsettle/server/service/manager/impl/PunchInManagerImpl.java
  36. 36 3
      src/main/java/com/punchsettle/server/service/manager/impl/ScratchManagerImpl.java
  37. 320 123
      src/main/java/com/punchsettle/server/service/manager/impl/SettleManagerImpl.java
  38. 2 3
      src/main/java/com/punchsettle/server/service/manager/impl/TaskManagerImpl.java
  39. 5 25
      src/main/java/com/punchsettle/server/utiis/DateUtils.java

+ 6 - 0
doc/sql/update-v2.sql

@@ -161,3 +161,9 @@ ALTER TABLE punch_settle.lottery_scratch_record CHANGE `type` action_type int NO
 ALTER TABLE punch_settle.lottery_scratch_record ADD revoke_id bigint NULL COMMENT '撤销的刮刮乐记录ID';
 ALTER TABLE punch_settle.lottery_scratch_record CHANGE revoke_id revoke_id bigint NULL COMMENT '撤销的刮刮乐记录ID' AFTER action_type;
 ALTER TABLE punch_settle.lottery_scratch_record DROP COLUMN revoke_id;
+
+ALTER TABLE punch_settle.lottery_scratch_record CHANGE win_amount amount decimal(10,2) DEFAULT 0.00 NOT NULL COMMENT '金额(元)';
+
+ALTER TABLE punch_settle.punch_in_record ADD punch_in_status int DEFAULT 0 NOT NULL COMMENT '打卡状态(0-进行中,1-完成,2-未完成) ,结算后修改状态,单次打卡除外';
+ALTER TABLE punch_settle.punch_in_record CHANGE punch_in_status punch_in_status int DEFAULT 0 NOT NULL COMMENT '打卡状态(0-进行中,1-完成,2-未完成) ,结算后修改状态,单次打卡除外' AFTER time_track;
+

+ 51 - 25
doc/技术文档.md

@@ -27,9 +27,9 @@ ui设计工具:即时设计
 
      说明:简单获取头像、微信openid、昵称,然后用于后台数据处理和前端显示即可
 
-  2. (这个最后开发)昵称修改
+  2. (未完成,这个最后开发)昵称修改
 
-  3. (这个最后开发)头像修改
+  3. (未完成,这个最后开发)头像修改
 
 * 打卡任务
 
@@ -60,16 +60,42 @@ ui设计工具:即时设计
 
   4. (已开发)打卡
 
-  5. (这个最后开发,要考虑结算的问题)补打卡
+  5. (已开发,待自测)补打卡
 
-  6. (这个最后开发,要考虑结算的问题)误打卡撤销
+     对于不同的打卡类型,都是默认直接完成打卡
+
+     如果完全没有打过卡:按照当时的打卡规则,直接生成一条新的打卡记录和结算记录
+
+     如果打过卡但是未完成的
+
+     * 对于单次打卡:不存在这种情况
+     * 对于计数打卡:修改打卡记录的数据并记录修改值和对应的状态位,修改结算记录并记录修改值和对应的状态位
+     * 对于计时打卡:修改打卡记录的数据并记录修改值和对应的状态位,修改结算记录并记录修改值和对应的状态位
+
+  6. (已开发,待自测)误打卡撤销
+
+     有两种情况:
+
+     1. 当天撤销
+
+        对于单次打卡:直接删除打卡记录即可
+
+        对于计数打卡:直接扣减次数(-1)即可,次数变成0即删除打卡记录
+
+        对于计时打卡:直接删除打卡记录即可,然后重新再打卡即可
+
+     2. (这种情况应该不存在,打卡当时应该就能即时处理不会留到结算后)~~结算后撤销,对于不同的打卡类型,都是默认直接没有打过卡~~
+
+        ~~删除对应记录,根据对应任务反向计算即可,并记录修改值和对应的状态位~~
 
   7. (已开发)归档
 
-  8. 针对单一任务查询时间范围内的打卡信息
+  8. (未完成)针对单一任务查询时间范围内的打卡信息
 
      显示内容:具体到天是否已打卡、打卡记录、打卡统计(打卡次数、全勤率)
 
+     注意:这里在表增加一个状态值辅助判断
+
 * 奖励
 
   1. (已开发)查询当前用户拥有的奖励数
@@ -88,22 +114,22 @@ ui设计工具:即时设计
 
      显示内容:投入金额、中奖金额
 
-  2. (已开发,待自测)追加投入金额记录/中奖记录
+  2. (已开发)追加投入金额记录/中奖记录
 
-  3. (已开发,待自测)撤销投入金额记录/中奖记录
+  3. (已开发)撤销投入金额记录/中奖记录
 
-  4. 按时间范围的刮刮乐查询记录
+  4. (已开发)按时间范围的刮刮乐查询记录
 
      显示内容:投入金额记录、中奖记录
 
 * 结算
 
-  1. 手动运维接口,可以指定具体结算日期
-  2. 按时间范围查询当前用户的结算记录
+  1. (已完成,待自测)手动运维接口,可以指定具体结算日期
+  2. (已完成)按时间范围查询当前用户的结算记录
 
 * 系统
 
-  1. (已开发)每晚自动结算
+  1. (已开发,待自测)每晚自动结算
 
 
 
@@ -201,19 +227,20 @@ ui设计工具:即时设计
 
 表名:punch_in_record
 
-| 字段             | 类型        | 描述                               |
-| ---------------- | ----------- | ---------------------------------- |
-| id               | bigint      | 主键                               |
-| punch_in_id      | bigint      | 打卡任务表主键                     |
-| punch_in_date    | varchar(10) | 打卡日期                           |
-| count_track      | int         | 次数记录                           |
-| time_track       | time        | 时间记录                           |
-| created_by       | bigint      | 创建人                             |
-| creation_time    | timestamp   | 创建时间                           |
-| last_updated_by  | bigint      | 最后更信人                         |
-| last_update_time | timestamp   | 最后更新时间                       |
-| version          | bigint      | 版本号                             |
-| delete_flag      | tinyint     | 逻辑删除标志(0-未删除,1-已删除) |
+| 字段             | 类型        | 描述                                                         |
+| ---------------- | ----------- | ------------------------------------------------------------ |
+| id               | bigint      | 主键                                                         |
+| punch_in_id      | bigint      | 打卡任务表主键                                               |
+| punch_in_date    | varchar(10) | 打卡日期                                                     |
+| count_track      | int         | 次数记录                                                     |
+| time_track       | time        | 时间记录                                                     |
+| punch_in_status  | int         | 打卡状态(0-进行中,1-完成,2-未完成) ,结算后修改状态,单次打卡除外 |
+| created_by       | bigint      | 创建人                                                       |
+| creation_time    | timestamp   | 创建时间                                                     |
+| last_updated_by  | bigint      | 最后更信人                                                   |
+| last_update_time | timestamp   | 最后更新时间                                                 |
+| version          | bigint      | 版本号                                                       |
+| delete_flag      | tinyint     | 逻辑删除标志(0-未删除,1-已删除)                           |
 
 
 
@@ -400,4 +427,3 @@ ui设计工具:即时设计
 * [Mybatis拦截器教程及几个实用自定义拦截器分享](https://juejin.cn/post/7242129949179248700#heading-5)
 * [tk.mybatis 2.1.15 如何将枚举类型在类中进行自动转换](https://blog.csdn.net/qq_42766445/article/details/134196790)
 
-

+ 3 - 0
src/main/java/com/punchsettle/server/atomic/entity/LotteryScratchRecord.java

@@ -1,11 +1,13 @@
 package com.punchsettle.server.atomic.entity;
 
 import com.punchsettle.server.common.entity.BaseEntity;
+import com.punchsettle.server.common.typehandler.EnumValueTypeHandler;
 import com.punchsettle.server.constant.ScratchActionTypeEnum;
 import jakarta.persistence.Column;
 import jakarta.persistence.Table;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import tk.mybatis.mapper.annotation.ColumnType;
 
 import java.io.Serial;
 import java.io.Serializable;
@@ -34,6 +36,7 @@ public class LotteryScratchRecord extends BaseEntity implements Serializable {
     /**
      * 动作类型(0-投入/购买,1-中奖,2-撤销投入,3-撤销中奖)
      */
+    @ColumnType(typeHandler = EnumValueTypeHandler.class)
     @Column(name = "action_type")
     private ScratchActionTypeEnum actionType;
 

+ 10 - 0
src/main/java/com/punchsettle/server/atomic/entity/PunchInRecord.java

@@ -6,10 +6,13 @@ import java.time.LocalTime;
 
 import com.punchsettle.server.common.entity.BaseEntity;
 
+import com.punchsettle.server.common.typehandler.EnumValueTypeHandler;
+import com.punchsettle.server.constant.PunchInStatusEnum;
 import jakarta.persistence.Column;
 import jakarta.persistence.Table;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import tk.mybatis.mapper.annotation.ColumnType;
 
 /**
  * @description 打卡任务记录表 实体
@@ -48,4 +51,11 @@ public class PunchInRecord extends BaseEntity implements Serializable {
      */
     @Column(name = "time_track")
     private LocalTime timeTrack;
+
+    /**
+     * 打卡状态(0-进行中,1-完成,2-未完成) ,结算后修改状态,单次打卡除外
+     */
+    @ColumnType(typeHandler = EnumValueTypeHandler.class)
+    @Column(name = "punch_in_status")
+    private PunchInStatusEnum punchInStatus;
 }

+ 16 - 2
src/main/java/com/punchsettle/server/atomic/entity/PunchInRecordSettlementRela.java

@@ -5,10 +5,14 @@ import java.io.Serializable;
 
 import com.punchsettle.server.common.entity.BaseEntity;
 
+import com.punchsettle.server.common.typehandler.EnumValueTypeHandler;
+import com.punchsettle.server.constant.PunchInCategoryEnum;
+import com.punchsettle.server.constant.PunchInRuleEnum;
 import jakarta.persistence.Column;
 import jakarta.persistence.Table;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import tk.mybatis.mapper.annotation.ColumnType;
 
 /**
  * @description 打卡任务记录与结算关联表 实体
@@ -36,15 +40,25 @@ public class PunchInRecordSettlementRela extends BaseEntity implements Serializa
     @Column(name = "settlement_id")
     private Long settlementId;
 
+    /**
+     * 结算奖励数
+     */
+    @Column(name = "reward_num")
+    private Integer rewardNum;
+
     /**
      * 打卡类型(0-单次打卡,1-计数、2计时)
+     * @see PunchInCategoryEnum
      */
+    @ColumnType(typeHandler = EnumValueTypeHandler.class)
     @Column(name = "category")
-    private Integer category;
+    private PunchInCategoryEnum category;
 
     /**
      * 比较规则(0-大于,1-大于等于,2-小于,3-小于等于)
+     * @see PunchInRuleEnum
      */
+    @ColumnType(typeHandler = EnumValueTypeHandler.class)
     @Column(name = "rule")
-    private Integer rule;
+    private PunchInRuleEnum rule;
 }

+ 3 - 1
src/main/java/com/punchsettle/server/atomic/mapper/PunchInRecordMapper.java

@@ -2,6 +2,8 @@ package com.punchsettle.server.atomic.mapper;
 
 import com.punchsettle.server.atomic.entity.PunchInRecord;
 
+import tk.mybatis.mapper.additional.insert.InsertListMapper;
+import tk.mybatis.mapper.additional.update.batch.BatchUpdateSelectiveMapper;
 import tk.mybatis.mapper.common.Mapper;
 
 /**
@@ -10,5 +12,5 @@ import tk.mybatis.mapper.common.Mapper;
  * @date 2024/11/25 10:57
  * @author tyuio
  */
-public interface PunchInRecordMapper extends Mapper<PunchInRecord> {
+public interface PunchInRecordMapper extends Mapper<PunchInRecord>, InsertListMapper<PunchInRecord>, BatchUpdateSelectiveMapper<PunchInRecord> {
 }

+ 10 - 0
src/main/java/com/punchsettle/server/atomic/service/ILotteryScratchRecordService.java

@@ -1,6 +1,9 @@
 package com.punchsettle.server.atomic.service;
 
 import com.punchsettle.server.atomic.entity.LotteryScratchRecord;
+import com.punchsettle.server.dto.scratch.ScratchQuery;
+
+import java.util.List;
 
 /**
  * @author tyuio
@@ -28,4 +31,11 @@ public interface ILotteryScratchRecordService {
      * @return
      */
     LotteryScratchRecord selectById(Long id);
+
+    /**
+     * 按条件查询
+     * @param query
+     * @return
+     */
+    List<LotteryScratchRecord> listByCondition(ScratchQuery query);
 }

+ 17 - 0
src/main/java/com/punchsettle/server/atomic/service/IPunchInRecordService.java

@@ -19,12 +19,24 @@ public interface IPunchInRecordService {
      */
     void insert(PunchInRecord record);
 
+    /**
+     * 批量新增
+     * @param records
+     */
+    void batchInsert(List<PunchInRecord> records);
+
     /**
      * 更新打卡记录
      * @param record
      */
     void update(PunchInRecord record);
 
+    /**
+     * 批量更新
+     * @param records
+     */
+    void batchUpdate(List<PunchInRecord> records);
+
     /**
      * 根据查询条件查询打卡记录
      * @param query
@@ -38,4 +50,9 @@ public interface IPunchInRecordService {
      * @return
      */
     List<PunchInRecord> listByCondition(PunchInRecordQuery query);
+
+    /**
+     * 根据ID删除
+     */
+    void delete(Long id);
 }

+ 8 - 0
src/main/java/com/punchsettle/server/atomic/service/IPunchInSettlementService.java

@@ -3,6 +3,7 @@ package com.punchsettle.server.atomic.service;
 import java.util.List;
 
 import com.punchsettle.server.atomic.entity.PunchInSettlement;
+import com.punchsettle.server.dto.settle.SettleQuery;
 
 /**
  * @author tyuio
@@ -17,4 +18,11 @@ public interface IPunchInSettlementService {
      * @param punchInSettlements
      */
     void batchInsert(List<PunchInSettlement> punchInSettlements);
+
+    /**
+     * 按条件查询
+     * @param query
+     * @return
+     */
+    List<PunchInSettlement> listByCondition(SettleQuery query);
 }

+ 20 - 0
src/main/java/com/punchsettle/server/atomic/service/impl/LotteryScratchRecordServiceImpl.java

@@ -4,9 +4,14 @@ import com.punchsettle.server.atomic.entity.LotteryScratchRecord;
 import com.punchsettle.server.atomic.mapper.LotteryScratchRecordMapper;
 import com.punchsettle.server.atomic.service.ILotteryScratchRecordService;
 import com.punchsettle.server.common.utils.Assert;
+import com.punchsettle.server.dto.scratch.ScratchQuery;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import tk.mybatis.mapper.weekend.Weekend;
+import tk.mybatis.mapper.weekend.WeekendCriteria;
 
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -41,4 +46,19 @@ public class LotteryScratchRecordServiceImpl implements ILotteryScratchRecordSer
 
         return scratchRecordMapper.selectByPrimaryKey(id);
     }
+
+    @Override
+    public List<LotteryScratchRecord> listByCondition(ScratchQuery query) {
+        Assert.isNull(query);
+
+        Weekend<LotteryScratchRecord> weekend = Weekend.of(LotteryScratchRecord.class);
+        WeekendCriteria<LotteryScratchRecord, Object> criteria = weekend.weekendCriteria();
+        if (StringUtils.hasText(query.getStartDate()) && StringUtils.hasText(query.getEndDate())) {
+            criteria.andBetween(LotteryScratchRecord::getCreationTime, query.getStartDate(), query.getEndDate());
+        }
+        if (!Objects.isNull(query.getUserId())) {
+            criteria.andEqualTo(LotteryScratchRecord::getUserId, query.getUserId());
+        }
+        return scratchRecordMapper.selectByExample(weekend);
+    }
 }

+ 21 - 3
src/main/java/com/punchsettle/server/atomic/service/impl/PunchInRecordServiceImpl.java

@@ -13,7 +13,7 @@ import com.punchsettle.server.atomic.service.IPunchInRecordService;
 import com.punchsettle.server.common.utils.Assert;
 import com.punchsettle.server.dto.punchin.PunchInRecordQuery;
 
-import io.micrometer.common.util.StringUtils;
+import org.springframework.util.StringUtils;
 import tk.mybatis.mapper.weekend.Weekend;
 import tk.mybatis.mapper.weekend.WeekendCriteria;
 
@@ -35,12 +35,24 @@ public class PunchInRecordServiceImpl implements IPunchInRecordService {
         punchInRecordMapper.insertSelective(record);
     }
 
+    @Override
+    public void batchInsert(List<PunchInRecord> records) {
+        Assert.notEmpty(records);
+        punchInRecordMapper.insertList(records);
+    }
+
     @Override
     public void update(PunchInRecord record) {
         Assert.isNull(record);
         punchInRecordMapper.updateByPrimaryKeySelective(record);
     }
 
+    @Override
+    public void batchUpdate(List<PunchInRecord> records) {
+        Assert.notEmpty(records);
+        punchInRecordMapper.batchUpdateSelective(records);
+    }
+
     @Override
     public PunchInRecord selectOneByCondition(PunchInRecordQuery query) {
         Assert.isNull(query);
@@ -50,7 +62,7 @@ public class PunchInRecordServiceImpl implements IPunchInRecordService {
         if (!Objects.isNull(query.getPunchInId())) {
             criteria.andEqualTo(PunchInRecord::getPunchInId, query.getPunchInId());
         }
-        if (StringUtils.isNotBlank(query.getPunchInDate())) {
+        if (StringUtils.hasText(query.getPunchInDate())) {
             criteria.andEqualTo(PunchInRecord::getPunchInDate, query.getPunchInDate());
         }
         return punchInRecordMapper.selectOneByExample(weekend);
@@ -64,7 +76,7 @@ public class PunchInRecordServiceImpl implements IPunchInRecordService {
 
         Weekend<PunchInRecord> weekend = Weekend.of(PunchInRecord.class);
         WeekendCriteria<PunchInRecord, Object> criteria = weekend.weekendCriteria();
-        if (StringUtils.isNotBlank(query.getStartDate()) && StringUtils.isNotBlank(query.getEndDate())) {
+        if (StringUtils.hasText(query.getStartDate()) && StringUtils.hasText(query.getEndDate())) {
             criteria.andBetween(PunchInRecord::getPunchInDate, query.getStartDate(), query.getEndDate());
         }
         if (!CollectionUtils.isEmpty(query.getPunchInIds())) {
@@ -72,4 +84,10 @@ public class PunchInRecordServiceImpl implements IPunchInRecordService {
         }
         return punchInRecordMapper.selectByExample(weekend);
     }
+
+    @Override
+    public void delete(Long id) {
+        Assert.isNull(id);
+        punchInRecordMapper.deleteByPrimaryKey(id);
+    }
 }

+ 4 - 6
src/main/java/com/punchsettle/server/atomic/service/impl/PunchInRecordSettlementRelaServiceImpl.java

@@ -2,6 +2,7 @@ package com.punchsettle.server.atomic.service.impl;
 
 import java.util.List;
 
+import com.punchsettle.server.common.utils.Assert;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
@@ -23,11 +24,8 @@ public class PunchInRecordSettlementRelaServiceImpl implements IPunchInRecordSet
     private PunchInRecordSettlementRelaMapper punchInRecordSettlementRelaMapper;
 
     @Override
-    public void batchInsert(List<PunchInRecordSettlementRela> punchInRecordSettlementRelas) {
-        if (CollectionUtils.isEmpty(punchInRecordSettlementRelas)) {
-            return;
-        }
-
-        punchInRecordSettlementRelaMapper.insertList(punchInRecordSettlementRelas);
+    public void batchInsert(List<PunchInRecordSettlementRela> relas) {
+        Assert.notEmpty(relas);
+        punchInRecordSettlementRelaMapper.insertList(relas);
     }
 }

+ 3 - 0
src/main/java/com/punchsettle/server/atomic/service/impl/PunchInServiceImpl.java

@@ -48,6 +48,9 @@ public class PunchInServiceImpl implements IPunchInService {
         if (!CollectionUtils.isEmpty(query.getUserIds())) {
             criteria.andIn(PunchIn::getCreatedBy, query.getUserIds());
         }
+        if (!CollectionUtils.isEmpty(query.getPunchInIds())) {
+            criteria.andIn(PunchIn::getId, query.getPunchInIds());
+        }
         if (!Objects.isNull(query.getArchiveFlag())) {
             criteria.andEqualTo(PunchIn::getArchiveFlag, query.getArchiveFlag());
         }

+ 21 - 0
src/main/java/com/punchsettle/server/atomic/service/impl/PunchInSettlementServiceImpl.java

@@ -1,14 +1,20 @@
 package com.punchsettle.server.atomic.service.impl;
 
 import java.util.List;
+import java.util.Objects;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
 
 import com.punchsettle.server.atomic.entity.PunchInSettlement;
 import com.punchsettle.server.atomic.mapper.PunchInSettlementMapper;
 import com.punchsettle.server.atomic.service.IPunchInSettlementService;
 import com.punchsettle.server.common.utils.Assert;
+import com.punchsettle.server.dto.settle.SettleQuery;
+
+import tk.mybatis.mapper.weekend.Weekend;
+import tk.mybatis.mapper.weekend.WeekendCriteria;
 
 /**
  * @author tyuio
@@ -27,4 +33,19 @@ public class PunchInSettlementServiceImpl implements IPunchInSettlementService {
         Assert.notEmpty(punchInSettlements);
         punchInSettlementMapper.insertList(punchInSettlements);
     }
+
+    @Override
+    public List<PunchInSettlement> listByCondition(SettleQuery query) {
+        Assert.isNull(query);
+
+        Weekend<PunchInSettlement> weekend = Weekend.of(PunchInSettlement.class);
+        WeekendCriteria<PunchInSettlement, Object> criteria = weekend.weekendCriteria();
+        if (StringUtils.hasText(query.getStartDate()) && StringUtils.hasText(query.getEndDate())) {
+            criteria.andBetween(PunchInSettlement::getSettlementTime, query.getStartDate(), query.getEndDate());
+        }
+        if (!Objects.isNull(query.getUserId())) {
+            criteria.andEqualTo(PunchInSettlement::getUserId, query.getUserId());
+        }
+        return punchInSettlementMapper.selectByExample(weekend);
+    }
 }

+ 36 - 0
src/main/java/com/punchsettle/server/constant/PunchInSettleTypeEnum.java

@@ -0,0 +1,36 @@
+package com.punchsettle.server.constant;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.punchsettle.server.common.annotation.EnumValue;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 打卡的结算类型枚举
+ * @date 2024/12/14 16:2
+ */
+@Getter
+@AllArgsConstructor
+public enum PunchInSettleTypeEnum {
+
+
+    AUTO("定时任务结算", 1),
+    OPS("运维结算", 2),
+    REMAKE("补卡结算", 3)
+    ;
+
+    /**
+     * 名称
+     */
+    private String name;
+
+    /**
+     * 编码
+     */
+    @JsonValue
+    @EnumValue
+    private Integer code;
+}

+ 8 - 7
src/main/java/com/punchsettle/server/constant/PunchInStatusEnum.java

@@ -1,24 +1,24 @@
 package com.punchsettle.server.constant;
 
 import com.fasterxml.jackson.annotation.JsonValue;
+import com.punchsettle.server.common.annotation.EnumValue;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
 /**
  * @author tyuio
  * @version 1.0.0
- * @description 打卡状态 枚举
+ * @description 打卡状态(0-进行中,1-完成,2-未完成,3-补打卡),结算后修改状态,单次打卡除外
  * @date 2024/11/26 21:59
  */
 @Getter
 @AllArgsConstructor
 public enum PunchInStatusEnum {
 
-    UNCREATED("未创建打卡任务", "uncreated"),
-    PUNCH_IN("已打卡", "punchIn"),
-    UN_PUNCH_IN("没打卡", "unPunchIn"),
-    TODAY_UNKNOWN("当天打卡状态未知", "todayUnknown"),
-    FUTURE_TIME("未到打卡时间", "futureTime");
+    DOING("进行中", 0),
+    FINISH("完成", 1),
+    UN_FINISH("未完成", 2),
+    REMAKE_FINISH("完成(补打卡)", 3);
 
     /**
      * 名称
@@ -29,5 +29,6 @@ public enum PunchInStatusEnum {
      * 编码
      */
     @JsonValue
-    private String code;
+    @EnumValue
+    private Integer code;
 }

+ 33 - 0
src/main/java/com/punchsettle/server/constant/PunchInStatusViewEnum.java

@@ -0,0 +1,33 @@
+package com.punchsettle.server.constant;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 打卡状态 枚举
+ * @date 2024/11/26 21:59
+ */
+@Getter
+@AllArgsConstructor
+public enum PunchInStatusViewEnum {
+
+    UNCREATED("未创建打卡任务", "uncreated"),
+    PUNCH_IN("已打卡", "punchIn"),
+    UN_PUNCH_IN("没打卡", "unPunchIn"),
+    TODAY_UNKNOWN("当天打卡状态未知", "todayUnknown"),
+    FUTURE_TIME("未到打卡时间", "futureTime");
+
+    /**
+     * 名称
+     */
+    private String name;
+
+    /**
+     * 编码
+     */
+    @JsonValue
+    private String code;
+}

+ 5 - 0
src/main/java/com/punchsettle/server/dto/punchin/PunchInQuery.java

@@ -20,6 +20,11 @@ public class PunchInQuery {
      */
     private List<Long> userIds;
 
+    /**
+     * 打卡任务ID
+     */
+    private List<Long> punchInIds;
+
     /**
      * 是否归档标志(0-不是,1-是)
      */

+ 12 - 3
src/main/java/com/punchsettle/server/dto/punchin/PunchInRecordDto.java

@@ -1,6 +1,8 @@
 package com.punchsettle.server.dto.punchin;
 
-import com.punchsettle.server.constant.PunchInStatusEnum;
+import com.punchsettle.server.constant.PunchInStatusViewEnum;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -14,14 +16,21 @@ import lombok.EqualsAndHashCode;
 @EqualsAndHashCode
 public class PunchInRecordDto {
 
+    /**
+     * 打卡任务ID
+     */
+    @NotNull(message = "打卡任务ID不能为空")
+    private Long punchInId;
+
     /**
      * 打卡日期
      */
+    @NotBlank(message = "补打卡日期不能为空")
     private String punchInDate;
 
     /**
      * 打卡状态
-     * @see PunchInStatusEnum
+     * @see PunchInStatusViewEnum
      */
-    private PunchInStatusEnum punchInStatus;
+    private PunchInStatusViewEnum punchInStatus;
 }

+ 2 - 1
src/main/java/com/punchsettle/server/dto/punchin/PunchInWithRecordDto.java

@@ -3,6 +3,7 @@ package com.punchsettle.server.dto.punchin;
 import java.time.LocalTime;
 import java.util.List;
 
+import com.punchsettle.server.constant.PunchInCategoryEnum;
 import lombok.Data;
 
 /**
@@ -32,7 +33,7 @@ public class PunchInWithRecordDto {
     /**
      * 打卡类型(0-单次打卡,1-计数、2计时)
      */
-    private Integer category;
+    private PunchInCategoryEnum category;
 
     /**
      * 比较规则(0-大于,1-大于等于,2-小于,3-小于等于)

+ 27 - 0
src/main/java/com/punchsettle/server/dto/punchin/RemakePunchInDto.java

@@ -0,0 +1,27 @@
+package com.punchsettle.server.dto.punchin;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 补打卡dto
+ * @date 2024/12/14 14:40
+ */
+@Data
+public class RemakePunchInDto {
+
+    /**
+     * 打卡任务ID
+     */
+    @NotNull(message = "打卡任务ID不能为空")
+    private Long id;
+
+    /**
+     * 打卡日期
+     */
+    @NotBlank(message = "补打卡日期不能为空")
+    private String punchInDate;
+}

+ 5 - 0
src/main/java/com/punchsettle/server/dto/scratch/ScratchQuery.java

@@ -21,6 +21,11 @@ public class ScratchQuery {
      */
     private ScratchActionTypeEnum actionType;
 
+    /**
+     * 用户主键
+     */
+    private Long userId;
+
     /*
      * 查询时间范围 开始
      */

+ 47 - 0
src/main/java/com/punchsettle/server/dto/settle/SettleDto.java

@@ -0,0 +1,47 @@
+package com.punchsettle.server.dto.settle;
+
+import java.sql.Timestamp;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 结算dto
+ * @date 2024/12/15 15:36
+ */
+@Data
+@EqualsAndHashCode
+public class SettleDto {
+
+    /**
+     * 主键
+     */
+    private Long id;
+
+    /**
+     * 结算奖励数
+     */
+    private Integer settleRewardNum;
+
+    /**
+     * 结算前用户拥有的奖励数
+     */
+    private Integer beforeSettleRewardNum;
+
+    /**
+     * 结算后用户拥有的奖励数
+     */
+    private Integer afterSettleRewardNum;
+
+    /**
+     * 结算任务表id
+     */
+    private Long settlementTaskId;
+
+    /**
+     * 结算时间
+     */
+    private Timestamp settlementTime;
+}

+ 55 - 0
src/main/java/com/punchsettle/server/dto/settle/SettleInfoDto.java

@@ -0,0 +1,55 @@
+package com.punchsettle.server.dto.settle;
+
+import com.punchsettle.server.constant.PunchInSettleTypeEnum;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 结算请求dto
+ * @date 2024/12/14 14:40
+ */
+@Data
+@EqualsAndHashCode
+public class SettleInfoDto {
+
+    /**
+     * 结算类型
+     * @see PunchInSettleTypeEnum
+     */
+    private PunchInSettleTypeEnum settleType;
+
+    /**
+     * 待结算日期
+     */
+    private LocalDate settleDate;
+
+    /**
+     * 待结算日期(字符串)
+     */
+    private String settleDateStr;
+
+    /**
+     * 是否周末标志位
+     */
+    private Boolean weekendFlag;
+
+    /**
+     * 是否周日
+     */
+    private Boolean sundayFlag;
+
+    public SettleInfoDto(PunchInSettleTypeEnum settleType, LocalDate settleDate) {
+        this.settleType = settleType;
+        this.settleDate = settleDate;
+        settleDateStr = settleDate.toString();
+        // 判断是否是周末
+        weekendFlag = DayOfWeek.SATURDAY.equals(settleDate.getDayOfWeek()) || DayOfWeek.SUNDAY.equals(settleDate.getDayOfWeek());
+        // 判断是否周日
+        sundayFlag = DayOfWeek.SUNDAY.equals(settleDate.getDayOfWeek());
+    }
+}

+ 27 - 0
src/main/java/com/punchsettle/server/dto/settle/SettleQuery.java

@@ -0,0 +1,27 @@
+package com.punchsettle.server.dto.settle;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 结算查询dto
+ * @date 2024/12/15 15:36
+ */
+@Data
+@EqualsAndHashCode
+public class SettleQuery {
+
+    @NotBlank(message = "开始时间不能为空")
+    private String startDate;
+
+    @NotBlank(message = "结束时间不能为空")
+    private String endDate;
+
+    /**
+     * 用户主键
+     */
+    private Long userId;
+}

+ 43 - 0
src/main/java/com/punchsettle/server/dto/settle/SettleRequest.java

@@ -0,0 +1,43 @@
+package com.punchsettle.server.dto.settle;
+
+import java.util.List;
+
+import com.punchsettle.server.constant.PunchInSettleTypeEnum;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 结算请求dto
+ * @date 2024/12/15 15:36
+ */
+@Data
+@EqualsAndHashCode
+public class SettleRequest {
+
+    /**
+     * 结算类型
+     */
+    @NotNull(message = "结算类型不能为空")
+    private PunchInSettleTypeEnum settleType;
+
+    /**
+     * 结算日期
+     */
+    @NotBlank(message = "结算日期不能为空")
+    private String settleDate;
+
+    /**
+     * 待结算的用户
+     */
+    private List<Long> userIds;
+
+    /**
+     * 待结算用户的打卡任务ID
+     */
+    private List<Long> punchInIds;
+}

+ 46 - 0
src/main/java/com/punchsettle/server/dto/settle/SettleResultDto.java

@@ -0,0 +1,46 @@
+package com.punchsettle.server.dto.settle;
+
+import com.punchsettle.server.atomic.entity.PunchInRecord;
+import com.punchsettle.server.atomic.entity.PunchInRecordSettlementRela;
+import com.punchsettle.server.atomic.entity.PunchInSettlement;
+import com.punchsettle.server.atomic.entity.User;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 结算结果dto
+ * @date 2024/12/14 14:40
+ */
+@Data
+@EqualsAndHashCode
+public class SettleResultDto {
+
+    /**
+     * 待更新的用户信息
+     */
+    private User updateUser;
+
+    /**
+     * 待新增的打卡记录
+     */
+    private List<PunchInRecord> addPunchInRecords;
+
+    /**
+     * 待更新的打卡记录
+     */
+    private List<PunchInRecord> updatePunchInRecords;
+
+    /**
+     * 待新增的结算记录
+     */
+    private PunchInSettlement addPunchInSettlements;
+
+    /**
+     * 待新增的打卡结算关联记录
+     */
+    private List<PunchInRecordSettlementRela> addRelas;
+}

+ 22 - 0
src/main/java/com/punchsettle/server/service/controller/PunchInController.java

@@ -3,6 +3,7 @@ package com.punchsettle.server.service.controller;
 import java.util.List;
 
 import com.punchsettle.server.common.valid.Query;
+import com.punchsettle.server.dto.punchin.PunchInRecordDto;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -76,8 +77,29 @@ public class PunchInController {
         punchInManager.doPunchIn(dto);
     }
 
+    /**
+     * 归档
+     * @param dto
+     */
     @PostMapping("/archivePunchIn")
     public void archivePunchIn(@RequestBody @Validated({PunchInDto.Archive.class}) PunchInDto dto) {
         punchInManager.archivePunchIn(dto.getId());
     }
+
+    /**
+     * 补打卡
+     */
+    @PostMapping("/remakePunchIn")
+    public void remakePunchIn(@RequestBody @Validated PunchInRecordDto dto) {
+        punchInManager.remakePunchIn(dto);
+    }
+
+    /**
+     * 撤销误打卡
+     * @param dto
+     */
+    @PostMapping("/revokePunchIn")
+    public void revokePunchIn(@RequestBody @Validated PunchInDto dto) {
+        punchInManager.revokePunchIn(dto);
+    }
 }

+ 53 - 0
src/main/java/com/punchsettle/server/service/controller/ScratchController.java

@@ -0,0 +1,53 @@
+package com.punchsettle.server.service.controller;
+
+import com.punchsettle.server.common.valid.Save;
+import com.punchsettle.server.dto.scratch.ScratchDto;
+import com.punchsettle.server.dto.scratch.ScratchQuery;
+import com.punchsettle.server.service.manager.IScratchManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 刮刮乐 controller
+ * @date 2024/12/13 21:57
+ */
+@RestController
+@RequestMapping("/scratch")
+public class ScratchController {
+
+    @Autowired
+    private IScratchManager scratchManager;
+
+    /**
+     * 增加投入金额/中奖金额记录
+     * @param dto
+     */
+    @PostMapping("/addScratchRecord")
+    public void addScratchRecord(@RequestBody @Validated({Save.class}) ScratchDto dto) {
+        scratchManager.addScratchRecord(dto);
+    }
+
+    /**
+     * 撤销投入金额记录/中奖记录
+     */
+    @PostMapping("/revokeScratchRecord")
+    public void revokeScratchRecord(@RequestBody @Validated({ScratchDto.Revoke.class})ScratchDto dto) {
+        scratchManager.revokeScratchRecord(dto);
+    }
+
+    /**
+     * 按时间范围查询刮刮乐记录
+     */
+    @PostMapping("/queryScratchRecord")
+    public List<ScratchDto> queryScratchRecord(@RequestBody @Validated ScratchQuery query) {
+        return scratchManager.queryScratchRecord(query);
+    }
+}

+ 43 - 0
src/main/java/com/punchsettle/server/service/controller/SettleController.java

@@ -0,0 +1,43 @@
+package com.punchsettle.server.service.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.punchsettle.server.dto.settle.SettleQuery;
+import com.punchsettle.server.dto.settle.SettleRequest;
+import com.punchsettle.server.service.manager.ISettleManager;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 结算 controller
+ * @date 2024/11/26 15:04
+ */
+@RestController
+@RequestMapping("/settle")
+public class SettleController {
+
+    @Autowired
+    private ISettleManager settleManager;
+
+    /**
+     * 手动调起结算定时任务
+     */
+    @GetMapping("/manualSettle")
+    public void manualSettle(@RequestBody @Validated SettleRequest settleRequest) {
+        settleManager.manualSettle(settleRequest);
+    }
+
+    /**
+     * 手动调起结算定时任务
+     */
+    @PostMapping("/querySettle")
+    public void querySettle(@RequestBody @Validated SettleQuery query) {
+        settleManager.querySettle(query);
+    }
+}

+ 0 - 30
src/main/java/com/punchsettle/server/service/controller/TaskController.java

@@ -1,30 +0,0 @@
-package com.punchsettle.server.service.controller;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.punchsettle.server.service.manager.ITaskManager;
-
-/**
- * @author tyuio
- * @version 1.0.0
- * @description 结算定时任务 controller
- * @date 2024/11/26 15:04
- */
-@RestController
-@RequestMapping("/task")
-public class TaskController {
-
-    @Autowired
-    private ITaskManager taskManager;
-
-    /**
-     * 手动调起结算定时任务
-     */
-    @GetMapping("/manualSettle")
-    public void manualSettle() {
-        taskManager.autoSettle();
-    }
-}

+ 13 - 0
src/main/java/com/punchsettle/server/service/manager/IPunchInManager.java

@@ -3,6 +3,7 @@ package com.punchsettle.server.service.manager;
 import java.util.List;
 
 import com.punchsettle.server.dto.punchin.PunchInDto;
+import com.punchsettle.server.dto.punchin.PunchInRecordDto;
 import com.punchsettle.server.dto.punchin.PunchInWithRecordDto;
 
 /**
@@ -47,4 +48,16 @@ public interface IPunchInManager {
      * @param punchInId
      */
     void archivePunchIn(Long punchInId);
+
+    /**
+     * 补打卡
+     * @param dto
+     */
+    void remakePunchIn(PunchInRecordDto dto);
+
+    /**
+     * 撤销误打卡
+     * @param dto
+     */
+    void revokePunchIn(PunchInDto dto);
 }

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

@@ -14,10 +14,10 @@ import java.util.List;
 public interface IScratchManager {
 
     /**
-     * 修改投入金额/中奖金额
+     * 加投入金额/中奖金额记录
      * @param dto
      */
-    void modifyScratchAmount(ScratchDto dto);
+    void addScratchRecord(ScratchDto dto);
 
     /**
      * 撤销投入金额记录/中奖记录

+ 23 - 7
src/main/java/com/punchsettle/server/service/manager/ISettleManager.java

@@ -1,6 +1,12 @@
 package com.punchsettle.server.service.manager;
 
-import com.punchsettle.server.dto.task.SettleRewardTaskDto;
+import java.time.LocalDate;
+import java.util.List;
+
+import com.punchsettle.server.constant.PunchInSettleTypeEnum;
+import com.punchsettle.server.dto.settle.SettleDto;
+import com.punchsettle.server.dto.settle.SettleQuery;
+import com.punchsettle.server.dto.settle.SettleRequest;
 
 /**
  * @author tyuio
@@ -11,14 +17,24 @@ import com.punchsettle.server.dto.task.SettleRewardTaskDto;
 public interface ISettleManager {
 
     /**
-     * 结算奖励
-     * 结算范围:昨天的所有用户
+     * 打卡结算
+     * @param settleType 结算类型
+     * @param settleDate 结算日期
+     * @param userIds 待结算的用户
+     * @param punchInIds 待结算用户的打卡任务ID
+     */
+    void settleHandler(PunchInSettleTypeEnum settleType, LocalDate settleDate, List<Long> userIds, List<Long> punchInIds);
+
+    /**
+     * 手动结算
+     * @param settleRequest
      */
-    void settleReward();
+    void manualSettle(SettleRequest settleRequest);
 
     /**
-     * 结算奖励
-     * 结算范围:dto中指定的打卡日期、用户
+     * 查询结算
+     * @param query
+     * @return
      */
-    void settleReward(SettleRewardTaskDto dto);
+    List<SettleDto> querySettle(SettleQuery query);
 }

+ 63 - 10
src/main/java/com/punchsettle/server/service/manager/impl/PunchInManagerImpl.java

@@ -11,8 +11,6 @@ import java.util.Optional;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
-import com.punchsettle.server.constant.PunchInCategoryEnum;
-import com.punchsettle.server.dto.punchin.PunchInQuery;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -25,12 +23,16 @@ import com.punchsettle.server.atomic.service.IPunchInRecordService;
 import com.punchsettle.server.atomic.service.IPunchInService;
 import com.punchsettle.server.common.exception.BusinessException;
 import com.punchsettle.server.common.utils.Assert;
-import com.punchsettle.server.constant.PunchInStatusEnum;
+import com.punchsettle.server.constant.PunchInCategoryEnum;
+import com.punchsettle.server.constant.PunchInSettleTypeEnum;
+import com.punchsettle.server.constant.PunchInStatusViewEnum;
 import com.punchsettle.server.dto.punchin.PunchInDto;
+import com.punchsettle.server.dto.punchin.PunchInQuery;
 import com.punchsettle.server.dto.punchin.PunchInRecordDto;
 import com.punchsettle.server.dto.punchin.PunchInRecordQuery;
 import com.punchsettle.server.dto.punchin.PunchInWithRecordDto;
 import com.punchsettle.server.service.manager.IPunchInManager;
+import com.punchsettle.server.service.manager.ISettleManager;
 import com.punchsettle.server.utiis.DateUtils;
 import com.punchsettle.server.utiis.UserUtils;
 
@@ -52,6 +54,9 @@ public class PunchInManagerImpl implements IPunchInManager {
     @Autowired
     private IPunchInRecordService punchInRecordService;
 
+    @Autowired
+    private ISettleManager settleManager;
+
     @Override
     public List<PunchInWithRecordDto> queryPunchInAndRecord() {
         // 获取当前用户ID
@@ -123,15 +128,15 @@ public class PunchInManagerImpl implements IPunchInManager {
 
                 // 还没到当前日期则跳过,根据过往打卡记录设置打卡状态
                 if (currentDate.isAfter(today)) {
-                    punchInRecordDto.setPunchInStatus(PunchInStatusEnum.FUTURE_TIME);
+                    punchInRecordDto.setPunchInStatus(PunchInStatusViewEnum.FUTURE_TIME);
                 } else if (currentDate.isBefore(punchInCreationDate)) {
-                    punchInRecordDto.setPunchInStatus(PunchInStatusEnum.UNCREATED);
+                    punchInRecordDto.setPunchInStatus(PunchInStatusViewEnum.UNCREATED);
                 } else if (recordWeekMap.containsKey(currentDateStr)) {
-                    punchInRecordDto.setPunchInStatus(PunchInStatusEnum.PUNCH_IN);
+                    punchInRecordDto.setPunchInStatus(PunchInStatusViewEnum.PUNCH_IN);
                 } else if (currentDate.isEqual(today) && !recordWeekMap.containsKey(currentDateStr)) {
-                    punchInRecordDto.setPunchInStatus(PunchInStatusEnum.TODAY_UNKNOWN);
+                    punchInRecordDto.setPunchInStatus(PunchInStatusViewEnum.TODAY_UNKNOWN);
                 } else {
-                    punchInRecordDto.setPunchInStatus(PunchInStatusEnum.UN_PUNCH_IN);
+                    punchInRecordDto.setPunchInStatus(PunchInStatusViewEnum.UN_PUNCH_IN);
                 }
 
             }
@@ -217,12 +222,12 @@ public class PunchInManagerImpl implements IPunchInManager {
 
         // 打卡类型:计数,需要累加打卡次数
         if (PunchInCategoryEnum.COUNT.equals(punchIn.getCategory())) {
-            punchInDto.setCountTrack(Optional.ofNullable(punchIn.getCountTrack()).orElse(0) + 1);
+            punchInRecord.setCountTrack(Optional.ofNullable(punchInRecord.getCountTrack()).orElse(0) + 1);
         }
 
         // 打卡类型:计时,需要记录最新打卡时长
         if (PunchInCategoryEnum.TIME.equals(punchIn.getCategory())) {
-            punchInDto.setTimeTrack(punchIn.getTimeTrack());
+            punchInRecord.setTimeTrack(punchInDto.getTimeTrack());
         }
 
         // 新增或更新
@@ -242,4 +247,52 @@ public class PunchInManagerImpl implements IPunchInManager {
         punchIn.setArchiveFlag(true);
         punchInService.update(punchIn);
     }
+
+    @Override
+    public void remakePunchIn(PunchInRecordDto dto) {
+        // 待补卡的打卡日期
+        LocalDate punchInDate = LocalDate.parse(dto.getPunchInDate());
+
+        // 如果补打卡日期是今天,则不允许补卡
+        if (punchInDate.isEqual(LocalDate.now())) {
+            BusinessException.throwFail("补打卡日期不能为今天");
+        }
+
+        // 获取打卡任务
+        PunchIn punchIn = Optional.ofNullable(punchInService.getById(dto.getPunchInId())).orElseThrow(() -> BusinessException.fail("无法查询到该打卡任务"));
+        // 补打卡的打卡日期不能早于打卡任务创建日期
+        if (punchInDate.isBefore(punchIn.getCreationTime().toLocalDateTime().toLocalDate())) {
+            BusinessException.throwFail("补打卡日期不能早于打卡任务创建日期");
+        }
+
+        settleManager.settleHandler(PunchInSettleTypeEnum.REMAKE, punchInDate, Arrays.asList(UserUtils.getCurrentUserId()), Arrays.asList(dto.getPunchInId()));
+    }
+
+    @Override
+    public void revokePunchIn(PunchInDto dto) {
+        // 获取打卡任务
+        PunchIn punchIn = Optional.ofNullable(punchInService.getById(dto.getId())).orElseThrow(() -> BusinessException.fail("无法查询到该打卡任务"));
+
+        PunchInRecordQuery punchInRecordQuery = new PunchInRecordQuery();
+        punchInRecordQuery.setPunchInId(dto.getId());
+        punchInRecordQuery.setPunchInDate(LocalDate.now().toString());
+        PunchInRecord punchInRecord = punchInRecordService.selectOneByCondition(punchInRecordQuery);
+        if (Objects.isNull(punchInRecord)) {
+            log.info("打卡任务:{},打卡日期:{} 没有找到当天打卡记录,进行撤销", punchInRecordQuery.getPunchInId(), punchInRecordQuery.getPunchInDate());
+            return;
+        }
+
+        // 单次打卡/计时打卡 直接删除记录即可
+        if (PunchInCategoryEnum.SINGLE.equals(punchIn.getCategory()) || PunchInCategoryEnum.TIME.equals(punchIn.getCategory())) {
+            punchInRecordService.delete(punchInRecord.getId());
+        }
+
+        // 计数打卡,需要减去打卡次数
+        if (PunchInCategoryEnum.COUNT.equals(punchIn.getCategory()) && Optional.ofNullable(punchInRecord.getCountTrack()).orElse(0) > 0) {
+            PunchInRecord updateRecord = new PunchInRecord();
+            updateRecord.setId(punchInRecord.getId());
+            updateRecord.setCountTrack(punchInRecord.getCountTrack() - 1);
+            punchInRecordService.update(punchInRecord);
+        }
+    }
 }

+ 36 - 3
src/main/java/com/punchsettle/server/service/manager/impl/ScratchManagerImpl.java

@@ -12,9 +12,13 @@ import com.punchsettle.server.service.manager.IScratchManager;
 import com.punchsettle.server.utiis.UserUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 
 /**
@@ -23,6 +27,7 @@ import java.util.Optional;
  * @description TODO
  * @date 2024/12/13 16:25
  */
+@Service
 public class ScratchManagerImpl implements IScratchManager {
 
     @Autowired
@@ -33,7 +38,10 @@ public class ScratchManagerImpl implements IScratchManager {
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void modifyScratchAmount(ScratchDto dto) {
+    public void addScratchRecord(ScratchDto dto) {
+        if (ScratchActionTypeEnum.REVOKE_WIN.equals(dto.getActionType()) || ScratchActionTypeEnum.REVOKE_INVEST.equals(dto.getActionType())) {
+            BusinessException.throwFail("传入了错误的记录动作类型");
+        }
         Long currentUserId = UserUtils.getCurrentUserId();
         User user = Optional.ofNullable(userService.getById(currentUserId)).orElseThrow(() -> BusinessException.fail("无法获取当前用户信息"));
 
@@ -49,13 +57,18 @@ public class ScratchManagerImpl implements IScratchManager {
 
         LotteryScratchRecord scratchRecord = new LotteryScratchRecord();
         BeanUtils.copyProperties(dto, scratchRecord);
-        dto.setId(null);
+        scratchRecord.setId(null);
+        scratchRecord.setUserId(user.getId());
         scratchRecordService.insert(scratchRecord);
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void revokeScratchRecord(ScratchDto dto) {
+        if (ScratchActionTypeEnum.WIN.equals(dto.getActionType()) || ScratchActionTypeEnum.INVEST.equals(dto.getActionType())) {
+            BusinessException.throwFail("传入了错误的记录动作类型");
+        }
+
         // 获取当前用户信息
         Long currentUserId = UserUtils.getCurrentUserId();
         User user = Optional.ofNullable(userService.getById(currentUserId)).orElseThrow(() -> BusinessException.fail("无法获取当前用户信息"));
@@ -95,7 +108,27 @@ public class ScratchManagerImpl implements IScratchManager {
 
     @Override
     public List<ScratchDto> queryScratchRecord(ScratchQuery query) {
-        return List.of();
+        if (Objects.isNull(query) || !StringUtils.hasText(query.getStartDate()) || !StringUtils.hasText(query.getEndDate())) {
+            BusinessException.throwFail("请选择待查询的时间范围");
+        }
+        query.setStartDate(String.format("%s 00:00:00.000", query.getStartDate()));
+        query.setEndDate(String.format("%s 23:59:59.999", query.getEndDate()));
+
+        // 获取当前用户信息
+        Long currentUserId = UserUtils.getCurrentUserId();
+        User user = Optional.ofNullable(userService.getById(currentUserId)).orElseThrow(() -> BusinessException.fail("无法获取当前用户信息"));
+        query.setUserId(user.getId());
+
+        List<LotteryScratchRecord> scratchRecords = scratchRecordService.listByCondition(query);
+        if (CollectionUtils.isEmpty(scratchRecords)) {
+            return List.of();
+        }
+
+        return scratchRecords.stream().map(record -> {
+            ScratchDto dto = new ScratchDto();
+            BeanUtils.copyProperties(record, dto);
+            return dto;
+        }).toList();
     }
 
 

+ 320 - 123
src/main/java/com/punchsettle/server/service/manager/impl/SettleManagerImpl.java

@@ -7,9 +7,23 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
+import com.punchsettle.server.common.exception.BusinessException;
+import com.punchsettle.server.common.utils.Assert;
+import com.punchsettle.server.constant.PunchInCategoryEnum;
+import com.punchsettle.server.constant.PunchInRuleEnum;
+import com.punchsettle.server.constant.PunchInSettleTypeEnum;
+import com.punchsettle.server.constant.PunchInStatusEnum;
+import com.punchsettle.server.dto.settle.SettleDto;
+import com.punchsettle.server.dto.settle.SettleInfoDto;
+import com.punchsettle.server.dto.settle.SettleQuery;
+import com.punchsettle.server.dto.settle.SettleRequest;
+import com.punchsettle.server.dto.settle.SettleResultDto;
+import com.punchsettle.server.utiis.SpringUtils;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -34,6 +48,7 @@ import com.punchsettle.server.service.manager.ISettleManager;
 import com.punchsettle.server.utiis.DateUtils;
 
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
 
 /**
  * @author tyuio
@@ -63,176 +78,358 @@ public class SettleManagerImpl implements ISettleManager {
     @Autowired
     private IPunchInRecordSettlementRelaService punchInRecordSettlementRelaService;
 
-    @Override
-    public void settleReward() {
-
-    }
-
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void settleReward(SettleRewardTaskDto taskDto) {
-        if (Objects.isNull(taskDto) || Objects.isNull(taskDto.getSettleDate())) {
+    public void settleHandler(PunchInSettleTypeEnum settleType, LocalDate settleDate, List<Long> userIds, List<Long> punchInIds) {
+        if (Objects.isNull(settleType)) {
+            log.info("结算任务异常,原因:没有指定结算类型");
+            return;
+        }
+
+        if (Objects.isNull(settleDate)) {
             log.info("结算任务异常,原因:没有指定结算日期");
             return;
         }
 
-        log.info("结算任务开始,结算日期:{}, 结算用户:{}", taskDto.getSettleDate(), taskDto.getUserId());
+        if (settleDate.isAfter(LocalDate.now())) {
+            BusinessException.throwFail(String.format("结算任务异常,原因:结算日期{} 不能大于等于今天", settleDate));
+        }
 
+        log.info("结算任务开始,结算类型:{}, 结算日期:{}", settleType.getName(), settleDate);
         Timestamp settleStartTime = new Timestamp(System.currentTimeMillis());
 
-        // 获取昨天的日期,
-        LocalDate settleDate = LocalDate.parse(taskDto.getSettleDate());
-        String settleDateStr = taskDto.getSettleDate();
-        // 判断是否是周末
-        boolean weekendFlag =
-                DayOfWeek.SATURDAY.equals(settleDate.getDayOfWeek()) || DayOfWeek.SUNDAY.equals(settleDate.getDayOfWeek());
-        boolean sundayFlag = DayOfWeek.SUNDAY.equals(settleDate.getDayOfWeek());
+        SettleInfoDto settleInfo = new SettleInfoDto(settleType, settleDate);
 
-        PunchInRecordQuery recordQuery = new PunchInRecordQuery();
-        recordQuery.setStartDate(settleDateStr);
-        recordQuery.setEndDate(settleDateStr);
-        List<PunchInRecord> punchInRecords = punchInRecordService.listByCondition(recordQuery);
-        if (CollectionUtils.isEmpty(punchInRecords)) {
-            log.info("结算任务结束,原因:没有打卡记录");
+        // 读取用户数据,如果userIds为空则结算所有的用户
+        userIds = Optional.ofNullable(userIds).orElseGet(() -> {
+            PunchInRecordQuery recordQuery = new PunchInRecordQuery();
+            recordQuery.setStartDate(settleInfo.getSettleDateStr());
+            recordQuery.setEndDate(settleInfo.getSettleDateStr());
+            List<PunchInRecord> punchInRecords = punchInRecordService.listByCondition(recordQuery);
+            return punchInRecords.stream().map(PunchInRecord::getCreatedBy).collect(Collectors.toList());
+        });
+        List<User> users = userService.listByIds(userIds);
+        if (CollectionUtils.isEmpty(users)) {
+            log.info("结算任务结束,原因:没有找到待结算的用户信息");
             return;
         }
 
-        List<Long> userIds = punchInRecords.stream().map(PunchInRecord::getCreatedBy).collect(Collectors.toList());
-
-        // 读取用户的打卡任务
+        // 读取用户的打卡任务,如果punchIds为空则结算用户所有的打卡任务
         PunchInQuery punchInQuery = new PunchInQuery();
         punchInQuery.setUserIds(userIds);
+        punchInQuery.setPunchInIds(punchInIds);
         List<PunchIn> punchIns = punchInService.listByCondition(punchInQuery);
         if (CollectionUtils.isEmpty(punchIns)) {
-            log.info("结算任务结束,原因:根据打卡记录没有找到打卡任务");
+            log.info("结算任务结束,原因:没有找到打卡任务");
             return;
         }
+        // 获取最新的打卡任务ID
+        punchInIds = punchIns.stream().map(PunchIn::getId).collect(Collectors.toList());
 
-        // 读取用户奖励数据
-        List<User> users = userService.listByIds(userIds);
-        if (CollectionUtils.isEmpty(users)) {
-            log.info("结算任务结束,原因:没有找到用户奖励信息");
+        // 获取结算日的打卡记录
+        PunchInRecordQuery recordQuery = new PunchInRecordQuery();
+        recordQuery.setStartDate(settleInfo.getSettleDateStr());
+        recordQuery.setEndDate(settleInfo.getSettleDateStr());
+        recordQuery.setPunchInIds(punchInIds);
+        List<PunchInRecord> punchInRecords = punchInRecordService.listByCondition(recordQuery);
+        if (CollectionUtils.isEmpty(punchInRecords)) {
+            log.info("结算任务结束,原因:没有打卡记录");
             return;
         }
 
         // 获取一周的打卡记录
-        Map<Long, List<PunchInRecord>> punchInRecordForWeek = Map.of();
-        if (sundayFlag) {
-            List<Long> punchInIds = punchIns.stream().map(PunchIn::getId).collect(Collectors.toList());
-            PunchInRecordQuery recordForWeekQuery = new PunchInRecordQuery();
-            recordForWeekQuery.setPunchInIds(punchInIds);
-            recordForWeekQuery.setStartDate(DateUtils.getLastWeekMondayStr());
-            recordForWeekQuery.setEndDate(DateUtils.getLastWeekSundayStr());
-            List<PunchInRecord> punchInRecordForWeeks = punchInRecordService.listByCondition(recordForWeekQuery);
+        Map<Long, List<PunchInRecord>> weeklyPunchInRecords = Map.of();
+        if (settleInfo.getSundayFlag() || PunchInSettleTypeEnum.REMAKE.equals(settleInfo.getSettleType())) {
+            PunchInRecordQuery weeklyRecordQuery = new PunchInRecordQuery();
+            weeklyRecordQuery.setPunchInIds(punchInIds);
+            weeklyRecordQuery.setStartDate(DateUtils.getLastWeekMonday(settleInfo.getSettleDate()).toString());
+            weeklyRecordQuery.setEndDate(DateUtils.getLastWeekSunday(settleInfo.getSettleDate()).toString());
+            List<PunchInRecord> punchInRecordForWeeks = punchInRecordService.listByCondition(weeklyRecordQuery);
             if (!CollectionUtils.isEmpty(punchInRecordForWeeks)) {
-                punchInRecordForWeek =
-                        punchInRecordForWeeks.stream().collect(Collectors.groupingBy(PunchInRecord::getPunchInId));
+                weeklyPunchInRecords = punchInRecordForWeeks.stream().collect(Collectors.groupingBy(PunchInRecord::getPunchInId));
             }
         }
 
-        // 打卡任务容器
-        Map<Long, PunchIn> punchInMap =
-                punchIns.stream().collect(Collectors.toMap(PunchIn::getId, Function.identity(), (key1, key2) -> key1));
+        // 用户-打卡任务分组
+        Map<Long, List<PunchIn>> userPunchInMap = punchIns.stream().collect(Collectors.groupingBy(PunchIn::getCreatedBy));
+        // 打卡任务-打卡记录 map
+        Map<Long, PunchInRecord> punchInRecordMap = punchInRecords.stream().collect(Collectors.toMap(PunchInRecord::getPunchInId, Function.identity(), (key1, key2) -> key1));
 
-        // 用户-打卡记录分组
-        Map<Long, List<PunchInRecord>> userPunchInRecordMap =
-                punchInRecords.stream().collect(Collectors.groupingBy(PunchInRecord::getCreatedBy));
-
-        // 用户-奖励分组
-        Map<Long, User> userRewardMap = users.stream()
-                .collect(Collectors.toMap(User::getId, Function.identity(), (key1, key2) -> key1));
+        // 待更新的用户
+        List<User> updateUsers = new ArrayList<>();
+        // 新增的打卡结算关联关系
+        List<PunchInRecordSettlementRela> addRelas = new ArrayList<>();
+        // 待更新的打卡记录
+        List<PunchInRecord> updatePunchInRecords = new ArrayList<>();
+        // 待新增的打卡记录
+        List<PunchInRecord> addPunchInRecords = new ArrayList<>();
+        // 待新增的打卡结算信息
+        List<PunchInSettlement> addPunchInSettlements = new ArrayList<>();
 
         // 先创建结算任务执行记录
         SettlementTask settlementTask = new SettlementTask();
-        settlementTask.setSettleDate(settleDateStr);
+        settlementTask.setSettleDate(settleDate.toString());
         settlementTask.setStartTime(settleStartTime);
-        settlementTask.setProcessedNum(userPunchInRecordMap.size());
+        settlementTask.setProcessedNum(users.size());
         settlementTaskService.insert(settlementTask);
 
-        // 结算时间
-        Timestamp settlementTime = new Timestamp(System.currentTimeMillis());
-        List<PunchInSettlement> addPunchInSettlements = new ArrayList<>();
-        List<User> updateUsers = new ArrayList<>();
-        List<PunchInRecordSettlementRela> punchInRecordSettlementRelas = new ArrayList<>();
-
-        // 有打卡记录的打卡任务则进行结算
-        for (Long userId : userPunchInRecordMap.keySet()) {
-            // 获取用户打卡记录
-            List<PunchInRecord> punchInRecordList = userPunchInRecordMap.get(userId);
-            if (CollectionUtils.isEmpty(punchInRecordList)) {
-                log.info("用户{}没有打卡记录,无须结算", userId);
-                continue;
-            }
-            // 获取用户打卡奖励
-            User user = userRewardMap.get(userId);
-            if (Objects.isNull(user)) {
-                log.info("用户{}没有奖励信息,无法结算", userId);
-                continue;
-            }
-
-            // 根据打卡记录上的打卡任务ID获取具体的打卡任务信息进行结算
-            int settleRewardNum = 0;
-            for (PunchInRecord punchInRecord : punchInRecordList) {
-                PunchIn punchIn = punchInMap.get(punchInRecord.getPunchInId());
-                if (Objects.isNull(punchIn)) {
-                    continue;
-                }
-                // 周末双倍奖励,否则计算普通奖励
-                settleRewardNum += weekendFlag ? punchIn.getRewardNum() * 2 : punchIn.getRewardNum();
-                // 周日计算一次全勤双倍奖励
-                if (sundayFlag && punchIn.getFullAttendanceFlag()) {
-                    List<PunchInRecord> tempRecordList = punchInRecordForWeek.get(punchInRecord.getPunchInId());
-                    if (!CollectionUtils.isEmpty(tempRecordList) && tempRecordList.size() >= 6) {
-                        settleRewardNum += punchIn.getRewardNum() * 2;
-                    }
-                }
-
-                // 构建结算任务与记录关联信息
-                PunchInRecordSettlementRela rela = new PunchInRecordSettlementRela();
-                rela.setRecordId(punchInRecord.getId());
-                rela.setSettlementId(settlementTask.getId());
-                punchInRecordSettlementRelas.add(rela);
-            }
-
-            // 计算结算前后,用户奖励数的变化
-            int beforeSettleRewardNum = user.getUnclaimedRewardNum();
-            int afterSettleRewardNum = beforeSettleRewardNum + settleRewardNum;
-            int totalRewardNum = user.getTotalRewardNum() + settleRewardNum;
-
-            // 构造结算信息
-            PunchInSettlement addPunchInSettlement = new PunchInSettlement();
-            addPunchInSettlement.setSettlementTaskId(settlementTask.getId());
-            addPunchInSettlement.setUserId(userId);
-            addPunchInSettlement.setSettleRewardNum(settleRewardNum);
-            addPunchInSettlement.setSettlementTime(settlementTime);
-            addPunchInSettlement.setBeforeSettleRewardNum(beforeSettleRewardNum);
-            addPunchInSettlement.setAfterSettleRewardNum(afterSettleRewardNum);
-            addPunchInSettlements.add(addPunchInSettlement);
-
-            // 构造用户奖励信息
-            User updateUser = new User();
-            updateUser.setId(user.getId());
-            updateUser.setTotalRewardNum(totalRewardNum);
-            updateUser.setUnclaimedRewardNum(afterSettleRewardNum);
-            updateUsers.add(updateUser);
+        // 结算
+        for (User user : users) {
+            SettleResultDto settleResult = settle(settleInfo, user, userPunchInMap.get(user.getId()), punchInRecordMap, weeklyPunchInRecords);
+            updateUsers.add(settleResult.getUpdateUser());
+            addRelas.addAll(settleResult.getAddRelas());
+            addPunchInRecords.addAll(settleResult.getAddPunchInRecords());
+            updatePunchInRecords.addAll(settleResult.getUpdatePunchInRecords());
+            addPunchInSettlements.add(settleResult.getAddPunchInSettlements());
         }
 
         // 更新用户奖励信息
-        userService.batchUpdateUser(updateUsers);
+        if (!CollectionUtils.isEmpty(updateUsers)) {
+            userService.batchUpdateUser(updateUsers);
+        }
+
+        // 新增打卡记录
+        if (!CollectionUtils.isEmpty(addPunchInRecords)) {
+            punchInRecordService.batchInsert(addPunchInRecords);
+        }
+
+        if (!CollectionUtils.isEmpty(updatePunchInRecords)) {
+            punchInRecordService.batchUpdate(updatePunchInRecords);
+        }
 
         // 新增结算信息
-        punchInSettlementService.batchInsert(addPunchInSettlements);
+        if (!CollectionUtils.isEmpty(addPunchInSettlements)) {
+            punchInSettlementService.batchInsert(addPunchInSettlements);
+        }
 
-        // 构造并新增关联信息
-        punchInRecordSettlementRelaService.batchInsert(punchInRecordSettlementRelas);
+        // 新增关联信息
+        if (!CollectionUtils.isEmpty(addRelas)) {
+            punchInRecordSettlementRelaService.batchInsert(addRelas);
+        }
 
         // 构造并新增结算任务信息
         settlementTask.setProcessedSettleNum(addPunchInSettlements.size());
-        settlementTask
-                .setProcessedUnsettleNum(settlementTask.getProcessedNum() - settlementTask.getProcessedSettleNum());
+        settlementTask.setProcessedUnsettleNum(settlementTask.getProcessedNum() - settlementTask.getProcessedSettleNum());
         settlementTask.setEndTime(new Timestamp(System.currentTimeMillis()));
         settlementTaskService.update(settlementTask);
 
         log.info("结算任务结束");
     }
+
+    /**
+     * 结算
+     * @param settleInfo 结算基本信息
+     * @param user 待结算的用户
+     * @param punchIns 打卡任务
+     * @param punchInRecordMap 打卡任务-打卡记录 map
+     * @param weeklyPunchInRecordMap 打卡任务-一周打卡记录 map
+     * @return
+     */
+    private SettleResultDto settle(SettleInfoDto settleInfo, User user, List<PunchIn> punchIns, Map<Long, PunchInRecord> punchInRecordMap, Map<Long, List<PunchInRecord>> weeklyPunchInRecordMap) {
+        // 结算奖励数
+        int settleRewardNum = 0;
+        // 新增的打卡结算关联关系
+        List<PunchInRecordSettlementRela> addRelas = new ArrayList<>();
+        // 待更新的打卡记录
+        List<PunchInRecord> updatePunchInRecords = new ArrayList<>();
+        // 待新增的打卡记录
+        List<PunchInRecord> addPunchInRecords = new ArrayList<>();
+        // 结算
+        for (PunchIn punchIn : punchIns) {
+            // 获取打卡记录
+            PunchInRecord punchInRecord = punchInRecordMap.get(punchIn.getId());
+            // 判断是否满足打卡规则
+            PunchInStatusEnum punchInStatus = judgePunchInStatus(punchIn, punchInRecord);
+            //不满足则跳过无需接续,如果是补卡则直接完成继续计算
+            if (!PunchInSettleTypeEnum.REMAKE.equals(settleInfo.getSettleType()) && PunchInStatusEnum.UN_FINISH.equals(punchInStatus)) {
+                punchInRecord.setPunchInStatus(punchInStatus);
+                updatePunchInRecords.add(punchInRecord);
+                continue;
+            }
+
+            // 补打卡,完全没有打卡记录则需要补充打卡记录
+            if (PunchInSettleTypeEnum.REMAKE.equals(settleInfo.getSettleType()) && Objects.isNull(punchInRecord)) {
+                punchInRecord = new PunchInRecord();
+                punchInRecord.setPunchInId(punchIn.getId());
+                punchInRecord.setPunchInDate(settleInfo.getSettleDateStr());
+                addPunchInRecords.add(punchInRecord);
+            }
+
+            // 补打卡,已有打卡记录但是不满足打卡规则,则需要对打卡记录做准备
+            if (PunchInSettleTypeEnum.REMAKE.equals(settleInfo.getSettleType()) && PunchInStatusEnum.UN_FINISH.equals(punchInStatus)) {
+                punchInRecord.setPunchInStatus(PunchInStatusEnum.REMAKE_FINISH);
+                fillTrack(punchIn, punchInRecord);
+            }
+
+            // 周末双倍奖励,否则计算普通奖励
+            settleRewardNum += settleInfo.getWeekendFlag() ? punchIn.getRewardNum() * 2 : punchIn.getRewardNum();
+            // 计算全勤双倍奖励
+            if (judgeFullAttendance(settleInfo, punchIn, weeklyPunchInRecordMap)) {
+                settleRewardNum += punchIn.getRewardNum() * 2;
+            }
+
+            // 构建结算任务与记录关联信息
+            PunchInRecordSettlementRela rela = new PunchInRecordSettlementRela();
+            rela.setRecordId(punchInRecord.getId());
+            rela.setRewardNum(punchIn.getRewardNum());
+            rela.setCategory(punchIn.getCategory());
+            rela.setRule(punchIn.getRule());
+            addRelas.add(rela);
+        }
+
+        // 计算结算前后,用户奖励数的变化
+        int beforeSettleRewardNum = user.getUnclaimedRewardNum();
+        int afterSettleRewardNum = beforeSettleRewardNum + settleRewardNum;
+        int totalRewardNum = user.getTotalRewardNum() + settleRewardNum;
+
+        // 构造结算信息
+        PunchInSettlement addPunchInSettlement = new PunchInSettlement();
+        addPunchInSettlement.setUserId(user.getId());
+        addPunchInSettlement.setSettleRewardNum(settleRewardNum);
+        addPunchInSettlement.setSettlementTime(new Timestamp(System.currentTimeMillis()));
+        addPunchInSettlement.setBeforeSettleRewardNum(beforeSettleRewardNum);
+        addPunchInSettlement.setAfterSettleRewardNum(afterSettleRewardNum);
+
+        // 构造用户奖励信息
+        User updateUser = new User();
+        updateUser.setId(user.getId());
+        updateUser.setTotalRewardNum(totalRewardNum);
+        updateUser.setUnclaimedRewardNum(afterSettleRewardNum);
+
+        SettleResultDto settleResultDto = new SettleResultDto();
+        settleResultDto.setAddRelas(addRelas);
+        settleResultDto.setUpdateUser(updateUser);
+        settleResultDto.setAddPunchInRecords(addPunchInRecords);
+        settleResultDto.setUpdatePunchInRecords(updatePunchInRecords);
+        settleResultDto.setAddPunchInSettlements(addPunchInSettlement);
+        return settleResultDto;
+    }
+
+    /**
+     * 判断是否满足打卡规则完成打卡
+     * @param punchIn
+     * @param punchInRecord
+     * @return PunchInStatusEnum
+     */
+    private PunchInStatusEnum judgePunchInStatus(PunchIn punchIn, PunchInRecord punchInRecord) {
+        // 没有打卡记录,直接没完成,包含单次打卡的情况无需额外判断
+        if (Objects.isNull(punchInRecord)) {
+            return PunchInStatusEnum.UN_FINISH;
+        }
+
+        // 计数打卡
+        if (PunchInCategoryEnum.COUNT.equals(punchIn.getCategory())) {
+            if (PunchInRuleEnum.GREATER.equals(punchIn.getRule()) && punchInRecord.getCountTrack().compareTo(punchIn.getCountTrack()) < 1) {
+                return PunchInStatusEnum.UN_FINISH;
+            }
+            if (PunchInRuleEnum.GREATER_OR_EQUAL.equals(punchIn.getRule()) && punchInRecord.getCountTrack().compareTo(punchIn.getCountTrack()) == -1) {
+                return PunchInStatusEnum.UN_FINISH;
+            }
+            if (PunchInRuleEnum.LESS.equals(punchIn.getRule()) && punchInRecord.getCountTrack().compareTo(punchIn.getCountTrack()) > -1) {
+                return PunchInStatusEnum.UN_FINISH;
+            }
+            if (PunchInRuleEnum.LESS_OR_EQUAL.equals(punchIn.getRule()) && punchInRecord.getCountTrack().compareTo(punchIn.getCountTrack()) == 1) {
+                return PunchInStatusEnum.UN_FINISH;
+            }
+
+            return PunchInStatusEnum.FINISH;
+        }
+
+        // 计时打卡
+        if (PunchInCategoryEnum.TIME.equals(punchIn.getCategory())) {
+            if (PunchInRuleEnum.GREATER.equals(punchIn.getRule()) && punchInRecord.getTimeTrack().compareTo(punchIn.getTimeTrack()) < 1) {
+                return PunchInStatusEnum.UN_FINISH;
+            }
+            if (PunchInRuleEnum.GREATER_OR_EQUAL.equals(punchIn.getRule()) && punchInRecord.getTimeTrack().compareTo(punchIn.getTimeTrack()) == -1) {
+                return PunchInStatusEnum.UN_FINISH;
+            }
+            if (PunchInRuleEnum.LESS.equals(punchIn.getRule()) && punchInRecord.getTimeTrack().compareTo(punchIn.getTimeTrack()) > -1) {
+                return PunchInStatusEnum.UN_FINISH;
+            }
+            if (PunchInRuleEnum.LESS_OR_EQUAL.equals(punchIn.getRule()) && punchInRecord.getTimeTrack().compareTo(punchIn.getTimeTrack()) == 1) {
+                return PunchInStatusEnum.UN_FINISH;
+            }
+
+            return PunchInStatusEnum.FINISH;
+        }
+
+        return PunchInStatusEnum.UN_FINISH;
+    }
+
+    /**
+     * 填充打卡记录的记录信息(不用考虑单次打卡的情况)
+     * @param punchIn
+     * @param punchInRecord
+     */
+    private void fillTrack(PunchIn punchIn, PunchInRecord punchInRecord) {
+        // 计数打卡
+        if (PunchInCategoryEnum.COUNT.equals(punchIn.getCategory())) {
+            if (PunchInRuleEnum.GREATER.equals(punchIn.getRule())) {
+                punchInRecord.setCountTrack(punchIn.getCountTrack() + 1);
+            }
+            if (PunchInRuleEnum.LESS.equals(punchIn.getRule())){
+                punchInRecord.setCountTrack(punchIn.getCountTrack() - 1);
+            }
+        }
+
+        // 计时打卡
+        if (PunchInCategoryEnum.TIME.equals(punchIn.getCategory())) {
+            if (PunchInRuleEnum.GREATER.equals(punchIn.getRule())) {
+                punchInRecord.setTimeTrack(punchIn.getTimeTrack().plusSeconds(1));
+            }
+            if (PunchInRuleEnum.LESS.equals(punchIn.getRule())){
+                punchInRecord.setTimeTrack(punchIn.getTimeTrack().minusSeconds(1));
+            }
+        }
+    }
+
+    /**
+     * 判断是否进行全勤结算
+     * @param settleInfo 结算信息
+     * @param punchIn 打卡任务
+     * @param weeklyPunchInRecordMap 一周打卡记录
+     * @return true-
+     */
+    private boolean judgeFullAttendance(SettleInfoDto settleInfo, PunchIn punchIn, Map<Long, List<PunchInRecord>> weeklyPunchInRecordMap) {
+        // 没有启用全勤奖励则跳过
+        if (!punchIn.getFullAttendanceFlag()) {
+            return false;
+        }
+
+        // 不是周日结算或者补打卡则跳过,
+        if (!settleInfo.getSundayFlag() && !PunchInSettleTypeEnum.REMAKE.equals(settleInfo.getSettleType())) {
+            return false;
+        }
+
+        // 获取一周的完成打卡的打卡记录,并且要排除结算日这天的记录
+        List<PunchInRecord> weeklyFinishRecord = Optional.ofNullable(weeklyPunchInRecordMap.get(punchIn.getId())).orElse(new ArrayList<>())
+                .stream().filter(record -> !settleInfo.getSettleDateStr().equals(record.getPunchInDate()) && (PunchInStatusEnum.FINISH.equals(record.getPunchInStatus()) || PunchInStatusEnum.REMAKE_FINISH.equals(record.getPunchInStatus())))
+                .collect(Collectors.toList());
+
+        // 1个是容错允许不打卡或未完成打卡,1个是当天的结算打卡来到这段逻辑就认为已经完成打卡,因此只要有5个完成打卡,则认为全勤
+        return weeklyFinishRecord.size() >= 5;
+    }
+
+    @Override
+    public void manualSettle(SettleRequest settleRequest) {
+        Assert.isNullInBusiness(settleRequest, "结算请求不能为空");
+        SpringUtils.getBean(ISettleManager.class).settleHandler(settleRequest.getSettleType(), LocalDate.parse(settleRequest.getSettleDate()), settleRequest.getUserIds(), settleRequest.getPunchInIds());
+    }
+
+    @Override
+    public List<SettleDto> querySettle(SettleQuery query) {
+        if (Objects.isNull(query) || !StringUtils.hasText(query.getStartDate()) || !StringUtils.hasText(query.getEndDate())) {
+            BusinessException.throwFail("请选择待查询的结算记录时间范围");
+        }
+
+        query.setStartDate(String.format("%s 00:00:00.000", query.getStartDate()));
+        query.setEndDate(String.format("%s 23:59:59.999", query.getEndDate()));
+
+        List<PunchInSettlement> punchInSettlements = punchInSettlementService.listByCondition(query);
+        return punchInSettlements.stream().map(settlement -> {
+            SettleDto dto = new SettleDto();
+            BeanUtils.copyProperties(settlement, dto);
+            return dto;
+        }).toList();
+    }
 }

+ 2 - 3
src/main/java/com/punchsettle/server/service/manager/impl/TaskManagerImpl.java

@@ -1,5 +1,6 @@
 package com.punchsettle.server.service.manager.impl;
 
+import com.punchsettle.server.constant.PunchInSettleTypeEnum;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
@@ -29,9 +30,7 @@ public class TaskManagerImpl implements ITaskManager {
     public void autoSettle() {
         log.info("结算定时任务开始");
 
-        SettleRewardTaskDto taskDto = new SettleRewardTaskDto();
-        taskDto.setSettleDate(DateUtils.getYesterdayDateStr());
-        settleManager.settleReward(taskDto);
+        settleManager.settleHandler(PunchInSettleTypeEnum.AUTO, DateUtils.getYesterdayDate(), null, null);
 
         log.info("结算定时任务结束");
     }

+ 5 - 25
src/main/java/com/punchsettle/server/utiis/DateUtils.java

@@ -42,41 +42,21 @@ public class DateUtils {
         return today.minusDays(1);
     }
 
-    /**
-     * 获取昨天的日期(字符串)
-     * @return
-     */
-    public static String getYesterdayDateStr() {
-        return getYesterdayDate().toString();
-    }
-
     /**
      * 获取上周的周一日期
      * @return
      */
-    public static LocalDate getLastWeekMonday() {
-        // 获取当前日期
-        LocalDate today = LocalDate.now();
+    public static LocalDate getLastWeekMonday(LocalDate date) {
         // 获取上周的周一日期
-        return today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusWeeks(1);
+        return date.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusWeeks(1);
     }
 
     /**
-     * 获取上周的周一日期(字符串)
+     * 获取上周的周日日期
      * @return
      */
-    public static String getLastWeekMondayStr() {
-        // 获取当前日期
-        LocalDate today = LocalDate.now();
-        // 获取上周的周一日期
-        return today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusWeeks(1).toString();
+    public static LocalDate getLastWeekSunday(LocalDate date) {
+        return getLastWeekMonday(date).plusDays(6);
     }
 
-    /**
-     * 获取上周的周日日期(字符串)
-     * @return
-     */
-    public static String getLastWeekSundayStr() {
-        return getLastWeekMonday().plusDays(6).toString();
-    }
 }