Selaa lähdekoodia

Merge remote-tracking branch 'origin/master' into lih

# Conflicts:
#	yt-agent/agent-service/src/main/java/com/ytpm/config/feign/FeignBuilderConfig.java
#	yt-agent/agent-service/src/main/java/com/ytpm/handle/DynamicFeignClientFactory.java
#	yt-agent/agent-service/src/main/java/com/ytpm/utils/FeignClientInvoker.java
#	yt-middle/middle-platform/src/main/java/com/ytpm/middle/config/FeignBuilderConfig.java
#	yt-middle/middle-platform/src/main/java/com/ytpm/middle/handle/DynamicFeignClientFactory.java
#	yt-middle/middle-platform/src/main/java/com/ytpm/middle/util/FeignClientInvoker.java
#	yt-risk/risk-manage/src/main/java/com/ytpm/config/feign/FeignBuilderConfig.java
#	yt-risk/risk-manage/src/main/java/com/ytpm/service/impl/RiskServiceImpl.java
#	yt-risk/risk-manage/src/main/java/com/ytpm/util/FeignClientInvoker.java
hidewnd 1 viikko sitten
vanhempi
commit
b2b0ecc5d8
24 muutettua tiedostoa jossa 400 lisäystä ja 109 poistoa
  1. 24 0
      ReadMe.md
  2. 8 3
      yt-agent/agent-service/src/main/java/com/ytpm/config/feign/FeignBuilderConfig.java
  3. 10 2
      yt-agent/agent-service/src/main/java/com/ytpm/utils/FeignClientInvoker.java
  4. 2 0
      yt-common/src/main/java/com/ytpm/app/view/WxDefaultConfig.java
  5. 9 0
      yt-ios-lemon/lemon-ios-feign/src/main/java/com/ytpm/lemonios/feign/feign/LineFunIosFeign.java
  6. 9 0
      yt-ios-lemon/lemon-ios-feign/src/main/java/com/ytpm/lemonios/feign/feign/MaintainIosFeign.java
  7. 1 1
      yt-ios-lemon/lemon-ios-service/pom.xml
  8. 2 2
      yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/LemonIosApplication.java
  9. 29 0
      yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/config/JacksonConfig.java
  10. 19 1
      yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/config/YtWebMvcConfigurerAdapter.java
  11. 7 5
      yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/config/interceptor/HttpInterceptor.java
  12. 11 9
      yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/controller/WxController.java
  13. 2 2
      yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/dao/AppUserMapper.java
  14. 5 0
      yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/service/AppUserService.java
  15. 109 58
      yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/service/impl/AdServiceImpl.java
  16. 14 5
      yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/service/impl/AppUserServiceImpl.java
  17. 3 3
      yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/service/impl/QuestionServiceImpl.java
  18. 42 5
      yt-ios-lemon/lemon-ios-service/src/main/resources/mapper/AppUserMapper.xml
  19. 9 2
      yt-middle/middle-platform/src/main/java/com/ytpm/middle/config/FeignBuilderConfig.java
  20. 9 3
      yt-middle/middle-platform/src/main/java/com/ytpm/middle/util/FeignClientInvoker.java
  21. 9 2
      yt-risk/risk-manage/src/main/java/com/ytpm/config/feign/FeignBuilderConfig.java
  22. 52 0
      yt-risk/risk-manage/src/main/java/com/ytpm/handle/DynamicFeignClientFactory.java
  23. 5 4
      yt-risk/risk-manage/src/main/java/com/ytpm/service/impl/RiskServiceImpl.java
  24. 10 2
      yt-risk/risk-manage/src/main/java/com/ytpm/util/FeignClientInvoker.java

+ 24 - 0
ReadMe.md

@@ -782,6 +782,30 @@ nohup java -jar -Xms3072m -Xmx3072m -XX:MaxMetaspaceSize=256M -XX:+UseCompressed
 warehouseios-service.jar > warehouseios.log 2>&1 &
 ```
 
+#### maintainios maintainios
+
+```shell
+# maintainios maintainios
+# 10.206.16.10
+nohup java -jar -Xms3072m -Xmx3072m -XX:MaxMetaspaceSize=256M -XX:+UseCompressedOops \
+-XX:+UseG1GC -XX:ConcGCThreads=2 -XX:InitiatingHeapOccupancyPercent=35 \
+-XX:G1ReservePercent=10 -XX:MaxGCPauseMillis=300 \
+-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/www/app/ytpm/service-ios-maintain/maintainios.hprof \
+maintainios-service.jar > maintainios.log 2>&1 &
+```
+
+#### maintainios maintainios
+
+```shell
+# linefunios linefunios
+# 10.206.16.10
+nohup java -jar -Xms3072m -Xmx3072m -XX:MaxMetaspaceSize=256M -XX:+UseCompressedOops \
+-XX:+UseG1GC -XX:ConcGCThreads=2 -XX:InitiatingHeapOccupancyPercent=35 \
+-XX:G1ReservePercent=10 -XX:MaxGCPauseMillis=300 \
+-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/www/app/ytpm/service-ios-linefun/lemonios.hprof \
+lemonios-service.jar > lemonios.log 2>&1 &
+```
+
 
 #### 速算大比拼 quick
 

+ 8 - 3
yt-agent/agent-service/src/main/java/com/ytpm/config/feign/FeignBuilderConfig.java

@@ -1,10 +1,10 @@
 package com.ytpm.config.feign;
 
-
 import feign.Feign;
 import feign.Request;
 import feign.Retryer;
 import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
 import org.springframework.cloud.openfeign.support.SpringDecoder;
 import org.springframework.cloud.openfeign.support.SpringEncoder;
@@ -22,6 +22,11 @@ import java.util.concurrent.TimeUnit;
  */
 @Configuration
 public class FeignBuilderConfig {
+    @Value("${feign.client.config.default.connectTimeout:3000}")
+    private Integer connectTimeoutMillis;
+
+    @Value("${feign.client.config.default.readTimeout:3000}")
+    private Integer readTimeoutMillis;
 
     @Bean
     @Scope("prototype")
@@ -31,8 +36,8 @@ public class FeignBuilderConfig {
                 .encoder(new SpringEncoder(messageConverters))
                 .decoder(new SpringDecoder(messageConverters))
                 .retryer(new Retryer.Default(100, TimeUnit.SECONDS.toMicros(1), 3))
-                .options(new Request.Options(3000, 3000))
+                .options(new Request.Options(connectTimeoutMillis, readTimeoutMillis))
                 .requestInterceptor(new FeignConfiguration());
 
     }
-}
+}

+ 10 - 2
yt-agent/agent-service/src/main/java/com/ytpm/utils/FeignClientInvoker.java

@@ -22,7 +22,6 @@ public class FeignClientInvoker {
     private final ApplicationContext applicationContext;
     private final Map<String, Object> feignClientCache = new HashMap<>();
     private final Map<String, Method> methodCache = new HashMap<>();
-
     @Resource
     private DynamicFeignClientFactory feignClientFactory;
 
@@ -53,6 +52,7 @@ public class FeignClientInvoker {
     }
 
     private Object getFeignClient(String serviceName) {
+        // 优先从缓存获取
         if (feignClientCache.containsKey(serviceName)) {
             return feignClientCache.get(serviceName);
         }
@@ -70,15 +70,23 @@ public class FeignClientInvoker {
             }
             // 构建动态FeignClient
             if (feignClient == null) {
-                feignClient = feignClientFactory.getClient(serviceName, BaseFeign.class);
+                Class<?> clazz = com.ytpm.question.base.BaseFeign.class;
+                if (serviceName.contains("ios")) {
+                    clazz = com.ytpm.lemonios.feign.base.BaseFeign.class;
+                }
+                feignClient = feignClientFactory.getClient(serviceName, clazz);
             }
             feignClientCache.put(serviceName, feignClient);
         } catch (Exception e) {
             throw new IllegalArgumentException("未找到服务: " + serviceName);
         }
+        if (feignClient == null) {
+            throw new IllegalArgumentException("未找到服务: " + serviceName);
+        }
         return feignClient;
     }
 
+
     private Method getTargetMethod(Object feignClient, String methodName, Object[] args) 
         throws NoSuchMethodException {
         

+ 2 - 0
yt-common/src/main/java/com/ytpm/app/view/WxDefaultConfig.java

@@ -38,6 +38,8 @@ public class WxDefaultConfig {
     private String takuRewardPid;
     @ApiModelProperty("taku插屏")
     private String takuInterstitialPid;
+    @ApiModelProperty("taku插屏")
+    private String takuScreenPid;
     @ApiModelProperty("是否允许root")
     private Integer canUseRoot;
     @ApiModelProperty("是否允许adb")

+ 9 - 0
yt-ios-lemon/lemon-ios-feign/src/main/java/com/ytpm/lemonios/feign/feign/LineFunIosFeign.java

@@ -0,0 +1,9 @@
+package com.ytpm.lemonios.feign.feign;
+
+import com.ytpm.lemonios.feign.base.BaseFeign;
+import org.springframework.cloud.openfeign.FeignClient;
+
+@FeignClient(name = "linefunios-service")
+public interface LineFunIosFeign extends BaseFeign {
+
+}

+ 9 - 0
yt-ios-lemon/lemon-ios-feign/src/main/java/com/ytpm/lemonios/feign/feign/MaintainIosFeign.java

@@ -0,0 +1,9 @@
+package com.ytpm.lemonios.feign.feign;
+
+import com.ytpm.lemonios.feign.base.BaseFeign;
+import org.springframework.cloud.openfeign.FeignClient;
+
+@FeignClient(name = "maintainios-service")
+public interface MaintainIosFeign extends BaseFeign {
+
+}

+ 1 - 1
yt-ios-lemon/lemon-ios-service/pom.xml

@@ -81,7 +81,7 @@
                 </executions>
                 <configuration>
                     <includeSystemScope>true</includeSystemScope>
-                    <mainClass>com.ytpm.lemonios.LemoniosApplication</mainClass>
+                    <mainClass>com.ytpm.lemonios.LemonIosApplication</mainClass>
                 </configuration>
             </plugin>
         </plugins>

+ 2 - 2
yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/LemoniosApplication.java → yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/LemonIosApplication.java

@@ -17,10 +17,10 @@ import org.springframework.web.bind.annotation.RestController;
 })
 @EnableDiscoveryClient
 @EnableFeignClients(basePackages = {"com.ytpm.feign"})
-public class LemoniosApplication
+public class LemonIosApplication
 {
     public static void main( String[] args )
     {
-        SpringApplication.run(LemoniosApplication.class, args);
+        SpringApplication.run(LemonIosApplication.class, args);
     }
 }

+ 29 - 0
yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/config/JacksonConfig.java

@@ -0,0 +1,29 @@
+package com.ytpm.lemonios.config;
+
+
+import com.ytpm.handle.MultiFormatDateDeserializer;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Date;
+
+/**
+ * @author lih
+ * @date 2025-10-15 10:22
+ */
+@Configuration
+public class JacksonConfig {
+
+    @Value("${spring.jackson.time-zone:Asia/Shanghai}")
+    private String timeZone;
+
+    @Bean
+    public Jackson2ObjectMapperBuilderCustomizer customJacksonConfig() {
+        return builder -> {
+            // 注册Date类型的序列化器和反序列化器
+            builder.deserializerByType(Date.class, new MultiFormatDateDeserializer(timeZone));
+        };
+    }
+}

+ 19 - 1
yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/config/YtWebMvcConfigurerAdapter.java

@@ -1,19 +1,37 @@
 package com.ytpm.lemonios.config;
 
 import com.ytpm.lemonios.config.interceptor.HttpInterceptor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+
 /**
  * 自定义拦截器
  */
+@Slf4j
 @Configuration
 public class YtWebMvcConfigurerAdapter implements WebMvcConfigurer {
 
+    @Resource
+    private HttpInterceptor httpInterceptor;
+
+    @Value("${spring.application.name:}")
+    private String applicationName;
 
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
-        registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/**").excludePathPatterns("/resources/**");
+        registry.addInterceptor(httpInterceptor).
+                addPathPatterns("/**")
+                .excludePathPatterns("/resources/**");
+    }
+
+    @PostConstruct
+    private void handlePostConstruct() {
+        log.info("current service: {}", applicationName);
     }
 }

+ 7 - 5
yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/config/interceptor/HttpInterceptor.java

@@ -1,5 +1,6 @@
 package com.ytpm.lemonios.config.interceptor;
 
+import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
@@ -18,11 +19,12 @@ public class HttpInterceptor implements HandlerInterceptor {
     private String applicationNameZh;
 
     @Override
-    public boolean preHandle(HttpServletRequest request,
-                             HttpServletResponse response, Object obj) throws Exception {
+    public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
+                             @NonNull Object obj) {
         // 获取客户端IP地址
         String clientIp = getClientIp(request);
-        log.info("{}收到来自客户端[{}]的用户请求", applicationNameZh, clientIp);
+        String requestURI = request.getRequestURI();
+        log.info("{}收到来自客户端[{}]的用户请求:{}", applicationNameZh, clientIp, requestURI);
         return true;
     }
 
@@ -38,8 +40,8 @@ public class HttpInterceptor implements HandlerInterceptor {
      * 请求处理之后调用;在视图渲染之前,controller处理之后。
      */
     @Override
-    public void postHandle(HttpServletRequest request,
-                           HttpServletResponse response, Object obj, ModelAndView mv)
+    public void postHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
+                           @NonNull Object obj, ModelAndView mv)
             throws Exception {
         response.setDateHeader("Expires", 0);
         response.setHeader("Buffer", "True");

+ 11 - 9
yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/controller/WxController.java

@@ -253,6 +253,7 @@ public class WxController {
         return loginResult;
     }
 
+    @Deprecated
     @ApiOperation("获取微信默认配置项")
     @GetMapping("/defaultConfig")
     public Result<WxDefaultConfig> getWxDefaultConfig(int appType) {
@@ -266,15 +267,16 @@ public class WxController {
     @ApiOperation("体力增加")
     @GetMapping("/addPower")
     @Transactional(rollbackFor = Exception.class)
-    public Result<IosPowerResView> addPower(@RequestParam("userId")String userId) {
-        appUserMapper.addOnePower(userId);
-        YtDyzPowerRecord record = new YtDyzPowerRecord();
-        record.setUserId(userId);
-        record.setRecordId(IdUtil.fastSimpleUUID());
-        record.setAddTime(new Date());
-        record.setType(1);
-        record.setRemark("增加体力");
-        appUserMapper.addPowerRecord(record);
+    @Deprecated
+    public Result<IosPowerResView> addPower(@RequestParam("userId") String userId) {
+//        appUserMapper.addOnePower(userId);
+//        YtDyzPowerRecord record = new YtDyzPowerRecord();
+//        record.setUserId(userId);
+//        record.setRecordId(IdUtil.fastSimpleUUID());
+//        record.setAddTime(new Date());
+//        record.setType(1);
+//        record.setRemark("增加体力");
+//        appUserMapper.addPowerRecord(record);
         YtDyzUser user = appUserMapper.selectById(userId);
         IosPowerResView view = new IosPowerResView();
         view.setPower(user.getPower());

+ 2 - 2
yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/dao/AppUserMapper.java

@@ -1,10 +1,8 @@
 package com.ytpm.lemonios.dao;
 
-import com.ytpm.agent.param.AdRecordListParam;
 import com.ytpm.agent.param.AuditUserParam;
 import com.ytpm.agent.view.AgentAuditCheckVO;
 import com.ytpm.app.model.YtAppDefaultConfig;
-import com.ytpm.app.model.YtDyzAdRecord;
 import com.ytpm.app.model.YtDyzPowerRecord;
 import com.ytpm.app.model.YtDyzUser;
 import com.ytpm.app.param.AppUserParam;
@@ -231,4 +229,6 @@ public interface AppUserMapper {
     List<AgentAuditCheckVO> queryTodayUserAd(AuditUserParam auditParam);
 
     List<YtDyzUser> getMonthRegistryUser(@Param("appIds") String appIds, @Param("type") Integer type);
+
+    void updateTotal(@Param("userId") String userId, @Param("videoCount") int videoCount, @Param("revenue") BigDecimal revenue);
 }

+ 5 - 0
yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/service/AppUserService.java

@@ -26,4 +26,9 @@ public interface AppUserService {
     YtDyzUser crudForNewTransIos(IosLoginParam param, IosUserInfo userInfo, String loginType);
 
     Result<?> addDefaultConfig(YtDitch param);
+
+    /**
+     * 新增体力
+     */
+    void addPower(String userId);
 }

+ 109 - 58
yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/service/impl/AdServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSONObject;
 import com.ytpm.advertise.enums.AdPlatformTypeEnum;
 import com.ytpm.advertise.enums.AdSourceTypeEnum;
 import com.ytpm.agent.enums.UserStatusEnum;
@@ -20,7 +21,15 @@ import com.ytpm.general.StatusCode;
 import com.ytpm.lemonios.dao.AdRecordMapper;
 import com.ytpm.lemonios.dao.AppUserMapper;
 import com.ytpm.lemonios.service.AdService;
-import com.ytpm.middle.view.*;
+import com.ytpm.lemonios.service.AppUserService;
+import com.ytpm.middle.view.AppRankingListVO;
+import com.ytpm.middle.view.AppRevenueHourVO;
+import com.ytpm.middle.view.AppUserHourVO;
+import com.ytpm.middle.view.DashboardAppRevenueVO;
+import com.ytpm.middle.view.DashboardRankingListVO;
+import com.ytpm.middle.view.DashboardRevenueVO;
+import com.ytpm.middle.view.DashboardRiskVO;
+import com.ytpm.middle.view.UserRankingListVO;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -29,11 +38,19 @@ import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.annotation.Resource;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
 import java.time.LocalDateTime;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.stream.Collectors;
 
@@ -54,6 +71,9 @@ public class AdServiceImpl implements AdService {
     @Value("${yt.ios.appid:}")
     private String appId;
 
+    @Resource
+    private AppUserService appUserService;
+
     /**
      * 保存广告记录
      */
@@ -61,41 +81,100 @@ public class AdServiceImpl implements AdService {
     @Transactional(rollbackFor = Exception.class)
     public Result<?> saveRecord(DyzAdRecordParam param) {
         if (StrUtil.isEmpty(param.getUserId())) {
-            YtDyzAdRecord adRecord = new YtDyzAdRecord();
-            if(Objects.isNull(param.getBeginTime()) || "null".equals(param.getBeginTime())){
-                if ("null".equals(param.getBeginTime())) {
-                    log.warn("param beginTime is null !");
-                }
-                param.setBeginTime(param.getFinishTime());
+            // 登陆前保存广告(游客登陆)
+            saveRecordAndChangeUser(param, null);
+            return Result.resultOk(RepMessage.SAVE_SUCCESS);
+        }
+        // 登陆后保存广告
+        YtDyzUser user = appUserMapper.selectPrimaryKey(param.getUserId());
+        if (Objects.isNull(user)) {
+            return Result.resultOk(RepMessage.SAVE_SUCCESS);
+        }
+        if (!UserStatusEnum.NORMAL.getCode().equals(user.getUserStatus())) {
+            return new Result<>(StatusCode.ACCESS_ERR, getTipsMsg());
+        }
+        //增加广告记录
+        saveRecordAndChangeUser(param, user);
+        //调用风控广告校验
+        if(AdSourceTypeEnum.rewarded_video.getAdSourceType() == param.getAdSourceType()){
+            appUserService.addPower(user.getUserId());
+            Result<?> result = riskFeign.checkAdRisk(user);
+            if(result.getCode()!=200){
+                return new Result<>(StatusCode.ACCESS_ERR, getTipsMsg());
+            }
+        }
+        return Result.resultOk(RepMessage.SAVE_SUCCESS);
+    }
+
+    /**
+     * 统一处理入参时间
+     */
+    private void handleParam(DyzAdRecordParam param){
+        String timeFormat = "yyyy-MM-dd HH:mm:ss";
+        if (param.getBegintimestamp() != null && param.getFinishtimestamp() != null) {
+            // 处理 begintimestamp(兼容秒级和毫秒级)
+            long beginTimestamp = param.getBegintimestamp();
+            if (String.valueOf(beginTimestamp).length() == 10) { // 秒级时间戳(10位)
+                beginTimestamp *= 1000; // 转为毫秒级
+            }
+            param.setBeginTime(DateUtil.format(new Date(beginTimestamp), timeFormat));
+
+            // 处理 finishtimestamp(兼容秒级和毫秒级)
+            long finishTimestamp = param.getFinishtimestamp();
+            if (String.valueOf(finishTimestamp).length() == 10) { // 秒级时间戳(10位)
+                finishTimestamp *= 1000; // 转为毫秒级
             }
-            BeanUtils.copyProperties(param, adRecord);
-            if (param.getBegintimestamp() != null && param.getFinishtimestamp() != null) {
-                adRecord.setBeginTime(DateUtil.format(new Date(param.getBegintimestamp()), "yyyy-MM-dd HH:mm:ss"));
-                adRecord.setFinishTime(DateUtil.format(new Date(param.getFinishtimestamp()), "yyyy-MM-dd HH:mm:ss"));
+            param.setFinishTime(DateUtil.format(new Date(finishTimestamp), timeFormat));
+        }
+        if (StrUtil.isEmpty(param.getFinishTime())) {
+            param.setFinishTime(DateUtil.format(new Date(), timeFormat));
+        }
+        if(Objects.isNull(param.getBeginTime()) || "null".equals(param.getBeginTime())){
+            if ("null".equals(param.getBeginTime())) {
+                log.warn("param beginTime is null !");
             }
-            adRecord.setIosId(param.getIosId());
+            param.setBeginTime(param.getFinishTime());
+        }
+        // ecpm 值统一解析json获取
+        if (StrUtil.isNotEmpty(param.getResultJson())) {
+            JSONObject resultJson = JSONObject.parseObject(param.getResultJson());
+            String ecpmStr = resultJson.getString("adsource_price");
+            if (StrUtil.isNotEmpty(ecpmStr)) {
+                param.setEcpm(new BigDecimal(ecpmStr));
+            }
+        }
+    }
+
+    /**
+     * 保存记录
+     *  始终创建新的事务以保障子方法的独立事务
+     */
+//    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
+    public void saveRecordAndChangeUser(DyzAdRecordParam param, YtDyzUser user) {
+        //增加广告记录
+        //修改用户信息, 广告次数+1  总收益 + revenue
+//        YtDyzUser dyzUser = new YtDyzUser();
+//        dyzUser.setUserId(user.getUserId());
+//        dyzUser.setTotalVideo(Objects.isNull(user.getTotalVideo())?1:(user.getTotalVideo()+1));
+//        dyzUser.setTotalIncome(user.getTotalIncome().add(param.getRevenue()));
+//        appUserMapper.updateUser(dyzUser);
+        YtDyzAdRecord adRecord = new YtDyzAdRecord();
+        handleParam(param);
+        BeanUtils.copyProperties(param, adRecord);
+        adRecord.setRecordId(IdUtil.fastSimpleUUID());
+        adRecord.setIosId(param.getIosId());
+        adRecord.setNetworkName(AdPlatformTypeEnum.getDesc(Integer.parseInt(param.getNetworkFormId())));
+        if (user == null) {
             adRecord.setAppId(appId);
-            adRecord.setRecordId(IdUtil.fastSimpleUUID());
-            adRecord.setNetworkName(AdPlatformTypeEnum.getDesc(Integer.parseInt(param.getNetworkFormId())));
             adRecordMapper.addOneVisitor(adRecord);
         } else {
-            YtDyzUser user = appUserMapper.selectPrimaryKey(param.getUserId());
-            if(!UserStatusEnum.NORMAL.getCode().equals(user.getUserStatus())){
-                return new Result<>(StatusCode.ACCESS_ERR,getTipsMsg());
-            }
-            if(Objects.isNull(user)){
-                return Result.resultOk(RepMessage.SAVE_SUCCESS);
-            }
-            saveRecordAndChangeUser(param, user);
-            //调用风控广告校验
-            if(AdSourceTypeEnum.rewarded_video.getAdSourceType() == param.getAdSourceType()){
-                Result<?> result = riskFeign.checkAdRisk(user);
-                if(result.getCode()!=200){
-                    return new Result<>(StatusCode.ACCESS_ERR, getTipsMsg());
-                }
+            adRecord.setUserId(user.getUserId());
+            adRecordMapper.addOne(adRecord);
+            if (param.getRevenue() == null) {
+                param.setRevenue(BigDecimal.ZERO);
             }
+            appUserMapper.updateTotal(user.getUserId(), 1, param.getRevenue());
         }
-        return Result.resultOk(RepMessage.SAVE_SUCCESS);
     }
 
     private String getTipsMsg(){
@@ -294,32 +373,4 @@ public class AdServiceImpl implements AdService {
         return vos;
     }
 
-    /**
-     * 保存记录
-     *  始终创建新的事务以保障子方法的独立事务
-     */
-//    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
-    public void saveRecordAndChangeUser(DyzAdRecordParam param,YtDyzUser user) {
-        //增加广告记录
-        YtDyzAdRecord adRecord = new YtDyzAdRecord();
-        if(Objects.isNull(param.getBeginTime())){
-            param.setBeginTime(param.getFinishTime());
-        }
-        BeanUtils.copyProperties(param, adRecord);
-        if (param.getBegintimestamp() != null && param.getFinishtimestamp() != null) {
-            adRecord.setBeginTime(DateUtil.format(new Date(param.getBegintimestamp()), "yyyy-MM-dd HH:mm:ss"));
-            adRecord.setFinishTime(DateUtil.format(new Date(param.getFinishtimestamp()), "yyyy-MM-dd HH:mm:ss"));
-        }
-        adRecord.setIosId(param.getIosId());
-        adRecord.setRecordId(IdUtil.fastSimpleUUID());
-        adRecord.setUserId(user.getUserId());
-        adRecord.setNetworkName(AdPlatformTypeEnum.getDesc(Integer.parseInt(param.getNetworkFormId())));
-        adRecordMapper.addOne(adRecord);
-        //修改用户信息, 广告次数+1  总收益 + revenue
-        YtDyzUser dyzUser = new YtDyzUser();
-        dyzUser.setUserId(user.getUserId());
-        dyzUser.setTotalVideo(Objects.isNull(user.getTotalVideo())?1:(user.getTotalVideo()+1));
-        dyzUser.setTotalIncome(user.getTotalIncome().add(param.getRevenue()));
-        appUserMapper.updateUser(dyzUser);
-    }
 }

+ 14 - 5
yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/service/impl/AppUserServiceImpl.java

@@ -6,10 +6,7 @@ import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.StrUtil;
 import com.ytpm.agent.enums.UserStatusEnum;
 import com.ytpm.agent.model.YtDitch;
-import com.ytpm.app.model.YtAppDefaultConfig;
-import com.ytpm.app.model.YtDyzAnswerRecord;
-import com.ytpm.app.model.YtDyzLoginRecord;
-import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.model.*;
 import com.ytpm.app.param.IosLoginParam;
 import com.ytpm.app.param.WxLoginParam;
 import com.ytpm.app.view.IosUserInfo;
@@ -121,6 +118,18 @@ public class AppUserServiceImpl implements AppUserService {
         return Result.resultOk(RepMessage.ADD_SUCCESS);
     }
 
+    @Override
+    public void addPower(String userId) {
+        appUserMapper.addOnePower(userId);
+        YtDyzPowerRecord record = new YtDyzPowerRecord();
+        record.setUserId(userId);
+        record.setRecordId(IdUtil.fastSimpleUUID());
+        record.setAddTime(new Date());
+        record.setType(2);
+        record.setRemark("增加体力");
+        appUserMapper.addPowerRecord(record);
+    }
+
     /**
      * 设置扩展信息
      */
@@ -144,8 +153,8 @@ public class AppUserServiceImpl implements AppUserService {
         List<YtDyzAnswerRecord> recordList = questionMapper.getAnswerRecords(old.getUserId());
         List<String> viewList  = new ArrayList<>();
         int count = recordList.size();
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
         for (YtDyzAnswerRecord ytDyzAnswerRecord : recordList) {
-            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
             String timeStr = count + ":" + sdf.format(ytDyzAnswerRecord.getAnswerTime());
             count--;
             viewList.add(timeStr);

+ 3 - 3
yt-ios-lemon/lemon-ios-service/src/main/java/com/ytpm/lemonios/service/impl/QuestionServiceImpl.java

@@ -56,9 +56,9 @@ public class QuestionServiceImpl implements QuestionService {
         if(!UserStatusEnum.NORMAL.getCode().equals(user.getUserStatus())){
             return new Result<>(StatusCode.ACCESS_ERR,"当前用户处于风控中");
         }
-        if (user.getPower() == null || user.getPower() <= 0) {
-            return new Result<>(StatusCode.ACCESS_ERR, "用户体力不足,无法答题");
-        }
+//        if (user.getPower() == null || user.getPower() <= 0) {
+//            return new Result<>(StatusCode.ACCESS_ERR, "用户体力不足,无法答题");
+//        }
         YtDyzAnswerRecord record = new YtDyzAnswerRecord();
         BeanUtil.copyProperties(param,record);
         record.setRecordId(IdUtil.fastSimpleUUID());

+ 42 - 5
yt-ios-lemon/lemon-ios-service/src/main/resources/mapper/AppUserMapper.xml

@@ -360,6 +360,7 @@
         <result column="app_id" property="appId" />
         <result column="platform_id" property="platformId" />
         <result column="power" property="power" />
+        <result column="nearly_income" property="nearlyIncome" />
         <collection property="loginRecordList" ofType="com.ytpm.app.model.YtDyzLoginRecord">
             <result column="record_id" property="recordId" />
             <result column="user_id" property="userId" />
@@ -397,6 +398,7 @@
             du.app_id,
             du.platform_id,
             du.power,
+            du.nearly_income,
             lr.record_id,
             lr.login_time,
             lr.device_brand,
@@ -426,7 +428,7 @@
     <select id="queryAllByTime" resultType="com.ytpm.app.model.YtDyzUser">
         SELECT
         user_id,app_id,phone,device_id, nick_name, head_img, power, registry_time,
-        last_login_time, last_login_ip, login_days, total_video,
+        last_login_time, last_login_ip, login_days, total_video,nearly_income,
         total_income, red_packet_balance, red_packet_amount,
         points_balance, points_total, withdraw_total, sign_days,
         user_status, risk_reason, wx_open_id, ios_id, platform_id
@@ -615,6 +617,7 @@
             taku_native_pid,
             taku_reward_pid,
             taku_interstitial_pid,
+            taku_screen_pid,
             can_use_root,
             can_use_adb,
             can_use_float,
@@ -719,13 +722,18 @@
     </select>
     <select id="queryByOpenid" resultType="com.ytpm.app.model.YtDyzUser">
         select
-            user_id,phone,device_id, head_img, nick_name, registry_time, last_login_time, last_login_ip, login_days, total_video, total_income, red_packet_balance, red_packet_amount, points_balance, points_total, withdraw_total, sign_days, user_status, risk_reason, wx_open_id, ditch_id, app_id, platform_id, power
+            user_id,phone,device_id, head_img, nick_name, registry_time, last_login_time, last_login_ip, login_days,
+            total_video, total_income,nearly_income, red_packet_balance, red_packet_amount, points_balance, points_total,
+            withdraw_total, sign_days, user_status, risk_reason, wx_open_id, ditch_id, app_id, platform_id, power
         from yt_dyz_user
         where wx_open_id = #{openid}
     </select>
     <select id="queryByIosId" resultType="com.ytpm.app.model.YtDyzUser">
         select
-            user_id,phone,device_id, head_img, nick_name, registry_time, last_login_time, last_login_ip, login_days, total_video, total_income, red_packet_balance, red_packet_amount, points_balance, points_total, withdraw_total, sign_days, user_status, risk_reason, wx_open_id, ditch_id, app_id, platform_id, power
+            user_id,phone,device_id, head_img, nick_name, registry_time,
+            last_login_time, last_login_ip, login_days, total_video, total_income, nearly_income,
+            red_packet_balance, red_packet_amount, points_balance, points_total, withdraw_total,
+            sign_days, user_status, risk_reason, wx_open_id, ditch_id, app_id, platform_id, power
         from yt_dyz_user
         where ios_id = #{iosId}
     </select>
@@ -947,7 +955,10 @@
     </select>
     <select id="queryByUserIds" resultType="com.ytpm.app.model.YtDyzUser">
         select
-            user_id, head_img, nick_name, registry_time, last_login_time, last_login_ip, login_days, total_video, total_income, red_packet_balance, red_packet_amount, points_balance, points_total, withdraw_total, sign_days, user_status, risk_reason, wx_open_id, ditch_id, app_id, platform_id, power, phone, device_id
+            user_id, head_img, nick_name, registry_time, last_login_time, last_login_ip, login_days,
+            total_video, total_income, nearly_income, red_packet_balance, red_packet_amount, points_balance,
+            points_total, withdraw_total, sign_days, user_status, risk_reason, wx_open_id, ditch_id, app_id, platform_id,
+            power, phone, device_id
         from yt_dyz_user
         where user_id in
         <foreach collection="userIds.split(',')" separator="," item="item"  open="(" close=")">
@@ -1000,7 +1011,10 @@
     </select>
     <select id="getMonthRegistryUser" resultType="com.ytpm.app.model.YtDyzUser">
         select
-        user_id, head_img, nick_name, registry_time, last_login_time, last_login_ip, login_days, total_video, total_income, red_packet_balance, red_packet_amount, points_balance, points_total, withdraw_total, sign_days, user_status, risk_reason, wx_open_id, ditch_id, app_id, platform_id, power, phone, device_id
+        user_id, head_img, nick_name, registry_time, last_login_time, last_login_ip, login_days,
+        total_video, total_income, nearly_income, red_packet_balance, red_packet_amount, points_balance, points_total,
+        withdraw_total, sign_days, user_status, risk_reason, wx_open_id, ditch_id, app_id,
+        platform_id, power, phone, device_id
         from yt_dyz_user
         where app_id in
         <foreach collection="appIds.split(',')" separator="," item="item" open="(" close=")">
@@ -1022,4 +1036,27 @@
             #{item}
         </foreach>
     </update>
+    <update id="updateTotal">
+        UPDATE yt_dyz_user
+        SET
+            -- 处理近4天未登录 或间隔时间超过4天
+            nearly_income = (
+                CASE
+                    WHEN last_login_time <![CDATA[ < ]]> DATE_SUB(DATE(NOW()), INTERVAL 4 DAY)
+                         OR IFNULL(nearly_begin_time, NOW()) <![CDATA[ < ]]> DATE_SUB(DATE(NOW()), INTERVAL 4 DAY)
+                        THEN #{revenue}
+                    ELSE COALESCE(nearly_income, 0) + #{revenue}
+                END
+            ),
+            nearly_begin_time = CASE
+                WHEN last_login_time <![CDATA[ < ]]> DATE_SUB(DATE(NOW()), INTERVAL 4 DAY)
+                    OR IFNULL(nearly_begin_time, NOW()) <![CDATA[ < ]]> DATE_SUB(DATE(NOW()), INTERVAL 4 DAY)
+                    THEN NOW()
+                WHEN nearly_begin_time IS NULL THEN NOW()
+                ELSE nearly_begin_time
+            END,
+            total_video = COALESCE(total_video, 0) + #{videoCount},
+            total_income = COALESCE(total_income, 0) + #{revenue}
+        WHERE user_id = #{userId};
+    </update>
 </mapper>

+ 9 - 2
yt-middle/middle-platform/src/main/java/com/ytpm/middle/config/FeignBuilderConfig.java

@@ -5,6 +5,7 @@ import feign.Feign;
 import feign.Request;
 import feign.Retryer;
 import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
 import org.springframework.cloud.openfeign.support.SpringDecoder;
 import org.springframework.cloud.openfeign.support.SpringEncoder;
@@ -23,6 +24,12 @@ import java.util.concurrent.TimeUnit;
 @Configuration
 public class FeignBuilderConfig {
 
+    @Value("${feign.client.config.default.connectTimeout:3000}")
+    private Integer connectTimeoutMillis;
+
+    @Value("${feign.client.config.default.readTimeout:3000}")
+    private Integer readTimeoutMillis;
+
     @Bean
     @Scope("prototype")
     public Feign.Builder feignBuilder(ObjectFactory<HttpMessageConverters> messageConverters) {
@@ -31,7 +38,7 @@ public class FeignBuilderConfig {
                 .encoder(new SpringEncoder(messageConverters))
                 .decoder(new SpringDecoder(messageConverters))
                 .retryer(new Retryer.Default(100, TimeUnit.SECONDS.toMicros(1), 3))
-                .options(new Request.Options(3000, 3000));
+                .options(new Request.Options(connectTimeoutMillis, readTimeoutMillis));
 
     }
-}
+}

+ 9 - 3
yt-middle/middle-platform/src/main/java/com/ytpm/middle/util/FeignClientInvoker.java

@@ -1,7 +1,6 @@
 package com.ytpm.middle.util;
 
 import com.ytpm.middle.handle.DynamicFeignClientFactory;
-import com.ytpm.question.base.BaseFeign;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.context.ApplicationContext;
 import org.springframework.stereotype.Component;
@@ -22,7 +21,6 @@ public class FeignClientInvoker {
     private final ApplicationContext applicationContext;
     private final Map<String, Object> feignClientCache = new HashMap<>();
     private final Map<String, Method> methodCache = new HashMap<>();
-
     @Resource
     private DynamicFeignClientFactory feignClientFactory;
 
@@ -53,6 +51,7 @@ public class FeignClientInvoker {
     }
 
     private Object getFeignClient(String serviceName) {
+        // 优先从缓存获取
         if (feignClientCache.containsKey(serviceName)) {
             return feignClientCache.get(serviceName);
         }
@@ -70,12 +69,19 @@ public class FeignClientInvoker {
             }
             // 构建动态FeignClient
             if (feignClient == null) {
-                feignClient = feignClientFactory.getClient(serviceName, BaseFeign.class);
+                Class<?> clazz = com.ytpm.question.base.BaseFeign.class;
+                if (serviceName.contains("ios")) {
+                    clazz = com.ytpm.lemonios.feign.base.BaseFeign.class;
+                }
+                feignClient = feignClientFactory.getClient(serviceName, clazz);
             }
             feignClientCache.put(serviceName, feignClient);
         } catch (Exception e) {
             throw new IllegalArgumentException("未找到服务: " + serviceName);
         }
+        if (feignClient == null) {
+            throw new IllegalArgumentException("未找到服务: " + serviceName);
+        }
         return feignClient;
     }
 

+ 9 - 2
yt-risk/risk-manage/src/main/java/com/ytpm/config/feign/FeignBuilderConfig.java

@@ -5,6 +5,7 @@ import feign.Feign;
 import feign.Request;
 import feign.Retryer;
 import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
 import org.springframework.cloud.openfeign.support.SpringDecoder;
 import org.springframework.cloud.openfeign.support.SpringEncoder;
@@ -23,6 +24,12 @@ import java.util.concurrent.TimeUnit;
 @Configuration
 public class FeignBuilderConfig {
 
+    @Value("${feign.client.config.default.connectTimeout:3000}")
+    private Integer connectTimeoutMillis;
+
+    @Value("${feign.client.config.default.readTimeout:3000}")
+    private Integer readTimeoutMillis;
+
     @Bean
     @Scope("prototype")
     public Feign.Builder feignBuilder(ObjectFactory<HttpMessageConverters> messageConverters) {
@@ -31,8 +38,8 @@ public class FeignBuilderConfig {
                 .encoder(new SpringEncoder(messageConverters))
                 .decoder(new SpringDecoder(messageConverters))
                 .retryer(new Retryer.Default(100, TimeUnit.SECONDS.toMicros(1), 3))
-                .options(new Request.Options(3000, 3000))
+                .options(new Request.Options(connectTimeoutMillis, readTimeoutMillis))
                 .requestInterceptor(new FeignConfiguration());
 
     }
-}
+}

+ 52 - 0
yt-risk/risk-manage/src/main/java/com/ytpm/handle/DynamicFeignClientFactory.java

@@ -0,0 +1,52 @@
+package com.ytpm.handle;
+
+import feign.Feign;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+import org.springframework.stereotype.Component;
+
+/**
+ * 动态FeignClient 构造工厂
+ * @author lih
+ * @date 2025-10-24 09:45
+ */
+@Slf4j
+@Component
+public class DynamicFeignClientFactory {
+
+    private final Feign.Builder feignBuilder;
+    private final LoadBalancerClient loadBalancerClient;
+
+
+    public DynamicFeignClientFactory(Feign.Builder feignBuilder,
+                                     LoadBalancerClient loadBalancerClient) {
+        this.feignBuilder = feignBuilder;
+        this.loadBalancerClient = loadBalancerClient;
+    }
+
+    public <T> T getClient(String serviceName, Class<T> clazz) {
+        int maxRetry = 3;
+        int retryCount = 0;
+        Exception lastException = null;
+
+        while (retryCount < maxRetry) {
+            try {
+                ServiceInstance instance = loadBalancerClient.choose(serviceName);
+                if (instance == null) {
+                    throw new RuntimeException("未找到可用的服务实例:" + serviceName);
+                }
+                String url = instance.getUri().toString();
+                log.info("为服务{}构建FeignClient,目标地址为:{}", serviceName, url);
+                return feignBuilder.target(clazz, url);
+            } catch (Exception e) {
+                lastException = e;
+                log.warn("第 {} 次尝试获取FeignClient失败,服务名:{},错误信息:{}", retryCount + 1, serviceName, e.getMessage());
+                retryCount++;
+
+            }
+        }
+        throw new RuntimeException("创建FeignClient失败,服务名:" + serviceName, lastException);
+    }
+
+}

+ 5 - 4
yt-risk/risk-manage/src/main/java/com/ytpm/service/impl/RiskServiceImpl.java

@@ -16,10 +16,7 @@ import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.ytpm.advertise.enums.AdSourceTypeEnum;
 import com.ytpm.agent.enums.UserStatusEnum;
-import com.ytpm.agent.model.YtApp;
-import com.ytpm.agent.model.YtPlatformBanned;
-import com.ytpm.agent.model.YtPlatformDeblocking;
-import com.ytpm.agent.model.YtPlatformUserApp;
+import com.ytpm.agent.model.*;
 import com.ytpm.agent.view.AgentAppClassView;
 import com.ytpm.agent.view.AgentEnableAppView;
 import com.ytpm.agent.view.AgentUserInfo;
@@ -765,6 +762,10 @@ public class RiskServiceImpl implements RiskService {
         param.setBannedReason(bannedReason);
         param.setOperatorName(operator);
         param.setAgentId(configMapper.getApplicationOwner(dyzUser.getAppId()));
+        if (configMapper.getApplicationOwner(dyzUser.getAppId()) == null){
+            YtPlatformUserApp userApp = appMapper.selectParentApp(dyzUser.getAppId());
+            param.setAgentId(userApp.getUserId());
+        }
         addBannedRecord(Collections.singletonList(dyzUser.getUserId()),param);
     }
 

+ 10 - 2
yt-risk/risk-manage/src/main/java/com/ytpm/util/FeignClientInvoker.java

@@ -1,7 +1,8 @@
 package com.ytpm.util;
 
+
+
 import com.ytpm.config.handle.DynamicFeignClientFactory;
-import com.ytpm.question.base.BaseFeign;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.context.ApplicationContext;
@@ -107,12 +108,19 @@ public class FeignClientInvoker {
             }
             // 构建动态FeignClient
             if (feignClient == null) {
-                feignClient = feignClientFactory.getClient(serviceName, BaseFeign.class);
+                Class<?> clazz = com.ytpm.question.base.BaseFeign.class;
+                if (serviceName.contains("ios")) {
+                    clazz = com.ytpm.lemonios.feign.base.BaseFeign.class;
+                }
+                feignClient = feignClientFactory.getClient(serviceName, clazz);
             }
             feignClientCache.put(serviceName, feignClient);
         } catch (Exception e) {
             throw new IllegalArgumentException("未找到服务: " + serviceName);
         }
+        if (feignClient == null) {
+            throw new IllegalArgumentException("未找到服务: " + serviceName);
+        }
         return feignClient;
     }