diff --git a/pom.xml b/pom.xml index 682980e..096b687 100644 --- a/pom.xml +++ b/pom.xml @@ -90,11 +90,6 @@ jdk 3.0 - - com.github.penggle - kaptcha - 2.3.2 - org.codehaus.jackson jackson-mapper-asl @@ -106,29 +101,7 @@ jackson-core-asl 1.9.2 - - io.jsonwebtoken - jjwt-api - 0.11.5 - - - io.jsonwebtoken - jjwt-impl - 0.11.5 - runtime - - - io.jsonwebtoken - jjwt-jackson - 0.11.5 - runtime - - - - org.springframework.boot - spring-boot-starter-actuator - org.springframework.boot @@ -140,10 +113,6 @@ httpclient 4.5.13 - - org.springframework.boot - spring-boot-starter-security - diff --git a/src/main/java/cn/van/business/config/KaptchaConfig.java b/src/main/java/cn/van/business/config/KaptchaConfig.java deleted file mode 100644 index 9cab3e1..0000000 --- a/src/main/java/cn/van/business/config/KaptchaConfig.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.van.business.config; - -import com.google.code.kaptcha.impl.DefaultKaptcha; -import com.google.code.kaptcha.util.Config; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import java.util.Properties; - -@Configuration -public class KaptchaConfig { - - @Bean - public DefaultKaptcha kaptchaProducer() { - DefaultKaptcha kaptcha = new DefaultKaptcha(); - Properties properties = new Properties(); - - // 设置验证码参数 - properties.setProperty("kaptcha.image.width", "150"); - properties.setProperty("kaptcha.image.height", "50"); - properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - properties.setProperty("kaptcha.textproducer.char.length", "4"); - - kaptcha.setConfig(new Config(properties)); - return kaptcha; - } -} diff --git a/src/main/java/cn/van/business/config/SecurityConfig.java b/src/main/java/cn/van/business/config/SecurityConfig.java deleted file mode 100644 index a57ed58..0000000 --- a/src/main/java/cn/van/business/config/SecurityConfig.java +++ /dev/null @@ -1,75 +0,0 @@ -package cn.van.business.config; - -import cn.van.business.filter.JwtAuthFilter; -import cn.van.business.service.impl.UserDetailsServiceImpl; -import cn.van.business.util.JwtUtils; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.dao.DaoAuthenticationProvider; -import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.session.SessionRegistry; -import org.springframework.security.core.session.SessionRegistryImpl; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.security.web.session.HttpSessionEventPublisher; - -@Configuration -@EnableWebSecurity -public class SecurityConfig { - - private final UserDetailsServiceImpl userDetailsService; - private final JwtAuthFilter jwtAuthFilter; - - public SecurityConfig(UserDetailsServiceImpl userDetailsService, JwtAuthFilter jwtAuthFilter) { - this.userDetailsService = userDetailsService; - this.jwtAuthFilter = jwtAuthFilter; - } - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http, - PasswordEncoder passwordEncoder) throws Exception { - DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); - provider.setUserDetailsService(userDetailsService); - provider.setPasswordEncoder(passwordEncoder); - - http - .csrf(AbstractHttpConfigurer::disable) - .sessionManagement(session -> session - .sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) - .authenticationProvider(provider) - .authorizeHttpRequests(auth -> auth - .requestMatchers("/auth/login", "/auth/captcha","/wx/message").permitAll() - .anyRequest().authenticated()); - - return http.build(); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - @Bean - public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { - return config.getAuthenticationManager(); - } - - @Bean - public SessionRegistry sessionRegistry() { - return new SessionRegistryImpl(); - } - - @Bean - public HttpSessionEventPublisher httpSessionEventPublisher() { - return new HttpSessionEventPublisher(); - } - -} diff --git a/src/main/java/cn/van/business/controller/BaseController.java b/src/main/java/cn/van/business/controller/BaseController.java deleted file mode 100644 index 46a5631..0000000 --- a/src/main/java/cn/van/business/controller/BaseController.java +++ /dev/null @@ -1,67 +0,0 @@ -package cn.van.business.controller; - -import cn.van.business.model.user.UserInfo; -import cn.van.business.util.JwtUtils; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.util.StringUtils; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.mvc.AbstractController; - -import java.util.Collections; -import java.util.List; - -public class BaseController extends AbstractController { - - @Autowired - private JwtUtils jwtUtils; - - // 获取当前用户信息 - protected UserInfo getCurrentUser(HttpServletRequest request) { - String token = extractToken(request); - if (token != null && jwtUtils.validateToken(token)) { - String username = jwtUtils.extractUsername(token); - // 从数据库获取用户信息 - return loadUserInfo(username); - } - return null; - } - - // 检查用户是否已登录 - protected boolean isLogin(HttpServletRequest request) { - return getCurrentUser(request) != null; - } - - // 获取当前用户ID - protected String getCurrentUserId(HttpServletRequest request) { - UserInfo user = getCurrentUser(request); - return user != null ? user.getId() : null; - } - - // 获取当前用户角色 - protected List getCurrentUserRoles(HttpServletRequest request) { - UserInfo user = getCurrentUser(request); - return user != null ? user.getRoles() : Collections.emptyList(); - } - - // 提取token - private String extractToken(HttpServletRequest request) { - String bearerToken = request.getHeader("Authorization"); - if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { - return bearerToken.substring(7); - } - return null; - } - - // 加载用户信息 - private UserInfo loadUserInfo(String username) { - // 实现用户信息加载 - return new UserInfo(); - } - - @Override - protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { - return null; - } -} diff --git a/src/main/java/cn/van/business/controller/jd/OrderController.java b/src/main/java/cn/van/business/controller/jd/OrderController.java deleted file mode 100644 index e98e7cc..0000000 --- a/src/main/java/cn/van/business/controller/jd/OrderController.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.van.business.controller.jd; - -import cn.van.business.filter.RequireAuth; -import cn.van.business.model.ApiResponse; -import cn.van.business.model.jd.ProductOrder; -import cn.van.business.mq.MessageProducerService; -import cn.van.business.repository.ProductOrderRepository; -import cn.van.business.util.JDUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; - - -/** - * @author Leo - * @version 1.0 - * @create 2024/11/7 13:39 - * @description: - */ -@RestController -@RequestMapping("/recordOrder") -@RequireAuth -public class OrderController { - - public static String TOKEN = "dd7f298cc465e7a9791796ae1303a1f6c031a18d4894ecd9e75d44fdb0ef32d80a49e4c5811b98d8ac65d73d22a54083630d5329c7c9de2276317669ecb2e544"; - @Autowired - private MessageProducerService messageProducerService; - @Autowired - private ProductOrderRepository productOrderRepository; - - @RequestMapping("/list") - @ResponseBody - public ApiResponse list() throws Exception { - try { - - List all = productOrderRepository.findAll(); - return ApiResponse.success(all); - } catch (Exception e) { - return ApiResponse.error(500, "Server Error: " + e.getMessage()); - } - } - -} diff --git a/src/main/java/cn/van/business/controller/jd/erp/TestController.java b/src/main/java/cn/van/business/controller/jd/erp/TestController.java deleted file mode 100644 index 80bebdc..0000000 --- a/src/main/java/cn/van/business/controller/jd/erp/TestController.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.van.business.controller.jd.erp; - -import cn.hutool.http.HttpRequest; -import cn.hutool.http.HttpResponse; -import cn.van.business.erp.request.AuthorizeListQueryRequest; -import cn.van.business.erp.request.ERPAccount; -import cn.van.business.erp.request.ProductListQueryRequest; -import com.alibaba.fastjson2.JSONObject; - -/** - * @author Leo - * @version 1.0 - * @create 2025/6/17 21:06 - * @description: - */ -public class TestController { - public static void main(String[] args) { - //AuthorizeListQueryRequest authorizeListQueryRequest = new AuthorizeListQueryRequest(ERPAccount.ACCOUNT_HUGE, new JSONObject()); - //String responseBody = authorizeListQueryRequest.getResponseBody(); - //System.out.println(responseBody); - System.out.println("--------------"); - ProductListQueryRequest productListQueryRequest = new ProductListQueryRequest(ERPAccount.ACCOUNT_HUGE); - JSONObject jsonObject = new JSONObject(); - jsonObject.put("page_no", 1); - jsonObject.put("page_size", 1); - productListQueryRequest.setRequestBody(jsonObject); - String responseBody1 = productListQueryRequest.getResponseBody(); - System.out.println(responseBody1); - } -} diff --git a/src/main/java/cn/van/business/controller/login/AuthController.java b/src/main/java/cn/van/business/controller/login/AuthController.java deleted file mode 100644 index cf10101..0000000 --- a/src/main/java/cn/van/business/controller/login/AuthController.java +++ /dev/null @@ -1,146 +0,0 @@ -package cn.van.business.controller.login; - -import cn.van.business.model.ApiResponse; -import cn.van.business.model.user.LoginRequest; -import cn.van.business.model.user.LoginResponse; -import cn.van.business.service.CaptchaService; -import cn.van.business.util.JwtUtils; -import cn.van.business.util.WXUtil; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.http.ResponseEntity; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.web.bind.annotation.*; - -import java.util.concurrent.TimeUnit; - -@RestController -@RequestMapping("/auth") -public class AuthController { - - private static final Logger logger = LoggerFactory.getLogger(AuthController.class); - - - private final CaptchaService captchaService; - private final JwtUtils jwtUtils; - private final AuthenticationManager authenticationManager; - private final StringRedisTemplate redisTemplate; - - public AuthController(CaptchaService captchaService, JwtUtils jwtUtils, AuthenticationManager authenticationManager, StringRedisTemplate redisTemplate) { - this.captchaService = captchaService; - this.jwtUtils = jwtUtils; - this.authenticationManager = authenticationManager; - this.redisTemplate = redisTemplate; - } - - private static final String USER_TOKEN_PREFIX = "user:token:"; - - @PostMapping("/login") - public ApiResponse login(@RequestBody LoginRequest request) { - logger.info("用户登录"); - logger.info("用户名:{}", request.getUsername()); - logger.info("密码:{}", request.getPassword()); - logger.info("验证码:{}", request.getCaptcha()); - logger.info("生成的验证码:{}", request.getGeneratedCaptcha()); - // 1. 基础校验 - if (StringUtils.isBlank(request.getUsername()) || StringUtils.isBlank(request.getPassword())) { - logger.error("用户名或密码不能为空"); - return ApiResponse.badRequest(); - } - - // 2. 验证码校验 - if (!captchaService.validateCaptcha(request.getCaptcha(), request.getGeneratedCaptcha())) { - logger.error("验证码错误"); - return ApiResponse.badRequest(); - } - // 在验证码校验后、认证前添加防爆破逻辑 - String loginFailKey = "login:fail:" + request.getUsername(); - Long failCount = redisTemplate.opsForValue().increment(loginFailKey); - if (failCount != null && failCount > 5) { - logger.error("尝试次数过多,请稍后再试"); - return ApiResponse.badRequest(); - }else { - redisTemplate.expire(loginFailKey, 5, TimeUnit.MINUTES); - } - - // 3. 用户认证(Spring Security 标准流程) - logger.info("3. 用户认证(Spring Security 标准流程)"); - try { - Authentication authentication = authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())); - } catch (Exception e) { - logger.error("用户认证失败:{}", e.getMessage()); - } - - // 4. 生成 JWT Token - logger.info("用户认证成功"); - String token = jwtUtils.generateToken(request.getUsername()); - String refreshToken = generateRefreshToken(request.getUsername()); - logger.info("生成 JWT Token:{}", token); - - // 5. 存入 Redis - saveUserToken(request.getUsername(), token, jwtUtils.getExpiration(token)); - - // 6. 构造响应 - LoginResponse response = new LoginResponse(); - response.setToken(token); - response.setRefreshToken(refreshToken); - response.setUsername(request.getUsername()); - - return ApiResponse.success(response); - } - - - @PostMapping("/logout") - public ApiResponse logout(@RequestHeader("Authorization") String token) { - String actualToken = token.startsWith("Bearer ") ? token.substring(7) : token; - String username = jwtUtils.extractUsername(actualToken); - String userKey = USER_TOKEN_PREFIX + username; - - // 从 Redis 删除用户 Token - redisTemplate.delete(userKey); - - // 添加 Token 到黑名单 - addToBlacklist(actualToken, jwtUtils.getRemainingExpirationTime(actualToken)); - - return ApiResponse.success(); - } - - - @GetMapping("/captcha") - public ApiResponse getCaptcha() throws Exception { - logger.info("获取验证码"); - String captchaImage = captchaService.generateCaptchaImage(); - return ApiResponse.success(captchaImage); - } - - /** - * 将用户 Token 存入 Redis,并设置相同有效期 - */ - private void saveUserToken(String username, String token, long expiration) { - String key = USER_TOKEN_PREFIX + username; - redisTemplate.opsForValue().set(key, token, expiration, TimeUnit.MILLISECONDS); - } - - /** - * 将 Token 加入黑名单 - */ - private void addToBlacklist(String token, long remainingTime) { - String blacklistKey = "blacklist:token:" + token; - redisTemplate.opsForValue().set(blacklistKey, "invalid", remainingTime, TimeUnit.MILLISECONDS); - } - - /** - * 生成刷新 Token(Refresh Token) - */ - private String generateRefreshToken(String username) { - return jwtUtils.generateToken(username); - } - -} diff --git a/src/main/java/cn/van/business/controller/login/CaptchaController.java b/src/main/java/cn/van/business/controller/login/CaptchaController.java deleted file mode 100644 index a3e5ad6..0000000 --- a/src/main/java/cn/van/business/controller/login/CaptchaController.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.van.business.controller.login; - -import cn.van.business.service.CaptchaService; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/captcha") -public class CaptchaController { - - private final CaptchaService captchaService; - - public CaptchaController(CaptchaService captchaService) { - this.captchaService = captchaService; - } - - @GetMapping("/generate") - public ResponseEntity generateCaptcha() throws Exception { - String base64Image = captchaService.generateCaptchaImage(); - return ResponseEntity.ok(base64Image); - } -} diff --git a/src/main/java/cn/van/business/filter/JwtAuthFilter.java b/src/main/java/cn/van/business/filter/JwtAuthFilter.java deleted file mode 100644 index 51a3190..0000000 --- a/src/main/java/cn/van/business/filter/JwtAuthFilter.java +++ /dev/null @@ -1,63 +0,0 @@ -package cn.van.business.filter; - -import cn.van.business.service.impl.UserDetailsServiceImpl; -import cn.van.business.util.JwtUtils; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; - -@Component -@RequiredArgsConstructor -public class JwtAuthFilter extends OncePerRequestFilter { - - private final JwtUtils jwtUtils; - private final UserDetailsServiceImpl userDetailsService; - - @Override - protected void doFilterInternal(@NonNull HttpServletRequest request, - @NonNull HttpServletResponse response, - @NonNull FilterChain filterChain) - throws ServletException, IOException { - try { - logger.debug("JwtAuthFilter.doFilterInternal"); - String jwt = parseJwt(request); - if (jwt != null && jwtUtils.validateToken(jwt)) { - String username = jwtUtils.extractUsername(jwt); - - UserDetails userDetails = userDetailsService.loadUserByUsername(username); - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( - userDetails, null, userDetails.getAuthorities()); - - authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - - } catch (Exception e) { - logger.error("无法认证用户: {}"); - } - - filterChain.doFilter(request, response); - } - - private String parseJwt(HttpServletRequest request) { - String headerAuth = request.getHeader("Authorization"); - - if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) { - return headerAuth.substring(7); - } - - return null; - } -} diff --git a/src/main/java/cn/van/business/filter/RequireAuth.java b/src/main/java/cn/van/business/filter/RequireAuth.java deleted file mode 100644 index 47d3d81..0000000 --- a/src/main/java/cn/van/business/filter/RequireAuth.java +++ /dev/null @@ -1,13 +0,0 @@ -package cn.van.business.filter; - -import org.springframework.security.access.prepost.PreAuthorize; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ElementType.METHOD, ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@PreAuthorize("hasAnyRole('ADMIN', 'USER')") -public @interface RequireAuth {} diff --git a/src/main/java/cn/van/business/model/pl/TaobaoComment.java b/src/main/java/cn/van/business/model/pl/TaobaoComment.java new file mode 100644 index 0000000..f926f9e --- /dev/null +++ b/src/main/java/cn/van/business/model/pl/TaobaoComment.java @@ -0,0 +1,48 @@ +package cn.van.business.model.pl; + +import jakarta.persistence.*; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * @author Van + * @version 1.0 + * @create 2025/7/6 + * @description 淘宝评论实体类 + */ +@Entity +@Table(name = "taobao_comments") +@Data +public class TaobaoComment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(name = "product_id", nullable = false, length = 255) + private String productId; + + @Column(name = "user_name", length = 255) + private String userName; + + @Lob + @Column(name = "comment_text") + private String commentText; + + @Column(name = "comment_id", length = 255, unique = false) + private String commentId; + + @Lob + @Column(name = "picture_urls") + private String pictureUrls; + + @Column(name = "created_at") + private LocalDateTime createdAt; + + @Column(name = "comment_date", length = 255) + private String commentDate; + + @Column(name = "is_use", columnDefinition = "int default 0") + private Integer isUse; +} diff --git a/src/main/java/cn/van/business/model/update-schema.sql b/src/main/java/cn/van/business/model/update-schema.sql deleted file mode 100644 index 30d8edb..0000000 --- a/src/main/java/cn/van/business/model/update-schema.sql +++ /dev/null @@ -1,18 +0,0 @@ -CREATE TABLE product_order -( - id BIGINT AUTO_INCREMENT NOT NULL, - sku_name VARCHAR(255) NULL, - sku_type INT NULL, - order_id VARCHAR(255) NULL, - order_time datetime NULL, - order_account VARCHAR(255) NULL, - is_reviewed BIT(1) NULL, - review_time datetime NULL, - is_cashback_received BIT(1) NULL, - cashback_time datetime NULL, - recipient_name VARCHAR(255) NULL, - recipient_phone VARCHAR(255) NULL, - recipient_address VARCHAR(255) NULL, - who_order VARCHAR(255) NULL, - CONSTRAINT pk_product_order PRIMARY KEY (id) -); diff --git a/src/main/java/cn/van/business/model/user/AdminUser.java b/src/main/java/cn/van/business/model/user/AdminUser.java deleted file mode 100644 index f97da64..0000000 --- a/src/main/java/cn/van/business/model/user/AdminUser.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.van.business.model.user; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import lombok.Getter; -import lombok.Setter; -import jakarta.persistence.*; - - -import java.time.LocalDateTime; - -/** - * @author Leo - * @version 1.0 - * @create 2025/6/16 10:55 - * @description: - */ -@Getter -@Setter -@Entity -@Table(name = "admin_users") -public class AdminUser { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(unique = true, nullable = false) - private String username; - - private String password; - - private String nickname; - - private String avatarUrl; - - private String roles; // 角色列表,可用逗号分隔 - - private LocalDateTime lastLoginTime; - - private LocalDateTime createTime; - - private LocalDateTime updateTime; -} - diff --git a/src/main/java/cn/van/business/model/user/LoginRequest.java b/src/main/java/cn/van/business/model/user/LoginRequest.java deleted file mode 100644 index 479ea74..0000000 --- a/src/main/java/cn/van/business/model/user/LoginRequest.java +++ /dev/null @@ -1,13 +0,0 @@ -package cn.van.business.model.user; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class LoginRequest { - private String username; - private String password; - private String captcha; // 验证码字段(如果需要) - private String generatedCaptcha; -} diff --git a/src/main/java/cn/van/business/model/user/LoginResponse.java b/src/main/java/cn/van/business/model/user/LoginResponse.java deleted file mode 100644 index 9981e0f..0000000 --- a/src/main/java/cn/van/business/model/user/LoginResponse.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.van.business.model.user; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class LoginResponse { - private String token; - private Long expiresIn; - private String refreshToken; - private UserInfo userInfo; - private String username; -} diff --git a/src/main/java/cn/van/business/model/user/UserInfo.java b/src/main/java/cn/van/business/model/user/UserInfo.java deleted file mode 100644 index 619411f..0000000 --- a/src/main/java/cn/van/business/model/user/UserInfo.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.van.business.model.user; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Getter -@Setter -public class UserInfo { - private String username; - private String nickname; - private String id; - private List roles; - private String avatarUrl; -} diff --git a/src/main/java/cn/van/business/repository/TaobaoCommentRepository.java b/src/main/java/cn/van/business/repository/TaobaoCommentRepository.java new file mode 100644 index 0000000..e5a0d5b --- /dev/null +++ b/src/main/java/cn/van/business/repository/TaobaoCommentRepository.java @@ -0,0 +1,21 @@ +package cn.van.business.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import cn.van.business.model.pl.TaobaoComment; + +import java.util.List; + +/** + * @author Van + * @version 1.0 + * @create 2025/7/6 + * @description 淘宝评论数据访问层接口 + */ +@Repository +public interface TaobaoCommentRepository extends JpaRepository { + + List findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(String productId, Integer isUse); + + List findByProductIdAndPictureUrlsIsNotNull(String productId); +} diff --git a/src/main/java/cn/van/business/service/AdminUserService.java b/src/main/java/cn/van/business/service/AdminUserService.java deleted file mode 100644 index 5be6f3a..0000000 --- a/src/main/java/cn/van/business/service/AdminUserService.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.van.business.service; - -import cn.hutool.crypto.digest.MD5; -import cn.van.business.model.user.AdminUser; -import cn.van.business.util.Util; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.stereotype.Service; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -@Service -public class AdminUserService { - - - // 模拟数据库存储 - private static final Map users = new HashMap<>(); - - static { - // 初始化一个测试用户(生产环境应从数据库加载) - AdminUser user = new AdminUser(); - user.setUsername("van"); - String password = Util.md5("LK.807878712"); - user.setPassword(new BCryptPasswordEncoder().encode(password)); - users.put("van", user); - } - - public AdminUser findByUsername(String username) { - return users.get(username); - } -} diff --git a/src/main/java/cn/van/business/service/CaptchaService.java b/src/main/java/cn/van/business/service/CaptchaService.java deleted file mode 100644 index e2ba0b6..0000000 --- a/src/main/java/cn/van/business/service/CaptchaService.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.van.business.service; - -import com.google.code.kaptcha.Producer; -import org.springframework.stereotype.Service; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; -import java.util.Base64; - -@Service -public class CaptchaService { - - private final Producer kaptchaProducer; - - public CaptchaService(Producer kaptchaProducer) { - this.kaptchaProducer = kaptchaProducer; - } - - public String generateCaptchaImage() throws Exception { - String text = kaptchaProducer.createText(); - BufferedImage image = kaptchaProducer.createImage(text); - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - ImageIO.write(image, "PNG", outputStream); - return Base64.getEncoder().encodeToString(outputStream.toByteArray()); - } - - public boolean validateCaptcha(String userInput, String generatedCaptcha) { - return userInput.equalsIgnoreCase("Succ"); - } -} diff --git a/src/main/java/cn/van/business/service/impl/UserDetailsServiceImpl.java b/src/main/java/cn/van/business/service/impl/UserDetailsServiceImpl.java deleted file mode 100644 index 1b0b18d..0000000 --- a/src/main/java/cn/van/business/service/impl/UserDetailsServiceImpl.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.van.business.service.impl; - -import cn.van.business.model.user.AdminUser; -import cn.van.business.service.AdminUserService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; - -@Service -public class UserDetailsServiceImpl implements UserDetailsService { - - @Autowired - private AdminUserService adminUserService; - - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - AdminUser user = adminUserService.findByUsername(username); - if (user == null) { - throw new UsernameNotFoundException("用户不存在"); - } - - return User.builder() - .username(user.getUsername()) - .password(user.getPassword()) - .build(); - } - -} diff --git a/src/main/java/cn/van/business/util/AuthEntryPoint.java b/src/main/java/cn/van/business/util/AuthEntryPoint.java deleted file mode 100644 index 5fd44ee..0000000 --- a/src/main/java/cn/van/business/util/AuthEntryPoint.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.van.business.util; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.stereotype.Component; - -import java.io.IOException; - -@Component -public class AuthEntryPoint implements AuthenticationEntryPoint { - - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "需要登录认证"); - } -} diff --git a/src/main/java/cn/van/business/util/JDScheduleJob.java b/src/main/java/cn/van/business/util/JDScheduleJob.java index 2f9d8b9..1be02f9 100644 --- a/src/main/java/cn/van/business/util/JDScheduleJob.java +++ b/src/main/java/cn/van/business/util/JDScheduleJob.java @@ -548,10 +548,10 @@ public class JDScheduleJob { hashOps.putAll(key, hours.stream().collect(Collectors.toMap(h -> h, h -> "1"))); } - @Scheduled(cron = "0 0 0,8 * * ?") + @Scheduled(cron = "0 0 0 * * ?") public void checkGiftCouponsExpiry() { Set keys = redisTemplate.keys("gift_coupon:*"); - if (keys == null || keys.isEmpty()) return; + if (keys.isEmpty()) return; for (String key : keys) { Map entries = redisTemplate.opsForHash().entries(key); @@ -578,6 +578,7 @@ public class JDScheduleJob { if (isAboutToExpire) { String message = String.format("[礼金提醒]\n商品:%s\n礼金Key:%s\n类型:%s\n将在 %s 过期", skuName, giftKey, "g".equals(owner) ? "自营" : "POP", expireTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))); wxUtil.sendTextMessage(WXUtil.default_super_admin_wxid, message, 1, "bot", false); + redisTemplate.delete(key); } } } diff --git a/src/main/java/cn/van/business/util/JDUtil.java b/src/main/java/cn/van/business/util/JDUtil.java index ef78133..47fe8e3 100644 --- a/src/main/java/cn/van/business/util/JDUtil.java +++ b/src/main/java/cn/van/business/util/JDUtil.java @@ -6,9 +6,11 @@ import cn.hutool.http.HttpResponse; import cn.van.business.model.jd.JDOrder; import cn.van.business.model.jd.OrderRow; import cn.van.business.model.pl.Comment; +import cn.van.business.model.pl.TaobaoComment; import cn.van.business.repository.CommentRepository; import cn.van.business.repository.JDOrderRepository; import cn.van.business.repository.OrderRowRepository; +import cn.van.business.repository.TaobaoCommentRepository; import cn.van.business.util.ds.DeepSeekClientUtil; import cn.van.business.util.ds.GPTClientUtil; import com.alibaba.fastjson2.JSON; @@ -81,7 +83,10 @@ public class JDUtil { private static final Logger logger = LoggerFactory.getLogger(JDUtil.class); private static final String INTERACTION_STATE_PREFIX = "interaction_state:"; private static final String PRODUCT_TYPE_MAP_PREFIX = "product_type_map"; + private static final String PRODUCT_TYPE_MAP_PREFIX_TB = "product_type_map_tb"; private static HashMap productTypeMap = new HashMap<>(); + private static HashMap productTypeMapTB = new HashMap<>(); + private static final String COMMENT_TEMPLATES_DS = "我需要为我的商品模拟一些商品评论。你协助我生成2条不同的评价内容,京东商品评价的风格,每条评价100字左右,要基于原来的评论稍作修改,不要更换产品类型,只需要好评。不需要太浮夸,也不要太像ai生成,尽量模拟真实客户评价,不要提到以旧换新和国家补贴,只要回复我评论的内容就可以。这个是给你参考的其他真实用户的评论:"; private static final long TIMEOUT_MINUTES = 30; private static final String WENAN_FANAN_LQD = "提供方法自己下单\n" + "全程都是自己的账号下单\n" + "标价就是下单的到手价\n" + "本人有耐心,会一步一步提供教程\n" + "以后有什么质量问题都是用自己的账号走京东售后\n" + "\n" + "更新\n" + "\n" + "用你自己的账号下单\n" + "官方店铺 提供方法自己下单\n" + "————————————————————\n" + "同行可长久合作,可提供神级家电线报\n" + "\n" + "配合家电线报可以自己下单,不用找代购和代下,订单和利润都掌握在自己手中。\n" + "\n" + "一次入会永久使用,包含家电帮,雷神价,韭菜帮,河南&湖南帮,各种暗号帮后返等内部独家家电线报\n" + "\n" + "JD采销采购不定时发放独家优惠券\n" + "\n" + "基本上你能看到的京东家电低价都是从这些渠道里面出来。\n" + "\n" + "2025年家电项目新方向,配合家电线报下单,秒省1K+。"; @@ -122,6 +127,7 @@ public class JDUtil { private final OrderRowRepository orderRowRepository; private final CommentRepository commentRepository; private JDOrderRepository jdOrderRepository; + private final TaobaoCommentRepository taobaoCommentRepository; private final OrderUtil orderUtil; private final DeepSeekClientUtil deepSeekClientUtil; @@ -136,11 +142,12 @@ public class JDUtil { // 构造函数中注入StringRedisTemplate @Autowired - public JDUtil(JDOrderRepository jdOrderRepository, StringRedisTemplate redisTemplate, OrderRowRepository orderRowRepository, WXUtil wxUtil, OrderUtil orderUtil, DeepSeekClientUtil deepSeekClientUtil, CommentRepository commentRepository, GPTClientUtil gptClientUtil) { + public JDUtil(JDOrderRepository jdOrderRepository, StringRedisTemplate redisTemplate, OrderRowRepository orderRowRepository, WXUtil wxUtil, TaobaoCommentRepository taobaoCommentRepository, OrderUtil orderUtil, DeepSeekClientUtil deepSeekClientUtil, CommentRepository commentRepository, GPTClientUtil gptClientUtil) { this.jdOrderRepository = jdOrderRepository; this.redisTemplate = redisTemplate; this.orderRowRepository = orderRowRepository; this.wxUtil = wxUtil; + this.taobaoCommentRepository = taobaoCommentRepository; this.orderUtil = orderUtil; this.deepSeekClientUtil = deepSeekClientUtil; this.commentRepository = commentRepository; @@ -149,10 +156,11 @@ public class JDUtil { } private void handleProductWithJF() { - productWithJF.put("ZQD130F-EB130", "https://u.jd.com/3GPePLu"); - productWithJF.put("ZQD130F-EB130B", "https://u.jd.com/3gPeGf6"); - productWithJF.put("ZQD180F-EB200", "https://u.jd.com/3OPe8m7"); - productWithJF.put("ZQD150F-EB150", "https://u.jd.com/3rPevYD"); + productWithJF.put("ZQD130F-EB130", "https://u.jd.com/21x8qP6"); + productWithJF.put("ZQD130F-EB130B", "https://u.jd.com/2ax86hv"); + productWithJF.put("ZQD180F-EB200", "https://u.jd.com/26x8t4M"); + productWithJF.put("ZQD150F-EB150", "https://u.jd.com/21x8M6G"); + productWithJF.put("CXW-358-E900C51", "https://u.jd.com/2gx8MvZ"); } private List filterOrdersByDate(List orderRows, int daysBack) { @@ -1772,13 +1780,19 @@ public class JDUtil { // 思路是将地址解析出来,保存到 Redis 中,并设置过期时间为 24 小时。 String address = split[5].trim(); String addressKey = "address:" + address; - if (redisTemplate.opsForValue().get(addressKey) != null) { - for (int i = 0; i < 5; i++) { - wxUtil.sendTextMessage(fromWxid, "[炸弹] [炸弹] [炸弹] 此地址已经存在,请勿重复生成订单 [炸弹] [炸弹] [炸弹] ", 1, fromWxid, false); + String addressRedisValue = redisTemplate.opsForValue().get(addressKey); + if (addressRedisValue != null) { + logger.info("address {}", addressKey); + if (addressRedisValue.contains("李波") || addressRedisValue.contains("吴胜硕") || addressRedisValue.contains("小硕硕")) { + } else { + for (int i = 0; i < 5; i++) { + wxUtil.sendTextMessage(fromWxid, "[炸弹] [炸弹] [炸弹] 此地址已经存在,请勿重复生成订单 [炸弹] [炸弹] [炸弹] ", 1, fromWxid, false); + } + return; } - return; + } - redisTemplate.opsForValue().set(addressKey, "1", 1, TimeUnit.DAYS); + redisTemplate.opsForValue().set(addressKey, address, 1, TimeUnit.DAYS); // 先一次性增加计数器,并设置到 Redis(原子性操作) int startCount = count + 1; @@ -1836,7 +1850,7 @@ public class JDUtil { //String value = entry.getValue(); productTypeStr.append(k).append("\n"); } - wxUtil.sendTextMessage(fromWxid, "请选择要生成的评论类型\n(烟灶套餐 晒图 单烟机型号,新型号联系管理员添加(970用C61的图):\n\n" + productTypeStr, 1, fromWxid, true); + wxUtil.sendTextMessage(fromWxid, "请选择要生成的评论类型\n(烟灶套餐 晒图 单烟机型号,新型号联系管理员:\n\n" + productTypeStr, 1, fromWxid, true); logger.info("进入生成评论流程 - 用户: {}", fromWxid); } catch (Exception e) { @@ -1874,6 +1888,41 @@ public class JDUtil { redisTemplate.opsForHash().delete(PRODUCT_TYPE_MAP_PREFIX, key); } + public HashMap getProductTypeMapForTB() { + Map rawMap = redisTemplate.opsForHash().entries(PRODUCT_TYPE_MAP_PREFIX_TB); + + if (!rawMap.isEmpty()) { + productTypeMapTB.clear(); + for (Map.Entry entry : rawMap.entrySet()) { + productTypeMapTB.put(entry.getKey().toString(), entry.getValue().toString()); + } + // 排序 + productTypeMapTB = productTypeMapTB.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); + return productTypeMapTB; + } else { + logger.warn("Redis 中未找到键为 {} 的 Hash 数据", PRODUCT_TYPE_MAP_PREFIX_TB); + } + return null; + } + + /** + * 根据原始的 product_id 查询淘宝映射后的 ID + * + * @param rawProductId 原始 product_id + * @return 映射后的 ID,如果不存在则返回原值 + */ + public static String getMappedProductId(String rawProductId) { + // 确保 map 已加载(可选:调用一次 getProductTypeMapForTB()) + if (productTypeMapTB == null || productTypeMapTB.isEmpty()) { + // 可以在这里自动加载(需传入 RedisTemplate 或通过 Spring 注入) + // 示例:JDUtil.getProductTypeMapForTB(); + return rawProductId; + } + + // 查找映射值 + return productTypeMapTB.getOrDefault(rawProductId, rawProductId); + } + /** * 处理生成评论流程中的用户交互 @@ -1896,16 +1945,18 @@ public class JDUtil { /** - * 生成评论内容 + * 根据商品类型生成评论内容,并优先使用京东评论,若无则使用淘宝评论 + * + * @param fromWxid 微信用户ID + * @param productType 商品类型(如:烟灶套餐、单烟机等) */ private synchronized void generateComment(String fromWxid, String productType) { - - //wxUtil.sendTextMessage(fromWxid, "已接收到评论生成指令,等候过程请勿重复输入", 1, fromWxid, true); int allCommentCount = 0; int usedCommentCount = 0; int canUseComentCount = 0; int addCommentCount = 0; - // 获取产品ID + boolean isTb = false; + getProductTypeMap(); String product_id = productTypeMap.get(productType); if (product_id == null || product_id.isEmpty()) { @@ -1913,95 +1964,167 @@ public class JDUtil { return; } - // 从数据库获取可用评论 + // 获取本地可用的京东评论 List availableComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 1); List usedComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 0); + canUseComentCount = availableComments.size(); usedCommentCount = usedComments.size(); - + allCommentCount = canUseComentCount + usedCommentCount; Comment commentToUse = null; + // 1️⃣ 先尝试使用本地可用的京东评论 if (!availableComments.isEmpty()) { - // 随机选取一个未使用的评论 Collections.shuffle(availableComments); commentToUse = availableComments.get(0); } else { - wxUtil.sendTextMessage(fromWxid, "没有本地评论,调用外部接口抓取", 1, fromWxid, true); - // 没有本地评论,调用外部接口抓取 - try { - String fetchUrl = "http://192.168.8.6:5000/fetch_comments?product_id=" + product_id; - // 用hutool发起post请求 - HttpResponse response = HttpRequest.post(fetchUrl).timeout(1000 * 60).execute(); - logger.info("fetchUrl: {}", fetchUrl); -// code = 200 表示成功,-200 表示失败 - if (response.getStatus() == 200) { - wxUtil.sendTextMessage(fromWxid, "已获取新的评论,请稍等", 1, fromWxid, true); - // ✅ 关键修改:重新从数据库中查询,而不是使用内存中的 fetchedComments - availableComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 1); - if (!availableComments.isEmpty()) { - addCommentCount = availableComments.size() - canUseComentCount; - Collections.shuffle(availableComments); - commentToUse = availableComments.get(0); + /** + * ✅ 新增逻辑:先尝试从淘宝获取评论,但前提是 productTypeMapTB 存在对应映射 + */ + getProductTypeMapForTB(); // 加载淘宝映射表 + String taobaoProductId = productTypeMapTB.getOrDefault(product_id, null); + + if (taobaoProductId != null && !taobaoProductId.isEmpty()) { + logger.info("发现淘宝映射ID,尝试从淘宝获取评论"); + wxUtil.sendTextMessage(fromWxid, "本地无可用京东评论,已发现淘宝映射ID,从淘宝获取评论", 1, null, true); + Comment taobaoComment = generateTaobaoComment(productType); + if (taobaoComment != null) { + commentToUse = taobaoComment; + isTb = true; + } + } else { + logger.info("未找到淘宝映射ID,继续使用京东评论流程"); + } + + /** + * 2️⃣ 然后尝试从京东外部接口获取新的评论 + */ + if (!isTb) { + try { + String fetchUrl = "http://192.168.8.6:5000/fetch_comments?product_id=" + product_id; + HttpResponse response = HttpRequest.post(fetchUrl).timeout(1000 * 60).execute(); + + logger.info("fetchUrl: {}", fetchUrl); + + if (response.getStatus() == 200) { + wxUtil.sendTextMessage(fromWxid, "已获取新的评论,请稍等", 1, fromWxid, true); + + availableComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 1); + if (!availableComments.isEmpty()) { + addCommentCount = availableComments.size() - canUseComentCount; + Collections.shuffle(availableComments); + commentToUse = availableComments.get(0); + } + } else if (response.getStatus() == -200) { + wxUtil.sendTextMessage(fromWxid, "请求响应:暂无新的评论。为您随机选取使用过的评论", 1, fromWxid, false); + if (!usedComments.isEmpty()) { + Collections.shuffle(usedComments); + commentToUse = usedComments.get(0); + } } - } else if (response.getStatus() == -200) { - wxUtil.sendTextMessage(fromWxid, "请求响应:暂无新的评论。为您随机选取使用过的评论", 1, fromWxid, false); - // 随机选取一个使用过的评论 - Collections.shuffle(usedComments); - commentToUse = usedComments.get(0); - } - } catch (Exception e) { - logger.error("调用外部接口获取评论失败", e); - wxUtil.sendTextMessage(fromWxid, "请求响应:响应超时(60s)。为您随机选取使用过的评论", 1, fromWxid, false); - commentToUse = usedComments.get(0); - } - } - if (commentToUse == null) { - wxUtil.sendTextMessage(fromWxid, "本地和京东均无可用的评论数据,请检查sku", 1, fromWxid, false); - return; - } - wxUtil.sendTextMessage(fromWxid, commentToUse.getCommentText(), 1, fromWxid, true); - // 发送图片(如果有) - String pictureUrls = commentToUse.getPictureUrls(); - if (pictureUrls != null && !pictureUrls.isEmpty()) { - // 使用 fastjson 将 JSON 字符串解析为 List - List urlList = JSON.parseArray(pictureUrls, String.class); - - for (String url : urlList) { - wxUtil.sendImageMessage(fromWxid, url.trim()); // 假设 sendImageMessage 支持 URL - } - } - // 调用 DeepSeek 生成新的评论内容 - StringBuilder deepSeekPrompt = new StringBuilder(COMMENT_TEMPLATES_DS + commentToUse.getCommentText()); - //String deepSeekResponse = ""; - String gptResponse = ""; - - try { - //deepSeekResponse = deepSeekClientUtil.getDeepSeekResponse(deepSeekPrompt); - List comments = commentRepository.findByProductIdAndPictureUrlsIsNotNull(product_id); - allCommentCount = comments.size(); - // 随机截取至多10个 - comments = comments.subList(0, Math.min(10, comments.size())); - for (Comment comment : comments) { - String commentText = comment.getCommentText(); - if (commentText != null && !commentText.isEmpty()) { - deepSeekPrompt.append("\n").append(commentText); + } catch (Exception e) { + logger.error("调用外部接口获取评论失败", e); + wxUtil.sendTextMessage(fromWxid, "请求响应:响应超时(60s)。为您随机选取使用过的评论", 1, fromWxid, false); + if (!usedComments.isEmpty()) { + Collections.shuffle(usedComments); + commentToUse = usedComments.get(0); + } } } - gptResponse = gptClientUtil.getGPTResponse(deepSeekPrompt.toString()); - } catch (Exception e) { - logger.error("生成评论异常 - 用户: {}", fromWxid, e); - wxUtil.sendTextMessage(fromWxid, "AI 评论生成失败", 1, fromWxid, false); } - // 发送生成的评论文本 - wxUtil.sendTextMessage(fromWxid, gptResponse, 1, fromWxid, true); - wxUtil.sendTextMessage(fromWxid, "评论统计:\n" + "型号 " + productType + "\n" + "新增:" + addCommentCount + "\n" + "已使用:" + usedCommentCount + "\n" + "可用:" + canUseComentCount + "\n" + "总数:" + allCommentCount, 1, fromWxid, true); - // 更新评论状态为已使用 - commentToUse.setIsUse(1); - commentRepository.save(commentToUse); + /** + * 🚀 如果京东本地和接口都没有,则再次尝试从淘宝评论获取(兜底逻辑) + */ + //if (commentToUse == null) { + // wxUtil.sendTextMessage(fromWxid, "京东无可用评论,尝试从淘宝评论中获取", 1, fromWxid, false); + // generateTaobaoComment(fromWxid, productType); + // return; + //} + + /** + * ✨ 发送最终评论内容及图片 + */ + if (commentToUse != null) { + wxUtil.sendTextMessage(fromWxid, commentToUse.getCommentText(), 1, fromWxid, true); + String pictureUrls = commentToUse.getPictureUrls(); + if (pictureUrls != null && !pictureUrls.isEmpty()) { + List urlList = JSON.parseArray(pictureUrls, String.class); + for (String url : urlList) { + wxUtil.sendImageMessage(fromWxid, url); + } + } + + /** + * 📝 更新评论状态为已使用(仅限数据库中的评论) + */ + if (commentToUse.getId() != null && !isTb) { + commentToUse.setIsUse(1); + commentRepository.save(commentToUse); + } + + /** + * 📊 发送统计信息 + */ + if (!isTb) { + wxUtil.sendTextMessage(fromWxid, + "京东评论统计:\n" + + "型号 " + productType + "\n" + + "新增:" + addCommentCount + "\n" + + "已使用:" + usedCommentCount + "\n" + + "可用:" + canUseComentCount + "\n" + + "总数:" + allCommentCount, 1, fromWxid, true); + } + } + + + } + + + private Comment generateTaobaoComment(String productType) { + getProductTypeMap(); // 加载京东的 productTypeMap + getProductTypeMapForTB(); // 加载淘宝的 productTypeMapTB + + String product_id = productTypeMap.get(productType); // 先查京东SKU + + if (product_id == null) { + logger.info("未找到对应的京东商品ID:{}", productType); + return null; + } + + // ✅ 在这里进行淘宝的 product_id 映射转换 + String taobaoProductId = productTypeMapTB.getOrDefault(product_id, product_id); + + // 然后使用 taobaoProductId 去查询 TaobaoComment + List taobaoComments = taobaoCommentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(taobaoProductId, 1); + logger.info("taobaoComments.size() {}", taobaoComments.size()); + if (!taobaoComments.isEmpty()) { + Collections.shuffle(taobaoComments); + TaobaoComment selected = taobaoComments.get(0); + // 将淘宝评论转换为京东评论返回 + Comment comment = new Comment(); + comment.setCommentText(selected.getCommentText()); + String pictureUrls = selected.getPictureUrls(); + if (pictureUrls != null) { + pictureUrls = pictureUrls.replace("//img.", "https://img."); + } + comment.setPictureUrls(pictureUrls); + comment.setCommentId(selected.getCommentId()); + comment.setProductId(product_id); + comment.setUserName(selected.getUserName()); + comment.setCreatedAt(selected.getCreatedAt()); + // 保存淘宝评论为已使用 + selected.setIsUse(1); + taobaoCommentRepository.save(selected); + + // 返回京东评论 + return comment; + } else { + return null; + } } diff --git a/src/main/java/cn/van/business/util/JwtUtils.java b/src/main/java/cn/van/business/util/JwtUtils.java deleted file mode 100644 index 53e0099..0000000 --- a/src/main/java/cn/van/business/util/JwtUtils.java +++ /dev/null @@ -1,106 +0,0 @@ -package cn.van.business.util; - -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.security.Keys; -import org.springframework.stereotype.Component; - -import java.util.Date; - -/** - * JWT 工具类 - * 提供 token 的生成、解析、验证和有效期管理功能 - */ -@Component -public class JwtUtils { - - private final String secret = "7b4c86d11fe2b64a5ce4d7d83de881b4afa76bf4a8bbc13ee50676c20b14c0835dda8a78a5a795b0f2201a26f758301b56ffbc52d021e158f1365d8be22fa6d7"; // 应配置在 application.yml 中 - private final long expiration = 86400000 * 7; // 24小时 - - /** - * 生成一个新的 JWT Token - * @param username 用户名 - * @param roles 用户角色(可选) - * @return 返回生成的 JWT Token 字符串 - */ -// 生成 Token 时去掉 roles 参数 - public String generateToken(String username) { - return Jwts.builder() - .setSubject(username) - .setExpiration(new Date(System.currentTimeMillis() + expiration)) - .signWith(SignatureAlgorithm.HS512, secret) - .compact(); - } - - - /** - * 从给定的 JWT Token 中提取用户名 - * - * @param token JWT Token - * @return 提取的用户名 - */ - public String extractUsername(String token) { - return Jwts.parser() - .setSigningKey(secret) - .parseClaimsJws(token) - .getBody() - .getSubject(); - } - - /** - * 获取 JWT Token 的过期时间 - * - * @param token JWT Token - * @return 返回 token 的过期时间 (Date) - */ - public Date extractExpiration(String token) { - return Jwts.parser() - .setSigningKey(secret) - .parseClaimsJws(token) - .getBody() - .getExpiration(); - } - - /** - * 验证指定的 JWT Token 是否有效 - * - * @param token JWT Token - * @return 如果 token 有效则返回 true,否则返回 false - */ - public Boolean validateToken(String token) { - return !isTokenExpired(token); - } - - - /** - * 检查 token 是否已过期 - * - * @param token JWT Token - * @return 如果已过期返回 true,否则返回 false - */ - private Boolean isTokenExpired(String token) { - return extractExpiration(token).before(new Date()); - } - - /** - * 获取 JWT Token 的剩余有效时间(毫秒) - * - * @param token JWT Token - * @return 剩余的有效时间(毫秒) - */ - public long getRemainingExpirationTime(String token) { - long now = System.currentTimeMillis(); - return Math.max(0, extractExpiration(token).getTime() - now); - } - - /** - * 获取 JWT Token 的过期时间(毫秒) - * - * @param token JWT Token - * @return 返回 token 的过期时间戳(毫秒) - */ - public long getExpiration(String token) { - return extractExpiration(token).getTime(); - } - -} diff --git a/src/main/java/cn/van/business/util/OtherUtil.java b/src/main/java/cn/van/business/util/OtherUtil.java new file mode 100644 index 0000000..3f33dfb --- /dev/null +++ b/src/main/java/cn/van/business/util/OtherUtil.java @@ -0,0 +1,80 @@ +package cn.van.business.util; + +import cn.hutool.http.HttpRequest; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import org.springframework.stereotype.Component; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Leo + * @version 1.0 + * @create 2025/7/8 21:13 + * @description: + */ +@Component +public class OtherUtil { + final WXUtil wxUtil; + + public OtherUtil(WXUtil wxUtil) { + this.wxUtil = wxUtil; + } + + public void tmyk(String msg, String fromWxid) { + // 正则表达式:匹配pagepath标签中的tid参数值 + // 解释: + // 匹配CDATA结束标签 + String regex = ""; + + // 编译正则表达式,忽略大小写(防止标签大小写不一致) + Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(msg); + + // 查找匹配项 + if (matcher.find()) { + // 返回第一个捕获组(即tid的值) + String tid = matcher.group(1); + // 构建post请求 url:https://tm.wx.hackp.net/App/zm/getlist,参数:tid=tid放在body + String url = "https://tm.wx.hackp.net/App/zm/getlist"; + String response = HttpRequest.post(url) + .body("tid=" + tid) + .execute().body(); + if (Util.isNotEmpty(response)) { + JSONObject jsonObject = JSON.parseObject(response); + /** + * { + * "code": "1", + * "msg": { + * "id": "1987", + * "type": "3", + * "tid": "52", + * "hot": "2", + * "title": "头条号项目玩法,从账号注册,素材获取到视频制作发布和裂变全方位教学 ", + * "picname": "https://tm.wx.hackp.net/data/attachment/temp/202111/07/130031qb5lw95b259ehlnn.jpg_thumb.jpg", + * "content": "1.项目玩法介绍mp4
\r\n
\r\n2.账号注册.mp4
\r\n
\r\n3.素材获取mp4
\r\n
\r\n4.视频批星制作.mp4
\r\n
\r\n5.视频发布.mp4
\r\n
\r\n6.视频裂变.mp4", + * "count": "10122", + * "dizhi": "下载地址:链接: https://pan.baidu.com/s/104YEJVgTx4ZSNVJjw7n9oQ提取码: 92kr", + * "price": "0", + * "addtime": "1636261305", + * "status": "1", + * "hackp_id": "36657", + * "buygoods": null + * } + * } + * */ + JSONObject msgResponse = jsonObject.getJSONObject("msg"); + // title和dizhi + wxUtil.sendTextMessage(fromWxid, msgResponse.getString("title") + "\n" + msgResponse.getString("dizhi"), 0, null, true); + } else { + wxUtil.sendTextMessage(fromWxid, "请求失败", 0, null, true); + } + } else { + wxUtil.sendTextMessage(fromWxid, "没有匹配到tid", 0, null, true); + } + } +} diff --git a/src/main/java/cn/van/business/util/WXUtil.java b/src/main/java/cn/van/business/util/WXUtil.java index 27a7a91..7f7a90d 100644 --- a/src/main/java/cn/van/business/util/WXUtil.java +++ b/src/main/java/cn/van/business/util/WXUtil.java @@ -199,6 +199,10 @@ public class WXUtil { jdidToWxidMap.put(admin13.getUnionId(), admin13.getWxid()); jdidToRemarkMap.put(admin13.getUnionId(), admin13.getName()); + /**/ + SuperAdmin admin14 = new SuperAdmin("wxid_m5ibcpe0ukw521", "小怪兽", "", "", ""); + super_admins.put(admin14.getWxid() + admin14.getUnionId(), admin14); + /* 内部管理群 */ diff --git a/src/main/java/cn/van/business/util/WxMessageConsumer.java b/src/main/java/cn/van/business/util/WxMessageConsumer.java index 8570f9f..fe5b885 100644 --- a/src/main/java/cn/van/business/util/WxMessageConsumer.java +++ b/src/main/java/cn/van/business/util/WxMessageConsumer.java @@ -26,9 +26,12 @@ public class WxMessageConsumer { private final JDUtil jdUtils; + private final OtherUtil otherUtil; + @Autowired - public WxMessageConsumer(@Lazy JDUtil jdUtils) { + public WxMessageConsumer(@Lazy JDUtil jdUtils, OtherUtil otherUtil) { this.jdUtils = jdUtils; + this.otherUtil = otherUtil; } @Async("threadPoolTaskExecutor") @@ -49,7 +52,7 @@ public class WxMessageConsumer { * * @param wxMessage */ - private void handlePrivateMessage(WxMessage wxMessage) throws Exception { + private void handlePrivateMessage(WxMessage wxMessage) { logger.info("处理私聊消息: {}", JSON.toJSONString(wxMessage)); WxMessage.DataSection data = wxMessage.getData(); @@ -77,6 +80,10 @@ public class WxMessageConsumer { jdUtils.sendOrderToWxByOrderD(msg.replace("单", ""), fromWxid); return; } + if (msg.contains("唐门云课")){ + logger.info("消息来自唐门云课,处理指令消息" ); + otherUtil.tmyk(msg,fromWxid); + } logger.info("未命中前置指令,开始命中 Default 流程"); jdUtils.sendOrderToWxByOrderDefault(msg, fromWxid); }