فهرست منبع

【第一版开发】
1.增加各种查询接口
2.增加定时任务

ChenYL 11 ماه پیش
والد
کامیت
bdeb67633e
74فایلهای تغییر یافته به همراه2885 افزوده شده و 695 حذف شده
  1. 6 3
      data-easy/src/main/java/com/dataeasy/server/atomic/entity/SubscriptionUserConfig.java
  2. 2 1
      data-easy/src/main/java/com/dataeasy/server/atomic/mapper/DataIpoBondMapper.java
  3. 2 1
      data-easy/src/main/java/com/dataeasy/server/atomic/mapper/DataIpoStockMapper.java
  4. 2 1
      data-easy/src/main/java/com/dataeasy/server/atomic/mapper/DataProductHuntPostMapper.java
  5. 2 1
      data-easy/src/main/java/com/dataeasy/server/atomic/mapper/UserMapper.java
  6. 7 0
      data-easy/src/main/java/com/dataeasy/server/atomic/service/IDataDaLeTouService.java
  7. 16 0
      data-easy/src/main/java/com/dataeasy/server/atomic/service/IDataIpoBondService.java
  8. 16 0
      data-easy/src/main/java/com/dataeasy/server/atomic/service/IDataIpoStockService.java
  9. 16 0
      data-easy/src/main/java/com/dataeasy/server/atomic/service/IDataProductHuntPostService.java
  10. 14 0
      data-easy/src/main/java/com/dataeasy/server/atomic/service/IDataShuangSeQiuService.java
  11. 11 0
      data-easy/src/main/java/com/dataeasy/server/atomic/service/ISubscriptionSourceService.java
  12. 3 2
      data-easy/src/main/java/com/dataeasy/server/atomic/service/ISubscriptionTaskConfigService.java
  13. 17 0
      data-easy/src/main/java/com/dataeasy/server/atomic/service/ISubscriptionUserConfigService.java
  14. 23 0
      data-easy/src/main/java/com/dataeasy/server/atomic/service/IUserService.java
  15. 14 2
      data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/DataDaLeTouServiceImpl.java
  16. 23 1
      data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/DataIpoBondServiceImpl.java
  17. 23 1
      data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/DataIpoStockServiceImpl.java
  18. 27 1
      data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/DataProductHuntPostServiceImpl.java
  19. 20 1
      data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/DataShuangSeQiuServiceImpl.java
  20. 31 1
      data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/SubscriptionSourceServiceImpl.java
  21. 23 10
      data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/SubscriptionTaskConfigServiceImpl.java
  22. 46 1
      data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/SubscriptionUserConfigServiceImpl.java
  23. 46 3
      data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/UserServiceImpl.java
  24. 20 0
      data-easy/src/main/java/com/dataeasy/server/constant/ExecuteOptionEnum.java
  25. 20 0
      data-easy/src/main/java/com/dataeasy/server/constant/PushOptionEnum.java
  26. 5 1
      data-easy/src/main/java/com/dataeasy/server/constant/ScheduleTaskEnum.java
  27. 2 2
      data-easy/src/main/java/com/dataeasy/server/core/config/HzApiProperties.java
  28. 9 10
      data-easy/src/main/java/com/dataeasy/server/core/config/WxMessageProperties.java
  29. 50 0
      data-easy/src/main/java/com/dataeasy/server/core/config/WxMpConfig.java
  30. 4 3
      data-easy/src/main/java/com/dataeasy/server/core/handler/WxMpLogHandler.java
  31. 60 0
      data-easy/src/main/java/com/dataeasy/server/core/handler/WxMpSubscribeHandler.java
  32. 50 0
      data-easy/src/main/java/com/dataeasy/server/core/handler/WxMpUnsubscribeHandler.java
  33. 0 9
      data-easy/src/main/java/com/dataeasy/server/demo/TestController.java
  34. 0 112
      data-easy/src/main/java/com/dataeasy/server/demo/msg/MpEntryController.java
  35. 0 76
      data-easy/src/main/java/com/dataeasy/server/demo/msg/SubscribeHandler.java
  36. 0 57
      data-easy/src/main/java/com/dataeasy/server/demo/msg/WeChatMsgPushUtil.java
  37. 0 106
      data-easy/src/main/java/com/dataeasy/server/demo/msg/WxMpConfiguration.java
  38. 23 0
      data-easy/src/main/java/com/dataeasy/server/feign/ProductHuntFeign.java
  39. 60 0
      data-easy/src/main/java/com/dataeasy/server/feign/dto/PostNode.java
  40. 171 0
      data-easy/src/main/java/com/dataeasy/server/pojo/data/DaLeTouVO.java
  41. 171 0
      data-easy/src/main/java/com/dataeasy/server/pojo/data/IpoBondVO.java
  42. 126 0
      data-easy/src/main/java/com/dataeasy/server/pojo/data/IpoStockVO.java
  43. 70 0
      data-easy/src/main/java/com/dataeasy/server/pojo/data/ProductHuntPostVO.java
  44. 115 0
      data-easy/src/main/java/com/dataeasy/server/pojo/data/ShuangSeQiuVO.java
  45. 28 0
      data-easy/src/main/java/com/dataeasy/server/pojo/subscription/PushOptionRequest.java
  46. 20 0
      data-easy/src/main/java/com/dataeasy/server/pojo/subscription/SubscriptionSourceQuery.java
  47. 50 0
      data-easy/src/main/java/com/dataeasy/server/pojo/subscription/SubscriptionSourceVO.java
  48. 23 0
      data-easy/src/main/java/com/dataeasy/server/pojo/subscription/SubscriptionTaskConfigQuery.java
  49. 39 0
      data-easy/src/main/java/com/dataeasy/server/pojo/subscription/SubscriptionUserConfigQuery.java
  50. 76 0
      data-easy/src/main/java/com/dataeasy/server/service/controller/DataController.java
  51. 49 0
      data-easy/src/main/java/com/dataeasy/server/service/controller/SubscriptionController.java
  52. 63 0
      data-easy/src/main/java/com/dataeasy/server/service/controller/WxController.java
  53. 50 0
      data-easy/src/main/java/com/dataeasy/server/service/manager/IDataManager.java
  54. 0 27
      data-easy/src/main/java/com/dataeasy/server/service/manager/IFinanceManager.java
  55. 0 25
      data-easy/src/main/java/com/dataeasy/server/service/manager/IHzApiManager.java
  56. 0 35
      data-easy/src/main/java/com/dataeasy/server/service/manager/IScheduleTaskManager.java
  57. 30 0
      data-easy/src/main/java/com/dataeasy/server/service/manager/ISubscriptionManager.java
  58. 45 0
      data-easy/src/main/java/com/dataeasy/server/service/manager/IWxManager.java
  59. 119 0
      data-easy/src/main/java/com/dataeasy/server/service/manager/impl/DataManagerImpl.java
  60. 0 47
      data-easy/src/main/java/com/dataeasy/server/service/manager/impl/FinanceManagerImpl.java
  61. 0 47
      data-easy/src/main/java/com/dataeasy/server/service/manager/impl/HzApiManagerImpl.java
  62. 98 0
      data-easy/src/main/java/com/dataeasy/server/service/manager/impl/SubscriptionManagerImpl.java
  63. 93 0
      data-easy/src/main/java/com/dataeasy/server/service/manager/impl/WxManagerImpl.java
  64. 32 0
      data-easy/src/main/java/com/dataeasy/server/task/AbstractHzApiTask.java
  65. 186 0
      data-easy/src/main/java/com/dataeasy/server/task/AbstractTask.java
  66. 44 36
      data-easy/src/main/java/com/dataeasy/server/task/DaLeTouTask.java
  67. 95 0
      data-easy/src/main/java/com/dataeasy/server/task/IpoBondTask.java
  68. 89 0
      data-easy/src/main/java/com/dataeasy/server/task/IpoStockTask.java
  69. 107 0
      data-easy/src/main/java/com/dataeasy/server/task/ProductHuntTask.java
  70. 84 0
      data-easy/src/main/java/com/dataeasy/server/task/ShuangSeQiuTask.java
  71. 37 57
      data-easy/src/main/resources/application-dev.yaml
  72. 37 0
      data-easy/src/main/resources/application-prod.yaml
  73. 16 6
      data-easy/src/main/resources/application.yaml
  74. 29 9
      doc/技术文档.md

+ 6 - 3
data-easy/src/main/java/com/dataeasy/server/atomic/entity/SubscriptionUserConfig.java

@@ -1,6 +1,7 @@
 package com.dataeasy.server.atomic.entity;
 
 import com.dataeasy.server.common.pojo.BaseEntity;
+import com.dataeasy.server.constant.PushOptionEnum;
 import jakarta.persistence.Column;
 import jakarta.persistence.Table;
 import lombok.Data;
@@ -8,6 +9,7 @@ import lombok.EqualsAndHashCode;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.sql.Timestamp;
 import java.util.Date;
 
 /**
@@ -40,17 +42,18 @@ public class SubscriptionUserConfig extends BaseEntity implements Serializable {
      * 订阅开始时间
      */
     @Column(name = "start_time")
-    private Date startTime;
+    private Timestamp startTime;
 
     /**
      * 订阅结束时间
      */
     @Column(name = "end_time")
-    private Date endTime;
+    private Timestamp endTime;
 
     /**
      * 消息推送选项(ENABLE-开启、DISABLE-关闭)
+     * @see PushOptionEnum
      */
     @Column(name = "push_option")
-    private String pushOption;
+    private PushOptionEnum pushOption;
 }

+ 2 - 1
data-easy/src/main/java/com/dataeasy/server/atomic/mapper/DataIpoBondMapper.java

@@ -2,6 +2,7 @@ package com.dataeasy.server.atomic.mapper;
 
 import com.dataeasy.server.atomic.entity.DataIpoBond;
 import tk.mybatis.mapper.common.Mapper;
+import tk.mybatis.mapper.common.special.InsertListMapper;
 
 /**
  * @author tyuio
@@ -9,6 +10,6 @@ import tk.mybatis.mapper.common.Mapper;
  * @description 新债数据表 mapper
  * @date 2025/3/6 15:30
  */
-public interface DataIpoBondMapper extends Mapper<DataIpoBond> {
+public interface DataIpoBondMapper extends Mapper<DataIpoBond>, InsertListMapper<DataIpoBond> {
 
 }

+ 2 - 1
data-easy/src/main/java/com/dataeasy/server/atomic/mapper/DataIpoStockMapper.java

@@ -2,6 +2,7 @@ package com.dataeasy.server.atomic.mapper;
 
 import com.dataeasy.server.atomic.entity.DataIpoStock;
 import tk.mybatis.mapper.common.Mapper;
+import tk.mybatis.mapper.common.special.InsertListMapper;
 
 /**
  * @author tyuio
@@ -9,6 +10,6 @@ import tk.mybatis.mapper.common.Mapper;
  * @description 新股数据表 mapper
  * @date 2025/3/6 15:30
  */
-public interface DataIpoStockMapper extends Mapper<DataIpoStock> {
+public interface DataIpoStockMapper extends Mapper<DataIpoStock>, InsertListMapper<DataIpoStock> {
 
 }

+ 2 - 1
data-easy/src/main/java/com/dataeasy/server/atomic/mapper/DataProductHuntPostMapper.java

@@ -2,6 +2,7 @@ package com.dataeasy.server.atomic.mapper;
 
 import com.dataeasy.server.atomic.entity.DataProductHuntPost;
 import tk.mybatis.mapper.common.Mapper;
+import tk.mybatis.mapper.common.special.InsertListMapper;
 
 /**
  * @author tyuio
@@ -9,6 +10,6 @@ import tk.mybatis.mapper.common.Mapper;
  * @description Product Hunt榜单的帖子数据 mapper
  * @date 2025/3/6 15:30
  */
-public interface DataProductHuntPostMapper extends Mapper<DataProductHuntPost> {
+public interface DataProductHuntPostMapper extends Mapper<DataProductHuntPost>, InsertListMapper<DataProductHuntPost> {
 
 }

+ 2 - 1
data-easy/src/main/java/com/dataeasy/server/atomic/mapper/UserMapper.java

@@ -2,6 +2,7 @@ package com.dataeasy.server.atomic.mapper;
 
 import com.dataeasy.server.atomic.entity.User;
 
+import tk.mybatis.mapper.additional.update.force.UpdateByPrimaryKeySelectiveForceMapper;
 import tk.mybatis.mapper.common.Mapper;
 
 /**
@@ -10,5 +11,5 @@ import tk.mybatis.mapper.common.Mapper;
  * @description 用户表 mapper
  * @date 2025/3/6 15:30
  */
-public interface UserMapper extends Mapper<User> {
+public interface UserMapper extends Mapper<User>, UpdateByPrimaryKeySelectiveForceMapper<User> {
 }

+ 7 - 0
data-easy/src/main/java/com/dataeasy/server/atomic/service/IDataDaLeTouService.java

@@ -15,4 +15,11 @@ public interface IDataDaLeTouService {
      * @param daLeTou
      */
     void insert(DataDaLeTou daLeTou);
+
+    /**
+     * 根据开奖日期查询
+     * @param drawDate 开奖日期
+     * @return
+     */
+    DataDaLeTou getByDrawDate(String drawDate);
 }

+ 16 - 0
data-easy/src/main/java/com/dataeasy/server/atomic/service/IDataIpoBondService.java

@@ -1,5 +1,9 @@
 package com.dataeasy.server.atomic.service;
 
+import com.dataeasy.server.atomic.entity.DataIpoBond;
+
+import java.util.List;
+
 /**
  * @author tyuio
  * @version 1.0.0
@@ -8,4 +12,16 @@ package com.dataeasy.server.atomic.service;
  */
 public interface IDataIpoBondService {
 
+    /**
+     * 批量新增
+     * @param ipoBonds
+     */
+    void insertList(List<DataIpoBond>ipoBonds);
+
+    /**
+     * 根据申购日期查询
+     * @param subscriptionDate 申购日期
+     * @return
+     */
+    List<DataIpoBond> getBySubscriptionDate(String subscriptionDate);
 }

+ 16 - 0
data-easy/src/main/java/com/dataeasy/server/atomic/service/IDataIpoStockService.java

@@ -1,5 +1,9 @@
 package com.dataeasy.server.atomic.service;
 
+import com.dataeasy.server.atomic.entity.DataIpoStock;
+
+import java.util.List;
+
 /**
  * @author tyuio
  * @version 1.0.0
@@ -8,4 +12,16 @@ package com.dataeasy.server.atomic.service;
  */
 public interface IDataIpoStockService {
 
+    /**
+     * 批量新增
+     * @param ipoStocks
+     */
+    void insertList(List<DataIpoStock> ipoStocks);
+
+    /**
+     * 根据申购日期查询
+     * @param subscriptionDate 申购日期
+     * @return
+     */
+    List<DataIpoStock> getBySubscriptionDate(String subscriptionDate);
 }

+ 16 - 0
data-easy/src/main/java/com/dataeasy/server/atomic/service/IDataProductHuntPostService.java

@@ -1,5 +1,9 @@
 package com.dataeasy.server.atomic.service;
 
+import com.dataeasy.server.atomic.entity.DataProductHuntPost;
+
+import java.util.List;
+
 /**
  * @author tyuio
  * @version 1.0.0
@@ -8,4 +12,16 @@ package com.dataeasy.server.atomic.service;
  */
 public interface IDataProductHuntPostService {
 
+    /**
+     * 批量新增
+     * @param posts
+     */
+    void insertList(List<DataProductHuntPost> posts);
+
+    /**
+     * 根据榜单日期查询数据
+     * @param rankDate 榜单日期
+     * @return
+     */
+    List<DataProductHuntPost> getByRankDate(String rankDate);
 }

+ 14 - 0
data-easy/src/main/java/com/dataeasy/server/atomic/service/IDataShuangSeQiuService.java

@@ -1,5 +1,7 @@
 package com.dataeasy.server.atomic.service;
 
+import com.dataeasy.server.atomic.entity.DataShuangSeQiu;
+
 /**
  * @author tyuio
  * @version 1.0.0
@@ -8,4 +10,16 @@ package com.dataeasy.server.atomic.service;
  */
 public interface IDataShuangSeQiuService {
 
+    /**
+     * 新增记录
+     * @param shuangSeQiu
+     */
+    void insert(DataShuangSeQiu shuangSeQiu);
+
+    /**
+     * 根据开奖日期查询
+     * @param drawDate 开奖日期
+     * @return
+     */
+    DataShuangSeQiu getByDrawDate(String drawDate);
 }

+ 11 - 0
data-easy/src/main/java/com/dataeasy/server/atomic/service/ISubscriptionSourceService.java

@@ -1,5 +1,10 @@
 package com.dataeasy.server.atomic.service;
 
+import com.dataeasy.server.atomic.entity.SubscriptionSource;
+import com.dataeasy.server.pojo.subscription.SubscriptionSourceQuery;
+
+import java.util.List;
+
 /**
  * @author tyuio
  * @version 1.0.0
@@ -8,4 +13,10 @@ package com.dataeasy.server.atomic.service;
  */
 public interface ISubscriptionSourceService {
 
+    /**
+     * 按条件查询订阅源
+     * @param query
+     * @return
+     */
+    List<SubscriptionSource> getByCondition(SubscriptionSourceQuery query);
 }

+ 3 - 2
data-easy/src/main/java/com/dataeasy/server/atomic/service/ISubscriptionTaskConfigService.java

@@ -1,6 +1,7 @@
 package com.dataeasy.server.atomic.service;
 
 import com.dataeasy.server.atomic.entity.SubscriptionTaskConfig;
+import com.dataeasy.server.pojo.subscription.SubscriptionTaskConfigQuery;
 
 import java.util.List;
 
@@ -14,8 +15,8 @@ public interface ISubscriptionTaskConfigService {
 
     /**
      * 根据定时任务编码获取配置列表
-     * @param taskCode
+     * @param query
      * @return
      */
-    List<SubscriptionTaskConfig> getByTaskCode(String taskCode);
+    List<SubscriptionTaskConfig> getByCondition(SubscriptionTaskConfigQuery query);
 }

+ 17 - 0
data-easy/src/main/java/com/dataeasy/server/atomic/service/ISubscriptionUserConfigService.java

@@ -1,5 +1,10 @@
 package com.dataeasy.server.atomic.service;
 
+import com.dataeasy.server.atomic.entity.SubscriptionUserConfig;
+import com.dataeasy.server.pojo.subscription.SubscriptionUserConfigQuery;
+
+import java.util.List;
+
 /**
  * @author tyuio
  * @version 1.0.0
@@ -8,4 +13,16 @@ package com.dataeasy.server.atomic.service;
  */
 public interface ISubscriptionUserConfigService {
 
+    /**
+     * 按条件查询
+     * @param query
+     * @return
+     */
+    List<SubscriptionUserConfig> getByCondition(SubscriptionUserConfigQuery query);
+
+    /**
+     * 根据主键更新
+     * @param subscriptionUserConfig
+     */
+    void updateById(SubscriptionUserConfig subscriptionUserConfig);
 }

+ 23 - 0
data-easy/src/main/java/com/dataeasy/server/atomic/service/IUserService.java

@@ -2,6 +2,9 @@ package com.dataeasy.server.atomic.service;
 
 import com.dataeasy.server.atomic.entity.User;
 
+import java.util.Collection;
+import java.util.List;
+
 /**
  * @author tyuio
  * @version 1.0.0
@@ -17,6 +20,13 @@ public interface IUserService {
      */
     User getById(Long id);
 
+    /**
+     * 根据ID批量查找用户
+     * @param ids
+     * @return
+     */
+    List<User> getByIds(Collection<Long> ids);
+
     /**
      * 根据小程序openId获取用户
      * @param maOpenId 小程序openid
@@ -24,6 +34,13 @@ public interface IUserService {
      */
     User getByMaOpenId(String maOpenId);
 
+    /**
+     * 根据微信unionId获取用户
+     * @param unionId
+     * @return
+     */
+    User getByUnionId(String unionId);
+
     /**
      * 新增用户
      * @param user
@@ -35,4 +52,10 @@ public interface IUserService {
      * @param user
      */
     void updateById(User user);
+
+    /**
+     * 根据ID重置 公众号/服务号的openid
+     * @param userId
+     */
+    void unsetMpOpenIdByID(Long userId);
 }

+ 14 - 2
data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/DataDaLeTouServiceImpl.java

@@ -1,12 +1,13 @@
 package com.dataeasy.server.atomic.service.impl;
 
-import com.dataeasy.server.atomic.entity.DataDaLeTou;
-import com.dataeasy.server.common.utils.Assert;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
 
+import com.dataeasy.server.atomic.entity.DataDaLeTou;
 import com.dataeasy.server.atomic.mapper.DataDaLeTouMapper;
 import com.dataeasy.server.atomic.service.IDataDaLeTouService;
+import com.dataeasy.server.common.utils.Assert;
 
 /**
  * @author tyuio
@@ -25,4 +26,15 @@ public class DataDaLeTouServiceImpl implements IDataDaLeTouService {
         Assert.isNull(daLeTou);
         daLeTouMapper.insert(daLeTou);
     }
+
+    @Override
+    public DataDaLeTou getByDrawDate(String drawDate) {
+        if (!StringUtils.hasText(drawDate)) {
+            return null;
+        }
+
+        DataDaLeTou daLeTouQuery = new DataDaLeTou();
+        daLeTouQuery.setQihao(drawDate);
+        return daLeTouMapper.selectOne(daLeTouQuery);
+    }
 }

+ 23 - 1
data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/DataIpoBondServiceImpl.java

@@ -1,10 +1,16 @@
 package com.dataeasy.server.atomic.service.impl;
 
+import com.dataeasy.server.atomic.entity.DataIpoBond;
+import com.dataeasy.server.common.utils.Assert;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import com.dataeasy.server.atomic.mapper.DataIpoBondMapper;
 import com.dataeasy.server.atomic.service.IDataIpoBondService;
+import org.springframework.util.StringUtils;
+
+import java.sql.Date;
+import java.util.List;
 
 /**
  * @author tyuio
@@ -16,6 +22,22 @@ import com.dataeasy.server.atomic.service.IDataIpoBondService;
 public class DataIpoBondServiceImpl implements IDataIpoBondService {
 
     @Autowired
-    private DataIpoBondMapper mapper;
+    private DataIpoBondMapper ipoBondMapper;
+
+    @Override
+    public void insertList(List<DataIpoBond> ipoBonds) {
+        Assert.notEmpty(ipoBonds);
+        ipoBondMapper.insertList(ipoBonds);
+    }
+
+    @Override
+    public List<DataIpoBond> getBySubscriptionDate(String subscriptionDate) {
+        if (!StringUtils.hasText(subscriptionDate)) {
+            return List.of();
+        }
 
+        DataIpoBond ipoBondQuery = new DataIpoBond();
+        ipoBondQuery.setOnlineSubscriptionDate(Date.valueOf(subscriptionDate));
+        return ipoBondMapper.select(ipoBondQuery);
+    }
 }

+ 23 - 1
data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/DataIpoStockServiceImpl.java

@@ -1,10 +1,16 @@
 package com.dataeasy.server.atomic.service.impl;
 
+import com.dataeasy.server.atomic.entity.DataIpoStock;
+import com.dataeasy.server.common.utils.Assert;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import com.dataeasy.server.atomic.mapper.DataIpoStockMapper;
 import com.dataeasy.server.atomic.service.IDataIpoStockService;
+import org.springframework.util.StringUtils;
+
+import java.sql.Date;
+import java.util.List;
 
 /**
  * @author tyuio
@@ -16,6 +22,22 @@ import com.dataeasy.server.atomic.service.IDataIpoStockService;
 public class DataIpoStockServiceImpl implements IDataIpoStockService {
 
     @Autowired
-    private DataIpoStockMapper mapper;
+    private DataIpoStockMapper ipoStockMapper;
+
+    @Override
+    public void insertList(List<DataIpoStock> ipoStocks) {
+        Assert.notEmpty(ipoStocks);
+        ipoStockMapper.insertList(ipoStocks);
+    }
+
+    @Override
+    public List<DataIpoStock> getBySubscriptionDate(String subscriptionDate) {
+        if (!StringUtils.hasText(subscriptionDate)) {
+            return List.of();
+        }
 
+        DataIpoStock ipoStockQuery = new DataIpoStock();
+        ipoStockQuery.setSubscriptionDate(Date.valueOf(subscriptionDate));
+        return ipoStockMapper.select(ipoStockQuery);
+    }
 }

+ 27 - 1
data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/DataProductHuntPostServiceImpl.java

@@ -1,10 +1,18 @@
 package com.dataeasy.server.atomic.service.impl;
 
+import java.util.List;
+
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
 
+import com.dataeasy.server.atomic.entity.DataProductHuntPost;
 import com.dataeasy.server.atomic.mapper.DataProductHuntPostMapper;
 import com.dataeasy.server.atomic.service.IDataProductHuntPostService;
+import com.dataeasy.server.common.utils.Assert;
+
+import tk.mybatis.mapper.weekend.Weekend;
+import tk.mybatis.mapper.weekend.WeekendCriteria;
 
 /**
  * @author tyuio
@@ -16,6 +24,24 @@ import com.dataeasy.server.atomic.service.IDataProductHuntPostService;
 public class DataProductHuntPostServiceImpl implements IDataProductHuntPostService {
 
     @Autowired
-    private DataProductHuntPostMapper mapper;
+    private DataProductHuntPostMapper productHuntPostMapper;
+
+    @Override
+    public void insertList(List<DataProductHuntPost> posts) {
+        Assert.notEmpty(posts);
+        productHuntPostMapper.insertList(posts);
+    }
+
+    @Override
+    public List<DataProductHuntPost> getByRankDate(String rankDate) {
+        if (!StringUtils.hasText(rankDate)) {
+            return List.of();
+        }
 
+        Weekend<DataProductHuntPost> weekend = Weekend.of(DataProductHuntPost.class);
+        WeekendCriteria<DataProductHuntPost, Object> criteria = weekend.weekendCriteria();
+        criteria.andEqualTo(DataProductHuntPost::getRankDate, rankDate);
+        weekend.orderBy(DataProductHuntPost::getRankNum);
+        return productHuntPostMapper.selectByExample(weekend);
+    }
 }

+ 20 - 1
data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/DataShuangSeQiuServiceImpl.java

@@ -1,10 +1,13 @@
 package com.dataeasy.server.atomic.service.impl;
 
+import com.dataeasy.server.atomic.entity.DataShuangSeQiu;
+import com.dataeasy.server.common.utils.Assert;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import com.dataeasy.server.atomic.mapper.DataShuangSeQiuMapper;
 import com.dataeasy.server.atomic.service.IDataShuangSeQiuService;
+import org.springframework.util.StringUtils;
 
 /**
  * @author tyuio
@@ -16,6 +19,22 @@ import com.dataeasy.server.atomic.service.IDataShuangSeQiuService;
 public class DataShuangSeQiuServiceImpl implements IDataShuangSeQiuService {
 
     @Autowired
-    private DataShuangSeQiuMapper mapper;
+    private DataShuangSeQiuMapper shuangSeQiuMapper;
 
+    @Override
+    public void insert(DataShuangSeQiu shuangSeQiu) {
+        Assert.isNull(shuangSeQiu);
+        shuangSeQiuMapper.insert(shuangSeQiu);
+    }
+
+    @Override
+    public DataShuangSeQiu getByDrawDate(String drawDate) {
+        if (!StringUtils.hasText(drawDate)) {
+            return null;
+        }
+
+        DataShuangSeQiu shuangSeQiuQuery = new DataShuangSeQiu();
+        shuangSeQiuQuery.setQihao(drawDate);
+        return shuangSeQiuMapper.selectOne(shuangSeQiuQuery);
+    }
 }

+ 31 - 1
data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/SubscriptionSourceServiceImpl.java

@@ -1,10 +1,18 @@
 package com.dataeasy.server.atomic.service.impl;
 
+import com.dataeasy.server.atomic.entity.SubscriptionSource;
+import com.dataeasy.server.pojo.subscription.SubscriptionSourceQuery;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import com.dataeasy.server.atomic.mapper.SubscriptionSourceMapper;
 import com.dataeasy.server.atomic.service.ISubscriptionSourceService;
+import org.springframework.util.CollectionUtils;
+import tk.mybatis.mapper.weekend.Weekend;
+import tk.mybatis.mapper.weekend.WeekendCriteria;
+
+import java.util.List;
+import java.util.Objects;
 
 /**
  * @author tyuio
@@ -16,6 +24,28 @@ import com.dataeasy.server.atomic.service.ISubscriptionSourceService;
 public class SubscriptionSourceServiceImpl implements ISubscriptionSourceService {
 
     @Autowired
-    private SubscriptionSourceMapper mapper;
+    private SubscriptionSourceMapper subscriptionSourceMapper;
+
+    @Override
+    public List<SubscriptionSource> getByCondition(SubscriptionSourceQuery query) {
+        if (Objects.isNull(query)) {
+            return List.of();
+        }
 
+        Weekend<SubscriptionSource> weekend = Weekend.of(SubscriptionSource.class);
+        WeekendCriteria<SubscriptionSource, Object> weekendCriteria = weekend.weekendCriteria();
+        if (!CollectionUtils.isEmpty(query.getIds())) {
+            weekendCriteria.andIn(SubscriptionSource::getId, query.getIds());
+        }
+        weekend.selectProperties(
+                SubscriptionSource::getId,
+                SubscriptionSource::getTitle,
+                SubscriptionSource::getSubTitle,
+                SubscriptionSource::getPics,
+                SubscriptionSource::getPushChannel,
+                SubscriptionSource::getPushFrequency,
+                SubscriptionSource::getDescription
+        );
+        return subscriptionSourceMapper.selectByExample(weekend);
+    }
 }

+ 23 - 10
data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/SubscriptionTaskConfigServiceImpl.java

@@ -1,18 +1,20 @@
 package com.dataeasy.server.atomic.service.impl;
 
-import com.dataeasy.server.atomic.entity.SubscriptionTaskConfig;
-import com.dataeasy.server.common.utils.Assert;
+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.dataeasy.server.atomic.entity.SubscriptionTaskConfig;
 import com.dataeasy.server.atomic.mapper.SubscriptionTaskConfigMapper;
 import com.dataeasy.server.atomic.service.ISubscriptionTaskConfigService;
-import tk.mybatis.mapper.entity.Example;
+import com.dataeasy.server.pojo.subscription.SubscriptionTaskConfigQuery;
+
 import tk.mybatis.mapper.weekend.Weekend;
 import tk.mybatis.mapper.weekend.WeekendCriteria;
 
-import java.util.List;
-
 /**
  * @author tyuio
  * @version 1.0.0
@@ -26,13 +28,24 @@ public class SubscriptionTaskConfigServiceImpl implements ISubscriptionTaskConfi
     private SubscriptionTaskConfigMapper taskConfigMapper;
 
     @Override
-    public List<SubscriptionTaskConfig> getByTaskCode(String taskCode) {
-        Assert.isNull(taskCode);
+    public List<SubscriptionTaskConfig> getByCondition(SubscriptionTaskConfigQuery query) {
+        if (Objects.isNull(query)) {
+            return List.of();
+        }
         Weekend<SubscriptionTaskConfig> weekend = Weekend.of(SubscriptionTaskConfig.class);
         WeekendCriteria<SubscriptionTaskConfig, Object> weekendCriteria = weekend.weekendCriteria();
-        weekendCriteria.andEqualTo(SubscriptionTaskConfig::getTaskCode, taskCode);
-        // TODO 缺了一个executeOption字段
-        weekend.selectProperties(SubscriptionTaskConfig::getSubscriptionSourceId);
+        if (StringUtils.hasText(query.getTaskCode())) {
+            weekendCriteria.andEqualTo(SubscriptionTaskConfig::getTaskCode, query.getTaskCode());
+        }
+        if (StringUtils.hasText(query.getExecuteOption())) {
+            weekendCriteria.andEqualTo(SubscriptionTaskConfig::getExecuteOption, query.getExecuteOption());
+        }
+        weekend.selectProperties(
+                SubscriptionTaskConfig::getId,
+                SubscriptionTaskConfig::getTaskCode,
+                SubscriptionTaskConfig::getTaskName,
+                SubscriptionTaskConfig::getExecuteOption,
+                SubscriptionTaskConfig::getSubscriptionSourceId);
         return taskConfigMapper.selectByExample(weekend);
     }
 }

+ 46 - 1
data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/SubscriptionUserConfigServiceImpl.java

@@ -1,10 +1,19 @@
 package com.dataeasy.server.atomic.service.impl;
 
+import com.dataeasy.server.atomic.entity.SubscriptionUserConfig;
+import com.dataeasy.server.common.utils.Assert;
+import com.dataeasy.server.pojo.subscription.SubscriptionUserConfigQuery;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import com.dataeasy.server.atomic.mapper.SubscriptionUserConfigMapper;
 import com.dataeasy.server.atomic.service.ISubscriptionUserConfigService;
+import org.springframework.util.CollectionUtils;
+import tk.mybatis.mapper.weekend.Weekend;
+import tk.mybatis.mapper.weekend.WeekendCriteria;
+
+import java.util.List;
+import java.util.Objects;
 
 /**
  * @author tyuio
@@ -16,6 +25,42 @@ import com.dataeasy.server.atomic.service.ISubscriptionUserConfigService;
 public class SubscriptionUserConfigServiceImpl implements ISubscriptionUserConfigService {
 
     @Autowired
-    private SubscriptionUserConfigMapper mapper;
+    private SubscriptionUserConfigMapper userConfigMapper;
+
+    @Override
+    public List<SubscriptionUserConfig> getByCondition(SubscriptionUserConfigQuery query) {
+        if (Objects.isNull(query)) {
+            return List.of();
+        }
+        Weekend<SubscriptionUserConfig> weekend = Weekend.of(SubscriptionUserConfig.class);
+        WeekendCriteria<SubscriptionUserConfig, Object> weekendCriteria = weekend.weekendCriteria();
+        if (!CollectionUtils.isEmpty(query.getSubscriptionSourceIds())) {
+            weekendCriteria.andIn(SubscriptionUserConfig::getSubscriptionSourceId, query.getSubscriptionSourceIds());
+        }
+        if (Objects.nonNull(query.getPushOption())) {
+            weekendCriteria.andEqualTo(SubscriptionUserConfig::getPushOption, query.getPushOption());
+        }
+        if (Objects.nonNull(query.getCurrentTime())) {
+            weekendCriteria.andGreaterThanOrEqualTo(SubscriptionUserConfig::getStartTime, query.getCurrentTime());
+            weekendCriteria.andLessThanOrEqualTo(SubscriptionUserConfig::getEndTime, query.getCurrentTime());
+        }
+        if (Objects.nonNull(query.getUserId())) {
+            weekendCriteria.andEqualTo(SubscriptionUserConfig::getUserId, query.getUserId());
+        }
+        weekend.selectProperties(
+                SubscriptionUserConfig::getId,
+                SubscriptionUserConfig::getSubscriptionSourceId,
+                SubscriptionUserConfig::getPushOption,
+                SubscriptionUserConfig::getStartTime,
+                SubscriptionUserConfig::getEndTime,
+                SubscriptionUserConfig::getUserId
+        );
+        return userConfigMapper.selectByExample(weekend);
+    }
 
+    @Override
+    public void updateById(SubscriptionUserConfig subscriptionUserConfig) {
+        Assert.isNull(subscriptionUserConfig);
+        userConfigMapper.updateByPrimaryKeySelective(subscriptionUserConfig);
+    }
 }

+ 46 - 3
data-easy/src/main/java/com/dataeasy/server/atomic/service/impl/UserServiceImpl.java

@@ -7,7 +7,13 @@ import com.dataeasy.server.common.utils.Assert;
 import io.micrometer.common.util.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import tk.mybatis.mapper.weekend.Weekend;
+import tk.mybatis.mapper.weekend.WeekendCriteria;
 
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -31,13 +37,41 @@ public class UserServiceImpl implements IUserService {
     }
 
     @Override
-    public User getByMaOpenId(String openId) {
-        if (StringUtils.isBlank(openId)) {
+    public List<User> getByIds(Collection<Long> ids) {
+        if (CollectionUtils.isEmpty(ids)) {
+            return List.of();
+        }
+        Weekend<User> weekend = Weekend.of(User.class);
+        WeekendCriteria<User, Object> weekendCriteria = weekend.weekendCriteria();
+        weekendCriteria.andIn(User::getId, ids);
+
+        weekend.selectProperties(User::getId,
+                User::getMaOpenId,
+                User::getMpOpenId,
+                User::getUnionId,
+                User::getNickname);
+        return userMapper.selectByExample(weekend);
+    }
+
+    @Override
+    public User getByMaOpenId(String unionId) {
+        if (StringUtils.isBlank(unionId)) {
             return null;
         }
 
         User queryUser = new User();
-        queryUser.setMaOpenId(openId);
+        queryUser.setMaOpenId(unionId);
+        return userMapper.selectOne(queryUser);
+    }
+
+    @Override
+    public User getByUnionId(String unionId) {
+        if (StringUtils.isBlank(unionId)) {
+            return null;
+        }
+
+        User queryUser = new User();
+        queryUser.setUnionId(unionId);
         return userMapper.selectOne(queryUser);
     }
 
@@ -52,4 +86,13 @@ public class UserServiceImpl implements IUserService {
         Assert.isNull(user);
         userMapper.updateByPrimaryKeySelective(user);
     }
+
+    @Override
+    public void unsetMpOpenIdByID(Long userId) {
+        Assert.isNull(userId);
+
+        User updateUser = new User();
+        updateUser.setId(userId);
+        userMapper.updateByPrimaryKeySelectiveForce(updateUser, Arrays.asList("mp_open_id"));
+    }
 }

+ 20 - 0
data-easy/src/main/java/com/dataeasy/server/constant/ExecuteOptionEnum.java

@@ -0,0 +1,20 @@
+package com.dataeasy.server.constant;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 执行选项(ENABLE-开启、DISABLE-关闭)
+ * @date 2025/3/7 8:36
+ */
+@Getter
+@AllArgsConstructor
+public enum ExecuteOptionEnum {
+
+    ENABLE("开启"),
+    DISABLE("关闭");
+
+    private String name;
+}

+ 20 - 0
data-easy/src/main/java/com/dataeasy/server/constant/PushOptionEnum.java

@@ -0,0 +1,20 @@
+package com.dataeasy.server.constant;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 消息推送选项(ENABLE-开启、DISABLE-关闭)
+ * @date 2025/3/7 8:53
+ */
+@Getter
+@AllArgsConstructor
+public enum PushOptionEnum {
+
+    ENABLE("开启"),
+    DISABLE("关闭");
+
+    private String name;
+}

+ 5 - 1
data-easy/src/main/java/com/dataeasy/server/constant/ScheduleTaskEnum.java

@@ -16,7 +16,11 @@ import java.util.Date;
 @AllArgsConstructor
 public enum ScheduleTaskEnum {
 
-    DA_LE_TOU("大乐透定时任务");
+    DA_LE_TOU("大乐透定时任务"),
+    SHUANG_SE_QIU("双色球定时任务"),
+    IPO_BOND("新债定时任务"),
+    IPO_STOCK("新股定时任务"),
+    PRODUCT_HUNT("ProductHunt热榜定时任务");
 
     private String name;
 

+ 2 - 2
data-easy/src/main/java/com/dataeasy/server/core/config/HzApiConfig.java → data-easy/src/main/java/com/dataeasy/server/core/config/HzApiProperties.java

@@ -8,13 +8,13 @@ import lombok.Data;
 /**
  * @author tyuio
  * @version 1.0.0
- * @description 接口盒子配置
+ * @description 接口盒子配置
  * @date 2025/2/28 11:25
  */
 @Data
 @Component
 @ConfigurationProperties("hz-api")
-public class HzApiConfig {
+public class HzApiProperties {
 
     /**
      * 平台接口地址

+ 9 - 10
data-easy/src/main/java/com/dataeasy/server/demo/msg/WeChatMsgPushConfig.java → data-easy/src/main/java/com/dataeasy/server/core/config/WxMessageProperties.java

@@ -1,24 +1,23 @@
-package com.dataeasy.server.demo.msg;
+package com.dataeasy.server.core.config;
 
-import lombok.Data;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component; /**
+import org.springframework.stereotype.Component;
+
+import lombok.Data;
+
+/**
  * @author myou
  * @version 1.0.0
  * @date 2025/3/1 21:35
- * @description TODO
+ * @description 微信模板信息配置
  */
 @Data
 @Component
 @ConfigurationProperties(prefix = "wechat")
-public class WeChatMsgPushConfig {
+public class WxMessageProperties {
+
     /**
      * 设置要取用的公众号模板
      */
-    @Value("${wechat.templateId}")
     private String templateId;
-    //颜色 、url我没配,你要是配了自己写上
-
-
 }

+ 50 - 0
data-easy/src/main/java/com/dataeasy/server/core/config/WxMpConfig.java

@@ -0,0 +1,50 @@
+package com.dataeasy.server.core.config;
+
+import com.dataeasy.server.core.handler.WxMpLogHandler;
+import com.dataeasy.server.core.handler.WxMpSubscribeHandler;
+import com.dataeasy.server.core.handler.WxMpUnsubscribeHandler;
+import me.chanjar.weixin.mp.api.WxMpMessageRouter;
+import me.chanjar.weixin.mp.api.WxMpService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static me.chanjar.weixin.common.api.WxConsts.EventType.SUBSCRIBE;
+import static me.chanjar.weixin.common.api.WxConsts.EventType.UNSUBSCRIBE;
+import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType.EVENT;
+
+/**
+ * @author myou
+ * @version 1.0.0
+ * @date 2025/3/1 21:36
+ * @description 微信公众号/服务号 配置
+ */
+@Configuration
+public class WxMpConfig {
+
+    @Autowired
+    private WxMpLogHandler logHandler;
+
+    @Autowired
+    private WxMpSubscribeHandler subscribeHandler;
+
+    @Autowired
+    private WxMpUnsubscribeHandler unsubscribeHandler;
+
+    @Bean
+    public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
+        final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
+
+        // 记录所有事件的日志 (异步执行)
+        newRouter.rule().handler(logHandler).next();
+
+        // 关注事件
+        newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(subscribeHandler).end();
+
+        // 取消关注事件
+        newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(unsubscribeHandler).end();
+
+        return newRouter;
+    }
+
+}

+ 4 - 3
data-easy/src/main/java/com/dataeasy/server/demo/msg/LogHandler.java → data-easy/src/main/java/com/dataeasy/server/core/handler/WxMpLogHandler.java

@@ -1,4 +1,4 @@
-package com.dataeasy.server.demo.msg;
+package com.dataeasy.server.core.handler;
 
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.session.WxSessionManager;
@@ -13,12 +13,13 @@ import java.util.Map;
 /**
  * @author tyuio
  * @version 1.0.0
- * @description TODO
+ * @description 微信公众号/服务号 日志处理器
  * @date 2025/3/3 14:31
  */
 @Slf4j
 @Component
-public class LogHandler implements WxMpMessageHandler {
+public class WxMpLogHandler implements WxMpMessageHandler {
+
     @Override
     public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
                                     Map<String, Object> context, WxMpService wxMpService,

+ 60 - 0
data-easy/src/main/java/com/dataeasy/server/core/handler/WxMpSubscribeHandler.java

@@ -0,0 +1,60 @@
+package com.dataeasy.server.core.handler;
+
+import java.util.Map;
+import java.util.Objects;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.dataeasy.server.atomic.entity.User;
+import com.dataeasy.server.atomic.service.IUserService;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpMessageHandler;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import me.chanjar.weixin.mp.builder.outxml.TextBuilder;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 微信公众号/服务号 订阅关注处理器
+ * @date 2025/3/3 14:11
+ */
+@Component
+@Slf4j
+public class WxMpSubscribeHandler implements WxMpMessageHandler {
+
+    @Autowired
+    private IUserService userService;
+
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
+                                    Map<String, Object> context, WxMpService weixinService,
+                                    WxSessionManager sessionManager) throws WxErrorException {
+
+        // 查询用户是否存在,不存在则新增用户
+        User user = userService.getByUnionId(wxMessage.getUnionId());
+        if (Objects.isNull(user)) {
+            // 新增
+            User addUser = new User();
+            addUser.setUnionId(wxMessage.getUnionId());
+            addUser.setMpOpenId(wxMessage.getOpenId());
+            userService.insert(addUser);
+        } else {
+            // 更新
+            User updateUser = new User();
+            updateUser.setId(user.getId());
+            updateUser.setMpOpenId(wxMessage.getOpenId());
+            userService.updateById(updateUser);
+        }
+
+        TextBuilder textBuilder = new TextBuilder();
+        textBuilder.content("欢迎关注小石知数");
+        textBuilder.toUser(wxMessage.getOpenId());
+        return textBuilder.build();
+    }
+}

+ 50 - 0
data-easy/src/main/java/com/dataeasy/server/core/handler/WxMpUnsubscribeHandler.java

@@ -0,0 +1,50 @@
+package com.dataeasy.server.core.handler;
+
+import java.util.Map;
+import java.util.Objects;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.dataeasy.server.atomic.entity.User;
+import com.dataeasy.server.atomic.service.IUserService;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpMessageHandler;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import me.chanjar.weixin.mp.builder.outxml.TextBuilder;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 微信公众号/服务号 取消订阅关注处理器
+ * @date 2025/3/3 14:11
+ */
+@Component
+@Slf4j
+public class WxMpUnsubscribeHandler implements WxMpMessageHandler {
+
+    @Autowired
+    private IUserService userService;
+
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
+                                    Map<String, Object> context, WxMpService weixinService,
+                                    WxSessionManager sessionManager) throws WxErrorException {
+
+        // 用户取消关注,置空openid
+        User user = userService.getByUnionId(wxMessage.getUnionId());
+        if (Objects.nonNull(user)) {
+            userService.unsetMpOpenIdByID(user.getId());
+        }
+
+        TextBuilder textBuilder = new TextBuilder();
+        textBuilder.content("已关注小石知数,再会!");
+        textBuilder.toUser(wxMessage.getOpenId());
+        return textBuilder.build();
+    }
+}

+ 0 - 9
data-easy/src/main/java/com/dataeasy/server/demo/TestController.java

@@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
-import com.dataeasy.server.demo.msg.WeChatMsgPushUtil;
 import com.github.binarywang.wxpay.exception.WxPayException;
 
 import cn.binarywang.wx.miniapp.api.WxMaService;
@@ -28,14 +27,6 @@ import me.chanjar.weixin.common.error.WxErrorException;
 @RestController
 public class TestController {
 
-    @Autowired
-    private WeChatMsgPushUtil weChatMsgPushUtil;
-
-    @GetMapping("/test/t1")
-    public void t1() {
-        weChatMsgPushUtil.sendOrderMsg("oaeVR7FhHMDu3iqUOhcTgJJVdOM0", "www.baidu.com");
-    }
-
     @Autowired
     private PayService payService;
 

+ 0 - 112
data-easy/src/main/java/com/dataeasy/server/demo/msg/MpEntryController.java

@@ -1,112 +0,0 @@
-package com.dataeasy.server.demo.msg;
-
-import lombok.extern.slf4j.Slf4j;
-import me.chanjar.weixin.mp.api.WxMpMessageRouter;
-import me.chanjar.weixin.mp.api.WxMpService;
-import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
-import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-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.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-/**
- * @author tyuio
- * @version 1.0.0
- * @description TODO
- * @date 2025/2/28 16:39
- */
-@Component
-@RestController
-@Slf4j
-public class MpEntryController {
-
-    @Autowired
-    private WxMpService wxMpService;
-    @Autowired
-    private WxMpMessageRouter wxMpMessageRouter;
-
-    /**
-     * 微信接入
-     *
-     * @param signature 签名
-     * @param timestamp 时间戳
-     * @param nonce     随机数
-     * @param echoStr   随机字符串
-     * @return 接入成功返回 echoStr 的值,否则随便返回
-     */
-    @GetMapping("/WeChat/token")
-    public String entry(@RequestParam("signature") String signature,
-                        @RequestParam("timestamp") String timestamp,
-                        @RequestParam("nonce") String nonce,
-                        @RequestParam("echostr") String echoStr) {
-        log.info("微信公众号/服务号接入传递的参数 signature:[{}],timestamp:[{}],nonce:[{}],echostr:[{}]",
-                signature, timestamp, nonce, echoStr);
-
-        if (StringUtils.isAnyBlank(signature, timestamp, nonce, echoStr)) {
-            log.error("接收到微信认证信息,参数非法,存在为空的参数");
-            return "error";
-        }
-
-        boolean result = wxMpService.checkSignature(timestamp, nonce, signature);
-        log.info("微信公众号/服务号接入成功?[{}]", result);
-        return result ? echoStr : "error";
-    }
-
-    /**
-     * 微信回调
-     *
-     * @param requestBody 回复
-     * @param signature 签名
-     * @param timestamp 时间戳
-     * @param nonce 随机数
-     * @param openid 用户id
-     * @param encType 未知参数
-     * @param msgSignature 未知参数
-     * @return xml
-     */
-    @PostMapping("/WeChat/token")
-    public String entryCallback(@RequestBody String requestBody,
-                                @RequestParam("signature") String signature,
-                                @RequestParam("timestamp") String timestamp,
-                                @RequestParam("nonce") String nonce,
-                                @RequestParam("openid") String openid,
-                                @RequestParam(name = "encrypt_type", required = false) String encType,
-                                @RequestParam(name = "msg_signature", required = false) String msgSignature) {
-//        log.info("\n接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}],"
-//                        + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
-//                openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
-
-        if (!wxMpService.checkSignature(timestamp, nonce, signature)) {
-            throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
-        }
-
-        String out = null;
-        if (encType == null) {
-            // 明文传输的消息
-            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
-            WxMpXmlOutMessage outMessage = this.wxMpMessageRouter.route(inMessage);
-            if (outMessage == null) {
-                return "";
-            }
-            out = outMessage.toXml();
-        } else if ("aes".equalsIgnoreCase(encType)) {
-            // aes加密的消息
-            WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxMpService.getWxMpConfigStorage(),
-                    timestamp, nonce, msgSignature);
-            log.info("\n消息解密后内容为:\n[{}] ", inMessage.toString());
-            WxMpXmlOutMessage outMessage = this.wxMpMessageRouter.route(inMessage);
-            if (outMessage == null) {
-                return "";
-            }
-            out = outMessage.toEncryptedXml(wxMpService.getWxMpConfigStorage());
-        }
-        log.info("\n组装回复信息:[{}]", out);
-        return out;
-    }
-}
-

+ 0 - 76
data-easy/src/main/java/com/dataeasy/server/demo/msg/SubscribeHandler.java

@@ -1,76 +0,0 @@
-package com.dataeasy.server.demo.msg;
-
-import lombok.extern.slf4j.Slf4j;
-import me.chanjar.weixin.common.error.WxErrorException;
-import me.chanjar.weixin.common.session.WxSessionManager;
-import me.chanjar.weixin.mp.api.WxMpMessageHandler;
-import me.chanjar.weixin.mp.api.WxMpService;
-import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
-import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
-import me.chanjar.weixin.mp.bean.result.WxMpUser;
-import org.springframework.stereotype.Component;
-
-import java.util.Map;
-
-/**
- * @author tyuio
- * @version 1.0.0
- * @description TODO
- * @date 2025/3/3 14:11
- */
-@Component
-@Slf4j
-public class SubscribeHandler implements WxMpMessageHandler {
-
-    @Override
-    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
-                                    Map<String, Object> context, WxMpService weixinService,
-                                    WxSessionManager sessionManager) throws WxErrorException {
-
-        log.info("新关注用户 OPENID: " + wxMessage.getFromUser());
-//        用户关注之后会触发这个类,其他类也都可以例如:对话,取关什么的,看你业务需求
-        log.info("这就是你想要的openId{}",wxMessage.getOpenId());
-
-        // 获取微信用户基本信息
-        try {
-            WxMpUser userWxInfo = weixinService.getUserService()
-                    .userInfo(wxMessage.getFromUser(), null);
-            if (userWxInfo != null) {
-                // TODO 可以添加关注用户到本地数据库
-//                这里逻辑代码不展示
-            }
-        } catch (WxErrorException e) {
-            if (e.getError().getErrorCode() == 48001) {
-                log.info("该公众号没有获取用户信息权限!");
-            }
-        }
-
-
-        WxMpXmlOutMessage responseResult = null;
-        try {
-            responseResult = this.handleSpecial(wxMessage);
-        } catch (Exception e) {
-            log.error(e.getMessage(), e);
-        }
-
-        if (responseResult != null) {
-            return responseResult;
-        }
-        try {
-//            return new TextBuilder().build(WxMsgSendUserUtil.USER_SUBSCRIBE, wxMessage, weixinService);
-            return null;
-        } catch (Exception e) {
-            log.error(e.getMessage(), e);
-        }
-        return null;
-    }
-
-    /**
-     * 处理特殊请求,比如如果是扫码进来的,可以做相应处理
-     */
-    private WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage)
-            throws Exception {
-        //TODO
-        return null;
-    }
-}

+ 0 - 57
data-easy/src/main/java/com/dataeasy/server/demo/msg/WeChatMsgPushUtil.java

@@ -1,57 +0,0 @@
-package com.dataeasy.server.demo.msg;
-
-import lombok.extern.slf4j.Slf4j;
-import me.chanjar.weixin.common.error.WxErrorException;
-import me.chanjar.weixin.mp.api.WxMpService;
-import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
-import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component; /**
- * @author myou
- * @version 1.0.0
- * @date 2025/3/1 21:41
- * @description TODO
- */
-@Slf4j
-@Component
-public class WeChatMsgPushUtil {
-    @Autowired
-    private WxMpService wxMpService;
-    @Autowired
-    private WeChatMsgPushConfig weChatMsgPushConfig; //这个是什么配置的模板类
-
-    /**
-     * 对用户发送消息的接口
-     * @param tradeName 公司名称
-     * @param subscribeTime 预约时间
-     * @param subscribeNum 预约数量
-     * @param Kind 预约种类
-     * @return boolean
-     */
-    public Boolean sendOrderMsg(String openId,
-                                String urlRel) {
-
-
-        WxMpTemplateMessage templateMessage=WxMpTemplateMessage.builder()
-                .toUser(openId) //这个openid是你要推送的人,如果你不知道怎么拿用户的openid可以来往上面看看
-                .templateId(weChatMsgPushConfig.getTemplateId())//这里是你要发送消息的模板id
-                .url(urlRel)//这里是你的配置的url 例如:www.baidu.com,
-                .build();
-        templateMessage
-                .addData(new WxMpTemplateData("first", "预约成功"))
-                .addData(new WxMpTemplateData("keyword1", "测试111"))
-                .addData(new WxMpTemplateData("keyword2","测试2222"))
-                .addData(new WxMpTemplateData("keyword3","测试333"))
-                .addData(new WxMpTemplateData("keyword4","测试4444"))
-                .addData(new WxMpTemplateData("remark","测试备注"));
-
-        String msgPush=null;
-        try {
-            msgPush = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
-        } catch (WxErrorException e) {
-            throw new RuntimeException(e);
-        }
-        log.warn("·==++--·推送微信模板信息:{}·--++==·", msgPush != null ? "成功" : "失败");
-        return msgPush!=null;
-    }
-}

+ 0 - 106
data-easy/src/main/java/com/dataeasy/server/demo/msg/WxMpConfiguration.java

@@ -1,106 +0,0 @@
-package com.dataeasy.server.demo.msg;
-
-import lombok.AllArgsConstructor;
-import me.chanjar.weixin.mp.api.WxMpMessageRouter;
-import me.chanjar.weixin.mp.api.WxMpService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import static me.chanjar.weixin.common.api.WxConsts.EventType.SUBSCRIBE;
-import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType.EVENT;
-
-/**
- * @author myou
- * @version 1.0.0
- * @date 2025/3/1 21:36
- * @description TODO
- */
-@AllArgsConstructor
-@Configuration
-public class WxMpConfiguration {
-//    @Autowired
-//    private ImgHandler imgHandler;
-//    @Autowired
-//    private TextMsgHandler textMsgHandler;
-//    @Autowired
-//    private KfSessionHandler kfSessionHandler;
-//    @Autowired
-//    private MenuHandler menuHandler;
-//    @Autowired
-//    private ScanHandler scanHandler;
-//    @Autowired
-//    private MsgHandler msgHandler;
-    @Autowired
-    private LogHandler logHandler;
-//    @Autowired
-//    private WxMpProperties wxMpProperties;
-    @Autowired
-    private SubscribeHandler subscribeHandler;
-//    @Autowired
-//    private UnsubscribeHandler unsubscribeHandler;
-
-//    @Bean
-//    public WxMpService wxMpService() {
-//        WxMpServiceImpl wxMpService = new WxMpServiceImpl();
-//        wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
-//        // 设置多个微信公众号的配置
-//        // wxMpService.setMultiConfigStorages();
-//        return wxMpService;
-//    }
-
-    /**
-     * 这个地方的配置是保存在本地,生产环境需要自己扩展,可以保存在Redis中等等
-     *
-     * @return WxMpConfigStorage
-     */
-//    public WxMpConfigStorage wxMpConfigStorage() {
-//        WxMpDefaultConfigImpl storage = new WxMpDefaultConfigImpl();
-//        storage.setAppId(wxMpProperties.getAppId());
-//        storage.setSecret(wxMpProperties.getSecret());
-//        storage.setAesKey(wxMpProperties.getAesKey());
-//        storage.setToken(wxMpProperties.getToken());
-//        return storage;
-//    }
-
-    @Bean
-    public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
-        final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
-
-        // 记录所有事件的日志 (异步执行)
-        newRouter.rule().handler(this.logHandler).next();
-//
-//        // 接收客服会话管理事件
-//        newRouter.rule().async(false).msgType(EVENT).event(KF_CREATE_SESSION)
-//                .handler(this.kfSessionHandler).end();
-//        newRouter.rule().async(false).msgType(EVENT).event(KF_CLOSE_SESSION)
-//                .handler(this.kfSessionHandler).end();
-//        newRouter.rule().async(false).msgType(EVENT).event(KF_SWITCH_SESSION)
-//                .handler(this.kfSessionHandler).end();
-//
-//
-//        // 自定义菜单事件
-//        newRouter.rule().async(false).msgType(EVENT).event(EventType.CLICK).handler(this.menuHandler).end();
-//
-        // 关注事件
-        newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(this.subscribeHandler).end();
-
-        // 取消关注事件
-//        newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(this.unsubscribeHandler).end();
-//
-//        // 扫码事件
-//        newRouter.rule().async(false).msgType(EVENT).event(EventType.SCAN).handler(this.scanHandler).end();
-//
-//        // 文本消息处理
-//        newRouter.rule().async(false).msgType(XmlMsgType.TEXT).handler(this.textMsgHandler).end();
-//
-//        // 图片消息处理
-//        newRouter.rule().async(false).msgType(XmlMsgType.IMAGE).handler(this.imgHandler).end();
-//
-        // 默认
-//        newRouter.rule().async(false).handler(this.msgHandler).end();
-
-        return newRouter;
-    }
-
-}

+ 23 - 0
data-easy/src/main/java/com/dataeasy/server/feign/ProductHuntFeign.java

@@ -0,0 +1,23 @@
+package com.dataeasy.server.feign;
+
+import com.dataeasy.server.common.pojo.JsonResponse;
+import com.dataeasy.server.feign.dto.PostNode;
+import org.springframework.cloud.openfeign.FeignClient;
+
+import java.util.List;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description ProductHunt数据服务
+ * @date 2025/3/7 12:36
+ */
+@FeignClient(name = "product-hunt-service", url = "${product-hunt.base-url}")
+public interface ProductHuntFeign {
+
+    /**
+     * 获取产品热榜TOP30
+     * @return
+     */
+    JsonResponse<List<PostNode>> getTop30Posts();
+}

+ 60 - 0
data-easy/src/main/java/com/dataeasy/server/feign/dto/PostNode.java

@@ -0,0 +1,60 @@
+package com.dataeasy.server.feign.dto;
+
+import java.time.LocalDateTime;
+
+import lombok.Data;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description Product Hunt的Post信息
+ * @date 2025/2/24 15:44
+ */
+@Data
+public class PostNode {
+
+    /**
+     * 帖子ID
+     */
+    private Long id;
+
+    /**
+     * 帖子名称
+     */
+    private String name;
+
+    /**
+     * 帖子的标语
+     */
+    private String tagline;
+
+    /**
+     * 帖子信息
+     */
+    private String description;
+
+    /**
+     * 投票数
+     */
+    private Integer votesCount;
+
+    /**
+     * 帖子的创建日期和时间
+     */
+    private LocalDateTime createdAt;
+
+    /**
+     * 帖子被特色展示的日期和时间
+     */
+    private LocalDateTime featuredAt;
+
+    /**
+     * 指向该帖子网站的重定向 URL
+     */
+    private String website;
+
+    /**
+     * 帖子URL
+     */
+    private String url;
+}

+ 171 - 0
data-easy/src/main/java/com/dataeasy/server/pojo/data/DaLeTouVO.java

@@ -0,0 +1,171 @@
+package com.dataeasy.server.pojo.data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+import lombok.Data;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 大乐透 VO
+ * @date 2025/3/7 15:51
+ */
+@Data
+public class DaLeTouVO {
+
+    /**
+     * 开奖号码,以竖杠隔开
+     */
+    private String number;
+
+    /**
+     * 特别号码1,第一个尾号
+     */
+    private String number1;
+
+    /**
+     * 特别号码2,第二个尾号
+     */
+    private String number2;
+
+    /**
+     * 期号
+     */
+    private String qihao;
+
+    /**
+     * 开奖日期
+     */
+    private Date time;
+
+    /**
+     * 一等奖注数
+     */
+    private Integer no1num;
+
+    /**
+     * 二等奖注数
+     */
+    private Integer no2num;
+
+    /**
+     * 三等奖注数
+     */
+    private Integer no3num;
+
+    /**
+     * 四等奖注数
+     */
+    private Integer no4num;
+
+    /**
+     * 五等奖注数
+     */
+    private Integer no5num;
+
+    /**
+     * 六等奖注数
+     */
+    private Integer no6num;
+
+    /**
+     * 七等奖注数
+     */
+    private Integer no7num;
+
+    /**
+     * 八等奖注数
+     */
+    private Integer no8num;
+
+    /**
+     * 九等奖注数
+     */
+    private Integer no9num;
+
+    /**
+     * 一等奖金额
+     */
+    private BigDecimal no1money;
+
+    /**
+     * 二等奖金额
+     */
+    private BigDecimal no2money;
+
+    /**
+     * 三等奖金额
+     */
+    private BigDecimal no3money;
+
+    /**
+     * 四等奖金额
+     */
+    private BigDecimal no4money;
+
+    /**
+     * 五等奖金额
+     */
+    private BigDecimal no5money;
+
+    /**
+     * 六等奖金额
+     */
+    private BigDecimal no6money;
+
+    /**
+     * 七等奖金额
+     */
+    private BigDecimal no7money;
+
+    /**
+     * 八等奖金额
+     */
+    private BigDecimal no8money;
+
+    /**
+     * 九等奖金额
+     */
+    private BigDecimal no9money;
+
+    /**
+     * 彩票名称
+     */
+    private String name;
+
+    /**
+     * 销售额
+     */
+    private BigDecimal xiaoshou;
+
+    /**
+     * 奖池金额
+     */
+    private BigDecimal jiangchi;
+
+    /**
+     * 截止兑奖时间
+     */
+    private Date endtime;
+
+    /**
+     * 一等奖追加注数
+     */
+    private Integer no1numjia;
+
+    /**
+     * 一等奖追加奖金
+     */
+    private BigDecimal no1moneyjia;
+
+    /**
+     * 二等奖追加注数
+     */
+    private Integer no2numjia;
+
+    /**
+     * 二等奖追加奖金
+     */
+    private BigDecimal no2moneyjia;
+}

+ 171 - 0
data-easy/src/main/java/com/dataeasy/server/pojo/data/IpoBondVO.java

@@ -0,0 +1,171 @@
+package com.dataeasy.server.pojo.data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+import lombok.Data;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 新债 VO
+ * @date 2025/3/7 15:49
+ */
+@Data
+public class IpoBondVO {
+
+    /**
+     * 债券代码
+     */
+    private String bondCode;
+
+    /**
+     * 债券简称
+     */
+    private String bondShortName;
+
+    /**
+     * 公告日期
+     */
+    private Date announcementDate;
+
+    /**
+     * 发行起始日
+     */
+    private Date issueStartDate;
+
+    /**
+     * 发行终止日
+     */
+    private Date issueEndDate;
+
+    /**
+     * 计划发行总量
+     */
+    private BigDecimal plannedIssueAmount;
+
+    /**
+     * 实际发行总量
+     */
+    private BigDecimal actualIssueAmount;
+
+    /**
+     * 发行面值
+     */
+    private BigDecimal issueParValue;
+
+    /**
+     * 发行价格
+     */
+    private BigDecimal issuePrice;
+
+    /**
+     * 发行方式
+     */
+    private String issueMethod;
+
+    /**
+     * 发行对象
+     */
+    private String issueTarget;
+
+    /**
+     * 发行范围
+     */
+    private String issueScope;
+
+    /**
+     * 承销方式
+     */
+    private String underwritingMethod;
+
+    /**
+     * 募资用途说明
+     */
+    private String fundraisingPurpose;
+
+    /**
+     * 初始转股价格
+     */
+    private BigDecimal initialConversionPrice;
+
+    /**
+     * 转股开始日期
+     */
+    private Date conversionStartDate;
+
+    /**
+     * 转股终止日期
+     */
+    private Date conversionEndDate;
+
+    /**
+     * 网上申购日期
+     */
+    private Date onlineSubscriptionDate;
+
+    /**
+     * 网上申购代码
+     */
+    private String onlineSubscriptionCode;
+
+    /**
+     * 网上申购简称
+     */
+    private String onlineSubscriptionShortName;
+
+    /**
+     * 网上申购数量上限
+     */
+    private BigDecimal onlineSubscriptionMax;
+
+    /**
+     * 网上申购数量下限
+     */
+    private BigDecimal onlineSubscriptionMin;
+
+    /**
+     * 网上申购单位
+     */
+    private BigDecimal onlineSubscriptionUnit;
+
+    /**
+     * 网上申购中签结果公告日及退款日
+     */
+    private Date onlineSubscriptionResultDate;
+
+    /**
+     * 优先申购日
+     */
+    private Date prioritySubscriptionDate;
+
+    /**
+     * 配售价格
+     */
+    private BigDecimal allotmentPrice;
+
+    /**
+     * 债权登记日
+     */
+    private Date creditorRegistrationDate;
+
+    /**
+     * 优先申购缴款日
+     */
+    private Date prioritySubscriptionPaymentDate;
+
+    /**
+     * 转股代码
+     */
+    private String conversionCode;
+
+    /**
+     * 交易市场
+     */
+    private String tradingMarket;
+
+    /**
+     * 债券名称
+     */
+    private String bondName;
+}

+ 126 - 0
data-easy/src/main/java/com/dataeasy/server/pojo/data/IpoStockVO.java

@@ -0,0 +1,126 @@
+package com.dataeasy.server.pojo.data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+import lombok.Data;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 新股 VO
+ * @date 2025/3/7 15:49
+ */
+@Data
+public class IpoStockVO {
+
+    /**
+     * 股票代码
+     */
+    private String stockCode;
+
+    /**
+     * 股票简称
+     */
+    private String stockShortName;
+
+    /**
+     * 申购代码
+     */
+    private String subscriptionCode;
+
+    /**
+     * 发行总数
+     */
+    private BigDecimal totalIssued;
+
+    /**
+     * 网上发行
+     */
+    private BigDecimal onlineIssued;
+
+    /**
+     * 顶格申购需配市值
+     */
+    private BigDecimal maxMarketValueForSubscription;
+
+    /**
+     * 申购上限
+     */
+    private Integer subscriptionLimit;
+
+    /**
+     * 发行价格
+     */
+    private BigDecimal issuePrice;
+
+    /**
+     * 最新价
+     */
+    private BigDecimal latestPrice;
+
+    /**
+     * 首日收盘价
+     */
+    private BigDecimal firstDayClosingPrice;
+
+    /**
+     * 申购日期
+     */
+    private Date subscriptionDate;
+
+    /**
+     * 中签号公布日
+     */
+    private Date winningNumberAnnouncementDate;
+
+    /**
+     * 中签缴款日期
+     */
+    private Date paymentDateForWinning;
+
+    /**
+     * 上市日期
+     */
+    private Date listingDate;
+
+    /**
+     * 发行市盈率
+     */
+    private BigDecimal issuePeRatio;
+
+    /**
+     * 行业市盈率
+     */
+    private BigDecimal industryPeRatio;
+
+    /**
+     * 中签率
+     */
+    private BigDecimal winningRate;
+
+    /**
+     * 询价累计报价倍数
+     */
+    private BigDecimal cumulativeBidMultiple;
+
+    /**
+     * 配售对象报价家数
+     */
+    private Integer biddingFirmsCount;
+
+    /**
+     * 连续一字板数量
+     */
+    private String consecutiveLimitUpDays;
+
+    /**
+     * 涨幅
+     */
+    private BigDecimal increaseRate;
+
+    /**
+     * 每中一签获利
+     */
+    private BigDecimal profitRerWinningLot;
+}

+ 70 - 0
data-easy/src/main/java/com/dataeasy/server/pojo/data/ProductHuntPostVO.java

@@ -0,0 +1,70 @@
+package com.dataeasy.server.pojo.data;
+
+import java.util.Date;
+
+import lombok.Data;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description ProductHunt热榜帖子 VO
+ * @date 2025/3/7 15:52
+ */
+@Data
+public class ProductHuntPostVO {
+
+    /**
+     * 榜单日期
+     */
+    private Date rankDate;
+
+    /**
+     * 排名位置
+     */
+    private Integer rankNum;
+
+    /**
+     * 帖子ID
+     */
+    private Long postId;
+
+    /**
+     * 帖子名称
+     */
+    private String name;
+
+    /**
+     * 帖子的标语
+     */
+    private String tagline;
+
+    /**
+     * 帖子信息
+     */
+    private String description;
+
+    /**
+     * 投票数
+     */
+    private Integer votesCount;
+
+    /**
+     * 帖子的创建日期和时间
+     */
+    private Date createdAt;
+
+    /**
+     * 帖子被特色展示的日期和时间
+     */
+    private Date featuredAt;
+
+    /**
+     * 指向该帖子网站的重定向 URL
+     */
+    private String website;
+
+    /**
+     * 帖子URL
+     */
+    private String url;
+}

+ 115 - 0
data-easy/src/main/java/com/dataeasy/server/pojo/data/ShuangSeQiuVO.java

@@ -0,0 +1,115 @@
+package com.dataeasy.server.pojo.data;
+
+import java.math.BigDecimal;
+
+import lombok.Data;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 双色球 VO
+ * @date 2025/3/7 15:51
+ */
+@Data
+public class ShuangSeQiuVO {
+
+    /**
+     * 开奖号码,以竖杠隔开
+     */
+    private String number;
+
+    /**
+     * 特别号码,尾号
+     */
+    private String number1;
+
+    /**
+     * 期号
+     */
+    private String qihao;
+
+    /**
+     * 开奖日期
+     */
+    private String time;
+
+    /**
+     * 一等奖注数
+     */
+    private Integer no1num;
+
+    /**
+     * 二等奖注数
+     */
+    private Integer no2num;
+
+    /**
+     * 三等奖注数
+     */
+    private Integer no3num;
+
+    /**
+     * 四等奖注数
+     */
+    private Integer no4num;
+
+    /**
+     * 五等奖注数
+     */
+    private Integer no5num;
+
+    /**
+     * 六等奖注数
+     */
+    private Integer no6num;
+
+    /**
+     * 一等奖金额
+     */
+    private BigDecimal no1money;
+
+    /**
+     * 二等奖金额
+     */
+    private BigDecimal no2money;
+
+    /**
+     * 三等奖金额
+     */
+    private BigDecimal no3money;
+
+    /**
+     * 四等奖金额
+     */
+    private BigDecimal no4money;
+
+    /**
+     * 五等奖金额
+     */
+    private BigDecimal no5money;
+
+    /**
+     * 六等奖金额
+     */
+    private BigDecimal no6money;
+
+    /**
+     * 彩票名称
+     */
+    private String name;
+
+    /**
+     * 销售额
+     */
+    private BigDecimal xiaoshou;
+
+    /**
+     * 奖池金额
+     */
+    private BigDecimal jiangchi;
+
+    /**
+     * 一等奖中奖地域
+     */
+    private String no1msg;
+}

+ 28 - 0
data-easy/src/main/java/com/dataeasy/server/pojo/subscription/PushOptionRequest.java

@@ -0,0 +1,28 @@
+package com.dataeasy.server.pojo.subscription;
+
+import com.dataeasy.server.constant.PushOptionEnum;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 消息推送选项 请求类
+ * @date 2025/3/7 15:38
+ */
+@Data
+public class PushOptionRequest {
+
+    /**
+     * 主键
+     */
+    @NotNull(message = "待修改的订阅源配置ID不能为空")
+    private Long id;
+
+    /**
+     * 推送选项
+     * @see PushOptionEnum
+     */
+    @NotNull(message = "推送选项不能为空")
+    private PushOptionEnum pushOption;
+}

+ 20 - 0
data-easy/src/main/java/com/dataeasy/server/pojo/subscription/SubscriptionSourceQuery.java

@@ -0,0 +1,20 @@
+package com.dataeasy.server.pojo.subscription;
+
+import java.util.Collection;
+
+import lombok.Data;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 订阅源 查询类
+ * @date 2025/3/7 16:03
+ */
+@Data
+public class SubscriptionSourceQuery {
+
+    /**
+     * 订阅源ID
+     */
+    private Collection<Long> ids;
+}

+ 50 - 0
data-easy/src/main/java/com/dataeasy/server/pojo/subscription/SubscriptionSourceVO.java

@@ -0,0 +1,50 @@
+package com.dataeasy.server.pojo.subscription;
+
+import java.util.List;
+
+import lombok.Data;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 订阅源 VO
+ * @date 2025/3/7 15:35
+ */
+@Data
+public class SubscriptionSourceVO {
+
+    /**
+     * 主键
+     */
+    private Long id;
+
+    /**
+     * 主标题
+     */
+    private String title;
+
+    /**
+     * 副标题
+     */
+    private String subTitle;
+
+    /**
+     * 推送渠道(WXMP-微信服务号)
+     */
+    private String pushChannel;
+
+    /**
+     * 推送频率
+     */
+    private String pushFrequency;
+
+    /**
+     * 详情描述
+     */
+    private String description;
+
+    /**
+     * 详情图片
+     */
+    private List<String> pics;
+}

+ 23 - 0
data-easy/src/main/java/com/dataeasy/server/pojo/subscription/SubscriptionTaskConfigQuery.java

@@ -0,0 +1,23 @@
+package com.dataeasy.server.pojo.subscription;
+
+import lombok.Data;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 订阅源与定时任务配置 查询类
+ * @date 2025/3/7 8:25
+ */
+@Data
+public class SubscriptionTaskConfigQuery {
+
+    /**
+     * 定时任务编码
+     */
+    private String taskCode;
+
+    /**
+     * 执行选项
+     */
+    private String executeOption;
+}

+ 39 - 0
data-easy/src/main/java/com/dataeasy/server/pojo/subscription/SubscriptionUserConfigQuery.java

@@ -0,0 +1,39 @@
+package com.dataeasy.server.pojo.subscription;
+
+import java.sql.Timestamp;
+import java.util.Collection;
+
+import com.dataeasy.server.constant.PushOptionEnum;
+
+import lombok.Data;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description  用户的订阅源配置 查询嘞
+ * @date 2025/3/7 8:46
+ */
+@Data
+public class SubscriptionUserConfigQuery {
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 订阅源ID
+     */
+    private Collection<Long> subscriptionSourceIds;
+
+    /**
+     * 消息推送选项(ENABLE-开启、DISABLE-关闭)
+     * @see PushOptionEnum
+     */
+    private PushOptionEnum pushOption;
+
+    /**
+     * 推送时间/当前时间
+     */
+    private Timestamp currentTime;
+}

+ 76 - 0
data-easy/src/main/java/com/dataeasy/server/service/controller/DataController.java

@@ -0,0 +1,76 @@
+package com.dataeasy.server.service.controller;
+
+import java.util.List;
+
+import com.dataeasy.server.service.manager.IDataManager;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.dataeasy.server.pojo.data.DaLeTouVO;
+import com.dataeasy.server.pojo.data.IpoBondVO;
+import com.dataeasy.server.pojo.data.IpoStockVO;
+import com.dataeasy.server.pojo.data.ProductHuntPostVO;
+import com.dataeasy.server.pojo.data.ShuangSeQiuVO;
+
+import jakarta.validation.constraints.NotBlank;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 数据 controller
+ * @date 2025/3/7 15:07
+ */
+@Validated
+@RestController
+@RequestMapping("/data")
+public class DataController {
+
+    @Autowired
+    private IDataManager dataManager;
+
+    /**
+     * 查询新债
+     * @return
+     */
+    @GetMapping("/queryIpoBond")
+    public List<IpoBondVO> queryIpoBond(@RequestParam @NotBlank(message = "申购日期不能为空") String subscriptionDate) {
+        return dataManager.queryIpoBond(subscriptionDate);
+    }
+
+    /**
+     * 查询新股
+     * @return
+     */
+    @GetMapping("/queryIpoStock")
+    public List<IpoStockVO> queryIpoStock(@RequestParam @NotBlank(message = "申购日期不能为空") String subscriptionDate) {
+        return dataManager.queryIpoStock(subscriptionDate);
+    }
+
+    /**
+     * 查询大乐透开奖数据
+     */
+    @GetMapping("/queryDaLeTou")
+    public DaLeTouVO queryDaLeTou(@RequestParam @NotBlank(message = "开奖日期不能为空") String drawDate) {
+        return dataManager.queryDaLeTou(drawDate);
+    }
+
+    /**
+     * 查询双色球数据
+     */
+    @GetMapping("/queryShuangSeQiu")
+    public ShuangSeQiuVO queryShuangSeQiu(@RequestParam @NotBlank(message = "开奖日期不能为空") String drawDate) {
+        return dataManager.queryShuangSeQiu(drawDate);
+    }
+
+    /**
+     * 查询ProductHunt热榜数据
+     */
+    @GetMapping("/queryProductHunt")
+    public List<ProductHuntPostVO> queryProductHunt(@RequestParam @NotBlank(message = "榜单日期不能为空") String rankDate) {
+        return dataManager.queryProductHunt(rankDate);
+    }
+}

+ 49 - 0
data-easy/src/main/java/com/dataeasy/server/service/controller/SubscriptionController.java

@@ -0,0 +1,49 @@
+package com.dataeasy.server.service.controller;
+
+import java.util.List;
+
+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.dataeasy.server.pojo.subscription.PushOptionRequest;
+import com.dataeasy.server.pojo.subscription.SubscriptionSourceVO;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 订阅 controller
+ * @date 2025/3/7 15:07
+ */
+@Validated
+@RestController
+@RequestMapping("/subscription")
+public class SubscriptionController {
+
+    /**
+     * 查询订阅源
+     */
+    @GetMapping("/querySource")
+    public List<SubscriptionSourceVO> querySource() {
+        return null;
+    }
+
+    /**
+     * 查询用户已订阅的订阅源
+     */
+    @GetMapping("/queryUserSubSource")
+    public List<SubscriptionSourceVO> queryUserSubSource() {
+        return null;
+    }
+
+    /**
+     * 修改消息推送选项
+     */
+    @PostMapping("/modifyPushOption")
+    public void modifyPushOption(@RequestBody PushOptionRequest request) {
+        return;
+    }
+}

+ 63 - 0
data-easy/src/main/java/com/dataeasy/server/service/controller/WxController.java

@@ -0,0 +1,63 @@
+package com.dataeasy.server.service.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.dataeasy.server.common.annotation.IgnoreResponseWrapper;
+import com.dataeasy.server.service.manager.IWxManager;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 微信 controller
+ * @date 2025/3/7 12:50
+ */
+@RestController
+@RequestMapping("/wx")
+public class WxController {
+
+    @Autowired
+    private IWxManager wxManager;
+
+    /**
+     * 微信公众号/服务号 接入
+     * 
+     * @param signature 签名
+     * @param timestamp 时间戳
+     * @param nonce 随机数
+     * @param echoStr 随机字符串
+     * @return 接入成功返回 echoStr 的值,否则随便返回
+     */
+    @IgnoreResponseWrapper
+    @GetMapping("/mp/entry")
+    public String mpEntry(@RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp,
+        @RequestParam("nonce") String nonce, @RequestParam("echostr") String echoStr) {
+        return wxManager.mpEntry(signature, timestamp, nonce, echoStr);
+    }
+
+    /**
+     * 微信公众号/ 服务号 回调
+     * 
+     * @param requestBody 回复
+     * @param signature 签名
+     * @param timestamp 时间戳
+     * @param nonce 随机数
+     * @param openid 用户id
+     * @param encType 未知参数
+     * @param msgSignature 未知参数
+     * @return xml
+     */
+    @IgnoreResponseWrapper
+    @PostMapping("/mp/callback")
+    public String entryCallback(@RequestBody String requestBody, @RequestParam("signature") String signature,
+        @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce,
+        @RequestParam("openid") String openid, @RequestParam(name = "encrypt_type", required = false) String encType,
+        @RequestParam(name = "msg_signature", required = false) String msgSignature) {
+        return wxManager.mpCallback(requestBody, signature, timestamp, nonce, openid, encType, msgSignature);
+    }
+}

+ 50 - 0
data-easy/src/main/java/com/dataeasy/server/service/manager/IDataManager.java

@@ -0,0 +1,50 @@
+package com.dataeasy.server.service.manager;
+
+import com.dataeasy.server.pojo.data.DaLeTouVO;
+import com.dataeasy.server.pojo.data.IpoBondVO;
+import com.dataeasy.server.pojo.data.IpoStockVO;
+import com.dataeasy.server.pojo.data.ProductHuntPostVO;
+import com.dataeasy.server.pojo.data.ShuangSeQiuVO;
+
+import java.util.List;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 数据服务类
+ * @date 2025/3/7 16:29
+ */
+public interface IDataManager {
+
+    /**
+     * 查询新债
+     * @param subscriptionDate 申购日期
+     * @return
+     */
+    List<IpoBondVO> queryIpoBond(String subscriptionDate);
+
+    /**
+     * 查询新股
+     * @param subscriptionDate 申购日期
+     * @return
+     */
+    List<IpoStockVO> queryIpoStock(String subscriptionDate);
+
+    /**
+     * 查询大乐透开奖数据
+     * @param drawDate 开奖日期
+     */
+    DaLeTouVO queryDaLeTou(String drawDate);
+
+    /**
+     * 查询双色球数据
+     * @param drawDate 开奖日期
+     */
+    ShuangSeQiuVO queryShuangSeQiu(String drawDate);
+
+    /**
+     * 查询ProductHunt热榜数据
+     * @param rankDate 榜单日期
+     */
+    List<ProductHuntPostVO> queryProductHunt(String rankDate);
+}

+ 0 - 27
data-easy/src/main/java/com/dataeasy/server/service/manager/IFinanceManager.java

@@ -1,27 +0,0 @@
-package com.dataeasy.server.service.manager;
-
-import com.dataeasy.server.feign.dto.finance.BondResponse;
-import com.dataeasy.server.feign.dto.finance.StockResponse;
-
-import java.util.List;
-
-/**
- * @author tyuio
- * @version 1.0.0
- * @description 金融相关 服务类
- * @date 2025/3/5 11:20
- */
-public interface IFinanceManager {
-
-    /**
-     * 获取股票列表
-     * @return
-     */
-    List<StockResponse> getStockList();
-
-    /**
-     * 获取债券列表(当天)
-     * @return
-     */
-    List<BondResponse> getBondList();
-}

+ 0 - 25
data-easy/src/main/java/com/dataeasy/server/service/manager/IHzApiManager.java

@@ -1,25 +0,0 @@
-package com.dataeasy.server.service.manager;
-
-import com.dataeasy.server.feign.dto.hzapi.DaLeTouResponse;
-import com.dataeasy.server.feign.dto.hzapi.ShuangSeQiuResponse;
-
-/**
- * @author tyuio
- * @version 1.0.0
- * @description 接口盒子平台 服务类
- * @date 2025/3/5 12:06
- */
-public interface IHzApiManager {
-
-    /**
-     * 获取大乐透开奖数据(当期)
-     * @return
-     */
-    DaLeTouResponse getDaLeTou();
-
-    /**
-     * 获取双色球开奖数据(当期)
-     * @return
-     */
-    ShuangSeQiuResponse getShuangSeQiu();
-}

+ 0 - 35
data-easy/src/main/java/com/dataeasy/server/service/manager/IScheduleTaskManager.java

@@ -1,35 +0,0 @@
-package com.dataeasy.server.service.manager;
-
-/**
- * @author tyuio
- * @version 1.0.0
- * @description 定时任务管理
- * @date 2025/3/6 20:14
- */
-public interface IScheduleTaskManager {
-
-    /**
-     * 大乐透定时任务
-     */
-    void autoDaLeTou();
-
-    /**
-     * 双色球定时任务
-     */
-    void autoShuangSeQiu();
-
-    /**
-     * 新债定时任务
-     */
-    void autoBond();
-
-    /**
-     * 新股定时任务
-     */
-    void autoStock();
-
-    /**
-     * ProductHunt热榜定时任务
-     */
-    void autoProductHunt();
-}

+ 30 - 0
data-easy/src/main/java/com/dataeasy/server/service/manager/ISubscriptionManager.java

@@ -0,0 +1,30 @@
+package com.dataeasy.server.service.manager;
+
+import java.util.List;
+
+import com.dataeasy.server.pojo.subscription.PushOptionRequest;
+import com.dataeasy.server.pojo.subscription.SubscriptionSourceVO;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 订阅服务类
+ * @date 2025/3/7 15:34
+ */
+public interface ISubscriptionManager {
+
+    /**
+     * 查询订阅源
+     */
+    List<SubscriptionSourceVO> querySource();
+
+    /**
+     * 查询用户已订阅的订阅源
+     */
+    List<SubscriptionSourceVO> queryUserSubSource();
+
+    /**
+     * 修改消息推送选项
+     */
+    void modifyPushOption(PushOptionRequest request);
+}

+ 45 - 0
data-easy/src/main/java/com/dataeasy/server/service/manager/IWxManager.java

@@ -0,0 +1,45 @@
+package com.dataeasy.server.service.manager;
+
+import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 微信 服务类
+ * @date 2025/3/7 10:24
+ */
+public interface IWxManager {
+
+    /**
+     * 微信公众号/服务号 接入
+     * 
+     * @param signature 签名
+     * @param timestamp 时间戳
+     * @param nonce 随机数
+     * @param echoStr 随机字符串
+     * @return 接入成功返回 echoStr 的值,否则随便返回
+     */
+    String mpEntry(String signature, String timestamp, String nonce, String echoStr);
+
+    /**
+     * 微信公众号/服务号 回调
+     * 
+     * @param requestBody 回复
+     * @param signature 签名
+     * @param timestamp 时间戳
+     * @param nonce 随机数
+     * @param openid 用户id
+     * @param encType 未知参数
+     * @param msgSignature 未知参数
+     * @return xml
+     */
+    String mpCallback(String requestBody, String signature, String timestamp, String nonce, String openid,
+        String encType, String msgSignature);
+
+    /**
+     * 发送模板信息
+     * 
+     * @param templateMessage
+     */
+    void sendTemplateMessage(WxMpTemplateMessage templateMessage);
+}

+ 119 - 0
data-easy/src/main/java/com/dataeasy/server/service/manager/impl/DataManagerImpl.java

@@ -0,0 +1,119 @@
+package com.dataeasy.server.service.manager.impl;
+
+import com.dataeasy.server.atomic.entity.DataDaLeTou;
+import com.dataeasy.server.atomic.entity.DataIpoBond;
+import com.dataeasy.server.atomic.entity.DataIpoStock;
+import com.dataeasy.server.atomic.entity.DataProductHuntPost;
+import com.dataeasy.server.atomic.entity.DataShuangSeQiu;
+import com.dataeasy.server.atomic.service.IDataDaLeTouService;
+import com.dataeasy.server.atomic.service.IDataIpoBondService;
+import com.dataeasy.server.atomic.service.IDataIpoStockService;
+import com.dataeasy.server.atomic.service.IDataProductHuntPostService;
+import com.dataeasy.server.atomic.service.IDataShuangSeQiuService;
+import com.dataeasy.server.pojo.data.DaLeTouVO;
+import com.dataeasy.server.pojo.data.IpoBondVO;
+import com.dataeasy.server.pojo.data.IpoStockVO;
+import com.dataeasy.server.pojo.data.ProductHuntPostVO;
+import com.dataeasy.server.pojo.data.ShuangSeQiuVO;
+import com.dataeasy.server.service.manager.IDataManager;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 数据服务类
+ * @date 2025/3/7 16:31
+ */
+
+@Slf4j
+@Component
+public class DataManagerImpl implements IDataManager {
+
+    @Autowired
+    private IDataIpoStockService ipoStockService;
+
+    @Autowired
+    private IDataIpoBondService ipoBondService;
+
+    @Autowired
+    private IDataProductHuntPostService productHuntPostService;
+
+    @Autowired
+    private IDataDaLeTouService daLeTouService;
+
+    @Autowired
+    private IDataShuangSeQiuService shuangSeQiuService;
+
+    @Override
+    public List<IpoBondVO> queryIpoBond(String subscriptionDate) {
+        List<DataIpoBond> ipoBonds = ipoBondService.getBySubscriptionDate(subscriptionDate);
+        if (CollectionUtils.isEmpty(ipoBonds)) {
+            return List.of();
+        }
+
+        return ipoBonds.stream().map(ipoStock -> {
+            IpoBondVO ipoBondVO = new IpoBondVO();
+            BeanUtils.copyProperties(ipoStock, ipoBondVO);
+            return ipoBondVO;
+        }).toList();
+    }
+
+    @Override
+    public List<IpoStockVO> queryIpoStock(String subscriptionDate) {
+        List<DataIpoStock> ipoStocks = ipoStockService.getBySubscriptionDate(subscriptionDate);
+        if (CollectionUtils.isEmpty(ipoStocks)) {
+            return List.of();
+        }
+
+        return ipoStocks.stream().map(ipoStock -> {
+            IpoStockVO ipoStockVO = new IpoStockVO();
+            BeanUtils.copyProperties(ipoStock, ipoStockVO);
+            return ipoStockVO;
+        }).toList();
+    }
+
+    @Override
+    public DaLeTouVO queryDaLeTou(String drawDate) {
+        DataDaLeTou daLeTou = daLeTouService.getByDrawDate(drawDate);
+        if (Objects.isNull(daLeTou)) {
+            return null;
+        }
+
+        DaLeTouVO daLeTouVO = new DaLeTouVO();
+        BeanUtils.copyProperties(daLeTou, daLeTouVO);
+        return daLeTouVO;
+    }
+
+    @Override
+    public ShuangSeQiuVO queryShuangSeQiu(String drawDate) {
+        DataShuangSeQiu shuangSeQiu = shuangSeQiuService.getByDrawDate(drawDate);
+        if (Objects.isNull(shuangSeQiu)) {
+            return null;
+        }
+
+        ShuangSeQiuVO shuangSeQiuVO = new ShuangSeQiuVO();
+        BeanUtils.copyProperties(shuangSeQiu, shuangSeQiuVO);
+        return shuangSeQiuVO;
+    }
+
+    @Override
+    public List<ProductHuntPostVO> queryProductHunt(String rankDate) {
+        List<DataProductHuntPost> posts = productHuntPostService.getByRankDate(rankDate);
+        if (CollectionUtils.isEmpty(posts)) {
+            return List.of();
+        }
+        return posts.stream().map(post -> {
+            ProductHuntPostVO productHuntPostVO = new ProductHuntPostVO();
+            BeanUtils.copyProperties(post, productHuntPostVO);
+            return productHuntPostVO;
+        }).collect(Collectors.toList());
+    }
+}

+ 0 - 47
data-easy/src/main/java/com/dataeasy/server/service/manager/impl/FinanceManagerImpl.java

@@ -1,47 +0,0 @@
-package com.dataeasy.server.service.manager.impl;
-
-import com.dataeasy.server.feign.FinanceFeign;
-import com.dataeasy.server.feign.dto.finance.BondResponse;
-import com.dataeasy.server.feign.dto.finance.FinanceRequest;
-import com.dataeasy.server.feign.dto.finance.StockResponse;
-import com.dataeasy.server.service.manager.IFinanceManager;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import java.text.SimpleDateFormat;
-import java.time.LocalDate;
-import java.util.List;
-
-/**
- * @author tyuio
- * @version 1.0.0
- * @description 金融 服务类
- * @date 2025/3/5 12:01
- */
-@Slf4j
-@Component
-public class FinanceManagerImpl implements IFinanceManager {
-
-    @Autowired
-    private FinanceFeign financeFeign;
-
-    @Override
-    public List<StockResponse> getStockList() {
-        FinanceRequest financeRequest = new FinanceRequest();
-        financeRequest.setSymbol("全部股票");
-        return financeFeign.getStockXgsglbEm(financeRequest);
-    }
-
-    @Override
-    public List<BondResponse> getBondList() {
-        LocalDate today = LocalDate.now();
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
-        String todayStr = sdf.format(today);
-
-        FinanceRequest financeRequest = new FinanceRequest();
-        financeRequest.setStartDate(todayStr);
-        financeRequest.setEndDate(todayStr);
-        return financeFeign.getBondCovIssueCninfo(financeRequest);
-    }
-}

+ 0 - 47
data-easy/src/main/java/com/dataeasy/server/service/manager/impl/HzApiManagerImpl.java

@@ -1,47 +0,0 @@
-package com.dataeasy.server.service.manager.impl;
-
-import com.dataeasy.server.core.config.HzApiConfig;
-import com.dataeasy.server.feign.HzApiFeign;
-import com.dataeasy.server.feign.dto.hzapi.DaLeTouResponse;
-import com.dataeasy.server.feign.dto.hzapi.HzApiRequest;
-import com.dataeasy.server.feign.dto.hzapi.ShuangSeQiuResponse;
-import com.dataeasy.server.service.manager.IHzApiManager;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-/**
- * @author tyuio
- * @version 1.0.0
- * @description 藉口盒子平台 服务类
- * @date 2025/3/5 12:08
- */
-@Slf4j
-@Component
-public class HzApiManagerImpl implements IHzApiManager {
-
-    @Autowired
-    private HzApiFeign hzApiFeign;
-
-    @Autowired
-    private HzApiConfig hzApiConfig;
-
-    private HzApiRequest buildCommonRequest() {
-        HzApiRequest request = new HzApiRequest();
-        request.setId(hzApiConfig.getId());
-        request.setKey(hzApiConfig.getKey());
-        return request;
-    }
-
-    @Override
-    public DaLeTouResponse getDaLeTou() {
-        HzApiRequest request = buildCommonRequest();
-        return hzApiFeign.getDaLeTou(request);
-    }
-
-    @Override
-    public ShuangSeQiuResponse getShuangSeQiu() {
-        HzApiRequest request = buildCommonRequest();
-        return hzApiFeign.getShuangSeQiu(request);
-    }
-}

+ 98 - 0
data-easy/src/main/java/com/dataeasy/server/service/manager/impl/SubscriptionManagerImpl.java

@@ -0,0 +1,98 @@
+package com.dataeasy.server.service.manager.impl;
+
+import com.dataeasy.server.atomic.entity.SubscriptionSource;
+import com.dataeasy.server.atomic.entity.SubscriptionUserConfig;
+import com.dataeasy.server.atomic.service.ISubscriptionSourceService;
+import com.dataeasy.server.atomic.service.ISubscriptionUserConfigService;
+import com.dataeasy.server.common.exception.BusinessException;
+import com.dataeasy.server.pojo.subscription.PushOptionRequest;
+import com.dataeasy.server.pojo.subscription.SubscriptionSourceQuery;
+import com.dataeasy.server.pojo.subscription.SubscriptionSourceVO;
+import com.dataeasy.server.pojo.subscription.SubscriptionUserConfigQuery;
+import com.dataeasy.server.service.manager.ISubscriptionManager;
+import com.dataeasy.server.utiis.UserUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 订阅 服务类
+ * @date 2025/3/7 16:00
+ */
+@Slf4j
+@Component
+public class SubscriptionManagerImpl implements ISubscriptionManager {
+
+    @Autowired
+    private ISubscriptionSourceService subscriptionSourceService;
+
+    @Autowired
+    private ISubscriptionUserConfigService subscriptionUserConfigService;
+
+    @Override
+    public List<SubscriptionSourceVO> querySource() {
+        SubscriptionSourceQuery query = new SubscriptionSourceQuery();
+        return querySubscriptionSourceVO(query);
+    }
+
+    @Override
+    public List<SubscriptionSourceVO> queryUserSubSource() {
+        Long userId = Optional.ofNullable(UserUtils.getCurrentUserId()).orElseThrow(() -> BusinessException.fail("无法获取当前用户ID"));
+        // 查询当前用户拥有的订阅源
+        SubscriptionUserConfigQuery configQuery = new SubscriptionUserConfigQuery();
+        configQuery.setUserId(userId);
+        List<SubscriptionUserConfig> userConfigList = subscriptionUserConfigService.getByCondition(configQuery);
+        if (CollectionUtils.isEmpty(userConfigList)) {
+            return List.of();
+        }
+
+        //查询订阅源信息
+        Set<Long> sourceIds = userConfigList.stream().map(SubscriptionUserConfig::getSubscriptionSourceId).collect(Collectors.toSet());
+        SubscriptionSourceQuery sourceQuery = new SubscriptionSourceQuery();
+        sourceQuery.setIds(sourceIds);
+        return querySubscriptionSourceVO(sourceQuery);
+    }
+
+    /**
+     * 根据条件查询订阅源
+     * @return
+     */
+    private List<SubscriptionSourceVO> querySubscriptionSourceVO(SubscriptionSourceQuery query) {
+        if (Objects.isNull(query)) {
+            return List.of();
+        }
+        List<SubscriptionSource> sourceList = subscriptionSourceService.getByCondition(query);
+        if (CollectionUtils.isEmpty(sourceList)) {
+            return List.of();
+        }
+        return sourceList.stream().map(source -> {
+            SubscriptionSourceVO sourceVO = new SubscriptionSourceVO();
+            BeanUtils.copyProperties(source, sourceVO);
+            if (StringUtils.hasText(source.getPics())) {
+                List<String> picList = Arrays.stream(source.getPics().split(",")).toList();
+                sourceVO.setPics(picList);
+            }
+            return sourceVO;
+        }).toList();
+    }
+
+    @Override
+    public void modifyPushOption(PushOptionRequest request) {
+        SubscriptionUserConfig subscriptionUserConfig = new SubscriptionUserConfig();
+        subscriptionUserConfig.setId(request.getId());
+        subscriptionUserConfig.setPushOption(request.getPushOption());
+        subscriptionUserConfigService.updateById(subscriptionUserConfig);
+    }
+}

+ 93 - 0
data-easy/src/main/java/com/dataeasy/server/service/manager/impl/WxManagerImpl.java

@@ -0,0 +1,93 @@
+package com.dataeasy.server.service.manager.impl;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.dataeasy.server.service.manager.IWxManager;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpMessageRouter;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 微信公众号/服务号 服务类
+ * @date 2025/3/7 10:26
+ */
+@Slf4j
+@Service
+public class WxManagerImpl implements IWxManager {
+
+    @Autowired
+    private WxMpService wxMpService;
+
+    @Autowired
+    private WxMpMessageRouter wxMpMessageRouter;
+
+    @Override
+    public String mpEntry(String signature, String timestamp, String nonce, String echoStr) {
+        log.info("微信公众号/服务号接入传递的参数 signature:[{}],timestamp:[{}],nonce:[{}],echostr:[{}]", signature, timestamp, nonce,
+            echoStr);
+
+        if (StringUtils.isAnyBlank(signature, timestamp, nonce, echoStr)) {
+            log.error("接收到微信认证信息,参数非法,存在为空的参数");
+            return "error";
+        }
+
+        boolean result = wxMpService.checkSignature(timestamp, nonce, signature);
+        log.info("微信公众号/服务号接入成功?[{}]", result);
+        return result ? echoStr : "error";
+    }
+
+    @Override
+    public String mpCallback(String requestBody, String signature, String timestamp, String nonce, String openid,
+        String encType, String msgSignature) {
+        log.info(
+            "接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}],"
+                + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
+            openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
+
+        if (!wxMpService.checkSignature(timestamp, nonce, signature)) {
+            throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
+        }
+
+        String out = null;
+        if (encType == null) {
+            // 明文传输的消息
+            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
+            WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage);
+            if (outMessage == null) {
+                return "";
+            }
+            out = outMessage.toXml();
+        } else if ("aes".equalsIgnoreCase(encType)) {
+            // aes加密的消息
+            WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxMpService.getWxMpConfigStorage(),
+                timestamp, nonce, msgSignature);
+            log.info("消息解密后内容为:\n[{}] ", inMessage.toString());
+            WxMpXmlOutMessage outMessage = wxMpMessageRouter.route(inMessage);
+            if (outMessage == null) {
+                return "";
+            }
+            out = outMessage.toEncryptedXml(wxMpService.getWxMpConfigStorage());
+        }
+        log.info("组装回复信息:[{}]", out);
+        return out;
+    }
+
+    @Override
+    public void sendTemplateMessage(WxMpTemplateMessage templateMessage) {
+        // TODO 这里要考虑把数据存入数据库,方便后续查询
+        try {
+            String msgId = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
+        } catch (WxErrorException e) {
+            log.error("发送模板消息失败", e);
+        }
+    }
+}

+ 32 - 0
data-easy/src/main/java/com/dataeasy/server/task/AbstractHzApiTask.java

@@ -0,0 +1,32 @@
+package com.dataeasy.server.task;
+
+import com.dataeasy.server.core.config.HzApiProperties;
+import com.dataeasy.server.feign.HzApiFeign;
+import com.dataeasy.server.feign.dto.hzapi.HzApiRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 使用接口盒子接口的定时任务 抽象类
+ * @date 2025/3/7 16:45
+ */
+public abstract class AbstractHzApiTask extends AbstractTask {
+
+    @Autowired
+    protected HzApiFeign hzApiFeign;
+
+    @Autowired
+    protected HzApiProperties hzApiProperties;
+
+    /**
+     * 构建 通用请求头
+     * @return
+     */
+    protected HzApiRequest buildCommonRequest() {
+        HzApiRequest request = new HzApiRequest();
+        request.setId(hzApiProperties.getId());
+        request.setKey(hzApiProperties.getKey());
+        return request;
+    }
+}

+ 186 - 0
data-easy/src/main/java/com/dataeasy/server/task/AbstractTask.java

@@ -0,0 +1,186 @@
+package com.dataeasy.server.task;
+
+import java.sql.Timestamp;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.dataeasy.server.core.config.WxMessageProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import com.dataeasy.server.atomic.entity.SubscriptionTaskConfig;
+import com.dataeasy.server.atomic.entity.SubscriptionUserConfig;
+import com.dataeasy.server.atomic.entity.SysScheduleTaskLog;
+import com.dataeasy.server.atomic.entity.User;
+import com.dataeasy.server.atomic.service.ISubscriptionTaskConfigService;
+import com.dataeasy.server.atomic.service.ISubscriptionUserConfigService;
+import com.dataeasy.server.atomic.service.ISysScheduleTaskLogService;
+import com.dataeasy.server.atomic.service.IUserService;
+import com.dataeasy.server.constant.ExecuteOptionEnum;
+import com.dataeasy.server.constant.PushOptionEnum;
+import com.dataeasy.server.constant.ScheduleTaskEnum;
+import com.dataeasy.server.constant.ScheduleTaskStatusEnum;
+import com.dataeasy.server.pojo.subscription.SubscriptionTaskConfigQuery;
+import com.dataeasy.server.pojo.subscription.SubscriptionUserConfigQuery;
+import com.dataeasy.server.service.manager.IWxManager;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
+import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 定时任务 抽象类
+ * @date 2025/3/7 10:44
+ */
+@Slf4j
+public abstract class AbstractTask {
+
+    @Autowired
+    protected ISysScheduleTaskLogService taskLogService;
+
+    @Autowired
+    protected ISubscriptionTaskConfigService taskConfigService;
+
+    @Autowired
+    protected ISubscriptionUserConfigService userConfigService;
+
+    @Autowired
+    protected IUserService userService;
+
+    @Autowired
+    protected IWxManager wxMpManager;
+
+    @Autowired
+    protected WxMessageProperties wxMessageProperties;
+
+    /**
+     * 定时任务执行主逻辑
+     */
+    public void executeMain() {
+        ScheduleTaskEnum task = getTaskEnum();
+        log.info("======= {} 开始执行 =======", task.getName());
+        // 新增任务执行记录
+        SysScheduleTaskLog addTaskLog = task.buildTaskLog();
+        taskLogService.insert(addTaskLog);
+
+        // 查询需要执行的任务配置
+        SubscriptionTaskConfigQuery taskConfigQuery = new SubscriptionTaskConfigQuery();
+        taskConfigQuery.setTaskCode(task.toString());
+        taskConfigQuery.setExecuteOption(ExecuteOptionEnum.ENABLE.toString());
+        List<SubscriptionTaskConfig> taskConfigList = taskConfigService.getByCondition(taskConfigQuery);
+        if (CollectionUtils.isEmpty(taskConfigList)) {
+            log.warn("任务:{},没有找到执行状态为启用的配置,结束执行", task.getName());
+            return;
+        }
+        Set<Long> subscriptionSourceIds = taskConfigList.stream().filter(v -> Objects.nonNull(v.getSubscriptionSourceId()))
+                .map(SubscriptionTaskConfig::getSubscriptionSourceId)
+                .collect(Collectors.toSet());
+        if (CollectionUtils.isEmpty(subscriptionSourceIds)) {
+            log.warn("任务:{},任务编码:{},没有找到有效的订阅源,结束执行", task.getName(), task);
+            return;
+        }
+
+        // 拉取数据并入库
+        fetchData();
+
+        // 寻找要推送数据的用户
+        SubscriptionUserConfigQuery userConfigQuery = new SubscriptionUserConfigQuery();
+        userConfigQuery.setSubscriptionSourceIds(subscriptionSourceIds);
+        userConfigQuery.setPushOption(PushOptionEnum.ENABLE);
+        userConfigQuery.setCurrentTime(new Timestamp(System.currentTimeMillis()));
+        List<SubscriptionUserConfig> userConfigList = userConfigService.getByCondition(userConfigQuery);
+        if (CollectionUtils.isEmpty(userConfigList)) {
+            log.warn("任务:{},任务编码:{},没有找到推送状态为启用的用户配置,结束执行", task.getName(), task);
+            return;
+        }
+        Set<Long> userIds = userConfigList.stream().filter(v -> Objects.nonNull(v.getUserId()))
+                .map(SubscriptionUserConfig::getUserId)
+                .collect(Collectors.toSet());
+        if (CollectionUtils.isEmpty(userIds)) {
+            log.warn("任务:{},任务编码:{},没有找到推送状态为启用的用户,结束执行", task.getName(), task);
+            return;
+        }
+        List<User> userList = userService.getByIds(userIds);
+        if (CollectionUtils.isEmpty(userList)) {
+            log.warn("任务:{},任务编码:{},没有找到待推送的用户列表,结束执行", task.getName(), task);
+            return;
+        }
+        Set<String> mpOpenIds = userList.stream().filter(v -> StringUtils.hasText(v.getMpOpenId()))
+                .map(User::getMpOpenId)
+                .collect(Collectors.toSet());
+        if (CollectionUtils.isEmpty(mpOpenIds)) {
+            log.warn("任务:{},任务编码:{},待推送的用户列表中没有找到服务号openid,结束执行", task.getName(), task);
+            return;
+        }
+
+        // 获取消息模板所需数据
+        TemplateMessage templateMessage = getTemplateMessage();
+
+        // 推送
+        for (String mpOpenId : mpOpenIds) {
+            // 生成模板信息
+            WxMpTemplateMessage mpTemplateMessage = WxMpTemplateMessage.builder()
+                    .toUser(mpOpenId)
+                    .templateId(templateMessage.getTemplateId())
+                    .url(templateMessage.getUrl())
+                    .data(templateMessage.getTemplateDataList())
+                    .build();
+            wxMpManager.sendTemplateMessage(mpTemplateMessage);
+        }
+
+        // 更新任务执行记录
+        SysScheduleTaskLog updateTaskLog = new SysScheduleTaskLog();
+        updateTaskLog.setId(addTaskLog.getId());
+        updateTaskLog.setProcessStatus(ScheduleTaskStatusEnum.SUCCESS);
+        taskLogService.updateById(updateTaskLog);
+
+        log.info("======= {} 执行结束 =======", task.getName());
+    }
+
+    /**
+     * 获取定时任务枚举
+     * @return
+     */
+    public abstract ScheduleTaskEnum getTaskEnum();
+
+    /**
+     * 拉取所需数据并入库
+     */
+    public abstract void fetchData();
+
+    /**
+     * 获取消息模板所需数据
+     * @return
+     */
+    public abstract TemplateMessage getTemplateMessage();
+
+    /**
+     * 定时任务中消息模板所需的数据
+     */
+    @Data
+    @Builder
+    public static class TemplateMessage {
+
+        /**
+         * 模板跳转链接
+         */
+        private String url;
+
+        /**
+         * 消息模板ID
+         */
+        private String templateId;
+
+        /**
+         * 模板所需数据
+         */
+        private List<WxMpTemplateData> templateDataList;
+    }
+}

+ 44 - 36
data-easy/src/main/java/com/dataeasy/server/task/DaLeTouTask.java

@@ -1,53 +1,54 @@
 package com.dataeasy.server.task;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
 import com.dataeasy.server.atomic.entity.DataDaLeTou;
-import com.dataeasy.server.atomic.entity.SysScheduleTaskLog;
 import com.dataeasy.server.atomic.service.IDataDaLeTouService;
-import com.dataeasy.server.atomic.service.ISubscriptionTaskConfigService;
-import com.dataeasy.server.atomic.service.ISysScheduleTaskLogService;
-import com.dataeasy.server.common.exception.BusinessException;
 import com.dataeasy.server.constant.HzApiStatusEnum;
 import com.dataeasy.server.constant.ScheduleTaskEnum;
-import com.dataeasy.server.constant.ScheduleTaskStatusEnum;
 import com.dataeasy.server.feign.dto.hzapi.DaLeTouResponse;
-import com.dataeasy.server.service.manager.IHzApiManager;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.BeanUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
+import com.dataeasy.server.feign.dto.hzapi.HzApiRequest;
 
-import java.util.Objects;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
 
 /**
  * @author tyuio
  * @version 1.0.0
- * @description 大乐透定时任务
+ * @description 大乐透定时任务,执行时间:每周一、三、六,晚上21:30
  * @date 2025/3/6 20:19
  */
 @Slf4j
 @Component
-public class DaLeTouTask {
-
-    @Autowired
-    private ISysScheduleTaskLogService taskLogService;
-
-    @Autowired
-    private IHzApiManager iHzApiManager;
+public class DaLeTouTask extends AbstractHzApiTask {
 
     @Autowired
     private IDataDaLeTouService daLeTouService;
 
-    @Autowired
-    private ISubscriptionTaskConfigService taskConfigService;
+    @Scheduled(cron = "30 21 * * 1,3,6")
+    @Transactional(rollbackFor = Exception.class)
+    public void exec() {
+        executeMain();
+    }
 
-    public void execute() {
-        log.info("======= {} 开始执行 =======", ScheduleTaskEnum.DA_LE_TOU.getName());
-        // 新增任务执行记录
-        SysScheduleTaskLog addTaskLog = ScheduleTaskEnum.DA_LE_TOU.buildTaskLog();
-        taskLogService.insert(addTaskLog);
+    @Override
+    public ScheduleTaskEnum getTaskEnum() {
+        return ScheduleTaskEnum.DA_LE_TOU;
+    }
 
+    @Override
+    public void fetchData() {
         // 拉取数据
-        DaLeTouResponse daLeTouResponse = iHzApiManager.getDaLeTou();
+        HzApiRequest request = buildCommonRequest();
+        DaLeTouResponse daLeTouResponse = hzApiFeign.getDaLeTou(request);
         if (Objects.isNull(daLeTouResponse)) {
             log.warn("拉取大乐透数据失败,返回结果对象为空");
             return;
@@ -61,16 +62,23 @@ public class DaLeTouTask {
         DataDaLeTou addDaLeTou = new DataDaLeTou();
         BeanUtils.copyProperties(daLeTouResponse, addDaLeTou);
         daLeTouService.insert(addDaLeTou);
+    }
 
-        // 寻找要推送数据的用户
-        // 推送数据
-
-        // 更新任务执行记录
-        SysScheduleTaskLog updateTaskLog = new SysScheduleTaskLog();
-        updateTaskLog.setId(addTaskLog.getId());
-        updateTaskLog.setProcessStatus(ScheduleTaskStatusEnum.SUCCESS);
-        taskLogService.updateById(updateTaskLog);
+    @Override
+    public TemplateMessage getTemplateMessage() {
+        // 这是模板所需数据
+        List<WxMpTemplateData> templateDataList = new ArrayList();
+        templateDataList.add(new WxMpTemplateData("first", "预约成功"));
+        templateDataList.add(new WxMpTemplateData("keyword1", "测试111"));
+        templateDataList.add(new WxMpTemplateData("keyword2","测试2222"));
+        templateDataList.add(new WxMpTemplateData("keyword3","测试333"));
+        templateDataList.add(new WxMpTemplateData("keyword4","测试4444"));
+        templateDataList.add(new WxMpTemplateData("remark","测试备注"));
 
-        log.info("======= {} 执行结束 =======", ScheduleTaskEnum.DA_LE_TOU.getName());
+        return TemplateMessage.builder()
+                .url("www.baidu.com")
+                .templateId(wxMessageProperties.getTemplateId())
+                .templateDataList(templateDataList)
+                .build();
     }
 }

+ 95 - 0
data-easy/src/main/java/com/dataeasy/server/task/IpoBondTask.java

@@ -0,0 +1,95 @@
+package com.dataeasy.server.task;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import com.dataeasy.server.atomic.entity.DataIpoBond;
+import com.dataeasy.server.atomic.service.IDataIpoBondService;
+import com.dataeasy.server.constant.ScheduleTaskEnum;
+import com.dataeasy.server.feign.FinanceFeign;
+import com.dataeasy.server.feign.dto.finance.BondResponse;
+import com.dataeasy.server.feign.dto.finance.FinanceRequest;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 新债定时任务,执行时间:每天早上8:45
+ * @date 2025/3/7 11:25
+ */
+@Slf4j
+@Component
+public class IpoBondTask extends AbstractTask {
+
+    @Autowired
+    private FinanceFeign financeFeign;
+
+    @Autowired
+    private IDataIpoBondService ipoBondService;
+
+    @Scheduled(cron = "45 8 * * *")
+    @Transactional(rollbackFor = Exception.class)
+    public void exec() {
+        executeMain();
+    }
+
+    @Override
+    public ScheduleTaskEnum getTaskEnum() {
+        return ScheduleTaskEnum.IPO_BOND;
+    }
+
+    @Override
+    public void fetchData() {
+        // 拉取数据
+        LocalDate today = LocalDate.now();
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+        String todayStr = sdf.format(today);
+        FinanceRequest financeRequest = new FinanceRequest();
+        financeRequest.setStartDate(todayStr);
+        financeRequest.setEndDate(todayStr);
+        List<BondResponse> bondCovIssueCninfos =  financeFeign.getBondCovIssueCninfo(financeRequest);
+        if (CollectionUtils.isEmpty(bondCovIssueCninfos)) {
+            log.warn("拉取新债数据失败,返回结果对象为空");
+            return;
+        }
+
+        List<DataIpoBond> ipoBonds = bondCovIssueCninfos.stream().map(v -> {
+            DataIpoBond ipoBond = new DataIpoBond();
+            BeanUtils.copyProperties(v, ipoBond);
+            return ipoBond;
+        }).collect(Collectors.toList());
+
+        // 数据入库
+        ipoBondService.insertList(ipoBonds);
+    }
+
+    @Override
+    public TemplateMessage getTemplateMessage() {
+        // 这是模板所需数据
+        List<WxMpTemplateData> templateDataList = new ArrayList();
+        templateDataList.add(new WxMpTemplateData("first", "预约成功"));
+        templateDataList.add(new WxMpTemplateData("keyword1", "测试111"));
+        templateDataList.add(new WxMpTemplateData("keyword2","测试2222"));
+        templateDataList.add(new WxMpTemplateData("keyword3","测试333"));
+        templateDataList.add(new WxMpTemplateData("keyword4","测试4444"));
+        templateDataList.add(new WxMpTemplateData("remark","测试备注"));
+
+        return TemplateMessage.builder()
+                .url("www.baidu.com")
+                .templateId(wxMessageProperties.getTemplateId())
+                .templateDataList(templateDataList)
+                .build();
+    }
+}

+ 89 - 0
data-easy/src/main/java/com/dataeasy/server/task/IpoStockTask.java

@@ -0,0 +1,89 @@
+package com.dataeasy.server.task;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import com.dataeasy.server.atomic.entity.DataIpoStock;
+import com.dataeasy.server.atomic.service.IDataIpoStockService;
+import com.dataeasy.server.constant.ScheduleTaskEnum;
+import com.dataeasy.server.feign.FinanceFeign;
+import com.dataeasy.server.feign.dto.finance.FinanceRequest;
+import com.dataeasy.server.feign.dto.finance.StockResponse;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 新股定时任务,执行时间:每天早上8:45
+ * @date 2025/3/7 11:26
+ */
+@Slf4j
+@Component
+public class IpoStockTask extends AbstractTask {
+
+    @Autowired
+    private FinanceFeign financeFeign;
+
+    @Autowired
+    private IDataIpoStockService ipoStockService;
+
+    @Scheduled(cron = "45 8 * * *")
+    @Transactional(rollbackFor = Exception.class)
+    public void exec() {
+        executeMain();
+    }
+
+    @Override
+    public ScheduleTaskEnum getTaskEnum() {
+        return ScheduleTaskEnum.IPO_STOCK;
+    }
+
+    @Override
+    public void fetchData() {
+        // 拉取数据
+        FinanceRequest financeRequest = new FinanceRequest();
+        financeRequest.setSymbol("全部股票");
+        List<StockResponse> stockXgsglbEmList =  financeFeign.getStockXgsglbEm(financeRequest);
+        if (CollectionUtils.isEmpty(stockXgsglbEmList)) {
+            log.warn("拉取新股数据失败,返回结果对象为空");
+            return;
+        }
+
+        List<DataIpoStock> ipoStocks = stockXgsglbEmList.stream().map(v -> {
+            DataIpoStock ipoStock = new DataIpoStock();
+            BeanUtils.copyProperties(v, ipoStock);
+            return ipoStock;
+        }).collect(Collectors.toList());
+
+        // 数据入库
+        ipoStockService.insertList(ipoStocks);
+    }
+
+    @Override
+    public TemplateMessage getTemplateMessage() {
+        // 这是模板所需数据
+        List<WxMpTemplateData> templateDataList = new ArrayList();
+        templateDataList.add(new WxMpTemplateData("first", "预约成功"));
+        templateDataList.add(new WxMpTemplateData("keyword1", "测试111"));
+        templateDataList.add(new WxMpTemplateData("keyword2","测试2222"));
+        templateDataList.add(new WxMpTemplateData("keyword3","测试333"));
+        templateDataList.add(new WxMpTemplateData("keyword4","测试4444"));
+        templateDataList.add(new WxMpTemplateData("remark","测试备注"));
+
+        return TemplateMessage.builder()
+                .url("www.baidu.com")
+                .templateId(wxMessageProperties.getTemplateId())
+                .templateDataList(templateDataList)
+                .build();
+    }
+}

+ 107 - 0
data-easy/src/main/java/com/dataeasy/server/task/ProductHuntTask.java

@@ -0,0 +1,107 @@
+package com.dataeasy.server.task;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import com.dataeasy.server.atomic.entity.DataProductHuntPost;
+import com.dataeasy.server.atomic.service.IDataProductHuntPostService;
+import com.dataeasy.server.common.constant.ResponseCodeEnum;
+import com.dataeasy.server.common.pojo.JsonResponse;
+import com.dataeasy.server.constant.ScheduleTaskEnum;
+import com.dataeasy.server.feign.ProductHuntFeign;
+import com.dataeasy.server.feign.dto.PostNode;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description ProductHunt热榜定时任务,执行时间:每天下午4:00
+ * @date 2025/3/7 11:26
+ */
+@Slf4j
+@Component
+public class ProductHuntTask extends AbstractTask {
+
+    @Autowired
+    private ProductHuntFeign productHuntFeign;
+
+    @Autowired
+    private IDataProductHuntPostService productHuntPostService;
+
+    @Scheduled(cron = "0 16 * * *")
+    @Transactional(rollbackFor = Exception.class)
+    public void exec() {
+        executeMain();
+    }
+
+    @Override
+    public ScheduleTaskEnum getTaskEnum() {
+        return ScheduleTaskEnum.PRODUCT_HUNT;
+    }
+
+    @Override
+    public void fetchData() {
+        // 拉取数据
+        JsonResponse<List<PostNode>> jsonResponse = productHuntFeign.getTop30Posts();
+        if (Objects.isNull(jsonResponse)) {
+            log.warn("拉取ProductHunt数据失败,返回结果对象为空");
+            return;
+        }
+        if (!ResponseCodeEnum.SUCCESS.getCode().equals(jsonResponse.getCode())) {
+            log.warn("拉取ProductHunt数据失败,返回错误信息:{}", jsonResponse.getMsg());
+            return;
+        }
+
+        List<PostNode> postNodes = jsonResponse.getData();
+        if (CollectionUtils.isEmpty(postNodes)) {
+            log.warn("拉取ProductHunt数据,数据列表为空");
+            return;
+        }
+
+        // TODO 查了翻译的逻辑
+
+        AtomicInteger rankNum = new AtomicInteger(1);
+        Date rankDate = new Date();
+        List<DataProductHuntPost> posts = postNodes.stream().map(v -> {
+            DataProductHuntPost post = new DataProductHuntPost();
+            BeanUtils.copyProperties(v, post);
+            post.setRankDate(rankDate);
+            post.setRankNum(rankNum.getAndIncrement());
+            return post;
+        }).collect(Collectors.toList());
+
+        // 数据入库
+        productHuntPostService.insertList(posts);
+    }
+
+    @Override
+    public TemplateMessage getTemplateMessage() {
+        // 这是模板所需数据
+        List<WxMpTemplateData> templateDataList = new ArrayList();
+        templateDataList.add(new WxMpTemplateData("first", "预约成功"));
+        templateDataList.add(new WxMpTemplateData("keyword1", "测试111"));
+        templateDataList.add(new WxMpTemplateData("keyword2","测试2222"));
+        templateDataList.add(new WxMpTemplateData("keyword3","测试333"));
+        templateDataList.add(new WxMpTemplateData("keyword4","测试4444"));
+        templateDataList.add(new WxMpTemplateData("remark","测试备注"));
+
+        return TemplateMessage.builder()
+                .url("www.baidu.com")
+                .templateId(wxMessageProperties.getTemplateId())
+                .templateDataList(templateDataList)
+                .build();
+    }
+}

+ 84 - 0
data-easy/src/main/java/com/dataeasy/server/task/ShuangSeQiuTask.java

@@ -0,0 +1,84 @@
+package com.dataeasy.server.task;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.dataeasy.server.atomic.entity.DataShuangSeQiu;
+import com.dataeasy.server.atomic.service.IDataShuangSeQiuService;
+import com.dataeasy.server.constant.HzApiStatusEnum;
+import com.dataeasy.server.constant.ScheduleTaskEnum;
+import com.dataeasy.server.feign.dto.hzapi.HzApiRequest;
+import com.dataeasy.server.feign.dto.hzapi.ShuangSeQiuResponse;
+
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
+
+/**
+ * @author tyuio
+ * @version 1.0.0
+ * @description 双色球定时任务,执行时间:每周二、周四、周日 晚上21:40
+ * @date 2025/3/7 11:15
+ */
+@Slf4j
+@Component
+public class ShuangSeQiuTask extends AbstractHzApiTask {
+
+    @Autowired
+    private IDataShuangSeQiuService shuangSeQiuService;
+
+    @Scheduled(cron = "40 21 * * 2,4,7")
+    @Transactional(rollbackFor = Exception.class)
+    public void exec() {
+        executeMain();
+    }
+
+    @Override
+    public ScheduleTaskEnum getTaskEnum() {
+        return ScheduleTaskEnum.SHUANG_SE_QIU;
+    }
+
+    @Override
+    public void fetchData() {
+        // 拉取数据
+        HzApiRequest request = buildCommonRequest();
+        ShuangSeQiuResponse shuangSeQiuResponse = hzApiFeign.getShuangSeQiu(request);
+        if (Objects.isNull(shuangSeQiuResponse)) {
+            log.warn("拉取双色球数据失败,返回结果对象为空");
+            return;
+        }
+        if (!HzApiStatusEnum.SUCCESS.getCode().equals(shuangSeQiuResponse.getCode())) {
+            log.warn("拉取双色球数据失败,接口盒子返回错误信息:{}", shuangSeQiuResponse.getMsg());
+            return;
+        }
+
+        // 数据入库
+        DataShuangSeQiu addShuangSeQiu = new DataShuangSeQiu();
+        BeanUtils.copyProperties(shuangSeQiuResponse, addShuangSeQiu);
+        shuangSeQiuService.insert(addShuangSeQiu);
+    }
+
+    @Override
+    public TemplateMessage getTemplateMessage() {
+        // 这是模板所需数据
+        List<WxMpTemplateData> templateDataList = new ArrayList();
+        templateDataList.add(new WxMpTemplateData("first", "预约成功"));
+        templateDataList.add(new WxMpTemplateData("keyword1", "测试111"));
+        templateDataList.add(new WxMpTemplateData("keyword2","测试2222"));
+        templateDataList.add(new WxMpTemplateData("keyword3","测试333"));
+        templateDataList.add(new WxMpTemplateData("keyword4","测试4444"));
+        templateDataList.add(new WxMpTemplateData("remark","测试备注"));
+
+        return TemplateMessage.builder()
+                .url("www.baidu.com")
+                .templateId(wxMessageProperties.getTemplateId())
+                .templateDataList(templateDataList)
+                .build();
+    }
+}

+ 37 - 57
data-easy/src/main/resources/application-dev.yaml

@@ -2,65 +2,45 @@ spring:
   datasource:
     username: root
     url: jdbc:mysql://localhost:3306/data_easy?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
+  ai:
+    openai:
+      base-url: ENC(H4FobFSxLct+ZapdAa5GWKbBC3u32wRgWrVaksCJyHvyTPmpqz2P4gq7QfFPIARI)
+      api-key: ENC(CXar5AEVP7jIiOpZfEzkh/4TEi0CePm+sx3ObEgYcUn4ZvDfFlprjyiU3j22x3n/bVmmC5hQlm4Zs23FnGNCB66SQ0ATB2hJ)
 
-
-biz:
-  wechat:
-    mini-program:
-      app-id: 0123456789
-      secret: 0123456789
-  token:
-    password: 0123456789
-
-logging:
-  level:
-    me.chanjar.weixin.mp: debug
-
-# 公众号配置(必填)
-#wx:
-#  mp:
-#    app-id: wx076677ddbd348bbc
-#    secret: 87972dca143f21348ff65c7ac19be1cc
-#    token: Csy2001
-#    aes-key: 123424
-#    use-stable-access-token: false
-#  wx.mp.app-id=appId
-#  wx.mp.secret=@secret
-#  wx.mp.token=@token
-#  wx.mp.aes-key=@aesKey
-#  wx.mp.use-stable-access-token=@useStableAccessToken
-#wx:
-#  appId:
-#
-#
-#  aesKey: rdDwt7K448074aL7Mt6QwOBPYCwIHqSaN
-# 公众号配置(必填)
+# 微信配置
 wx:
+  # 服务号(TODO:目前是测试号需要修改)
   mp:
-    app-id: wx076677ddbd348bbc
-    secret: 87972dca143f21348ff65c7ac19be1cc
-    token: Csy2001
-#    aes-key: "@aesKey"
-#    use-stable-access-token: true
-#    config-storage:
-#      type: edis
-#      key-prefix: wx
-#      redis:
-#        host: 127.0.0.1
-#        port: 6379
-        # sentinel-ips: "127.0.0.1:16379,127.0.0.1:26379"
-        # sentinel-name: mymaster
-#      http-client-type: httpclient
-#      http-proxy-host: ""
-#      http-proxy-port: ""
-#      http-proxy-username: ""
-#      http-proxy-password: ""
-    # hosts:
-    #   api-host: "http://proxy.com/"
-    #   open-host: "http://proxy.com/"
-    #   mp-host: "http://proxy.com/"
+    app-id: ENC(1ckb9lCI/Pnw0KviUNC6rYpwKdncC9rKK1eBkhnIj/Jcy4MB2Jg/aQ==)
+    secret: ENC(W6+k6pXJrVhn3gxNLU6Gu2J31npIXrPF35XLcgGuAYzRsNrs74ILl5sK1YqRRMRbjZOdBnKjNOc=)
+    token: ENC(rBHi/C6XNGPoNzXBWxiVBoQwiNO/OGGy)
+  # 微信支付
+  pay:
+    #小程序APPID
+    appId: ENC(hJidlovRcx0bjLzNm9faQ2RkldEsWQGNZeRBgk14rcgUEc/4fKnhGg==)
+    #商户号ID
+    mchId: ENC(9s7sOayxeYw1p5oSIW+brXfEm45LFczEXO8VQoa7LeU=)
+    apiv3-key: ENC(QivoTfZcIuJL//YJgDiW7OgtoK4JQ3GYhohcrJKJN3Y3syECB0RgfPbZp+jN12iFqt7vXXBxVY8=)
+    # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
+    keyPath: C:\Software\WXCertUtil\cert\apiclient_cert.p12
+    # pub_key.pem证书文件的绝对路径或者以classpath:开头的类路径.
+    publichKeyPath: E:\Download\pub_key.pem
+  # 小程序
+  miniapp:
+    appid: ENC(825U/6iYUhZJK7ojeBUya+WATlVilGB8py4AQPA0jwfY8oPwjZ53wA==)
+    secret: ENC(tgBCIrfBOMfj68qP7X7zI9ZTBMBccyqlHCv5mQaTbKpobbtTZsAQp77rDfbW0kKrs9ta+NiWMkM=)
 
+# 微信消息模板配置
 wechat:
-  templateId: Ckf2pycVvLM-238QGp-YvG_MTPuvYGM8jVsyH8IF7_c
-#  color: 这个可以不配就是文字的颜色你想要骚一点就找16进制的就可以了默认为黑色
-#  url:  这个可以不配点击模板跳转的url
+  templateId: wSq3Oj0Tb2clDwz7mMID_kqDy4vR8AOQc7PQMqoch7A
+
+# 接口盒子平台配置
+hz-api:
+  id: ENC(2myOnTLci1DlpQfDkcqN2fGoM24y/fairiO5hH7braM=)
+  key: ENC(SK18qyEQdxOACJ2xeNLTGbUZEDi+wRkvDBs4lL/+wCc1ANFwD42P2Q7Ssl3U3uu9fVRQ3BdgV2o=)
+
+# 系统配置
+biz:
+  token:
+    password: ENC()
+    expire: 7

+ 37 - 0
data-easy/src/main/resources/application-prod.yaml

@@ -0,0 +1,37 @@
+spring:
+  datasource:
+    username: ENC(NSemTCqIcv//3e9jGN32BW/7W5T/U41S)
+    password: ENC(JCvpZk2f9TBxI/JWILBrgj8CRGh9F6Bbx7TX42VBOsCuAwQw0kyppA==)
+    url: ENC(qquG8FQIQ1L/5VzI18bKQcpkwpNyeYQYUv0pWtdK9JjaNpIyJqcOee48ZqRFg7Kd0Hm2/tZBA6mp21zVX7lqLCL1QWWhZ1+7rldsEO1ujD3ZaAsNugFvAyyyeqAfnGwm9CWk+XwN0MfRLjF/SvD/+W9D9Y/A1EjfpB2zLWGS+W3zI1//3Lj9iZNTCGFw1XTcZg89D700vubl9VWqzmIXpOj+sJKXQ8DS)
+  ai:
+    openai:
+      base-url: ENC(aYgDpynp/i5RsEqxPYMei+leHCdZhhT+mr63Mr9ebQBwNZZMpYYsM2D3lNgrMFf/)
+      api-key: ENC(DxYwiHLqdAh0/PQGtSAIVhVVyGWSQcUjAwmm+0/PpgIUaF3Y4K1k7RBZTtop2kK+1MYEeLVYGEdnGo/YtJP3zu/mlQY5lwqi)
+
+# 微信配置
+wx:
+  # 服务号(TODO:目前是测试号需要修改)
+  mp:
+    app-id: ENC(pB1sj36RTQ3sHRpuM0CngaTJszmwIwowtg0LEF8clW8mA/fRRcBERQ==)
+    secret: ENC(TlGRcit561V7dh7r2myGJUjdAgSY3xk8reOsfJs5sOIVhlH7pXm7Six6HZD9qzH7WzIWZHCqkaQ=)
+    token: ENC(fmSgBmv3vORrnGuQpmfMnZOZRzIJ1pRV)
+  # 微信支付
+  pay:
+    #小程序APPID
+    appId: ENC(d4kbf+HT9E3R+g6CM0zJ9LKL6W9ah2rhQ2Zy/mG1NDZEEcdR32+dPw==)
+    #商户号ID
+    mchId: ENC(UD2K4Ycx8o13A7EL1JejkoJFF1sH2/eLnlP4lK/zJPg=)
+    apiv3-key: ENC(ajnSujhFqKJ7MevaiLMbEzOOBlAgkOY89BqniEMMO3P9aCmDSa5tF7flTgoZTKd4JGBlCta9mOs=)
+    # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
+    keyPath: C:\Software\WXCertUtil\cert\apiclient_cert.p12
+    # pub_key.pem证书文件的绝对路径或者以classpath:开头的类路径.
+    publichKeyPath: E:\Download\pub_key.pem
+  # 小程序
+  miniapp:
+    appid: ENC(Fjp10HwqBtwQt6FYpJq8xXKp+XCRAo+drZciqdqrNiLbdS1a4U4fxA==)
+    secret: ENC(REKI9+xOn3RD6ts1w/UQ7RAtVwPpglvJX8U7Ipu7vY9Kaq8HDgnw42G/1+qT9TNuQgDYStIW5Io=)
+
+# 接口盒子平台配置
+hz-api:
+  id: ENC(xOmDcVBaAxOJHC1bazlmG8m3E082uCTZ4o8jxT7Fi8Q=)
+  key: ENC(I2SZcgbv6EEyEndn4FSMihTlTxVZ3zhxaQAIeJAOdeos3ZH4Nb8QalW1L1phlIK8C/FdPmnsEw8=)

+ 16 - 6
data-easy/src/main/resources/application.yaml

@@ -9,6 +9,11 @@ spring:
     driver-class-name: com.mysql.cj.jdbc.Driver
   cache:
     type: caffeine
+  ai:
+    openai:
+      chat:
+        options:
+          model: deepseek-ai/DeepSeek-V3
 
 jasypt:
   encryptor:
@@ -16,9 +21,14 @@ jasypt:
     salt-generator-classname: org.jasypt.salt.RandomSaltGenerator
     iv-generator-classname: org.jasypt.iv.RandomIvGenerator
 
-biz:
-  wechat:
-    mini-program:
-      url: https://api.weixin.qq.com
-  token:
-    expire: 7
+# 接口盒子平台配置
+hz-api:
+  base-url: https://cn.apihz.cn
+
+# 金融接口
+finance:
+  base-url: http://127.0.0.1:8000
+
+# 自建ProductHunt服务配置
+product-hunt:
+  base-url: http://127.0.0.1:8080

+ 29 - 9
doc/技术文档.md

@@ -9,30 +9,50 @@
   * (小程序支持即可)注销
   * (已开发,待测试)修改昵称
   * (已开发,待测试)查询用户基本信息
+  
 * 小程序
-  * 可订阅列表查询
-  * 已订阅列表查询
-  * 已订阅源的消息发送控制配置
-  * 订阅源详情查看
-  * 订阅支付
-  * 每天推送的订阅源数据查看
+  * (已开发,待测试)可订阅列表查询/订阅源详情查看
+  
+    这里可以用同一个接口查看
+  
+  * (已开发,待测试)已订阅列表查询
+  
+  * (已开发,待测试)已订阅源的消息发送控制配置
+  
+  * (已开发,待测试)每天推送的订阅源数据查看
     * 新股
     * 新债
     * 双色球
     * 大乐透
     * Product Hunt的top30榜单
+  
 * 订单历史记录查询
-* 微信用户关注服务号登记
+
+* (已开发,待测试)微信用户关注服务号登记
+
 * 微信用户支付处理
-* 系统定时任务
+
+  * 订阅支付
+  * 支付回调处理
+
+* (已开发,待设置模板数据)系统定时任务
   * 获取新股、新债数据并推送
-    * 拉取数据、获取待推送用户、推送
   * 获取双色球数据并推送
   * 获取大乐透数据并推送
   * 获取Product Hunt数据并推送
 
 
 
+## 定时任务
+
+| 定时任务名称            | 执行时间                     | cron表达式      | 实现类          |
+| ----------------------- | ---------------------------- | --------------- | --------------- |
+| 大乐透定时任务          | 每周一、三、六,晚上21:30    | 30 21 * * 1,3,6 | DaLeTouTask     |
+| 双色球定时任务          | 每周二、周四、周日 晚上21:40 | 40 21 * * 2,4,7 | ShuangSeQiuTask |
+| 新债定时任务            | 每天早上8:45                 | 45 8 * * *      | IpoBondTask     |
+| 新股定时任务            | 每天早上8:45                 | 45 8 * * *      | IpoStockTask    |
+| ProductHunt热榜定时任务 | 每天下午4:00                 | 0 16 * * *      | ProductHuntTask |
+
 
 
 ## 表设计