Răsfoiți Sursa

【feat】【第一版开发】

1.新建项目
ChenYL 1 an în urmă
comite
2780cf5758
50 a modificat fișierele cu 2167 adăugiri și 0 ștergeri
  1. 39 0
      .gitignore
  2. 3 0
      .idea/.gitignore
  3. 7 0
      .idea/encodings.xml
  4. 6 0
      .idea/inspectionProfiles/Project_Default.xml
  5. 14 0
      .idea/misc.xml
  6. 6 0
      .idea/vcs.xml
  7. 132 0
      doc/sql/punch_settle-ddl.sql
  8. 236 0
      doc/技术文档.md
  9. 236 0
      pom.xml
  10. 19 0
      src/main/java/com/punchsettle/server/Application.java
  11. 49 0
      src/main/java/com/punchsettle/server/atomic/entity/PunchIn.java
  12. 32 0
      src/main/java/com/punchsettle/server/atomic/entity/PunchInRecord.java
  13. 31 0
      src/main/java/com/punchsettle/server/atomic/entity/PunchInRecordSettlementRela.java
  14. 74 0
      src/main/java/com/punchsettle/server/atomic/entity/PunchInSettlement.java
  15. 56 0
      src/main/java/com/punchsettle/server/atomic/entity/SettlementNotifyTask.java
  16. 56 0
      src/main/java/com/punchsettle/server/atomic/entity/SettlementTask.java
  17. 37 0
      src/main/java/com/punchsettle/server/atomic/entity/User.java
  18. 12 0
      src/main/java/com/punchsettle/server/atomic/mapper/PunchInMapper.java
  19. 12 0
      src/main/java/com/punchsettle/server/atomic/mapper/PunchInRecordMapper.java
  20. 15 0
      src/main/java/com/punchsettle/server/common/annotation/IgnoreResponseWrapper.java
  21. 25 0
      src/main/java/com/punchsettle/server/common/constant/ResponseCodeEnum.java
  22. 17 0
      src/main/java/com/punchsettle/server/common/converter/enums/IJsonEnum.java
  23. 33 0
      src/main/java/com/punchsettle/server/common/converter/enums/IntegerToEnumConverter.java
  24. 22 0
      src/main/java/com/punchsettle/server/common/converter/enums/IntegerToEnumConverterFactory.java
  25. 113 0
      src/main/java/com/punchsettle/server/common/dto/JsonResponse.java
  26. 54 0
      src/main/java/com/punchsettle/server/common/entity/BaseEntity.java
  27. 17 0
      src/main/java/com/punchsettle/server/common/exception/BusinessException.java
  28. 19 0
      src/main/java/com/punchsettle/server/common/exception/LoginException.java
  29. 19 0
      src/main/java/com/punchsettle/server/common/exception/TokenException.java
  30. 17 0
      src/main/java/com/punchsettle/server/constant/CacheConstant.java
  31. 35 0
      src/main/java/com/punchsettle/server/core/aop/GlobalExceptionHandler.java
  32. 46 0
      src/main/java/com/punchsettle/server/core/aop/ResponseControllerAdvice.java
  33. 52 0
      src/main/java/com/punchsettle/server/core/aop/WebOperateLogAspect.java
  34. 34 0
      src/main/java/com/punchsettle/server/core/config/BizConfig.java
  35. 71 0
      src/main/java/com/punchsettle/server/core/config/FeignConfig.java
  36. 11 0
      src/main/java/com/punchsettle/server/core/config/MyBatisConfig.java
  37. 26 0
      src/main/java/com/punchsettle/server/core/config/WebMvcConfig.java
  38. 49 0
      src/main/java/com/punchsettle/server/core/interceptor/AuthInterceptor.java
  39. 27 0
      src/main/java/com/punchsettle/server/dto/wechat/Code2SessionRequest.java
  40. 46 0
      src/main/java/com/punchsettle/server/dto/wechat/Code2SessionResponse.java
  41. 19 0
      src/main/java/com/punchsettle/server/dto/wechat/LoginRequest.java
  42. 25 0
      src/main/java/com/punchsettle/server/feign/WechatMiniProgramFeign.java
  43. 24 0
      src/main/java/com/punchsettle/server/service/controller/HealthController.java
  44. 35 0
      src/main/java/com/punchsettle/server/service/controller/WechatMiniProgramController.java
  45. 17 0
      src/main/java/com/punchsettle/server/service/manager/IWechatMiniProgramManager.java
  46. 69 0
      src/main/java/com/punchsettle/server/service/manager/impl/WechatMiniProgramManagerImpl.java
  47. 63 0
      src/main/java/com/punchsettle/server/utiis/CacheUtils.java
  48. 31 0
      src/main/java/com/punchsettle/server/utiis/SpringUtils.java
  49. 57 0
      src/main/java/com/punchsettle/server/utiis/TokenUtils.java
  50. 22 0
      src/main/resources/application.yaml

+ 39 - 0
.gitignore

@@ -0,0 +1,39 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
+

+ 3 - 0
.idea/.gitignore

@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml

+ 7 - 0
.idea/encodings.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding">
+    <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
+  </component>
+</project>

+ 6 - 0
.idea/inspectionProfiles/Project_Default.xml

@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="SerializableHasSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true" />
+  </profile>
+</component>

+ 14 - 0
.idea/misc.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="MavenProjectsManager">
+    <option name="originalFiles">
+      <list>
+        <option value="$PROJECT_DIR$/pom.xml" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_X" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 132 - 0
doc/sql/punch_settle-ddl.sql

@@ -0,0 +1,132 @@
+-- punch_settle.punch_in definition
+CREATE DATABASE `punch_settle` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
+
+CREATE TABLE `punch_in` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `task_name` varchar(128) NOT NULL COMMENT '任务名称',
+  `reward_num` int NOT NULL COMMENT '奖励倍数',
+  `weekend_double_flag` tinyint NOT NULL DEFAULT '0' COMMENT '是否启用周末双倍标志(0-不是,1-是)',
+  `full_attendance_flag` tinyint NOT NULL DEFAULT '0' COMMENT '是否启用全勤奖励标志(0-不是,1-是)',
+  `archive_flag` tinyint NOT NULL DEFAULT '0' COMMENT '是否归档标志(0-不是,1-是)',
+  `created_by` bigint NOT NULL COMMENT '创建人',
+  `creation_time` timestamp NOT NULL COMMENT '创建时间',
+  `last_updated_by` bigint NOT NULL COMMENT '最后更新人',
+  `last_update_time` timestamp NOT NULL COMMENT '最后更新时间',
+  `version` bigint NOT NULL DEFAULT '1' COMMENT '版本号',
+  `delete_flag` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='打卡任务表';
+
+
+-- punch_settle.punch_in_record definition
+
+CREATE TABLE `punch_in_record` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `punch_in_id` bigint NOT NULL COMMENT '打卡任务表主键',
+  `punch_in_time` timestamp NOT NULL COMMENT '打卡时间',
+  `created_by` bigint NOT NULL COMMENT '创建人',
+  `creation_time` timestamp NOT NULL COMMENT '创建时间',
+  `last_updated_by` bigint NOT NULL COMMENT '最后更新人',
+  `last_update_time` timestamp NOT NULL COMMENT '最后更新时间',
+  `version` bigint NOT NULL DEFAULT '1' COMMENT '版本号',
+  `delete_flag` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='打卡任务记录表';
+
+
+-- punch_settle.punch_in_record_settlement_rela definition
+
+CREATE TABLE `punch_in_record_settlement_rela` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `record_id` bigint NOT NULL COMMENT '打卡任务记录表ID',
+  `settlement_id` bigint NOT NULL COMMENT '打卡任务结算表ID',
+  `created_by` bigint NOT NULL COMMENT '创建人',
+  `creation_time` timestamp NOT NULL COMMENT '创建时间',
+  `last_updated_by` bigint NOT NULL COMMENT '最后更新人',
+  `last_update_time` timestamp NOT NULL COMMENT '最后更新时间',
+  `version` bigint NOT NULL DEFAULT '1' COMMENT '版本号',
+  `delete_flag` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='打卡任务记录与结算关联表';
+
+
+-- punch_settle.punch_in_settlement definition
+
+CREATE TABLE `punch_in_settlement` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `user_id` bigint NOT NULL COMMENT '用户表主键',
+  `reward_num` int NOT NULL COMMENT '结算奖励数',
+  `claim_reward_flag` tinyint NOT NULL DEFAULT '0' COMMENT '是否已领取奖励(0-未领取,1-已领取)',
+  `claim_reward_time` timestamp NULL DEFAULT NULL COMMENT '领取奖励时间',
+  `settlement_task_id` bigint NOT NULL COMMENT '结算任务表id',
+  `settlement_time` timestamp NOT NULL COMMENT '结算时间',
+  `notify_id` bigint DEFAULT NULL COMMENT '通知表ID',
+  `notify_status` varchar(20) DEFAULT NULL COMMENT '结算通知状态(fail-通知失败,success-成功通知,pending-待通知)',
+  `notify_time` timestamp NULL DEFAULT NULL COMMENT '结算通知时间',
+  `created_by` bigint NOT NULL COMMENT '创建人',
+  `creation_time` timestamp NOT NULL COMMENT '创建时间',
+  `last_updated_by` bigint NOT NULL COMMENT '最后更新人',
+  `last_update_time` timestamp NOT NULL COMMENT '最后更新时间',
+  `version` bigint NOT NULL DEFAULT '1' COMMENT '版本号',
+  `delete_flag` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='打卡任务结算表';
+
+
+-- punch_settle.settlement_notify_task definition
+
+CREATE TABLE `settlement_notify_task` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `start_time` timestamp NULL DEFAULT NULL COMMENT '任务开始时间',
+  `end_time` timestamp NULL DEFAULT NULL COMMENT '任务结束时间',
+  `task_status` varchar(20) NOT NULL DEFAULT 'processing' COMMENT '任务状态(success-成功,fail-失败,processing-处理中)',
+  `notify_num` int NOT NULL DEFAULT '0' COMMENT '通知数量',
+  `notify_success_num` int NOT NULL DEFAULT '0' COMMENT '通知成功数量',
+  `notify_fail_num` int NOT NULL DEFAULT '0' COMMENT '通知失败数量',
+  `error_message` text COMMENT '失败异常信息',
+  `created_by` bigint NOT NULL COMMENT '创建人',
+  `creation_time` timestamp NOT NULL COMMENT '创建时间',
+  `last_updated_by` bigint NOT NULL COMMENT '最后更新人',
+  `last_update_time` timestamp NOT NULL COMMENT '最后更新时间',
+  `version` bigint NOT NULL DEFAULT '1' COMMENT '版本号',
+  `delete_flag` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='通知定时任务执行记录表';
+
+
+-- punch_settle.settlement_task definition
+
+CREATE TABLE `settlement_task` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `start_time` timestamp NULL DEFAULT NULL COMMENT '任务开始时间',
+  `end_time` timestamp NULL DEFAULT NULL COMMENT '任务结束时间',
+  `task_status` varchar(20) NOT NULL DEFAULT 'processing' COMMENT '任务状态(success-成功,fail-失败,processing-处理中)',
+  `processed_num` int NOT NULL DEFAULT '0' COMMENT '处理数量',
+  `processed_success_num` int NOT NULL DEFAULT '0' COMMENT '处理成功数量',
+  `processed_fail_num` int NOT NULL DEFAULT '0' COMMENT '处理失败数量',
+  `error_message` text COMMENT '失败异常信息',
+  `created_by` bigint NOT NULL COMMENT '创建人',
+  `creation_time` timestamp NOT NULL COMMENT '创建时间',
+  `last_updated_by` bigint NOT NULL COMMENT '最后更新人',
+  `last_update_time` timestamp NOT NULL COMMENT '最后更新时间',
+  `version` bigint NOT NULL DEFAULT '1' COMMENT '版本号',
+  `delete_flag` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='奖励结算定时任务执行记录表';
+
+
+-- punch_settle.`user` definition
+
+CREATE TABLE `user` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `open_id` varchar(128) NOT NULL COMMENT '微信id',
+  `nickname` varchar(100) DEFAULT NULL COMMENT '微信昵称',
+  `avator` varchar(1000) DEFAULT NULL COMMENT '微信头像url',
+  `created_by` bigint NOT NULL COMMENT '创建人',
+  `creation_time` timestamp NOT NULL COMMENT '创建时间',
+  `last_updated_by` bigint NOT NULL COMMENT '最后更新人',
+  `last_update_time` timestamp NOT NULL COMMENT '最后更新时间',
+  `version` bigint NOT NULL DEFAULT '1' COMMENT '版本号',
+  `delete_flag` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除标志(0-未删除,1-已删除)',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';

+ 236 - 0
doc/技术文档.md

@@ -0,0 +1,236 @@
+# 打卡结算
+
+
+
+ui设计工具:即时设计
+
+小程序框架:uniapp
+
+后端框架:spring boot + mybatis + mybatis mapper + spring mvc
+
+数据库:mysql
+
+
+
+## 系统设计
+
+
+
+### 功能设计
+
+* 用户
+
+  1. 微信用户登录
+
+     说明:简单记录下头像、微信id、昵称,然后用于区分即可
+
+  2. 用户注销?-这个不要应该也可以
+
+* 打卡任务
+
+  1. 新建任务
+  2. 编辑任务
+  3. 删除任务
+  4. 用户完成任务
+
+* 系统
+
+  1. 每晚自动结算
+
+     说明:每日凌晨把昨天完成的任务获得的奖励进行自动统计
+
+  2. 每天自动推送结算通知
+
+     说明:把每日结算结果推送到微信
+
+
+
+### 表设计
+
+
+
+#### 用户表
+
+表名:user
+
+| 字段             | 类型          | 描述                               |
+| ---------------- | ------------- | ---------------------------------- |
+| id               | bigint        | 主键                               |
+| open_id          | varchar(128)  | 微信id                             |
+| nickname         | varchar(100)  | 微信昵称                           |
+| avator           | varchar(1000) | 微信头像url                        |
+| created_by       | bigint        | 创建人                             |
+| creation_time    | timestamp     | 创建时间                           |
+| last_updated_by  | bigint        | 最后更新人                         |
+| last_update_time | timestamp     | 最后更新时间                       |
+| version          | bigint        | 版本号                             |
+| delete_flag      | tinyint       | 逻辑删除标志(0-未删除,1-已删除) |
+
+
+
+#### 打卡任务表
+
+表名:punch_in
+
+| 字段                 | 类型         | 描述                               |
+| -------------------- | ------------ | ---------------------------------- |
+| id                   | bigint       | 主键                               |
+| task_name            | varchar(128) | 任务名称                           |
+| reward_num           | int          | 奖励倍数                           |
+| weekend_double_flag  | tinyint      | 是否启用周末双倍标志(0-不是,1-是) |
+| full_attendance_flag | tinyint      | 是否启用全勤奖励标志(0-不是,1-是) |
+| archive_flag         | tinyint      | 是否归档标志(0-不是,1-是)         |
+| created_by           | bigint       | 创建人                             |
+| creation_time        | timestamp    | 创建时间                           |
+| last_updated_by      | bigint       | 最后更信人                         |
+| last_update_time     | timestamp    | 最后更新时间                       |
+| version              | bigint       | 版本号                             |
+| delete_flag          | tinyint      | 逻辑删除标志(0-未删除,1-已删除) |
+
+
+
+#### 打卡任务记录表
+
+表名:punch_in_record
+
+| 字段             | 类型      | 描述                               |
+| ---------------- | --------- | ---------------------------------- |
+| id               | bigint    | 主键                               |
+| punch_in_id      | bigint    | 打卡任务表主键                     |
+| punch_in_time    | timestamp | 打卡时间                           |
+| created_by       | bigint    | 创建人                             |
+| creation_time    | timestamp | 创建时间                           |
+| last_updated_by  | bigint    | 最后更信人                         |
+| last_update_time | timestamp | 最后更新时间                       |
+| version          | bigint    | 版本号                             |
+| delete_flag      | tinyint   | 逻辑删除标志(0-未删除,1-已删除) |
+
+
+
+#### 打卡任务结算表
+
+表名:punch_in_settlement
+
+| 字段               | 类型        | 描述                                                         |
+| ------------------ | ----------- | ------------------------------------------------------------ |
+| id                 | bigint      | 主键                                                         |
+| user_id            | bigint      | 用户表主键                                                   |
+| reward_num         | int         | 结算奖励数                                                   |
+| claim_reward_flag  | tinyint     | 是否已领取奖励(0-未领取,1-已领取)                           |
+| claim_reward_time  | timestamp   | 领取奖励时间                                                 |
+| settlement_task_id | bigint      | 结算任务表id                                                 |
+| settlement_time    | timestamp   | 结算时间                                                     |
+| notify_id          | bigint      | 通知表ID                                                     |
+| notify_status      | varchar(20) | 结算通知状态(fail-通知失败,success-成功通知,pending-待通知) |
+| notify_time        | timestamp   | 结算通知时间                                                 |
+| created_by         | bigint      | 创建人                                                       |
+| creation_time      | timestamp   | 创建时间                                                     |
+| last_updated_by    | bigint      | 最后更信人                                                   |
+| last_update_time   | timestamp   | 最后更新时间                                                 |
+| version            | bigint      | 版本号                                                       |
+| delete_flag        | tinyint     | 逻辑删除标志(0-未删除,1-已删除)                           |
+
+
+
+#### 打卡任务记录与结算关联表
+
+表名:punch_in_record_settlement_rela
+
+| 字段             | 类型      | 描述                               |
+| ---------------- | --------- | ---------------------------------- |
+| id               | bigint    | 主键                               |
+| record_id        | bigint    | 打卡任务记录表ID                   |
+| settlement_id    | bigint    | 打卡任务结算表ID                   |
+| created_by       | bigint    | 创建人                             |
+| creation_time    | timestamp | 创建时间                           |
+| last_updated_by  | bigint    | 最后更信人                         |
+| last_update_time | timestamp | 最后更新时间                       |
+| version          | bigint    | 版本号                             |
+| delete_flag      | tinyint   | 逻辑删除标志(0-未删除,1-已删除) |
+
+
+
+#### 奖励结算定时任务执行记录表
+
+表名:settlement_task
+
+| 字段                  | 类型        | 描述                                                 |
+| --------------------- | ----------- | ---------------------------------------------------- |
+| id                    | bigint      | 主键                                                 |
+| start_time            | timestamp   | 任务开始时间                                         |
+| end_time              | timestamp   | 任务结束时间                                         |
+| task_status           | varchar(20) | 任务状态(success-成功,fail-失败,processing-处理中) |
+| processed_num         | int         | 处理数量                                             |
+| processed_success_num | int         | 处理成功数量                                         |
+| processed_fail_num    | int         | 处理失败数量                                         |
+| error_message         | text        | 失败异常信息                                         |
+| created_by            | bigint      | 创建人                                               |
+| creation_time         | timestamp   | 创建时间                                             |
+| last_updated_by       | bigint      | 最后更信人                                           |
+| last_update_time      | timestamp   | 最后更新时间                                         |
+| version               | bigint      | 版本号                                               |
+| delete_flag           | tinyint     | 逻辑删除标志(0-未删除,1-已删除)                   |
+
+
+
+#### 通知定时任务执行记录表
+
+表名:settlement_notify_task
+
+| 字段               | 类型        | 描述                                                 |
+| ------------------ | ----------- | ---------------------------------------------------- |
+| id                 | bigint      | 主键                                                 |
+| start_time         | timestamp   | 任务开始时间                                         |
+| end_time           | timestamp   | 任务结束时间                                         |
+| task_status        | varchar(20) | 任务状态(success-成功,fail-失败,processing-处理中) |
+| notify_num         | int         | 通知数量                                             |
+| notify_success_num | int         | 通知成功数量                                         |
+| notify_fail_num    | int         | 通知失败数量                                         |
+| error_message      | text        | 失败异常信息                                         |
+| created_by         | bigint      | 创建人                                               |
+| creation_time      | timestamp   | 创建时间                                             |
+| last_updated_by    | bigint      | 最后更信人                                           |
+| last_update_time   | timestamp   | 最后更新时间                                         |
+| version            | bigint      | 版本号                                               |
+| delete_flag        | tinyint     | 逻辑删除标志(0-未删除,1-已删除)                   |
+
+
+
+## 常用命令
+
+
+
+### Git命令
+
+```shell
+# 创建一个仓库目录,进入目录后执行命令
+mkdir my-repo
+cd my-repo
+
+# 初始化一个本地仓库,但该仓库是作为远程仓库使用。
+git init --bare
+```
+
+
+
+### MySQL命令
+
+```shell
+# 初始化后才能使用,如果失败则要进行配置
+mysqld --initialize-insecure
+
+# 注册mysql服务(需要管理员权限)
+mysqld -install
+
+# 启动mysql服务(需要管理员权限)
+net start mysql
+
+# 关闭mysql服务(需要管理员权限)
+net stop mysql
+```
+
+参考:
+
+* [MySQL中的basedir和datadir的作用](https://blog.csdn.net/wolfalcon/article/details/80528678)
+* [MYSQL8.0安装、配置、启动、登入与卸载详细步骤总结](https://blog.csdn.net/qq_51688022/article/details/137105751)
+

+ 236 - 0
pom.xml

@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>3.3.5</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>com.punchsettle</groupId>
+    <artifactId>server</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <properties>
+        <maven.compiler.source>21</maven.compiler.source>
+        <maven.compiler.target>21</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <java.version>21</java.version>
+        <spring-cloud.version>2023.0.3</spring-cloud.version>
+        <mybatis-mapper.version>2.2.3</mybatis-mapper.version>
+        <jwt.version>4.4.0</jwt.version>
+        <mybatis-springboot-starter.version>3.0.3</mybatis-springboot-starter.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${spring-cloud.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-cache</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-tx</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>io.mybatis</groupId>
+            <artifactId>mybatis-mapper</artifactId>
+            <version>${mybatis-mapper.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+            <version>${mybatis-springboot-starter.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>${jwt.version}</version>
+        </dependency>
+    </dependencies>
+
+    <!-- 配置阿里云仓库 -->
+    <repositories>
+        <repository>
+            <id>aliyun-repos</id>
+            <url>https://maven.aliyun.com/repository/public</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+    <pluginRepositories>
+        <pluginRepository>
+            <id>aliyun-repos</id>
+            <url>https://maven.aliyun.com/repository/public</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </pluginRepository>
+    </pluginRepositories>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <!--引入第三方jar包时,不添加则引入的第三方jar不会被打入jar包中-->
+                    <includeSystemScope>true</includeSystemScope>
+                    <!--排除第三方jar文件-->
+                    <includes>
+                        <include>
+                            <groupId>nothing</groupId>
+                            <artifactId>nothing</artifactId>
+                        </include>
+                    </includes>
+                    <excludes>
+                        <exclude>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                        </exclude>
+                    </excludes>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!-- 2、完成对Java代码的编译,可以指定项目源码的jdk版本,编译后的jdk版本,以及编码 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <!-- 源代码使用的JDK版本 -->
+                    <source>${java.version}</source>
+                    <!-- 需要生成的目标class文件的编译版本 -->
+                    <target>${java.version}</target>
+                    <!-- 字符集编码 -->
+                    <encoding>UTF-8</encoding>
+                    <!-- 用来传递编译器自身不包含但是却支持的参数选项 -->
+                    <!--                    <compilerArguments>-->
+                    <!--                        <verbose/>-->
+                    <!--                        &lt;!&ndash; windwos环境(二选一) &ndash;&gt;-->
+                    <!--                        <bootclasspath>${java.home}/lib/rt.jar:${java.home}/lib/jce.jar</bootclasspath>-->
+                    <!--                        &lt;!&ndash; Linux环境(二选一) &ndash;&gt;-->
+                    <!--                        <bootclasspath>${java.home}/lib/rt.jar:${java.home}/lib/jce.jar</bootclasspath>-->
+                    <!--                    </compilerArguments>-->
+                    <annotationProcessorPaths>
+                        <path>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                            <version>${lombok.version}</version>
+                        </path>
+                    </annotationProcessorPaths>
+                </configuration>
+            </plugin>
+
+            <!-- 3、将所有依赖的jar文件复制到target/lib目录 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-dependencies</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <!--复制到哪个路径,${project.build.directory} 缺醒为 target,其他内置参数见下面解释-->
+                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
+                            <overWriteReleases>false</overWriteReleases>
+                            <overWriteSnapshots>false</overWriteSnapshots>
+                            <overWriteIfNewer>true</overWriteIfNewer>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!-- 4、指定启动类,指定配置文件,将依赖打成外部jar包 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <!-- 是否要把第三方jar加入到类构建路径 -->
+                            <addClasspath>true</addClasspath>
+                            <!-- 外部依赖jar包的最终位置 -->
+                            <classpathPrefix>lib/</classpathPrefix>
+                            <!-- 项目启动类 -->
+                            <mainClass>com.zhixinghe1.ots.OnlineToolServiceApplication</mainClass>
+                        </manifest>
+                    </archive>
+                    <!--资源文件不打进jar包中,做到配置跟项目分离的效果-->
+                    <!--                    <excludes>-->
+                    <!--                        &lt;!&ndash; 业务jar中过滤application.properties/yml文件,在jar包外控制 &ndash;&gt;-->
+                    <!--                        <exclude>*.properties</exclude>-->
+                    <!--                        <exclude>*.xml</exclude>-->
+                    <!--                        <exclude>*.yml</exclude>-->
+                    <!--                    </excludes>-->
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 19 - 0
src/main/java/com/punchsettle/server/Application.java

@@ -0,0 +1,19 @@
+package com.punchsettle.server;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+/**
+ * @author tyuio
+ */
+@EnableCaching
+@EnableFeignClients
+@SpringBootApplication
+public class Application {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}

+ 49 - 0
src/main/java/com/punchsettle/server/atomic/entity/PunchIn.java

@@ -0,0 +1,49 @@
+package com.punchsettle.server.atomic.entity;
+
+import com.punchsettle.server.common.entity.BaseEntity;
+import io.mybatis.provider.Entity;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 打卡任务表
+ */
+@Data
+@Entity.Table("punch_in")
+public class PunchIn extends BaseEntity implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = -7816008596669087324L;
+
+    /**
+     * 任务名称
+     */
+    @Entity.Column("task_name")
+    private String taskName;
+
+    /**
+     * 奖励倍数
+     */
+    @Entity.Column("reward_num")
+    private Integer rewardNum;
+
+    /**
+     * 是否启用周末双倍标志(0-不是,1-是)
+     */
+    @Entity.Column("weekend_double_flag")
+    private Boolean weekendDoubleFlag;
+
+    /**
+     * 是否启用全勤奖励标志(0-不是,1-是)
+     */
+    @Entity.Column("full_attendance_flag")
+    private Boolean fullAttendanceFlag;
+
+    /**
+     * 是否归档标志(0-不是,1-是)
+     */
+    @Entity.Column("archive_flag")
+    private Boolean archiveFlag;
+}

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

@@ -0,0 +1,32 @@
+package com.punchsettle.server.atomic.entity;
+
+import com.punchsettle.server.common.entity.BaseEntity;
+import io.mybatis.provider.Entity;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.sql.Timestamp;
+
+/**
+ * 打卡任务记录表
+ */
+@Data
+@Entity.Table("punch_in_record")
+public class PunchInRecord extends BaseEntity implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = -7093764607676755235L;
+
+    /**
+     * 打卡任务表主键
+     */
+    @Entity.Column("punch_in_id")
+    private Long punchInId;
+
+    /**
+     * 打卡任务表主键
+     */
+    @Entity.Column("punch_in_id")
+    private Timestamp punchInTime;
+}

+ 31 - 0
src/main/java/com/punchsettle/server/atomic/entity/PunchInRecordSettlementRela.java

@@ -0,0 +1,31 @@
+package com.punchsettle.server.atomic.entity;
+
+import com.punchsettle.server.common.entity.BaseEntity;
+import io.mybatis.provider.Entity;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 打卡任务记录与结算关联表
+ */
+@Data
+@Entity.Table("punch_in_record_settlement_rela")
+public class PunchInRecordSettlementRela extends BaseEntity implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 7950526992192733109L;
+
+    /**
+     * 打卡任务记录表ID
+     */
+    @Entity.Column("record_id")
+    private Long recordId;
+
+    /**
+     * 打卡任务结算表ID
+     */
+    @Entity.Column("settlement_id")
+    private Long settlementId;
+}

+ 74 - 0
src/main/java/com/punchsettle/server/atomic/entity/PunchInSettlement.java

@@ -0,0 +1,74 @@
+package com.punchsettle.server.atomic.entity;
+
+import com.punchsettle.server.common.entity.BaseEntity;
+import io.mybatis.provider.Entity;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.sql.Timestamp;
+
+/**
+ * 打卡任务结算表
+ */
+@Data
+@Entity.Table("punch_in_settlement")
+public class PunchInSettlement extends BaseEntity implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = -3583266996949699066L;
+
+    /**
+     * 用户表主键
+     */
+    @Entity.Column("user_id")
+    private Long userId;
+
+    /**
+     * 结算奖励数
+     */
+    @Entity.Column("reward_num")
+    private Integer rewardNum;
+
+    /**
+     * 是否已领取奖励(0-未领取,1-已领取)
+     */
+    @Entity.Column("claim_reward_flag")
+    private Integer claimRewardFlag;
+
+    /**
+     * 领取奖励时间
+     */
+    @Entity.Column("claim_reward_time")
+    private Timestamp claimRewardTime;
+
+    /**
+     * 结算任务表id
+     */
+    @Entity.Column("settlement_task_id")
+    private Timestamp settlementTaskId;
+
+    /**
+     * 结算时间
+     */
+    @Entity.Column("settlement_time")
+    private Timestamp settlement_time;
+
+    /**
+     * 通知表ID
+     */
+    @Entity.Column("notify_id")
+    private Long notifyId;
+
+    /**
+     * 结算通知状态(fail-通知失败,success-成功通知,pending-待通知)
+     */
+    @Entity.Column("notify_status")
+    private String notifyStatus;
+
+    /**
+     * 结算通知时间
+     */
+    @Entity.Column("notify_time")
+    private Timestamp notifyTime;
+}

+ 56 - 0
src/main/java/com/punchsettle/server/atomic/entity/SettlementNotifyTask.java

@@ -0,0 +1,56 @@
+package com.punchsettle.server.atomic.entity;
+
+import com.punchsettle.server.common.entity.BaseEntity;
+import io.mybatis.provider.Entity;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.sql.Timestamp;
+
+/**
+ * 通知定时任务执行记录表
+ */
+@Data
+@Entity.Table("settlement_notify_task")
+public class SettlementNotifyTask extends BaseEntity implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = -5529226488225591121L;
+
+    /**
+     * 任务开始时间
+     */
+    @Entity.Column("start_time")
+    private Timestamp startTime;
+
+    /**
+     * 任务结束时间
+     */
+    @Entity.Column("end_time")
+    private Timestamp endTime;
+
+    /**
+     * 任务状态(success-成功,fail-失败,processing-处理中)
+     */
+    @Entity.Column("task_status")
+    private String taskStatus;
+
+    /**
+     * 通知数量
+     */
+    @Entity.Column("notify_num")
+    private Integer notifyNum;
+
+    /**
+     * 通知成功数量
+     */
+    @Entity.Column("notify_success_num")
+    private Integer notifySuccessNum;
+
+    /**
+     * 通知失败数量
+     */
+    @Entity.Column("notify_fail_num")
+    private Integer notifyFailNum;
+}

+ 56 - 0
src/main/java/com/punchsettle/server/atomic/entity/SettlementTask.java

@@ -0,0 +1,56 @@
+package com.punchsettle.server.atomic.entity;
+
+import com.punchsettle.server.common.entity.BaseEntity;
+import io.mybatis.provider.Entity;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.sql.Timestamp;
+
+/**
+ * 奖励结算定时任务执行记录表
+ */
+@Data
+@Entity.Table("settlement_task")
+public class SettlementTask extends BaseEntity implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 4359906154365259984L;
+
+    /**
+     * 任务开始时间
+     */
+    @Entity.Column("start_time")
+    private Timestamp startTime;
+
+    /**
+     * 任务结束时间
+     */
+    @Entity.Column("end_time")
+    private Timestamp endTime;
+
+    /**
+     * 任务状态(success-成功,fail-失败,processing-处理中)
+     */
+    @Entity.Column("task_status")
+    private String taskStatus;
+
+    /**
+     * 处理数量
+     */
+    @Entity.Column("processed_num")
+    private Integer processedNum;
+
+    /**
+     * 处理成功数量
+     */
+    @Entity.Column("processed_success_num")
+    private Integer processedSuccessNum;
+
+    /**
+     * 处理失败数量
+     */
+    @Entity.Column("processed_fail_num")
+    private Integer processedFailNum;
+}

+ 37 - 0
src/main/java/com/punchsettle/server/atomic/entity/User.java

@@ -0,0 +1,37 @@
+package com.punchsettle.server.atomic.entity;
+
+import com.punchsettle.server.common.entity.BaseEntity;
+import io.mybatis.provider.Entity;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 用户表
+ */
+@Data
+@Entity.Table("user")
+public class User extends BaseEntity implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = -7534708835316500183L;
+
+    /**
+     * 微信id
+     */
+    @Entity.Column("open_id")
+    private String openId;
+
+    /**
+     * 微信昵称
+     */
+    @Entity.Column("nickname")
+    private String nickname;
+
+    /**
+     * 微信头像url
+     */
+    @Entity.Column("avator")
+    private String avator;
+}

+ 12 - 0
src/main/java/com/punchsettle/server/atomic/mapper/PunchInMapper.java

@@ -0,0 +1,12 @@
+package com.punchsettle.server.atomic.mapper;
+
+import com.punchsettle.server.atomic.entity.PunchIn;
+import io.mybatis.mapper.logical.LogicalMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 打卡任务表 Mapper
+ */
+@Mapper
+public interface PunchInMapper extends LogicalMapper<PunchIn, Long> {
+}

+ 12 - 0
src/main/java/com/punchsettle/server/atomic/mapper/PunchInRecordMapper.java

@@ -0,0 +1,12 @@
+package com.punchsettle.server.atomic.mapper;
+
+import com.punchsettle.server.atomic.entity.PunchInRecord;
+import io.mybatis.mapper.logical.LogicalMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 打卡任务记录表 Mapper
+ */
+@Mapper
+public interface PunchInRecordMapper extends LogicalMapper<PunchInRecord, Long> {
+}

+ 15 - 0
src/main/java/com/punchsettle/server/common/annotation/IgnoreResponseWrapper.java

@@ -0,0 +1,15 @@
+package com.punchsettle.server.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 忽略接口返回统一处理
+ */
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface IgnoreResponseWrapper {}

+ 25 - 0
src/main/java/com/punchsettle/server/common/constant/ResponseCodeEnum.java

@@ -0,0 +1,25 @@
+package com.punchsettle.server.common.constant;
+
+/**
+ * 基本响应码
+ */
+public enum ResponseCodeEnum {
+
+    SUCCESS("0", "操作成功"), FAIL("999", "操作失败");
+
+    private String code;
+    private String msg;
+
+    ResponseCodeEnum(String code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+}

+ 17 - 0
src/main/java/com/punchsettle/server/common/converter/enums/IJsonEnum.java

@@ -0,0 +1,17 @@
+package com.punchsettle.server.common.converter.enums;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Json返回值枚举接口
+ *
+ * @author tyuio
+ */
+public interface IJsonEnum<T> {
+
+    /**
+     * 获取枚举编码
+     */
+    @JsonValue
+    T getCode();
+}

+ 33 - 0
src/main/java/com/punchsettle/server/common/converter/enums/IntegerToEnumConverter.java

@@ -0,0 +1,33 @@
+package com.punchsettle.server.common.converter.enums;
+
+import org.springframework.core.convert.converter.Converter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 转换器 数值转换为枚举
+ *
+ * @author tyuio
+ */
+public class IntegerToEnumConverter<T extends IJsonEnum<Integer>> implements Converter<Integer, T> {
+
+    private final Map<Integer, T> enumMap = new HashMap<>();
+
+    public IntegerToEnumConverter(Class<T> enumType) {
+        T[] enums = enumType.getEnumConstants();
+        for (T e : enums) {
+            enumMap.put(e.getCode(), e);
+        }
+    }
+
+    @Override
+    public T convert(Integer source) {
+        T t = enumMap.get(source);
+        if (t == null) {
+            throw new IllegalArgumentException("无法匹配对应的枚举类型");
+        }
+        return t;
+    }
+
+}

+ 22 - 0
src/main/java/com/punchsettle/server/common/converter/enums/IntegerToEnumConverterFactory.java

@@ -0,0 +1,22 @@
+package com.punchsettle.server.common.converter.enums;
+
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.core.convert.converter.ConverterFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class IntegerToEnumConverterFactory implements ConverterFactory<Integer, IJsonEnum> {
+
+    private static final Map<Class, Converter> CONVERTERS = new HashMap<>();
+
+    @Override
+    public <T extends IJsonEnum> Converter<Integer, T> getConverter(Class<T> targetType) {
+        Converter<Integer, T> converter = CONVERTERS.get(targetType);
+        if (converter == null) {
+            converter = new IntegerToEnumConverter<>(targetType);
+            CONVERTERS.put(targetType, converter);
+        }
+        return converter;
+    }
+}

+ 113 - 0
src/main/java/com/punchsettle/server/common/dto/JsonResponse.java

@@ -0,0 +1,113 @@
+package com.punchsettle.server.common.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.punchsettle.server.common.constant.ResponseCodeEnum;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * json 统一响应体
+ *
+ * @param <T>
+ */
+@Data
+@EqualsAndHashCode
+public class JsonResponse<T> implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = -5372948141725666083L;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss:SSS", timezone = "GMT+8")
+    private Date timestamp = new Date();
+
+    /**
+     * 是否成功标志位 true-成功 false-失败
+     */
+    private Boolean success;
+
+    /**
+     * 响应编码
+     */
+    private String code;
+
+    /**
+     * 错误信息
+     */
+    private String msg;
+
+    /**
+     * 返回数据
+     */
+    private T data;
+
+    public JsonResponse(Boolean success, String code, String msg) {
+        this.success = success;
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public JsonResponse(Boolean success, String code, String msg, T data) {
+        this.success = success;
+        this.code = code;
+        this.msg = msg;
+        this.data = data;
+    }
+
+    /**
+     * 返回成功响应体
+     *
+     * @param <T>
+     * @return
+     */
+    public static <T> JsonResponse<T> success() {
+        return new JsonResponse<>(true, ResponseCodeEnum.SUCCESS.getCode(), ResponseCodeEnum.SUCCESS.getMsg());
+    }
+
+    /**
+     * 返回成功响应体
+     *
+     * @param data 响应数据
+     * @param <T>
+     * @return
+     */
+    public static <T> JsonResponse<T> success(T data) {
+        return new JsonResponse<>(true, ResponseCodeEnum.SUCCESS.getCode(), ResponseCodeEnum.SUCCESS.getMsg(), data);
+    }
+
+    /**
+     * 返回错误响应体
+     *
+     * @param <T>
+     * @return
+     */
+    public static <T> JsonResponse<T> fail() {
+        return new JsonResponse<>(false, ResponseCodeEnum.FAIL.getCode(), ResponseCodeEnum.FAIL.getMsg());
+    }
+
+    /**
+     * 返回错误响应体
+     *
+     * @param msg 错误信息
+     * @param <T>
+     * @return
+     */
+    public static <T> JsonResponse<T> fail(String msg) {
+        return new JsonResponse<>(false, ResponseCodeEnum.FAIL.getCode(), msg);
+    }
+
+    /**
+     * 返回错误响应体
+     *
+     * @param code 错误编码
+     * @param msg 错误信息
+     * @param <T>
+     * @return
+     */
+    public static <T> JsonResponse<T> fail(String code, String msg) {
+        return new JsonResponse<>(false, code, msg);
+    }
+}

+ 54 - 0
src/main/java/com/punchsettle/server/common/entity/BaseEntity.java

@@ -0,0 +1,54 @@
+package com.punchsettle.server.common.entity;
+
+import io.mybatis.mapper.logical.LogicalColumn;
+import io.mybatis.provider.Entity;
+import lombok.Data;
+
+import java.sql.Timestamp;
+
+/**
+ * 实体类基础属性
+ */
+@Data
+public class BaseEntity {
+
+    /**
+     * 主键
+     */
+    @Entity.Column(id = true, updatable = false, insertable = false)
+    private Long id;
+
+    /**
+     * 创建人
+     */
+    private Long createdBy;
+
+    /**
+     * 创建时间
+     */
+    private Timestamp creationTime;
+
+    /**
+     * 最后更新人
+     */
+    private Long lastUpdatedBy;
+
+    /**
+     * 最后更新时间
+     */
+    private Timestamp lastUpdateTime;
+
+    /**
+     * 版本号
+     */
+    private Integer version;
+
+    /**
+     * 逻辑删除标志(0-未删除,1-已删除)
+     */
+
+    @LogicalColumn(delete = "1")
+    @Entity.Column(updatable = false, insertable = false)
+    private Boolean deleteFlag;
+
+}

+ 17 - 0
src/main/java/com/punchsettle/server/common/exception/BusinessException.java

@@ -0,0 +1,17 @@
+package com.punchsettle.server.common.exception;
+
+/**
+ * 业务异常
+ *
+ * @author tyuio
+ */
+public class BusinessException extends RuntimeException {
+
+    public BusinessException(String message) {
+        super(message);
+    }
+
+    public static BusinessException fail(String message) {
+        return new BusinessException(message);
+    }
+}

+ 19 - 0
src/main/java/com/punchsettle/server/common/exception/LoginException.java

@@ -0,0 +1,19 @@
+package com.punchsettle.server.common.exception;
+
+/**
+ * @className AuthException
+ * @description 登录校验异常
+ * @author ChenYL 
+ * @date 2023/07/29 20:27
+ * @version V1.0
+**/
+public class LoginException extends RuntimeException {
+
+    public LoginException(String message) {
+        super(message);
+    }
+
+    public static LoginException fail(String message) {
+        return new LoginException(message);
+    }
+}

+ 19 - 0
src/main/java/com/punchsettle/server/common/exception/TokenException.java

@@ -0,0 +1,19 @@
+package com.punchsettle.server.common.exception;
+
+/**
+ * @className AuthException
+ * @description 登录校验异常
+ * @author ChenYL
+ * @date 2023/07/29 20:27
+ * @version V1.0
+ **/
+public class TokenException extends RuntimeException {
+
+    public TokenException(String message) {
+        super(message);
+    }
+
+    public static TokenException fail(String message) {
+        return new TokenException(message);
+    }
+}

+ 17 - 0
src/main/java/com/punchsettle/server/constant/CacheConstant.java

@@ -0,0 +1,17 @@
+package com.punchsettle.server.constant;
+
+/**
+ * 缓存常量
+ */
+public class CacheConstant {
+
+    /**
+     * 微信小程序用户 session_key
+     */
+    public static final String WECHAT_MINI_PROGRAM_SESSION_KEY = "WECHAT:MINI-PROGRAM:SESSION-KEY";
+
+    /**
+     * 系统用户访问凭据(token)
+     */
+    public static final String SYSTEM_USER_TOKEN = "SYSTEM:USER:TOKEN";
+}

+ 35 - 0
src/main/java/com/punchsettle/server/core/aop/GlobalExceptionHandler.java

@@ -0,0 +1,35 @@
+package com.punchsettle.server.core.aop;
+
+import com.punchsettle.server.common.dto.JsonResponse;
+import com.punchsettle.server.common.exception.BusinessException;
+import com.punchsettle.server.common.exception.LoginException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * 全局异常处理
+ */
+@Slf4j
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    /**
+     * 业务异常处理
+     *
+     * @param e
+     * @return
+     */
+    @ExceptionHandler(BusinessException.class)
+    public JsonResponse businessExceptionHandler(Exception e) {
+        log.error(e.getMessage(), e);
+        return JsonResponse.fail(e.getMessage());
+    }
+
+    @ExceptionHandler(LoginException.class)
+    public ResponseEntity<Object> loginExceptionHandler(Exception e) {
+        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(JsonResponse.fail(e.getMessage()));
+    }
+}

+ 46 - 0
src/main/java/com/punchsettle/server/core/aop/ResponseControllerAdvice.java

@@ -0,0 +1,46 @@
+package com.punchsettle.server.core.aop;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.punchsettle.server.common.annotation.IgnoreResponseWrapper;
+import com.punchsettle.server.common.dto.JsonResponse;
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+/**
+ * 接口返回统一处理
+ */
+@RestControllerAdvice(basePackages = {"com.punchsettle.server.service.controller"})
+public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {
+    @Override
+    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
+        return returnType.getMethodAnnotation(IgnoreResponseWrapper.class) == null;
+    }
+
+    @Override
+    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
+        Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
+        ServerHttpResponse response) {
+        if (body instanceof JsonResponse || body instanceof ResponseEntity) {
+            return body;
+        }
+
+        Class<?> returnClass = returnType.getParameterType();
+        if (String.class.equals(returnClass)) {
+            ObjectMapper objectMapper = new ObjectMapper();
+            try {
+                return objectMapper.writeValueAsString(JsonResponse.success(body));
+            } catch (JsonProcessingException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        return JsonResponse.success(body);
+    }
+}

+ 52 - 0
src/main/java/com/punchsettle/server/core/aop/WebOperateLogAspect.java

@@ -0,0 +1,52 @@
+package com.punchsettle.server.core.aop;
+
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.Arrays;
+
+@Slf4j
+@Aspect
+@Component
+public class WebOperateLogAspect {
+
+    @Pointcut("execution(* com.punchsettle.server.service.controller..*.*(..))")
+    public void pointcut() {}
+
+    @Around("pointcut()")
+    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
+        long startTime = System.currentTimeMillis();
+
+        ServletRequestAttributes requestAttributes =
+            (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
+        HttpServletRequest request = requestAttributes.getRequest();
+
+        String requestURI = request.getRequestURI();
+        Object[] args = Arrays.stream(joinPoint.getArgs()).filter(arg -> !(arg instanceof MultipartFile)).toArray();
+        Object result = null;
+        Exception exception = null;
+        try {
+            result = joinPoint.proceed();
+        } catch (Exception e) {
+            exception = e;
+        }
+        long cost = System.currentTimeMillis() - startTime;
+
+        // 耗时、URI、入参、出参
+        log.info("{}|{}|{}|{}", cost, requestURI, args, result);
+
+        if (exception != null) {
+            throw exception;
+        }
+
+        return result;
+    }
+}

+ 34 - 0
src/main/java/com/punchsettle/server/core/config/BizConfig.java

@@ -0,0 +1,34 @@
+package com.punchsettle.server.core.config;
+
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Getter
+@Configuration
+public class BizConfig {
+
+    /**
+     * 微信小程序appid
+     */
+    @Value("${biz.wechat.mini-program.app-id}")
+    private String wechatMiniProgramAppId;
+
+    /**
+     * 微信小程序secret
+     */
+    @Value("${biz.wechat.mini-program.secret}")
+    private String wechatMiniProgramSecret;
+
+    /**
+     * 生成token的密钥
+     */
+    @Value("${biz.token.password}")
+    private String TokenPassword;
+
+    /**
+     * token过期时间
+     */
+    @Value("${biz.token.expire:1}")
+    private Integer TokenExpire;
+}

+ 71 - 0
src/main/java/com/punchsettle/server/core/config/FeignConfig.java

@@ -0,0 +1,71 @@
+package com.punchsettle.server.core.config;
+
+import feign.Logger;
+import feign.RequestInterceptor;
+import feign.codec.Decoder;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.cloud.openfeign.support.SpringDecoder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @className FeignConfig
+ * @description Feign配置
+ * @author ChenYL
+ * @date 2023/07/23 20:55
+ * @version V1.0
+ **/
+@Configuration
+public class FeignConfig {
+
+    @Autowired
+    private BizConfig bizConfig;
+
+    @Bean("myInterceptor")
+    public RequestInterceptor requestInterceptor() {
+        return template -> {
+            template.query("appid", bizConfig.getWechatMiniProgramAppId()).query("secret",
+                bizConfig.getWechatMiniProgramSecret());
+        };
+    }
+
+    /**
+     * 调整Feign日志输出 注:FeignClient只能输出debug等级的日志
+     * 
+     * @return
+     */
+    @Bean
+    public Logger.Level devLogLevel() {
+        return Logger.Level.FULL;
+    }
+
+    /**
+     * 增加转换支持
+     * 
+     * @return
+     */
+    @Bean
+    public Decoder feignDecoder() {
+        Jackson2HttpConverter jackson2HttpConverter = new Jackson2HttpConverter();
+        ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(jackson2HttpConverter);
+        return new SpringDecoder(objectFactory);
+    }
+
+    /**
+     * content-type = text/plain 增加JSON转换支持
+     */
+    public class Jackson2HttpConverter extends MappingJackson2HttpMessageConverter {
+        public Jackson2HttpConverter() {
+            List<MediaType> mediaTypes = new ArrayList<>();
+            mediaTypes.add(MediaType.TEXT_PLAIN);
+            setSupportedMediaTypes(mediaTypes);
+        }
+    }
+}

+ 11 - 0
src/main/java/com/punchsettle/server/core/config/MyBatisConfig.java

@@ -0,0 +1,11 @@
+package com.punchsettle.server.core.config;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * mybatis配置扫描路径
+ */
+@Configuration
+@MapperScan(basePackages = "com.punchsettle.server.atomic.mapper")
+public class MyBatisConfig {}

+ 26 - 0
src/main/java/com/punchsettle/server/core/config/WebMvcConfig.java

@@ -0,0 +1,26 @@
+package com.punchsettle.server.core.config;
+
+import com.punchsettle.server.common.converter.enums.IntegerToEnumConverterFactory;
+import com.punchsettle.server.core.interceptor.AuthInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class WebMvcConfig implements WebMvcConfigurer {
+
+    @Autowired
+    private AuthInterceptor authInterceptor;
+
+    @Override
+    public void addFormatters(FormatterRegistry registry) {
+        registry.addConverterFactory(new IntegerToEnumConverterFactory());
+    }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(authInterceptor).addPathPatterns("/watermark/**", "/oss/**");
+    }
+}

+ 49 - 0
src/main/java/com/punchsettle/server/core/interceptor/AuthInterceptor.java

@@ -0,0 +1,49 @@
+package com.punchsettle.server.core.interceptor;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+/**
+ * @className AuthInterceptor
+ * @description 登录权限拦截
+ * @author ChenYL
+ * @date 2023/07/29 16:22
+ * @version V1.0
+ **/
+@Slf4j
+@Component
+public class AuthInterceptor implements HandlerInterceptor {
+
+//    @Autowired
+//    private IWechatUserService wechatUserService;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+        throws Exception {
+
+        // String token = request.getHeader("Authorization");
+        // if (!StringUtils.hasText(token)) {
+        // throw LoginException.fail("登录校验异常,原因:没有token凭据");
+        // }
+        //
+        // Long userId = null;
+        // try {
+        // Map<String, Claim> verify = TokenUtils.verify(token);
+        // userId = verify.get("userId").asLong();
+        // } catch (Exception e) {
+        // log.error("登录校验异常,token:{}", token, e);
+        // throw LoginException.fail("登录校验异常");
+        // }
+        //
+        // WechatUserDto wechatUserDto = wechatUserService.getById(userId);
+        // if (wechatUserDto == null) {
+        // throw BusinessException.fail("不存在的用户");
+        // }
+
+        return true;
+    }
+}

+ 27 - 0
src/main/java/com/punchsettle/server/dto/wechat/Code2SessionRequest.java

@@ -0,0 +1,27 @@
+package com.punchsettle.server.dto.wechat;
+
+import feign.Param;
+import lombok.Data;
+
+/**
+ * @className WetChatMiniProgramLoginRequest
+ * @description 微信小程序登录请求DTO
+ * @author ChenYL
+ * @date 2023/07/23 17:01
+ * @version V1.0
+ **/
+@Data
+public class Code2SessionRequest {
+
+    /**
+     * 登录时获取的 code
+     */
+    @Param("js_code")
+    private String jsCode;
+
+    /**
+     * 授权类型,此处只需填写 authorization_code
+     */
+    @Param("grant_type")
+    private String grantType;
+}

+ 46 - 0
src/main/java/com/punchsettle/server/dto/wechat/Code2SessionResponse.java

@@ -0,0 +1,46 @@
+package com.punchsettle.server.dto.wechat;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import lombok.Data;
+
+/**
+ * @className WetChatMiniProgramResponse
+ * @description 微信小程序登录响应
+ * @author ChenYL
+ * @date 2023/07/23 17:01
+ * @version V1.0
+ **/
+@Data
+public class Code2SessionResponse {
+
+    /**
+     * 错误码
+     */
+    @JsonAlias("errcode")
+    private Integer errCode;
+
+    /**
+     * 错误信息
+     */
+    @JsonAlias("errmsg")
+    private String errMsg;
+
+    /**
+     * 会话密钥
+     */
+    @JsonAlias("session_key")
+    private String sessionKey;
+
+    /**
+     * 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台账号下会返回
+     */
+    @JsonAlias("unionid")
+    private String unionId;
+
+    /**
+     * 用户唯一标识
+     */
+    @JsonAlias("openid")
+    private String openId;
+
+}

+ 19 - 0
src/main/java/com/punchsettle/server/dto/wechat/LoginRequest.java

@@ -0,0 +1,19 @@
+package com.punchsettle.server.dto.wechat;
+
+import lombok.Data;
+
+/**
+ * @className LoginRequest
+ * @description 微信小程序登录DTO
+ * @author ChenYL
+ * @date 2023/07/23 16:09
+ * @version V1.0
+ **/
+@Data
+public class LoginRequest {
+
+    /**
+     * 微信小程序登录时获取的 code
+     */
+    private String code;
+}

+ 25 - 0
src/main/java/com/punchsettle/server/feign/WechatMiniProgramFeign.java

@@ -0,0 +1,25 @@
+package com.punchsettle.server.feign;
+
+import com.punchsettle.server.dto.wechat.Code2SessionRequest;
+import com.punchsettle.server.dto.wechat.Code2SessionResponse;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.cloud.openfeign.SpringQueryMap;
+import org.springframework.web.bind.annotation.GetMapping;
+
+/**
+ * @interfaceNName WeChatMiniProgramFeign
+ * @description 微信小程序服务接口
+ * @author ChenYL
+ * @date 2023/07/23 16:48
+ * @version V1.0
+ **/
+
+@FeignClient(name = "wechat-mini-program-service", url = "${biz.wechat.mini-program.url}")
+public interface WechatMiniProgramFeign {
+
+    /**
+     * 微信小程序登录
+     */
+    @GetMapping("/sns/jscode2session")
+    Code2SessionResponse code2Session(@SpringQueryMap Code2SessionRequest loginRequest);
+}

+ 24 - 0
src/main/java/com/punchsettle/server/service/controller/HealthController.java

@@ -0,0 +1,24 @@
+package com.punchsettle.server.service.controller;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 健康检查
+ * 
+ * @author ChenYL
+ * @version V1.0
+ * @className HealthController
+ * @description
+ * @date 2023/10/04 16:21
+ **/
+@RestController
+@RequestMapping("/health")
+public class HealthController {
+
+    @GetMapping("/info")
+    public String info() {
+        return "打卡结算后端服务 正在运行";
+    }
+}

+ 35 - 0
src/main/java/com/punchsettle/server/service/controller/WechatMiniProgramController.java

@@ -0,0 +1,35 @@
+package com.punchsettle.server.service.controller;
+
+import com.punchsettle.server.dto.wechat.LoginRequest;
+import com.punchsettle.server.service.manager.IWechatMiniProgramManager;
+import org.springframework.beans.factory.annotation.Autowired;
+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;
+
+/**
+ * @className WeChatMiniProgramController
+ * @description 微信小程序
+ * @author ChenYL
+ * @date 2023/07/23 16:08
+ * @version V1.0
+ **/
+@RestController
+@RequestMapping("/wechat/miniprogram")
+public class WechatMiniProgramController {
+
+    @Autowired
+    private IWechatMiniProgramManager weChatMiniProgramManager;
+
+    /**
+     * 微信小程序登录
+     * 
+     * @param request
+     * @return 微信登陆后获得的session_key
+     */
+    @PostMapping("/login")
+    public String login(@RequestBody LoginRequest request) {
+        return weChatMiniProgramManager.login(request);
+    }
+}

+ 17 - 0
src/main/java/com/punchsettle/server/service/manager/IWechatMiniProgramManager.java

@@ -0,0 +1,17 @@
+package com.punchsettle.server.service.manager;
+
+import com.punchsettle.server.dto.wechat.LoginRequest;
+
+/**
+ * 微信小程序 服务类
+ */
+public interface IWechatMiniProgramManager {
+
+    /**
+     * 微信小程序登录
+     * 
+     * @param request
+     * @return 微信登陆后获得的session_key
+     */
+    String login(LoginRequest request);
+}

+ 69 - 0
src/main/java/com/punchsettle/server/service/manager/impl/WechatMiniProgramManagerImpl.java

@@ -0,0 +1,69 @@
+package com.punchsettle.server.service.manager.impl;
+
+
+import com.punchsettle.server.common.exception.BusinessException;
+import com.punchsettle.server.core.config.BizConfig;
+import com.punchsettle.server.constant.CacheConstant;
+import com.punchsettle.server.dto.wechat.Code2SessionRequest;
+import com.punchsettle.server.dto.wechat.Code2SessionResponse;
+import com.punchsettle.server.dto.wechat.LoginRequest;
+import com.punchsettle.server.feign.WechatMiniProgramFeign;
+import com.punchsettle.server.service.manager.IWechatMiniProgramManager;
+import com.punchsettle.server.utiis.CacheUtils;
+import com.punchsettle.server.utiis.TokenUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.CacheManager;
+import org.springframework.stereotype.Service;
+
+import java.util.Calendar;
+
+/**
+ * 微信小程序服务类
+ */
+@Slf4j
+@Service
+public class WechatMiniProgramManagerImpl implements IWechatMiniProgramManager {
+
+    @Autowired
+    private WechatMiniProgramFeign wechatMiniProgramFeign;
+
+//    @Autowired
+//    private IWechatUserService wechatUserService;
+
+    @Autowired
+    private CacheManager cacheManager;
+
+    @Autowired
+    private BizConfig bizConfig;
+
+    @Override
+    public String login(LoginRequest request) {
+        Code2SessionRequest code2SessionRequest = new Code2SessionRequest();
+        code2SessionRequest.setGrantType("authorization_code");
+        code2SessionRequest.setJsCode(request.getCode());
+        Code2SessionResponse loginResponse = wechatMiniProgramFeign.code2Session(code2SessionRequest);
+        if (loginResponse.getErrCode() != null && loginResponse.getErrCode() != 0) {
+            throw BusinessException.fail(loginResponse.getErrMsg());
+        }
+
+        // 获取用户记录
+//        WechatUser wechatUser = wechatUserService.getAndAdd(loginResponse.getOpenId());
+//        if (wechatUser == null) {
+//            throw BusinessException.fail(String.format("不存在的用户,且无法新增,openid: %s", loginResponse.getOpenId()));
+//        }
+
+        // 缓存微信用户对应的session_key
+        CacheUtils.put(CacheConstant.WECHAT_MINI_PROGRAM_SESSION_KEY, loginResponse.getOpenId(),
+            loginResponse.getSessionKey());
+
+        // 创建token并缓存
+        Calendar instance = Calendar.getInstance();
+        instance.add(Calendar.DATE, bizConfig.getTokenExpire());
+//        String token = TokenUtils.createToken(wechatUser.getId(), instance.getTime());
+//        CacheUtils.put(CacheConstant.SYSTEM_USER_TOKEN, loginResponse.getOpenId(), token);
+
+//        return token;
+        return null;
+    }
+}

+ 63 - 0
src/main/java/com/punchsettle/server/utiis/CacheUtils.java

@@ -0,0 +1,63 @@
+package com.punchsettle.server.utiis;
+
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+
+/**
+ * 缓存工具类
+ */
+public class CacheUtils {
+
+    private static CacheManager cacheManager;
+
+    static {
+        cacheManager = SpringUtils.getBean(CacheManager.class);
+    }
+
+    /**
+     * 添加缓存
+     *
+     * @param cacheName 缓存名称
+     * @param key 缓存key
+     * @param value 缓存值
+     */
+    public static void put(String cacheName, String key, Object value) {
+        Cache cache = cacheManager.getCache(cacheName);
+        cache.put(key, value);
+    }
+
+    /**
+     * 获取缓存(泛型)
+     *
+     * @param cacheName 缓存名称
+     * @param key 缓存key
+     * @param clazz 缓存类
+     * @param <T> 返回值泛型
+     * @return
+     */
+    public static <T> T get(String cacheName, String key, Class<T> clazz) {
+        Cache cache = cacheManager.getCache(cacheName);
+        if (cache == null) {
+            return null;
+        }
+        Cache.ValueWrapper wrapper = cache.get(key);
+        if (wrapper == null) {
+            return null;
+        }
+        return (T)wrapper.get();
+    }
+
+    /**
+     * 失效缓存
+     *
+     * @param cacheName 缓存名称
+     * @param key 缓存key
+     */
+    public static void evict(String cacheName, String key) {
+        Cache cache = cacheManager.getCache(cacheName);
+        if (cache != null) {
+            cache.evict(key);
+        }
+    }
+
+}

+ 31 - 0
src/main/java/com/punchsettle/server/utiis/SpringUtils.java

@@ -0,0 +1,31 @@
+package com.punchsettle.server.utiis;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * Spring上下文 工具类
+ */
+@Component
+public class SpringUtils implements ApplicationContextAware {
+
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        SpringUtils.applicationContext = applicationContext;
+    }
+
+    /**
+     * 通过class获取Bean.
+     *
+     * @param clazz
+     * @param <T>
+     * @return
+     */
+    public static <T> T getBean(Class<T> clazz) {
+        return applicationContext.getBean(clazz);
+    }
+}

+ 57 - 0
src/main/java/com/punchsettle/server/utiis/TokenUtils.java

@@ -0,0 +1,57 @@
+package com.punchsettle.server.utiis;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.interfaces.Claim;
+import com.punchsettle.server.core.config.BizConfig;
+
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * @className JwtUtils
+ * @description Token凭证工具
+ * @author ChenYL
+ * @date 2023/07/29 16:25
+ * @version V1.0
+ **/
+public class TokenUtils {
+
+    /**
+     * token算法
+     */
+    private static Algorithm tokenAlgorithm;
+
+    /**
+     * token校验工具
+     */
+    private static JWTVerifier jwtVerifier;
+
+    static {
+        BizConfig bizConfig = SpringUtils.getBean(BizConfig.class);
+        tokenAlgorithm = Algorithm.HMAC256(bizConfig.getTokenPassword());
+        jwtVerifier = JWT.require(tokenAlgorithm).build();
+    }
+
+    /**
+     * 创建token
+     * 
+     * @param userId
+     * @param expiresAt
+     * @return
+     */
+    public static String createToken(Long userId, Date expiresAt) {
+        return JWT.create().withClaim("userId", userId).withExpiresAt(expiresAt).sign(tokenAlgorithm);
+    }
+
+    /**
+     * 校验token
+     * 
+     * @param token
+     * @return
+     */
+    public static Map<String, Claim> verify(String token) {
+        return jwtVerifier.verify(token).getClaims();
+    }
+}

+ 22 - 0
src/main/resources/application.yaml

@@ -0,0 +1,22 @@
+server:
+  port: 8080
+
+spring:
+  application:
+    name: punch-settle-server
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    username: root
+    url: url=jdbc:mysql://localhost:3306/punch_settle?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
+  cache:
+    type: caffeine
+
+biz:
+  wechat:
+    mini-program:
+      url: https://api.weixin.qq.com
+      app-id: ENC(qw0NNe6VoMWvpoGKZLitIJ8HWMHBF+5FbCAVQyJGdxREenOAaEB1oQ==)
+      secret: ENC(i2ZFfTtFRvAUqpXSW4CQd6HEAMTc0Ltc1dVbhDiIxe4a5H7BbTV3bfiGhFvtRszOMIgyQDoxeZI=)
+  token:
+    expire: 7
+    password: 12123