Quellcode durchsuchen

1.发送canary新注册用户定时任务
2.发送待审核adsAccount定时任务
3.增加审核接口

sjl vor 1 Monat
Ursprung
Commit
82eae98976
14 geänderte Dateien mit 757 neuen und 17 gelöschten Zeilen
  1. 4 0
      jeecg-module-demo/pom.xml
  2. 29 17
      jeecg-module-demo/src/main/java/org/jeecg/modules/demo/adsaccount/controller/AdsAccountController.java
  3. 11 0
      jeecg-module-demo/src/main/java/org/jeecg/modules/demo/adsaccount/entity/AdsAccount.java
  4. 37 0
      jeecg-module-demo/src/main/java/org/jeecg/modules/demo/adsaccount/entity/TimezoneID.java
  5. 6 0
      jeecg-module-demo/src/main/java/org/jeecg/modules/demo/adsaccount/service/IAdsAccountService.java
  6. 57 0
      jeecg-module-demo/src/main/java/org/jeecg/modules/demo/adsaccount/service/impl/AdsAccountServiceImpl.java
  7. 143 0
      jeecg-module-demo/src/main/java/org/jeecg/modules/demo/adsaccount/xxljob/SendPendingAdsAccount.java
  8. 145 0
      jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/job/SendNewRegisterLeads.java
  9. 164 0
      jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/userLogin/controller/UserLoginController.java
  10. 78 0
      jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/userLogin/entity/UserLogin.java
  11. 18 0
      jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/userLogin/mapper/UserLoginMapper.java
  12. 5 0
      jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/userLogin/mapper/xml/UserLoginMapper.xml
  13. 21 0
      jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/userLogin/service/IUserLoginService.java
  14. 39 0
      jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/userLogin/service/impl/UserLoginServiceImpl.java

+ 4 - 0
jeecg-module-demo/pom.xml

@@ -16,6 +16,10 @@
             <groupId>org.jeecgframework.boot</groupId>
             <artifactId>jeecg-boot-base-core</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.jeecgframework.boot</groupId>
+            <artifactId>jeecg-system-cloud-api</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 29 - 17
jeecg-module-demo/src/main/java/org/jeecg/modules/demo/adsaccount/controller/AdsAccountController.java

@@ -2,17 +2,12 @@ package org.jeecg.modules.demo.adsaccount.controller;
 
 import java.util.Arrays;
 import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
+
+import dm.jdbc.util.StringUtil;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import org.jeecg.common.api.vo.Result;
 import org.jeecg.common.system.query.QueryGenerator;
-import org.jeecg.common.util.oConvertUtils;
 import org.jeecg.modules.demo.adsaccount.entity.AdsAccount;
 import org.jeecg.modules.demo.adsaccount.service.IAdsAccountService;
 
@@ -21,18 +16,11 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import lombok.extern.slf4j.Slf4j;
 
-import org.jeecgframework.poi.excel.ExcelImportUtil;
-import org.jeecgframework.poi.excel.def.NormalExcelConstants;
-import org.jeecgframework.poi.excel.entity.ExportParams;
-import org.jeecgframework.poi.excel.entity.ImportParams;
-import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
 import org.jeecg.common.system.base.controller.JeecgController;
 import org.springframework.beans.factory.annotation.Autowired;
+
 import org.springframework.web.bind.annotation.*;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.multipart.MultipartHttpServletRequest;
 import org.springframework.web.servlet.ModelAndView;
-import com.alibaba.fastjson.JSON;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.jeecg.common.aspect.annotation.AutoLog;
@@ -51,8 +39,8 @@ import org.apache.shiro.authz.annotation.RequiresPermissions;
 public class AdsAccountController extends JeecgController<AdsAccount, IAdsAccountService> {
 	@Autowired
 	private IAdsAccountService adsAccountService;
-	
-	/**
+
+	 /**
 	 * 分页列表查询
 	 *
 	 * @param adsAccount
@@ -178,4 +166,28 @@ public class AdsAccountController extends JeecgController<AdsAccount, IAdsAccoun
         return super.importExcel(request, response, AdsAccount.class);
     }
 
+	 /**
+	  *  审核
+	  *
+	  * @param adsAccount
+	  * @return
+	  */
+	 @AutoLog(value = "广告账户管理-审核")
+	 @Operation(summary="广告账户管理-审核")
+	 @RequiresPermissions("adsaccount:ads_account:audit")
+	 @RequestMapping(value = "/audit", method = {RequestMethod.PUT,RequestMethod.POST})
+	 public Result<String> audit(@RequestBody AdsAccount adsAccount) {
+		 if(StringUtil.equals(adsAccount.getReviewStatus(),"APPROVED") && adsAccount.getAccountId().matches(".*[a-zA-Z].*")){
+			 String accountId =  adsAccountService.createGoogleAdsCustomer(adsAccount);
+			 if(!StringUtil.equals(accountId,"false")){
+				 adsAccount.setAccountId(accountId);
+			 } else {
+				 return Result.OK("创建谷歌广告账户失败!");
+			 }
+		 }
+		 adsAccount.setUpdatedAt(new Date());
+		 adsAccountService.updateById(adsAccount);
+		 return Result.OK("审核成功!");
+	 }
+
 }

+ 11 - 0
jeecg-module-demo/src/main/java/org/jeecg/modules/demo/adsaccount/entity/AdsAccount.java

@@ -94,6 +94,9 @@ public class AdsAccount implements Serializable {
     @Schema(description = "删除标识")
     @TableLogic(value="0",delval="1")
     private Integer isDeleted;
+	@Excel(name = "备注", width = 15)
+	@Schema(description = "备注")
+	private String remark;
 	/**创建时间*/
 	@Excel(name = "创建时间", width = 15, format = "yyyy-MM-dd")
 	@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
@@ -106,4 +109,12 @@ public class AdsAccount implements Serializable {
     @DateTimeFormat(pattern="yyyy-MM-dd")
     @Schema(description = "更新时间")
     private Date updatedAt;
+
+
+	public enum AChannel {
+		Google,
+		Meta,
+		TikTok,
+		Bing
+	}
 }

+ 37 - 0
jeecg-module-demo/src/main/java/org/jeecg/modules/demo/adsaccount/entity/TimezoneID.java

@@ -0,0 +1,37 @@
+package org.jeecg.modules.demo.adsaccount.entity;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@Getter
+@AllArgsConstructor
+public enum TimezoneID {
+    ASIA_SHANGHAI("Asia/Shanghai"),
+    ASIA_HONG_KONG("Asia/Hong_Kong"),
+    AMERICA_NEW_YORK("America/New_York"),
+    AMERICA_LOS_ANGELES("America/Los_Angeles"),
+    UNK("UNKNOWN");
+
+    private static final Map<String, TimezoneID> MAP =
+            Stream.of(TimezoneID.values())
+                    .collect(Collectors.toMap(TimezoneID::getId, Function.identity()));
+
+    @JsonCreator
+    public static TimezoneID valueOfId(String id) {
+        return MAP.getOrDefault(id, UNK);
+    }
+
+    @JsonValue
+    public String getId() {
+        return id;
+    }
+
+    private String id;
+}

+ 6 - 0
jeecg-module-demo/src/main/java/org/jeecg/modules/demo/adsaccount/service/IAdsAccountService.java

@@ -3,6 +3,8 @@ package org.jeecg.modules.demo.adsaccount.service;
 import org.jeecg.modules.demo.adsaccount.entity.AdsAccount;
 import com.baomidou.mybatisplus.extension.service.IService;
 
+import java.util.List;
+
 /**
  * @Description: 广告账户管理
  * @Author: jeecg-boot
@@ -11,4 +13,8 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface IAdsAccountService extends IService<AdsAccount> {
 
+    List<AdsAccount> getPendingReviewStatusAdsAccounts(String reviewStatus);
+
+    String createGoogleAdsCustomer(AdsAccount adsAccount);
+
 }

+ 57 - 0
jeecg-module-demo/src/main/java/org/jeecg/modules/demo/adsaccount/service/impl/AdsAccountServiceImpl.java

@@ -1,11 +1,20 @@
 package org.jeecg.modules.demo.adsaccount.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import org.jeecg.modules.demo.adsaccount.entity.AdsAccount;
+import org.jeecg.modules.demo.adsaccount.entity.TimezoneID;
 import org.jeecg.modules.demo.adsaccount.mapper.AdsAccountMapper;
 import org.jeecg.modules.demo.adsaccount.service.IAdsAccountService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
 import org.springframework.stereotype.Service;
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.List;
 
 /**
  * @Description: 广告账户管理
@@ -16,4 +25,52 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 @Service
 public class AdsAccountServiceImpl extends ServiceImpl<AdsAccountMapper, AdsAccount> implements IAdsAccountService {
 
+
+    @Autowired
+    private RestTemplate restTemplate;
+
+    @Override
+    public List<AdsAccount> getPendingReviewStatusAdsAccounts(String reviewStatus) {
+        // 使用 MyBatis-Plus 的查询方式,通过 reviewStatus 字段查找为 pending 的广告账户
+        return baseMapper.selectList(new QueryWrapper<AdsAccount>().eq("review_status", reviewStatus));
+    }
+
+    @Override
+    public String createGoogleAdsCustomer(AdsAccount adsAccount){
+        String accountName = adsAccount.getAccountName();
+        AdsAccount.AChannel accountChannel = AdsAccount.AChannel.Google;
+        String currencyCode = adsAccount.getCurrencyCode();
+
+        String timezoneId =  adsAccount.getAccountId().replaceAll("\\d+", "");
+        TimezoneID timezone = TimezoneID.valueOfId(timezoneId);
+
+        String url = "http://127.0.0.1:8090/api/ads_accounts/createGoogleAdsCustomer?accountName=" + accountName
+                + "&accountChannel=" + accountChannel
+                + "&currencyCode=" + currencyCode
+                + "&timezone=" + timezoneId;
+
+//        RequestConfig requestConfig = RequestConfig.custom()
+//					 .setSocketTimeout(5000)  // 设置读取超时时间
+//					 .setConnectTimeout(5000) // 设置连接超时时间
+//					 .build();
+//
+//			 HttpClient httpClient = HttpClients.custom()
+//					 .setDefaultRequestConfig(requestConfig)
+//					 .build();
+        // 使用 HttpComponentsClientHttpRequestFactory 包装 HttpClient
+        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
+
+        RestTemplate restTemplate = new RestTemplate(factory);
+
+        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, null, String.class);
+
+        if (response.getStatusCode().is2xxSuccessful()) {
+            String accountId = response.getBody();
+            adsAccount.setAccountId(accountId);
+            return accountId;
+        } else {
+            return "false";
+        }
+    }
+
 }

+ 143 - 0
jeecg-module-demo/src/main/java/org/jeecg/modules/demo/adsaccount/xxljob/SendPendingAdsAccount.java

@@ -0,0 +1,143 @@
+package org.jeecg.modules.demo.adsaccount.xxljob;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.system.api.ISysBaseAPI;
+import org.jeecg.common.system.vo.DictModel;
+import org.jeecg.common.util.DateUtils;
+import org.jeecg.modules.demo.adsaccount.entity.AdsAccount;
+import org.jeecg.modules.demo.adsaccount.service.IAdsAccountService;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+
+/**
+ * 示例不带参定时任务
+ *
+ * @Author Scott
+ */
+@Slf4j
+public class SendPendingAdsAccount implements Job {
+
+	@Autowired
+	private IAdsAccountService adsAccountService;
+	@Autowired
+	private ISysBaseAPI sysBaseAPI;
+
+	@Override
+	public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
+		log.info("Job Execution key:" + jobExecutionContext.getJobDetail().getKey());
+		log.info("Jeecg-Boot 普通定时任务:发送所有reviewStatus为pending的广告账户信息! 时间: " + DateUtils.getTimestamp());
+
+		// 获取所有 reviewStatus 为 pending 的 AdsAccount 对象
+		List<AdsAccount> pendingAdsAccounts = getPendingAdsAccounts();
+
+		if (pendingAdsAccounts.isEmpty()) {
+			log.info("没有找到 reviewStatus 为 pending 的广告账户");
+		} else {
+			// 将所有符合条件的广告账户信息合并成一个字符串
+			StringBuilder adsAccountInfo = new StringBuilder();
+			adsAccountInfo.append("有待审核的AdsAccount:\n");
+
+			for (AdsAccount adsAccount : pendingAdsAccounts) {
+				adsAccountInfo.append("\n")
+						.append("账号名: ").append(adsAccount.getAccountName()).append("\n")
+						.append("创建时间: ").append(adsAccount.getCreatedAt()).append("\n")
+						.append("--------------------\n");
+			}
+
+			// 发送合并的消息到飞书
+			try {
+				sendMessageToFeishu(adsAccountInfo.toString());
+			} catch (Exception e) {
+				log.error("发送消息失败", e);
+			}
+		}
+	}
+
+	// 这个方法负责发送消息到飞书
+	private void sendMessageToFeishu(String messageContent) throws Exception {
+		// Webhook URL (替换为你实际的 Webhook URL)
+//		String webhookUrl = "https://open.feishu.cn/open-apis/bot/v2/hook/14132c82-bafe-48ce-8c06-69de369d5287";
+		String webhookUrl = getFeishuWebhookUrl();
+
+		String escapeMessage = escapeSpecialCharacters(messageContent);
+		// 创建 JSON Payload,包含广告账户信息
+		String jsonPayload = "{ \"msg_type\": \"text\", \"content\": { \"text\": \"" + escapeMessage + "\" }}";
+
+		// 打印 JSON Payload 格式以进行调试
+		log.info("Payload: {}", jsonPayload);
+
+		// 发送 JSON Payload 到飞书通过 HTTP POST
+		URL url = new URL(webhookUrl);
+		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+		connection.setRequestMethod("POST");
+		connection.setRequestProperty("Content-Type", "application/json");
+		connection.setDoOutput(true);
+
+		try (OutputStream os = connection.getOutputStream()) {
+			byte[] input = jsonPayload.getBytes("utf-8");
+			os.write(input, 0, input.length);
+		}
+
+		// 获取响应码和响应体
+		int responseCode = connection.getResponseCode();
+		StringBuilder responseBody = new StringBuilder();
+
+		// 如果是 200 OK,读取正常响应体
+		if (responseCode == HttpURLConnection.HTTP_OK) {
+			try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
+				String inputLine;
+				while ((inputLine = in.readLine()) != null) {
+					responseBody.append(inputLine);
+				}
+			}
+			log.debug("Successfully sent pending AdsAccount reviewStatus to FeiShu group. Response code: {}, Response body: {}", responseCode, responseBody.toString());
+		} else {
+			// 如果是其他响应码,读取错误响应体
+			try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getErrorStream()))) {
+				String inputLine;
+				while ((inputLine = in.readLine()) != null) {
+					responseBody.append(inputLine);
+				}
+			}
+			log.error("Failed to send pending AdsAccount reviewStatus to FeiShu group. Response code: {}, Response body: {}", responseCode, responseBody.toString());
+		}
+
+		connection.disconnect();
+	}
+
+	private List<AdsAccount> getPendingAdsAccounts() {
+		// 查询 reviewStatus 为 pending 的广告账户
+		return adsAccountService.getPendingReviewStatusAdsAccounts("PENDING_REVIEW");
+	}
+
+	public static String escapeSpecialCharacters(String message) {
+		if (message == null) {
+			return null;
+		}
+		// 这里将换行符转义成 \\n
+		return message.replace("\n", "\\n");
+	}
+	public String getFeishuWebhookUrl() {
+		List<DictModel> dictItems = sysBaseAPI.getDictItems("feishu_bot");
+		String webhookUrl = null;
+		if (dictItems != null && !dictItems.isEmpty()) {
+			for (DictModel dictItem : dictItems) {
+				if ("NEW_REGIDSTER".equals(dictItem.getValue())) {
+					// 获取对应的 Webhook URL
+					webhookUrl = dictItem.getText(); // 假设 webhook URL 存储在 text 字段中
+					break; // 找到后跳出循环
+				}
+			}
+		}
+		return webhookUrl;
+	}
+}

+ 145 - 0
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/job/SendNewRegisterLeads.java

@@ -0,0 +1,145 @@
+package org.jeecg.modules.quartz.job;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.aspect.annotation.Dict;
+import org.jeecg.common.system.api.ISysBaseAPI;
+import org.jeecg.common.system.vo.DictModel;
+import org.jeecg.common.util.DateUtils;
+import org.jeecg.modules.userLogin.entity.UserLogin;
+import org.jeecg.modules.userLogin.service.IUserLoginService;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Date;
+import java.util.List;
+
+@Slf4j
+public class SendNewRegisterLeads implements Job {
+
+	@Autowired
+	private IUserLoginService userLoginService;
+	@Autowired
+	private ISysBaseAPI sysBaseAPI;
+
+	@Override
+	public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
+		log.info("Job Execution key:" + jobExecutionContext.getJobDetail().getKey());
+		log.info(String.format("Jeecg-Boot 普通定时任务 飞书机器人发送新注册的canary用户! 时间: " + DateUtils.getTimestamp()));
+
+		// 获取前一小时内注册的 UserLogin 对象
+		List<UserLogin> recentUserLogins = getRecentUserLogins();
+
+		if (recentUserLogins.isEmpty()) {
+			log.info("最近一小时没有新注册客户");
+		} else {
+			// 将所有新注册用户的信息合并成一个字符串
+			StringBuilder userInfo = new StringBuilder();
+			userInfo.append("最近一小时内出现了新注册的Canary用户:\n");
+
+			for (UserLogin userLogin : recentUserLogins) {
+				userInfo.append("\n")
+						.append("用户名: ").append(userLogin.getUsername()).append("\n")
+						.append("公司: ").append(userLogin.getCompany()).append("\n")
+						.append("电话: ").append(userLogin.getMobile()).append("\n")
+						.append("--------------------\n");
+			}
+
+			// 发送合并的消息到飞书
+			try {
+				sendMessageToFeishu(userInfo.toString());
+			} catch (Exception e) {
+				log.info("发送消息失败", e);
+			}
+		}
+	}
+
+	// 这个方法负责发送消息到飞书
+	private void sendMessageToFeishu(String messageContent) throws Exception {
+		// 处理消息内容中的特殊字符
+		String escapedMessage = escapeSpecialCharacters(messageContent);
+		// Webhook URL (替换为你实际的 Webhook URL)
+//		String webhookUrl = "https://open.feishu.cn/open-apis/bot/v2/hook/75f88884-8a99-4531-bdee-3a148e0fa782";
+		String webhookUrl = getFeishuWebhookUrl();
+
+		// 创建 JSON Payload,包含用户信息
+		String jsonPayload = "{ \"msg_type\": \"text\", \"content\": { \"text\": \"" + escapedMessage + "\" }}";
+
+		// 打印 JSON Payload 格式以进行调试
+		log.info("Payload: {}", jsonPayload);
+
+		// 发送 JSON Payload 到飞书通过 HTTP POST
+		URL url = new URL(webhookUrl);
+		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+		connection.setRequestMethod("POST");
+		connection.setRequestProperty("Content-Type", "application/json");
+		connection.setDoOutput(true);
+
+		try (OutputStream os = connection.getOutputStream()) {
+			byte[] input = jsonPayload.getBytes("utf-8");
+			os.write(input, 0, input.length);
+		}
+
+		// 获取响应码和响应体
+		int responseCode = connection.getResponseCode();
+		StringBuilder responseBody = new StringBuilder();
+
+		// 如果是 200 OK,读取正常响应体
+		if (responseCode == HttpURLConnection.HTTP_OK) {
+			try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
+				String inputLine;
+				while ((inputLine = in.readLine()) != null) {
+					responseBody.append(inputLine);
+				}
+			}
+			log.debug("Successfully sent new user login leads to FeiShu group. Response code: {}, Response body: {}", responseCode, responseBody.toString());
+		} else {
+			// 如果是其他响应码,读取错误响应体
+			try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getErrorStream()))) {
+				String inputLine;
+				while ((inputLine = in.readLine()) != null) {
+					responseBody.append(inputLine);
+				}
+			}
+			log.error("Failed to send new user login leads to FeiShu group. Response code: {}, Response body: {}", responseCode, responseBody.toString());
+		}
+
+		connection.disconnect();
+	}
+
+	private List<UserLogin> getRecentUserLogins() {
+		Date currentTime = new Date();
+		long oneHourAgoMillis = currentTime.getTime() - 3600000*24; // 毫秒级时间戳
+		Date oneHourAgo = new Date(oneHourAgoMillis); // 转换为 Date 对象
+		return userLoginService.getUserLoginsByRegistrationTime(oneHourAgo);
+	}
+
+	// 用于处理特殊字符的转义方法
+	public static String escapeSpecialCharacters(String message) {
+		if (message == null) {
+			return null;
+		}
+		return message.replace("\n", "\\n");  // 这里保持换行符为 \\n
+	}
+
+	public String getFeishuWebhookUrl() {
+		List<DictModel> dictItems = sysBaseAPI.getDictItems("feishu_bot");
+		String webhookUrl = null;
+		if (dictItems != null && !dictItems.isEmpty()) {
+			for (DictModel dictItem : dictItems) {
+				if ("NEW_REGIDSTER".equals(dictItem.getValue())) {
+					// 获取对应的 Webhook URL
+					webhookUrl = dictItem.getText(); // 假设 webhook URL 存储在 text 字段中
+					break; // 找到后跳出循环
+				}
+			}
+		}
+		return webhookUrl;
+	}
+}

+ 164 - 0
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/userLogin/controller/UserLoginController.java

@@ -0,0 +1,164 @@
+package org.jeecg.modules.userLogin.controller;
+
+import java.util.Arrays;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.system.query.QueryGenerator;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.extern.slf4j.Slf4j;
+
+import org.jeecg.modules.userLogin.entity.UserLogin;
+import org.jeecg.modules.userLogin.service.IUserLoginService;
+import org.jeecg.common.system.base.controller.JeecgController;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.ModelAndView;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+
+ /**
+ * @Description: user_login
+ * @Author: jeecg-boot
+ * @Date:   2025-01-21
+ * @Version: V1.0
+ */
+@Tag(name="user_login")
+@RestController
+@RequestMapping("/userLogin/userLogin")
+@Slf4j
+public class UserLoginController extends JeecgController<UserLogin, IUserLoginService> {
+	@Autowired
+	private IUserLoginService userLoginService;
+	
+	/**
+	 * 分页列表查询
+	 *
+	 * @param userLogin
+	 * @param pageNo
+	 * @param pageSize
+	 * @param req
+	 * @return
+	 */
+	//@AutoLog(value = "user_login-分页列表查询")
+	@Operation(summary="user_login-分页列表查询")
+	@GetMapping(value = "/list")
+	public Result<IPage<UserLogin>> queryPageList(UserLogin userLogin,
+								   @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
+								   @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
+								   HttpServletRequest req) {
+		QueryWrapper<UserLogin> queryWrapper = QueryGenerator.initQueryWrapper(userLogin, req.getParameterMap());
+		Page<UserLogin> page = new Page<UserLogin>(pageNo, pageSize);
+		IPage<UserLogin> pageList = userLoginService.page(page, queryWrapper);
+		return Result.OK(pageList);
+	}
+	
+	/**
+	 *   添加
+	 *
+	 * @param userLogin
+	 * @return
+	 */
+	@AutoLog(value = "user_login-添加")
+	@Operation(summary="user_login-添加")
+	@RequiresPermissions("userLogin:user_login:add")
+	@PostMapping(value = "/add")
+	public Result<String> add(@RequestBody UserLogin userLogin) {
+		userLoginService.save(userLogin);
+		return Result.OK("添加成功!");
+	}
+	
+	/**
+	 *  编辑
+	 *
+	 * @param userLogin
+	 * @return
+	 */
+	@AutoLog(value = "user_login-编辑")
+	@Operation(summary="user_login-编辑")
+	@RequiresPermissions("userLogin:user_login:edit")
+	@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
+	public Result<String> edit(@RequestBody UserLogin userLogin) {
+		userLoginService.updateById(userLogin);
+		return Result.OK("编辑成功!");
+	}
+	
+	/**
+	 *   通过id删除
+	 *
+	 * @param id
+	 * @return
+	 */
+	@AutoLog(value = "user_login-通过id删除")
+	@Operation(summary="user_login-通过id删除")
+	@RequiresPermissions("userLogin:user_login:delete")
+	@DeleteMapping(value = "/delete")
+	public Result<String> delete(@RequestParam(name="id",required=true) String id) {
+		userLoginService.removeById(id);
+		return Result.OK("删除成功!");
+	}
+	
+	/**
+	 *  批量删除
+	 *
+	 * @param ids
+	 * @return
+	 */
+	@AutoLog(value = "user_login-批量删除")
+	@Operation(summary="user_login-批量删除")
+	@RequiresPermissions("userLogin:user_login:deleteBatch")
+	@DeleteMapping(value = "/deleteBatch")
+	public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
+		this.userLoginService.removeByIds(Arrays.asList(ids.split(",")));
+		return Result.OK("批量删除成功!");
+	}
+	
+	/**
+	 * 通过id查询
+	 *
+	 * @param id
+	 * @return
+	 */
+	//@AutoLog(value = "user_login-通过id查询")
+	@Operation(summary="user_login-通过id查询")
+	@GetMapping(value = "/queryById")
+	public Result<UserLogin> queryById(@RequestParam(name="id",required=true) String id) {
+		UserLogin userLogin = userLoginService.getById(id);
+		if(userLogin==null) {
+			return Result.error("未找到对应数据");
+		}
+		return Result.OK(userLogin);
+	}
+
+    /**
+    * 导出excel
+    *
+    * @param request
+    * @param userLogin
+    */
+    @RequiresPermissions("userLogin:user_login:exportXls")
+    @RequestMapping(value = "/exportXls")
+    public ModelAndView exportXls(HttpServletRequest request, UserLogin userLogin) {
+        return super.exportXls(request, userLogin, UserLogin.class, "user_login");
+    }
+
+    /**
+      * 通过excel导入数据
+    *
+    * @param request
+    * @param response
+    * @return
+    */
+    @RequiresPermissions("userLogin:user_login:importExcel")
+    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
+    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
+        return super.importExcel(request, response, UserLogin.class);
+    }
+
+}

+ 78 - 0
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/userLogin/entity/UserLogin.java

@@ -0,0 +1,78 @@
+package org.jeecg.modules.userLogin.entity;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+import java.math.BigDecimal;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.jeecg.common.aspect.annotation.Dict;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * @Description: user_login
+ * @Author: jeecg-boot
+ * @Date:   2025-01-21
+ * @Version: V1.0
+ */
+@Data
+@TableName("user_login")
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = false)
+@Schema(description="user_login")
+public class UserLogin implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+	/**id*/
+	@TableId(type = IdType.ASSIGN_ID)
+    @Schema(description = "id")
+    private Integer id;
+	/**用户名*/
+	@Excel(name = "用户名", width = 15)
+    @Schema(description = "用户名")
+    private String username;
+	/**密码*/
+	@Excel(name = "密码", width = 15)
+    @Schema(description = "密码")
+    private String passwordCipher;
+	/**邮箱*/
+	@Excel(name = "邮箱", width = 15)
+    @Schema(description = "邮箱")
+    private String email;
+	/**手机号*/
+	@Excel(name = "手机号", width = 15)
+    @Schema(description = "手机号")
+    private String mobile;
+	/**公司名称*/
+	@Excel(name = "公司名称", width = 15)
+    @Schema(description = "公司名称")
+    private String company;
+	/**删除标记*/
+	@Excel(name = "删除标记", width = 15)
+    @Schema(description = "删除标记")
+    private Integer isDeleted;
+	/**创建时间*/
+	@Excel(name = "创建时间", width = 15, format = "yyyy-MM-dd")
+	@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern="yyyy-MM-dd")
+    @Schema(description = "创建时间")
+    private Date createdAt;
+	/**更新时间*/
+	@Excel(name = "更新时间", width = 15, format = "yyyy-MM-dd")
+	@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern="yyyy-MM-dd")
+    @Schema(description = "更新时间")
+    private Date updatedAt;
+	/**微信openId*/
+	@Excel(name = "微信openId", width = 15)
+    @Schema(description = "微信openId")
+    private String openId;
+}

+ 18 - 0
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/userLogin/mapper/UserLoginMapper.java

@@ -0,0 +1,18 @@
+package org.jeecg.modules.userLogin.mapper;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.jeecg.modules.userLogin.entity.UserLogin;
+
+/**
+ * @Description: user_login
+ * @Author: jeecg-boot
+ * @Date:   2025-01-21
+ * @Version: V1.0
+ */
+public interface UserLoginMapper extends BaseMapper<UserLogin> {
+
+}

+ 5 - 0
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/userLogin/mapper/xml/UserLoginMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.jeecg.modules.demo.userLogin.mapper.UserLoginMapper">
+
+</mapper>

+ 21 - 0
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/userLogin/service/IUserLoginService.java

@@ -0,0 +1,21 @@
+package org.jeecg.modules.userLogin.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.jeecg.modules.userLogin.entity.UserLogin;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @Description: user_login
+ * @Author: jeecg-boot
+ * @Date:   2025-01-21
+ * @Version: V1.0
+ */
+public interface IUserLoginService extends IService<UserLogin> {
+
+    // 获取前一小时注册的 UserLogin 列表
+    List<UserLogin> getUserLoginsByRegistrationTime(Date oneHourAgo);
+
+}

+ 39 - 0
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/userLogin/service/impl/UserLoginServiceImpl.java

@@ -0,0 +1,39 @@
+package org.jeecg.modules.userLogin.service.impl;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.jeecg.modules.userLogin.mapper.UserLoginMapper;
+import org.jeecg.modules.userLogin.entity.UserLogin;
+import org.jeecg.modules.userLogin.service.IUserLoginService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @Description: user_login
+ * @Author: jeecg-boot
+ * @Date:   2025-01-21
+ * @Version: V1.0
+ */
+@Service
+public class UserLoginServiceImpl extends ServiceImpl<UserLoginMapper, UserLogin> implements IUserLoginService {
+
+    @Autowired
+    private UserLoginMapper userLoginMapper;
+    @Override
+    public List<UserLogin> getUserLoginsByRegistrationTime(Date oneHourAgo) {
+        // 使用 QueryWrapper 构建查询条件
+        QueryWrapper<UserLogin> queryWrapper = new QueryWrapper<>();
+
+        // 确保查询条件:createdAt >= oneHourAgo
+        queryWrapper.ge("created_at", oneHourAgo);  // 这里查询创建时间大于等于一小时前的时间
+
+        // 返回查询结果
+        return userLoginMapper.selectList(queryWrapper);
+    }
+
+}