marxjaw пре 2 месеци
родитељ
комит
a5206a4853
36 измењених фајлова са 3530 додато и 0 уклоњено
  1. 1 0
      pom.xml
  2. 26 0
      yt-arithmetic/arithmetic-feign/pom.xml
  3. 113 0
      yt-arithmetic/arithmetic-feign/src/main/java/com/ytpm/arithmetic/feign/ArithmeticFeign.java
  4. 86 0
      yt-arithmetic/arithmetic-service/pom.xml
  5. 27 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/ArithmeticApplication.java
  6. 38 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/aop/DataSourceAspect.java
  7. 19 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/YtWebMvcConfigurerAdapter.java
  8. 56 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/datasource/DataSourceConfig.java
  9. 10 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/datasource/DynamicDataSource.java
  10. 20 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/datasource/DynamicDataSourceContextHolder.java
  11. 38 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/datasource/MyBatisConfig.java
  12. 48 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/interceptor/HttpInterceptor.java
  13. 29 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/redis/RedisListenerConfig.java
  14. 40 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/swagger/SwaggerConfig.java
  15. 94 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/controller/AdController.java
  16. 43 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/controller/QuestionController.java
  17. 356 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/controller/UserController.java
  18. 172 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/controller/WxController.java
  19. 47 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/dao/AdRecordMapper.java
  20. 180 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/dao/AppUserMapper.java
  21. 25 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/dao/LoginRecordMapper.java
  22. 38 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/dao/QuestionMapper.java
  23. 74 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/monitor/RedisKeyExpirationListener.java
  24. 275 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/redis/RedisService.java
  25. 49 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/service/AdService.java
  26. 18 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/service/AppUserService.java
  27. 17 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/service/QuestionService.java
  28. 283 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/service/impl/AdServiceImpl.java
  29. 157 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/service/impl/AppUserServiceImpl.java
  30. 69 0
      yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/service/impl/QuestionServiceImpl.java
  31. 60 0
      yt-arithmetic/arithmetic-service/src/main/resources/bootstrap.yml
  32. 145 0
      yt-arithmetic/arithmetic-service/src/main/resources/mapper/AdRecordMapper.xml
  33. 625 0
      yt-arithmetic/arithmetic-service/src/main/resources/mapper/AppUserMapper.xml
  34. 75 0
      yt-arithmetic/arithmetic-service/src/main/resources/mapper/LoginRecordMapper.xml
  35. 59 0
      yt-arithmetic/arithmetic-service/src/main/resources/mapper/QuestionMapper.xml
  36. 118 0
      yt-arithmetic/pom.xml

+ 1 - 0
pom.xml

@@ -41,6 +41,7 @@
         <module>yt-carp</module>
         <module>yt-nofeeds</module>
         <module>yt-lime</module>
+        <module>yt-arithmetic</module>
     </modules>
 
     <properties>

+ 26 - 0
yt-arithmetic/arithmetic-feign/pom.xml

@@ -0,0 +1,26 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.ytpm</groupId>
+        <artifactId>yt-arithmetic</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>arithmetic-feign</artifactId>
+    <packaging>jar</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot.version}</version>
+                <configuration>
+                    <skip>
+                        true
+                    </skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 113 - 0
yt-arithmetic/arithmetic-feign/src/main/java/com/ytpm/arithmetic/feign/ArithmeticFeign.java

@@ -0,0 +1,113 @@
+package com.ytpm.arithmetic.feign;
+
+import com.ytpm.agent.param.AuditCheckParam;
+import com.ytpm.agent.view.AgentAdGroupStaticsVO;
+import com.ytpm.agent.view.AgentTopCountView;
+import com.ytpm.app.model.YtAppDefaultConfig;
+import com.ytpm.app.model.YtDyzAdRecord;
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.param.AppQueryUserTodayTimeParam;
+import com.ytpm.app.param.AppUserParam;
+import com.ytpm.app.param.AppUserQueryParam;
+import com.ytpm.app.param.AppUserTodayBannedParam;
+import com.ytpm.app.param.YtAppUserListParam;
+import com.ytpm.app.view.WxDefaultConfig;
+import com.ytpm.app.view.YtAppUserListView;
+import com.ytpm.general.Result;
+import com.ytpm.general.ResultTable;
+import com.ytpm.middle.view.DashboardRankingListVO;
+import com.ytpm.middle.view.DashboardRevenueVO;
+import com.ytpm.middle.view.DashboardRiskVO;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+@FeignClient(name = "arithmetic-service")
+public interface ArithmeticFeign {
+    @PostMapping("/user/queryAll")
+    ResultTable<YtAppUserListView> queryAll(@RequestBody YtAppUserListParam param);
+    @GetMapping("/user/getUserInfo")
+    Result<YtDyzUser> getUserInfo(@RequestParam("userId") String userId);
+    @PostMapping("/user/getUserList")
+    ResultTable<YtDyzUser> getUserList(@RequestBody AppUserParam param);
+
+    @PostMapping("/user/getUserAll")
+    ResultTable<YtDyzUser> getUserAll(@RequestBody AppUserParam param);
+
+    @PostMapping("/user/updateUserInfo")
+    Result<?> updateUserInfo(@RequestBody YtDyzUser dyzUser);
+    @GetMapping("/user/adRecords")
+    ResultTable<YtDyzAdRecord> adRecords(@RequestParam(name = "userId",required = true) String userId, @RequestParam(name = "adsourceType",required = false)Integer adsourceType);
+
+    @PostMapping("/user/queryUserByTime")
+    List<YtDyzUser> queryUserByTime(@RequestBody AppUserQueryParam appUserQueryParam);
+
+    @PostMapping("/user/queryUserByTodayTime")
+    int[] queryUserByTodayTime(@RequestBody AppQueryUserTodayTimeParam appQueryUserTodayTimeParam);
+
+    @PostMapping("/user/queryLoginRecords")
+    List<String> queryLoginRecords(@RequestBody AppUserQueryParam appUserQueryParam);
+
+    @GetMapping("/user/queryByOpenid")
+    List<YtDyzUser> queryByOpenid(@RequestParam("openid") String openid);
+
+    @PostMapping("/wx/saveAppConfig")
+    Result<String> saveAppConfig(@RequestBody YtAppDefaultConfig defaultConfig);
+    @PostMapping("/qn/wx/saveAppConfig")
+    Result<String> qnSaveAppConfig(@RequestBody YtAppDefaultConfig defaultConfig);
+    @PostMapping("/nf/wx/saveAppConfig")
+    Result<String> nfSaveAppConfig(@RequestBody YtAppDefaultConfig defaultConfig);
+    @PostMapping("/qnm/wx/saveAppConfig")
+    Result<String> qnmSaveAppConfig(@RequestBody YtAppDefaultConfig defaultConfig);
+
+    @GetMapping("/wx/getConfigs")
+    List<WxDefaultConfig> getConfigs(@RequestParam(name = "appIds")String appIds);
+
+    @PostMapping("/user/queryTodayBanned")
+    List<YtDyzUser> queryTodayBanned(@RequestBody AppUserTodayBannedParam appUserTodayBannedParam);
+
+    @GetMapping("/ad/getAdCount")
+    Map<String, BigDecimal> getAdCount(@RequestParam(name = "appIds")String appIds);
+
+    @GetMapping("/ad/getAppTopCount")
+    AgentTopCountView getAppTopCount(@RequestParam(name = "appIds")String appIds);
+
+    @GetMapping("/ad/getAppRankingList")
+    DashboardRankingListVO queryRankingList(@RequestParam(name = "sortBy") Integer sortBy, @RequestParam(name = "limit") Integer limit);
+
+    @GetMapping("/ad/revenueStatics")
+    DashboardRevenueVO revenueStatics(@RequestParam(name = "apkIds") String apkIds);
+
+    @GetMapping("/ad/userStatics")
+    DashboardRiskVO userStatics(@RequestParam(name = "appId") String appId);
+
+    @GetMapping("/ad/getAgentProfit")
+    List<AgentAdGroupStaticsVO> getAgentProfit(@RequestParam(name = "appIds")String appIds);
+
+    @PostMapping("/user/getRevenueByTime")
+    BigDecimal getRevenueByTime(@RequestBody YtAppUserListParam param);
+
+    @PostMapping("/wx/delDefaultConfig")
+    void delAppConfig(@RequestParam("appId") String appId);
+
+    @PostMapping("/user/unLockUser")
+    void unLockUser(@RequestParam("userIds")String userIds);
+
+    /**
+     * 锁定用户
+     */
+    @GetMapping("/user/lockUser")
+    YtDyzUser lockUser(@RequestParam(name="userId")String userId,@RequestParam("userStatus")Integer userStatus);
+
+    /**
+     * 批量审核用户
+     */
+    @PostMapping("/user/batchAudit")
+    void batchAudit(@RequestBody AuditCheckParam auditCheckParam);
+}

+ 86 - 0
yt-arithmetic/arithmetic-service/pom.xml

@@ -0,0 +1,86 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.ytpm</groupId>
+        <artifactId>yt-arithmetic</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>arithmetic-service</artifactId>
+    <packaging>jar</packaging>
+    <description>算术小达人</description>
+
+    <name>arithmetic-service</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>31.0-jre</version>
+        </dependency>
+        <dependency>
+            <groupId>com.ytpm</groupId>
+            <artifactId>risk-feign</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+            <version>2.2.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.zaxxer</groupId>
+            <artifactId>HikariCP</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>arithmetic-service</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>8</source>
+                    <target>8</target>
+                    <encoding>utf-8</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.5.3</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <includeSystemScope>true</includeSystemScope>
+                    <mainClass>com.ytpm.arithmetic.ArithmeticApplication</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 27 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/ArithmeticApplication.java

@@ -0,0 +1,27 @@
+package com.ytpm.arithmetic;
+
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@SpringBootApplication(exclude = {
+        DataSourceAutoConfiguration.class,
+        DataSourceTransactionManagerAutoConfiguration.class,
+        JdbcTemplateAutoConfiguration.class
+})
+@EnableDiscoveryClient
+@EnableFeignClients(basePackages = {"com.ytpm.feign"})
+public class ArithmeticApplication
+{
+    public static void main( String[] args )
+    {
+        SpringApplication.run(ArithmeticApplication.class, args);
+    }
+}

+ 38 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/aop/DataSourceAspect.java

@@ -0,0 +1,38 @@
+package com.ytpm.arithmetic.aop;
+
+import com.ytpm.app.enums.DataSourceType;
+import com.ytpm.arithmetic.config.datasource.DynamicDataSourceContextHolder;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.After;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+@Aspect
+@Component
+@Order(1)
+public class DataSourceAspect {
+
+    @Pointcut("execution(* com.ytpm.arithmetic.service.*.*(..))")
+    public void servicePointcut() {}
+
+    @Before("servicePointcut()")
+    public void beforeService(JoinPoint joinPoint) {
+        String methodName = joinPoint.getSignature().getName();
+        if (methodName.startsWith("get") ||
+            methodName.startsWith("find") ||
+            methodName.startsWith("select") ||
+            methodName.startsWith("query")) {
+            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE);
+        } else {
+            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER);
+        }
+    }
+
+    @After("servicePointcut()")
+    public void afterService() {
+        DynamicDataSourceContextHolder.clear();
+    }
+}

+ 19 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/YtWebMvcConfigurerAdapter.java

@@ -0,0 +1,19 @@
+package com.ytpm.arithmetic.config;
+
+import com.ytpm.arithmetic.config.interceptor.HttpInterceptor;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * 自定义拦截器
+ */
+@Configuration
+public class YtWebMvcConfigurerAdapter implements WebMvcConfigurer {
+
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(new HttpInterceptor()).addPathPatterns("/**").excludePathPatterns("/resources/**");
+    }
+}

+ 56 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/datasource/DataSourceConfig.java

@@ -0,0 +1,56 @@
+package com.ytpm.arithmetic.config.datasource;
+
+import com.ytpm.app.enums.DataSourceType;
+import com.zaxxer.hikari.HikariDataSource;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.jdbc.DataSourceBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.DependsOn;
+import org.springframework.context.annotation.Primary;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.sql.DataSource;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 多数据源配置
+ * @Marx
+ */
+@Configuration
+@EnableTransactionManagement
+public class DataSourceConfig {
+    @Bean(name = "masterDataSource")
+    @ConfigurationProperties(prefix = "spring.datasource.master")
+    public DataSource masterDataSource() {
+        return DataSourceBuilder.create().type(HikariDataSource.class).build();
+    }
+
+    @Bean(name = "slaveDataSource")
+    @ConfigurationProperties(prefix = "spring.datasource.slave")
+    public DataSource slaveDataSource() {
+        return DataSourceBuilder.create().type(HikariDataSource.class).build();
+    }
+    @Primary
+    @Bean(name = "dynamicDataSource")
+    @DependsOn({"masterDataSource", "slaveDataSource"})
+    public DataSource dynamicDataSource() {
+        Map<Object, Object> dataSources = new HashMap<>();
+        dataSources.put(DataSourceType.MASTER, masterDataSource());
+        dataSources.put(DataSourceType.SLAVE, slaveDataSource());
+
+        DynamicDataSource ds = new DynamicDataSource();
+        ds.setDefaultTargetDataSource(masterDataSource());
+        ds.setTargetDataSources(dataSources);
+        return ds;
+    }
+
+    @Bean
+    public PlatformTransactionManager transactionManager(@Qualifier("dynamicDataSource")DataSource dynamicDataSource) {
+        return new DataSourceTransactionManager(dynamicDataSource);
+    }
+}

+ 10 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/datasource/DynamicDataSource.java

@@ -0,0 +1,10 @@
+package com.ytpm.arithmetic.config.datasource;
+
+import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
+
+public class DynamicDataSource extends AbstractRoutingDataSource {
+    @Override
+    protected Object determineCurrentLookupKey() {
+        return DynamicDataSourceContextHolder.getDataSourceType();
+    }
+}

+ 20 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/datasource/DynamicDataSourceContextHolder.java

@@ -0,0 +1,20 @@
+package com.ytpm.arithmetic.config.datasource;
+
+
+import com.ytpm.app.enums.DataSourceType;
+
+public class DynamicDataSourceContextHolder {
+    private static final ThreadLocal<DataSourceType> CONTEXT = new ThreadLocal<>();
+
+    public static void setDataSourceType(DataSourceType type) {
+        CONTEXT.set(type);
+    }
+
+    public static DataSourceType getDataSourceType() {
+        return CONTEXT.get() == null ? DataSourceType.MASTER : CONTEXT.get();
+    }
+
+    public static void clear() {
+        CONTEXT.remove();
+    }
+}

+ 38 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/datasource/MyBatisConfig.java

@@ -0,0 +1,38 @@
+package com.ytpm.arithmetic.config.datasource;
+
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.mybatis.spring.SqlSessionTemplate;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+
+import javax.sql.DataSource;
+
+@Configuration
+@MapperScan(basePackages = "com.ytpm.arithmetic.dao", sqlSessionFactoryRef = "sqlSessionFactory")
+public class MyBatisConfig {
+
+    @Bean
+    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
+        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
+        sessionFactory.setDataSource(dataSource);
+        // 添加XML映射文件位置(如果有)
+        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
+                .getResources("classpath*:mapper/**/*.xml"));
+        // 添加类型别名包
+        sessionFactory.setTypeAliasesPackage("com.ytpm.app.model");
+        // 配置其他MyBatis设置
+        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
+        configuration.setMapUnderscoreToCamelCase(true);
+        sessionFactory.setConfiguration(configuration);
+        return sessionFactory.getObject();
+    }
+
+    @Bean
+    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
+        return new SqlSessionTemplate(sqlSessionFactory);
+    }
+}

+ 48 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/interceptor/HttpInterceptor.java

@@ -0,0 +1,48 @@
+package com.ytpm.arithmetic.config.interceptor;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Date;
+
+@Component
+public class HttpInterceptor implements HandlerInterceptor {
+
+    @Override
+    public boolean preHandle(HttpServletRequest request,
+                             HttpServletResponse response, Object obj) throws Exception {
+        // 获取客户端IP地址
+        String clientIp = getClientIp(request);
+        System.out.println("算术小达人接收客户端" + clientIp + "发起用户请求");
+        // 可以在这里添加更多的逻辑,比如记录日志等
+        return true;
+    }
+
+    private String getClientIp(HttpServletRequest request) {
+        String xfHeader = request.getHeader("X-Forwarded-For");
+        if (xfHeader == null) {
+            return request.getRemoteAddr();
+        }
+        return xfHeader.split(",")[0]; // 可能会有多个IP,这里取第一个逗号前的IP
+    }
+    /**
+     * 请求处理之后调用;在视图渲染之前,controller处理之后。
+     */
+    @Override
+    public void postHandle(HttpServletRequest request,
+                           HttpServletResponse response, Object obj, ModelAndView mv)
+            throws Exception {
+        response.setDateHeader("Expires", 0);
+        response.setHeader("Buffer", "True");
+        response.setHeader("Cache-Control", "no-cache");
+        response.setHeader("Expires", "0");
+        response.setHeader("ETag", String.valueOf(System.currentTimeMillis()));
+        response.setHeader("Pragma", "no-cache");
+        response.setHeader("Date", String.valueOf(new Date()));
+        response.setHeader("Last-Modified", String.valueOf(new Date()));
+
+    }
+}

+ 29 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/redis/RedisListenerConfig.java

@@ -0,0 +1,29 @@
+package com.ytpm.arithmetic.config.redis;
+
+import com.ytpm.arithmetic.monitor.RedisKeyExpirationListener;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+
+/**
+ * redis 监听器配置
+ * @author marx
+ * @date 2025/7/29 16:09
+ */
+@Configuration
+public class RedisListenerConfig {
+
+    @Bean
+    RedisMessageListenerContainer listenerContainer(RedisConnectionFactory connectionFactory) {
+        RedisMessageListenerContainer listenerContainer = new RedisMessageListenerContainer();
+        listenerContainer.setConnectionFactory(connectionFactory);
+        return listenerContainer;
+    }
+
+    @Bean
+    KeyExpirationEventMessageListener redisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
+        return new RedisKeyExpirationListener(listenerContainer);
+    }
+}

+ 40 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/config/swagger/SwaggerConfig.java

@@ -0,0 +1,40 @@
+package com.ytpm.arithmetic.config.swagger;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+    private static final String VERSION = "1.0.0";
+
+    @Bean
+    public Docket createApi(){
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.ytpm.arithmetic.controller"))
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("")
+                .contact(new Contact("易推网络","http://www.sourcetreasure.com/index.html","marxjaw6466@163.com"))
+                .description("代理商管理系统")
+                .termsOfServiceUrl("https://juejin.cn/user/4310510864972254")
+                .license("The Apache License, Version 2.0")
+                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
+                .version(VERSION)
+                .build();
+    }
+}

+ 94 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/controller/AdController.java

@@ -0,0 +1,94 @@
+package com.ytpm.arithmetic.controller;
+
+import com.ytpm.agent.view.AgentAdGroupStaticsVO;
+import com.ytpm.agent.view.AgentTopCountView;
+import com.ytpm.app.param.DyzAdRecordParam;
+import com.ytpm.arithmetic.service.AdService;
+import com.ytpm.general.Result;
+import com.ytpm.middle.view.DashboardRankingListVO;
+import com.ytpm.middle.view.DashboardRevenueVO;
+import com.ytpm.middle.view.DashboardRiskVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+
+@Api(tags = "用户浏览广告记录")
+@RestController
+@RequestMapping("/ad")
+public class AdController {
+
+    @Autowired
+    private AdService adService;
+
+    /**
+     * 保存广告记录
+     */
+    @ApiOperation("保存")
+    @PostMapping("/saveRecord")
+    public Result<?> saveRecord(@RequestBody DyzAdRecordParam param){
+        return adService.saveRecord(param);
+    }
+
+    /**
+     * 根据应用ID查询广告数
+     */
+    @ApiOperation("根据应用ID查询广告数")
+    @GetMapping("/getAdCount")
+    public Map<String, BigDecimal> getAdCount(@RequestParam(name = "appIds")String appIds){
+        return adService.getAdCount(appIds);
+    }
+
+    /**
+     * 查询应用排行榜信息
+     */
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "sortBy", value = "排行榜排序规则",required = true),
+            @ApiImplicitParam(name = "limit", value = "排行榜显示数量",required = true),
+    })
+    @ApiOperation("查询应用排行榜信息")
+    @GetMapping("/getAppRankingList")
+    public DashboardRankingListVO queryRankingList(@RequestParam(name = "sortBy")Integer sortBy,@RequestParam(name = "limit")Integer limit){
+        return adService.queryRankingList(sortBy,limit);
+    }
+
+    /**
+     * 查询各渠道应用收益分时统计
+     */
+    @ApiImplicitParam(name = "apkIds", value = "渠道应用ID(,拼接)",required = true)
+    @ApiOperation("查询各渠道应用收益分时统计")
+    @GetMapping("/revenueStatics")
+    public DashboardRevenueVO revenueStatics(@RequestParam(name = "apkIds")String apkIds){
+        return adService.revenueStatics(apkIds);
+    }
+
+    @ApiOperation("查询渠道商顶部数据")
+    @GetMapping("/getAppTopCount")
+    public AgentTopCountView getAppTopCount(@RequestParam(name = "appIds")String appIds){
+        return adService.getAppTopCount(appIds);
+    }
+
+    @ApiOperation("查询用户风控分时数据统计")
+    @GetMapping("/userStatics")
+    public DashboardRiskVO userStatics(@RequestParam(name = "appId")String appId){
+        return adService.userStatics(appId);
+    }
+
+    @ApiOperation("查询代理商首页统计广告数据")
+    @GetMapping("/getAgentProfit")
+    public List<AgentAdGroupStaticsVO> getAgentProfit(@RequestParam(name = "appIds")String appIds){
+        return adService.getAgentProfit(appIds);
+    }
+}

+ 43 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/controller/QuestionController.java

@@ -0,0 +1,43 @@
+package com.ytpm.arithmetic.controller;
+
+import com.ytpm.app.param.AnswerRecordParam;
+import com.ytpm.app.view.QuestionListView;
+import com.ytpm.arithmetic.service.QuestionService;
+import com.ytpm.general.Result;
+import com.ytpm.general.ResultTable;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@Api(tags = "题目管理模块")
+@RestController
+@RequestMapping("/question")
+public class QuestionController {
+
+    @Autowired
+    private QuestionService questionService;
+
+    /**
+     * 获取题库
+     */
+    @ApiOperation("获取题库")
+    @GetMapping("/list")
+    public ResultTable<QuestionListView> questionList(){
+        return questionService.questionList();
+    }
+
+    /**
+     * 回答问题
+     */
+    @ApiOperation("回答问题")
+    @PostMapping("/answerQuestion")
+    public Result<String> answerQuestion(@RequestBody AnswerRecordParam param){
+        return questionService.answerQuestion(param);
+    }
+}

+ 356 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/controller/UserController.java

@@ -0,0 +1,356 @@
+package com.ytpm.arithmetic.controller;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateUtil;
+import com.github.pagehelper.PageInfo;
+import com.ytpm.advertise.enums.AdSourceTypeEnum;
+import com.ytpm.agent.enums.UserStatusEnum;
+import com.ytpm.agent.param.AuditCheckParam;
+import com.ytpm.agent.param.AuditUserParam;
+import com.ytpm.agent.view.AgentAuditCheckVO;
+import com.ytpm.app.model.YtDyzAdRecord;
+import com.ytpm.app.model.YtDyzLoginRecord;
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.param.AppQueryUserTodayTimeParam;
+import com.ytpm.app.param.AppUserParam;
+import com.ytpm.app.param.AppUserQueryParam;
+import com.ytpm.app.param.AppUserTodayBannedParam;
+import com.ytpm.app.param.YtAppUserListParam;
+import com.ytpm.app.view.HourCountView;
+import com.ytpm.app.view.YtAppUserListView;
+import com.ytpm.arithmetic.dao.AdRecordMapper;
+import com.ytpm.arithmetic.dao.AppUserMapper;
+import com.ytpm.arithmetic.dao.LoginRecordMapper;
+import com.ytpm.arithmetic.dao.QuestionMapper;
+import com.ytpm.arithmetic.redis.RedisService;
+import com.ytpm.general.RepMessage;
+import com.ytpm.general.Result;
+import com.ytpm.general.ResultTable;
+import com.ytpm.risk.enums.BannedTypeEnum;
+import com.ytpm.risk.view.RiskConfigView;
+import com.ytpm.risk.view.RiskTemplateView;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+@Slf4j
+@RestController
+@RequestMapping("/user")
+public class UserController {
+
+    @Autowired
+    private AppUserMapper appUserMapper;
+    @Autowired
+    private LoginRecordMapper loginRecordMapper;
+    @Autowired
+    private AdRecordMapper adRecordMapper;
+    @Autowired
+    private QuestionMapper questionMapper;
+
+    @Autowired
+    private RedisService redisService;
+
+    /**
+     * 查询app 所有玩家
+     *  汇聚分库应用
+     */
+    @PostMapping("/queryAll")
+    public ResultTable<YtAppUserListView> queryAll(@RequestBody YtAppUserListParam param) {
+        //查询主应用
+        List<YtAppUserListView> resultList = new ArrayList<>();
+        List<YtAppUserListView> dyzUserList = appUserMapper.queryAll(param);
+        if (CollUtil.isNotEmpty(dyzUserList)) {
+            resultList.addAll(dyzUserList);
+        }
+        int total = resultList.size()/param.getLimit();
+        long a =  resultList.size() % param.getLimit();
+        if(resultList.size() % param.getLimit() != 0){
+            total++;
+        }
+        int totalSize = resultList.size();
+        //判断最后一页时返回剩余数据
+        List<YtAppUserListView> result;
+        if(param.getPage().compareTo(total)>=0){
+            result = resultList.stream().limit(a).collect(Collectors.toList());
+        }else {
+            if(param.getPage()>1){
+                int skip = (param.getPage() - 1) *param.getLimit();
+                result = resultList.stream().skip(skip).limit(param.getLimit()).collect(Collectors.toList());
+            }else{
+                result = resultList.stream().limit(param.getLimit()).collect(Collectors.toList());
+            }
+
+        }
+        dyzUserList.clear();
+        resultList.clear();
+        String collect = result.stream().map(YtAppUserListView::getUserId).collect(Collectors.joining(","));
+
+        List<YtDyzAdRecord> adRecords = adRecordMapper.getByUserIds(collect);
+        List<YtDyzLoginRecord> dyzLogins = loginRecordMapper.getLoginRecordByIds(collect);
+        Map<String, List<YtDyzAdRecord>> userAdMap = adRecords.stream().collect(
+                Collectors.groupingBy(YtDyzAdRecord::getUserId));
+        Map<String, List<YtDyzLoginRecord>> loginMap = dyzLogins.stream().collect(
+                Collectors.groupingBy(YtDyzLoginRecord::getUserId));
+        setUserExtInfo(result,userAdMap,loginMap);
+        return ResultTable.resultTableOk(result , (long) totalSize,param.getLimit(),param.getPage(),total);
+    }
+
+    /**
+     * 设置视频记录和登录信息
+     */
+    private void setUserExtInfo(List<YtAppUserListView> result, Map<String, List<YtDyzAdRecord>> userAdMap,Map<String, List<YtDyzLoginRecord>> loginMap) {
+        for (YtAppUserListView user : result) {
+            if(loginMap.containsKey(user.getUserId())){
+                user.setLoginRecordList(loginMap.get(user.getUserId()));
+            }
+            //获取今日收益 | 今日视频播放数
+            if (userAdMap.containsKey(user.getUserId())) {
+                List<YtDyzAdRecord> ytDyzAdRecords = userAdMap.get(user.getUserId());
+                List<YtDyzAdRecord> collect = ytDyzAdRecords.stream().filter(
+                        s -> DateUtil.isSameDay(DateUtil.date(),DateUtil.parseDate(s.getFinishTime()))).collect(Collectors.toList());
+                BigDecimal todayIncome = collect.stream().map(YtDyzAdRecord::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add);
+                user.setTodayVideo((int) collect.stream().filter(
+                        s->s.getAdSourceType()== AdSourceTypeEnum.rewarded_video.getAdSourceType()).count());
+                user.setTodayIncome(todayIncome);
+            } else {
+                user.setTodayIncome(BigDecimal.ZERO);
+                user.setTodayVideo(0);
+            }
+        }
+    }
+
+    /**
+     * 根据时间统计收益
+     */
+    @PostMapping("/getRevenueByTime")
+    public BigDecimal getRevenueByTime(@RequestBody YtAppUserListParam param) {
+        BigDecimal result = new BigDecimal(0);
+        BigDecimal dyzRevenue = adRecordMapper.getRevenueByTime(param);
+        if(Objects.nonNull(dyzRevenue)){
+            result = result.add(dyzRevenue);
+        }
+        return result;
+    }
+
+
+    /**
+     * 主键查询用户信息
+     */
+    @GetMapping("/getUserInfo")
+    public Result<YtDyzUser> getUserInfo(@RequestParam("userId") String userId) {
+        YtDyzUser user = appUserMapper.selectPrimaryKey(userId);
+        user.setLoginRecordList(loginRecordMapper.getLoginRecords(userId));
+        user.setTodayAnswerCount(questionMapper.getAnswerCount(userId,1));
+        user.setHistoryAnswerCount(questionMapper.getAnswerCount(userId,2));
+        user.setAnswerRecordList(questionMapper.getAnswerRecords(userId));
+        return Result.resultObjOk(user);
+    }
+
+    /**
+     * 查询用户信息
+     */
+    @PostMapping("/getUserList")
+    public ResultTable<YtDyzUser> getUserList(@RequestBody AppUserParam param) {
+        List<YtDyzUser> resultList = new ArrayList<>();
+        List<YtDyzUser> userList = appUserMapper.getUserList(param);
+        if(CollUtil.isNotEmpty(userList)){resultList.addAll(userList);}
+        return ResultTable.resultTableOk(new PageInfo<>(resultList));
+    }
+
+    @PostMapping("/getUserAll")
+    public ResultTable<YtDyzUser> getUserAll(@RequestBody AppUserParam param) {
+        List<YtDyzUser> userList = appUserMapper.getUserList(param);
+        return ResultTable.resultTableOk(new PageInfo<>(userList));
+    }
+
+    /**
+     * 修改用户信息
+     */
+    @PostMapping("/updateUserInfo")
+    public Result<?> updateUserInfo(@RequestBody YtDyzUser dyzUser) {
+        appUserMapper.updateUser(dyzUser);
+        return Result.resultOk(RepMessage.MODIFY_SUCCESS);
+    }
+
+    /**
+     * 查询用户的广告记录
+     */
+    @GetMapping("/adRecords")
+    public ResultTable<YtDyzAdRecord> adRecords(@RequestParam(name = "userId",required = true) String userId,@RequestParam(name = "adsourceType",required = false)Integer adsourceType) {
+        List<YtDyzAdRecord> result = new ArrayList<>();
+        List<YtDyzAdRecord> adrecords = adRecordMapper.getByUserId(userId, adsourceType);
+        if(CollUtil.isNotEmpty(adrecords)){
+            result.addAll(adrecords);
+        }
+        return ResultTable.resultTableOk(new PageInfo<YtDyzAdRecord>(result));
+    }
+
+    @PostMapping("/queryUserByTime")
+    public List<YtDyzUser> queryUserByTime(@RequestBody AppUserQueryParam appUserQueryParam) {
+        List<YtDyzUser> ytDyzUsers = appUserMapper.queryAllByTime(appUserQueryParam.getStartTime(), appUserQueryParam.getEndTime(),appUserQueryParam.getAppIdList());
+        return ytDyzUsers;
+    }
+
+    @PostMapping("/queryUserByTodayTime")
+    public int[] queryUserByTodayTime(@RequestBody AppQueryUserTodayTimeParam appQueryUserTodayTimeParam) {
+        // 获取当前日期
+        LocalDate localDate = LocalDate.now();
+
+        // 构建当天起始时间段
+        LocalDateTime startOfDay = localDate.atStartOfDay();
+        LocalDateTime endOfDay = startOfDay.plusDays(1).minusSeconds(1);
+
+        Date startTime = Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant());
+        Date endTime = Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
+        // 查询用户注册数据
+        List<HourCountView> hourCountViews = appUserMapper.countUsersByHour(startTime, endTime,appQueryUserTodayTimeParam.getAppIdList());
+        // 初始化每小时用户数量数组
+        int[] hourlyCounts = new int[24];
+        for (HourCountView hc : hourCountViews) {
+            if (hc.getHour() >= 0 && hc.getHour() < 24) {
+                hourlyCounts[hc.getHour()] = hc.getCount();
+            }
+        }
+        return hourlyCounts;
+    }
+
+    @PostMapping("/queryLoginRecords")
+    public List<String> queryLoginRecords(@RequestBody AppUserQueryParam appUserQueryParam) {
+        return loginRecordMapper.queryLoginCount(appUserQueryParam);
+    }
+
+    @GetMapping("/queryByOpenid")
+    public List<YtDyzUser> queryByOpenid(@RequestParam("openid") String openid){
+        return appUserMapper.queryByOpenid(openid);
+    }
+    @PostMapping("/queryTodayBanned")
+    public List<YtDyzUser> queryTodayBanned(@RequestBody AppUserTodayBannedParam appUserTodayBannedParam){
+        return appUserMapper.queryTodayBanned(appUserTodayBannedParam.getStartTime(), appUserTodayBannedParam.getEndTime(), appUserTodayBannedParam.getUserIdList());
+    }
+    @PostMapping("/unLockUser")
+    public void unLockUser(@RequestParam("userIds")String userIds){
+        List<YtDyzUser> dyzUsers = appUserMapper.queryByUserIds(userIds);
+        if(CollUtil.isNotEmpty(dyzUsers)){
+            appUserMapper.unlockUser(userIds);
+            return;
+        }
+    }
+    @GetMapping("/lockUser")
+    public YtDyzUser lockUser(@RequestParam("userId")String userId,@RequestParam("userStatus")Integer userStatus){
+        YtDyzUser dyzUser = appUserMapper.selectPrimaryKey(userId);
+        YtDyzUser newUser = new YtDyzUser();
+        newUser.setUserId(userId);
+        newUser.setUserStatus(userStatus);
+        newUser.setRiskReason(BannedTypeEnum.CHANNEL.getDesc()+"禁止登录");
+        if(Objects.nonNull(dyzUser)){
+            appUserMapper.updateUser(newUser);
+            return dyzUser;
+        }
+        return null;
+    }
+
+    /**
+     * 批量审核用户是否满足风控规则
+     * 不满足风控规则的审核通过后直接封禁指定天数
+     */
+    @PostMapping("/batchAudit")
+    public void batchAudit(@RequestBody AuditCheckParam checkParam){
+        AuditUserParam auditParam = checkParam.getAuditParam();
+        RiskTemplateView ecpmLimit = checkParam.getEcpmLimit();
+        RiskTemplateView revenueLimit = checkParam.getRevenueLimit();
+        //查询用户,指定应用激励视频的广告记录
+        List<AgentAuditCheckVO> auditList = new ArrayList<>();
+        List<AgentAuditCheckVO> auditCheckList = appUserMapper.queryTodayUserAd(auditParam);
+        if(CollUtil.isNotEmpty(auditCheckList)){auditList.addAll(auditCheckList);}
+        Map<String, List<YtDyzAdRecord>> adRecordMap = auditList.stream().collect(
+                Collectors.toMap(AgentAuditCheckVO::getUserId, AgentAuditCheckVO::getAdRecordList));
+        checkRisk742(ecpmLimit,adRecordMap,auditParam);
+        checkRisk746(revenueLimit,adRecordMap,auditParam);
+    }
+
+    /**
+     * 校验默认风控规则742
+     */
+    private void checkRisk742(RiskTemplateView ecpmLimit,Map<String, List<YtDyzAdRecord>> adRecordMap,AuditUserParam auditParam) {
+        if(ecpmLimit.getEnabled()!=1)return;
+        Map<String, String> limitMap = ecpmLimit.getConfigList().stream().collect(
+                Collectors.toMap(RiskConfigView::getFieldName, RiskConfigView::getConfigVal));
+        int adCount = 0;
+        for (Map.Entry<String, List<YtDyzAdRecord>> entry : adRecordMap.entrySet()) {
+            adCount = CollUtil.isEmpty(entry.getValue())?adCount:entry.getValue().size();
+            ++adCount;
+            //判断当日观看视频数已经达到风控条件预设的视频数
+            int firstAdCount = Integer.parseInt(limitMap.get("firstAdCount"));
+            if(adCount>=firstAdCount){
+                //过滤出ecpm值小于预设值的视频数
+                int ecpm = Integer.parseInt(limitMap.get("ecpm"));
+                long count = entry.getValue().stream().filter(
+                        s -> Objects.nonNull(s.getEcpm())&&(s.getEcpm().compareTo(BigDecimal.valueOf(ecpm))) < 0).count();
+                int exact = Math.toIntExact(count);
+                //判断小于预设值的视频数超出预设次数
+                int haveCount = Integer.parseInt(limitMap.get("haveCount"));
+                if(exact>haveCount){//把风控不满足的用户记录redis 进行封禁
+                    checkLock(entry,auditParam);
+                }
+            }
+        }
+
+    }
+
+    /**
+     * 用户锁定操作
+     */
+    private void checkLock(Map.Entry<String, List<YtDyzAdRecord>> entry, AuditUserParam auditParam) {
+        log.error("执行对低价值用户{}的定时风控操作", entry.getKey());
+        if(auditParam.getEffectTime() == 0){ //立即锁定
+            lockUser(entry.getKey(), UserStatusEnum.RISK.getCode());
+            redisService.setTimeOutHoursStr("unlock_"+entry.getKey(),auditParam.getAppId(), auditParam.getBannedLimit()*24L);
+        }else{//延迟锁定
+            redisService.setTimeOutHoursStr("lock_"+entry.getKey(), auditParam.getAppId(), auditParam.getEffectTime());
+            redisService.setTimeOutHoursStr("unlock_"+entry.getKey(),auditParam.getAppId(),
+                    auditParam.getBannedLimit()*24L + auditParam.getEffectTime());
+        }
+    }
+
+    /**
+     * 校验默认风控规则746
+     */
+    private void checkRisk746(RiskTemplateView revenueLimit,Map<String, List<YtDyzAdRecord>> adRecordMap,AuditUserParam auditParam) {
+        if(revenueLimit.getEnabled()!=1)return;
+        Map<String, String> revenueMap = revenueLimit.getConfigList().stream().collect(
+                Collectors.toMap(RiskConfigView::getFieldName, RiskConfigView::getConfigVal));
+        for (Map.Entry<String, List<YtDyzAdRecord>> entry : adRecordMap.entrySet()) {
+            List<YtDyzAdRecord> revenues = entry.getValue().stream().filter(
+                    s -> Objects.nonNull(s.getRevenue())&&(s.getRevenue().compareTo(BigDecimal.ZERO)) > 0).collect(Collectors.toList());
+            int incomeCount = 0;
+            incomeCount = CollUtil.isEmpty(revenues)?incomeCount:entry.getValue().size();
+            //判断当日获得收益的广告达到预设数值,触发风控规则
+            int rewardCount = Integer.parseInt(revenueMap.get("rewardCount"));
+            if(incomeCount>=rewardCount){
+                BigDecimal income = new BigDecimal(revenueMap.get("income"));
+                //获取最先的两条
+                BigDecimal reduce = revenues.stream().map(YtDyzAdRecord::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add);
+                if(reduce.compareTo(income)<0){
+                    checkLock(entry,auditParam);
+                }
+            }
+        }
+
+    }
+}

+ 172 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/controller/WxController.java

@@ -0,0 +1,172 @@
+package com.ytpm.arithmetic.controller;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSON;
+import com.ytpm.app.enums.AppTypeEnums;
+import com.ytpm.app.model.YtAppDefaultConfig;
+import com.ytpm.app.model.YtDyzPowerRecord;
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.param.WxLoginParam;
+import com.ytpm.app.view.WxDefaultConfig;
+import com.ytpm.app.view.WxLoginResult;
+import com.ytpm.app.view.WxUserInfo;
+import com.ytpm.arithmetic.dao.AppUserMapper;
+import com.ytpm.arithmetic.service.AppUserService;
+import com.ytpm.feign.RiskFeign;
+import com.ytpm.general.RepMessage;
+import com.ytpm.general.Result;
+import com.ytpm.handle.CustomerException;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+
+@Slf4j
+@Api(tags = "微信开放能力模块")
+@RestController
+@RequestMapping("/wx")
+public class WxController {
+    private final static String GRANT_TYPE = "authorization_code";
+    @Autowired
+    private AppUserMapper appUserMapper;
+    @Autowired
+    private RiskFeign riskFeign;
+    @Autowired
+    private AppUserService appUserService;
+
+    @PostMapping("/login")
+    @ApiOperation("微信登录")
+    @Transactional
+    public Result<YtDyzUser> wxLogin(@RequestBody WxLoginParam param, HttpServletRequest request) {
+        //根据应用获取配置调用微信接口登录
+        WxDefaultConfig defaultConfig = appUserMapper.getDefaultConfig(param.getAppType());
+        if(Objects.isNull(defaultConfig)){
+            throw new CustomerException("微信登录失败,未找到相应配置!");
+        }
+        param.setAppId(defaultConfig.getPlatformAppId());
+        WxLoginResult loginResult = getWechatLoginInfo(param.getWxCode(),param.getAppType(),defaultConfig.getAppId(),defaultConfig.getSecret());
+        if(Objects.isNull(loginResult)|| StrUtil.isBlank(loginResult.getOpenid())){
+            throw new CustomerException("微信登录失败,请刷新授权码!");
+        }
+        WxUserInfo wxUserInfo = getWechatUserInfo(loginResult.getAccess_token(),loginResult.getOpenid());
+        if(Objects.isNull(wxUserInfo)) {
+            throw new CustomerException("微信用户登录失败");
+        }
+        param.setLoginIp(getClientIp(request));
+        YtDyzUser old = appUserService.crudForNewTrans(param,wxUserInfo,loginResult);
+        //调用风控服务校验默认风控配置
+        old.setRiskCode("313");
+        Result<?> result = riskFeign.checkRisk(old);
+        if(result.getCode()!=200){
+            throw new CustomerException(result.getMessage());
+        }
+        return Result.resultOk(RepMessage.LOGIN_SUCCESS, old);
+    }
+
+    private String getClientIp(HttpServletRequest request) {
+        String xfHeader = request.getHeader("X-Forwarded-For");
+        if (xfHeader == null) {
+            return request.getRemoteAddr();
+        }
+        return xfHeader.split(",")[0]; // 可能会有多个IP,这里取第一个逗号前的IP
+    }
+
+    /**
+     * 获取微信用户信息
+     */
+    private WxUserInfo getWechatUserInfo(String accessToken, String openid) {
+        // 根据token和openid 获取用户信息
+        String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token="+accessToken+"&openid="+openid+"&lang=zh_CN";
+        String curUser = HttpUtil.get(userInfoUrl);
+        WxUserInfo wxUserInfo = JSON.parseObject(curUser, WxUserInfo.class);
+        log.error("获取的用户信息:{}",wxUserInfo);
+        return wxUserInfo;
+    }
+
+    /**
+     * 微信登录
+     */
+    private WxLoginResult getWechatLoginInfo(String wxCode,int appType,String appId,String secret) {
+
+        String wxLoginUrl;
+        if(AppTypeEnums.QNJZ.getCode() == appType){
+            wxLoginUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
+                    +appId+"&secret="+secret+"&code="+wxCode+"&grant_type="+GRANT_TYPE;
+        }else{
+            wxLoginUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appId
+                    +"&secret="+secret+"&code="+wxCode+"&grant_type="+GRANT_TYPE;
+        }
+        //拿到授权码 请求微信登录返回access_token
+        String result = HttpUtil.get(wxLoginUrl);
+        WxLoginResult loginResult = JSON.parseObject(result, WxLoginResult.class);
+        log.error("授权码获取的登录结果:{}",loginResult);
+        return loginResult;
+    }
+
+    @ApiOperation("获取微信默认配置项")
+    @GetMapping("/defaultConfig")
+    public Result<String> getWxDefaultConfig(int appType) {
+        WxDefaultConfig config = appUserMapper.getDefaultConfig(appType);
+        if(Objects.isNull(config)){
+            return Result.resultErr("应用类型有误!");
+        }
+       return Result.resultObjOk(config.getAppId());
+    }
+
+    @ApiOperation("体力增加")
+    @GetMapping("/addPower")
+    @Transactional(rollbackFor = Exception.class)
+    public Result<YtDyzUser> 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);
+        return Result.resultOk(RepMessage.ADD_SUCCESS);
+    }
+
+    @ApiOperation("保存应用默认配置")
+    @PostMapping("/saveAppConfig")
+    public Result<String> saveAppConfig(@RequestBody YtAppDefaultConfig defaultConfig){
+        appUserMapper.saveAppConfig(defaultConfig);
+        return Result.resultOk(RepMessage.SAVE_SUCCESS);
+    }
+
+    @ApiOperation("根据APP_ID获取配置")
+    @GetMapping("/getConfigs")
+    public List<WxDefaultConfig> getConfigs(@RequestParam(name = "appIds")String appIds){
+        List<WxDefaultConfig> configs = new ArrayList<>();
+        List<WxDefaultConfig> dyzConfigs = appUserMapper.getConfigByIds(appIds);
+        if(CollUtil.isNotEmpty(dyzConfigs)){configs.addAll(dyzConfigs);}
+        return configs;
+    }
+
+    @ApiOperation("删除默认配置")
+    @GetMapping("/delDefaultConfig")
+    public void delDefaultConfig(@RequestParam(name = "appId")String appId){
+        List<WxDefaultConfig> dyzConfig = appUserMapper.getConfigByIds(appId);
+        if(CollUtil.isNotEmpty(dyzConfig)){
+            appUserMapper.delByAppId(appId);
+        }
+    }
+}

+ 47 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/dao/AdRecordMapper.java

@@ -0,0 +1,47 @@
+package com.ytpm.arithmetic.dao;
+
+import com.ytpm.app.model.YtDyzAdRecord;
+import com.ytpm.app.param.YtAppUserListParam;
+import com.ytpm.middle.view.AppRevenueHourVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Mapper
+public interface AdRecordMapper {
+    /**
+     * 新增广告记录
+     */
+    void addOne(YtDyzAdRecord adRecord);
+    /**
+     * 查询用户的广告记录
+     */
+    List<YtDyzAdRecord> getByUserId(@Param("userId") String userId,@Param("adsourceType") Integer adsourceType);
+
+    /**
+     * 查询激励视频记录
+     */
+    List<YtDyzAdRecord> getByUserIds(@Param("userIds") String userIds);
+
+    /**
+     * 查询应用分小时收益数据
+     */
+    List<AppRevenueHourVO> getHourRevenue(@Param("appId") String appId,@Param("type")int type);
+
+    /**
+     * 根据应用统计激励广告数
+     */
+    int countByAppIds(@Param("appIds")String appIds);
+
+    /**
+     * 根据时间统计收益
+     */
+    BigDecimal getRevenueByTime(YtAppUserListParam param);
+
+    /**
+     * 查询今日广告
+     */
+    List<YtDyzAdRecord> getTodayRecord(@Param("appIds")String appIds,@Param("type")Integer type);
+}

+ 180 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/dao/AppUserMapper.java

@@ -0,0 +1,180 @@
+package com.ytpm.arithmetic.dao;
+
+import com.ytpm.agent.param.AuditUserParam;
+import com.ytpm.agent.view.AgentAuditCheckVO;
+import com.ytpm.app.model.YtAppDefaultConfig;
+import com.ytpm.app.model.YtDyzPowerRecord;
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.param.AppUserParam;
+import com.ytpm.app.param.YtAppUserListParam;
+import com.ytpm.app.view.HourCountView;
+import com.ytpm.app.view.WxDefaultConfig;
+import com.ytpm.app.view.YtAppUserListView;
+import com.ytpm.middle.view.AppRankingListVO;
+import com.ytpm.middle.view.AppUserHourVO;
+import com.ytpm.middle.view.UserRankingListVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+@Mapper
+public interface AppUserMapper {
+
+    /**
+     * openid查询用户信息
+     */
+    YtDyzUser getYtAppUser(@Param("openid")String openid,@Param("ditchId")long ditchId);
+
+    /**
+     * 主键查询
+     */
+    YtDyzUser selectPrimaryKey(@Param("userId")String userId);
+
+    /**
+     * 新增应用用户
+     */
+    void addOne(YtDyzUser user);
+
+    /**
+     * 增加体力
+     */
+    void addOnePower(@Param("userId")String userId);
+    /**
+     * 减少体力
+     */
+    void subOnePower(@Param("userId")String userId);
+
+    /**
+     * 体力增加记录
+     */
+    void addPowerRecord(YtDyzPowerRecord record);
+
+    /**
+     * 修改用户信息
+     */
+    void updateUser(YtDyzUser newUser);
+    /**
+     * 查询所有玩家
+     */
+    List<YtAppUserListView> queryAll(YtAppUserListParam param);
+
+    /**
+     * 查询所有玩家
+     * 按照注册时间范围查询
+     * @param startTime 开始时间(可选)
+     * @param endTime 结束时间(可选)
+     */
+    List<YtDyzUser> queryAllByTime(
+            @Param("startTime") Date startTime,
+            @Param("endTime") Date endTime,
+            @Param("appIdList") List<String> appIdList
+    );
+
+    List<HourCountView> countUsersByHour(@Param("startTime") Date startTime,
+                                         @Param("endTime") Date endTime,
+                                         @Param("appIdList") List<String> appIdList
+    );
+
+    List<YtDyzUser> queryTodayBanned(@Param("startTime") Date startTime, @Param("endTime") Date endTime, @Param("userIdList") List<String> userIdList);
+
+    /**
+     * 查询用户信息
+     */
+    List<YtDyzUser> getUserList(AppUserParam param);
+
+    /**
+     * 根据应用类型查询应用默认配置
+     */
+    WxDefaultConfig getDefaultConfig(@Param("appType") int appType);
+
+    /**
+     * 根据微信openid查询用户
+     */
+    List<YtDyzUser> queryByOpenid(@Param("openid")String openid);
+
+    /**
+     * 根据设备ID查询平台ID
+     */
+    String getByDeviceId(@Param("deviceId")String deviceId,@Param("openid")String openid);
+
+    /**
+     * 保存应用配置
+     */
+    void saveAppConfig(YtAppDefaultConfig defaultConfig);
+
+    /**
+     * 根据APP_ID获取配置
+     */
+    List<WxDefaultConfig> getConfigByIds(@Param("appIds") String appIds);
+
+    /**
+     * 根据应用统计广告数
+     */
+    int getAdCount(@Param("appIds") String appIds);
+
+    /**
+     * 根据应用统计收益
+     */
+    BigDecimal getRevenueCount(@Param("appIds") String appIds);
+
+    /**
+     * 查询应用排行榜
+     */
+    List<AppRankingListVO> getAppRanking(@Param("sortBy") int sortBy,@Param("limit") int limit);
+
+    /**
+     * 查询用户排行榜数据
+     */
+    List<UserRankingListVO> getUserRanking(@Param("sortBy") int sortBy,@Param("limit") int limit);
+
+    /**
+     * 根据应用查询用户数量
+     */
+    int countUserByAppIds(@Param("appIds")String appIds);
+
+    /**
+     * 根据应用统计预估收益
+     */
+    BigDecimal countRevenueByAppIds(@Param("appIds")String appIds);
+
+    /**
+     * 根据应用查询风控用户数
+     */
+    int countBannedByAppIds(@Param("appIds")String appIds);
+
+    /**
+     * 统计注册用户数
+     */
+    int countRegistryUser(@Param("appId")String appId, @Param("type")int type);
+    /**
+     * 统计登录用户数
+     */
+    int countLoginUser(@Param("appId")String appId, @Param("type")int type);
+
+    /**
+     * 分时统计注册数
+     */
+    List<AppUserHourVO> countRegistryHour(@Param("appId")String appId, @Param("type")int type);
+
+    /**
+     * 分时统计登录
+     */
+    List<AppUserHourVO> countLoginHour(@Param("appId")String appId, @Param("type")int type);
+
+    /**
+     * 根据应用ID删除默认配置
+     */
+    void delByAppId(@Param("appId")String appId);
+
+    void unlockUser(@Param("userIds")String userIds);
+
+    List<YtDyzUser> queryByUserIds(@Param("userIds")String userIds);
+
+    /**
+     * 分类型查询用户当天视频广告
+     */
+    List<AgentAuditCheckVO> queryTodayUserAd(AuditUserParam auditParam);
+}

+ 25 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/dao/LoginRecordMapper.java

@@ -0,0 +1,25 @@
+package com.ytpm.arithmetic.dao;
+
+import com.ytpm.app.model.YtDyzLoginRecord;
+import com.ytpm.app.param.AppUserQueryParam;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface LoginRecordMapper {
+    /**
+     * 增加用户登录记录
+     */
+    void insertOne(YtDyzLoginRecord loginRecord);
+
+    /**
+     * 查询用户登录记录
+     */
+    List<YtDyzLoginRecord> getLoginRecords(@Param("userId")String userId);
+
+    List<YtDyzLoginRecord> getLoginRecordByIds(@Param("userIds")String userIds);
+
+    List<String> queryLoginCount(AppUserQueryParam appUserQueryParam);
+}

+ 38 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/dao/QuestionMapper.java

@@ -0,0 +1,38 @@
+package com.ytpm.arithmetic.dao;
+
+import com.ytpm.app.model.YtDyzAnswerRecord;
+import com.ytpm.app.view.QuestionListView;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface QuestionMapper {
+
+    /**
+     * 获取题库
+     */
+    List<QuestionListView> questionList(@Param("appId")String appId);
+
+    /**
+     * 回答问题
+     */
+    void saveAnswerRecord(YtDyzAnswerRecord record);
+
+    /**
+     * 获取最后一次的问题ID
+     */
+    String getLastQuestionId(@Param("userId") String userId);
+
+    /**
+     * 获取答题数
+     * @param type 1-今日 2-历史
+     */
+    Integer getAnswerCount(@Param("userId")String userId, @Param("type") int type);
+
+    /**
+     * 查询答题历史记录
+     */
+    List<YtDyzAnswerRecord> getAnswerRecords(@Param("userId") String userId);
+}

+ 74 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/monitor/RedisKeyExpirationListener.java

@@ -0,0 +1,74 @@
+package com.ytpm.arithmetic.monitor;
+
+import com.ytpm.agent.enums.UserStatusEnum;
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.arithmetic.dao.AppUserMapper;
+import com.ytpm.risk.enums.BannedTypeEnum;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import javax.annotation.Resource;
+import java.util.Objects;
+
+/**
+ * redis 监听器
+ */
+@Slf4j(topic = "RedisKeyExpire")
+@Component
+@RefreshScope
+public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
+
+    @Resource
+    private AppUserMapper appUserMapper;
+
+    /**
+     * Creates new {@link } for {@code __keyevent@*__:expired} messages.
+     *
+     * @param listenerContainer must not be {@literal null}.
+     */
+    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
+        super(listenerContainer);
+    }
+ 
+    /**
+     * 监听redis过期的 key 进行处理
+     */
+    @Override
+    public void onMessage(Message message, byte[] pattern) {
+        String key = String.valueOf(message);
+        log.error("App-Service 发现预定风控用户:{}",key);
+        if(key.startsWith("lock_")){
+            String[] arr = key.split("_");
+            lockUser(arr[1], UserStatusEnum.RISK.getCode());
+        }
+        /**
+         * 用户解锁
+         */
+        if(key.startsWith("unlock_")){
+            String[] arr = key.split("_");
+            YtDyzUser dyzUser = new YtDyzUser();
+            dyzUser.setUserId(arr[1]);
+            dyzUser.setUserStatus(1);
+            appUserMapper.updateUser(dyzUser);
+        }
+    }
+
+    /**
+     * 锁定用户
+     */
+     private void lockUser(@RequestParam("userId")String userId, @RequestParam("userStatus")Integer userStatus){
+        YtDyzUser dyzUser = appUserMapper.selectPrimaryKey(userId);
+        YtDyzUser newUser = new YtDyzUser();
+        newUser.setUserId(userId);
+        newUser.setUserStatus(userStatus);
+        newUser.setRiskReason(BannedTypeEnum.CHANNEL.getDesc()+"禁止登录");
+        if(Objects.nonNull(dyzUser)){
+            appUserMapper.updateUser(newUser);
+        }
+    }
+}

+ 275 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/redis/RedisService.java

@@ -0,0 +1,275 @@
+package com.ytpm.arithmetic.redis;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+@Service
+public class RedisService {
+    @Autowired
+    StringRedisTemplate stringRedisTemplate;
+
+
+    @Resource(name = "stringRedisTemplate")
+    ValueOperations<String, String> valOpsStr;
+
+    @Autowired
+    RedisTemplate<Object, Object> redisTemplate;
+
+    @Autowired(required = false)
+    public void setRedisTemplate(RedisTemplate redisTemplate) {
+        //序列化key值,防止key值前面乱码
+        RedisSerializer stringSerializer = new StringRedisSerializer();
+        redisTemplate.setKeySerializer(stringSerializer);
+        redisTemplate.setHashKeySerializer(stringSerializer);
+        this.redisTemplate = redisTemplate;
+    }
+
+    @Resource(name = "redisTemplate")
+    ValueOperations<Object, Object> valOpsObj;
+
+    /**
+     * 根据指定key获取String
+     * @param key
+     * @return
+     */
+    public String getStr(String key){
+        return valOpsStr.get(key);
+    }
+
+
+    /**
+     * 查询并获取key的有效时间
+     */
+    public Long getExpire(String key) {
+        return valOpsObj.getOperations().getExpire(key, TimeUnit.MINUTES);
+    }
+
+    /**
+     * 设置Str缓存
+     * @param key
+     * @param val
+     */
+    public void setStr(String key, String val){
+        valOpsStr.set(key,val);
+    }
+
+    /**
+     * 设置Str缓存 timeOut单位  毫秒
+     * @param key
+     * @param val
+     */
+    public void setTimeOutStr(String key, String val ,long timeOut){
+        valOpsStr.set(key,val,timeOut, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * 判断是否存在key
+     * @param key
+     * @return
+     */
+    public Boolean hasKey(String key) {
+        return redisTemplate.hasKey(key);
+    }
+
+    /**
+     * 设置Str缓存 timeOut单位  小时
+     * @param key
+     * @param val
+     * @param timeOut
+     */
+    public void setTimeOutHoursStr(String key, String val ,long timeOut){
+        valOpsStr.set(key,val,timeOut, TimeUnit.HOURS);
+    }
+
+    /**
+     * 设置Str缓存 timeOut单位  分钟
+     * @param key
+     * @param val
+     * @param timeOut
+     */
+    public void setTimeOutMinutesStr(String key, String val ,long timeOut){
+        valOpsStr.set(key,val,timeOut, TimeUnit.MINUTES);
+    }
+
+    /**
+     * 删除指定key
+     * @param key
+     */
+    public void del(String key){
+        stringRedisTemplate.delete(key);
+    }
+
+    /**
+     * 设置指定key值的超时时间
+     * @param key
+     */
+    public void expire(String key,long timeOut){
+        TimeUnit timeUnit=TimeUnit.MILLISECONDS;
+        stringRedisTemplate.expire(key,timeOut, timeUnit);
+    }
+
+    /**
+     * 根据前缀批量删除
+     * @param keysPrefix
+     */
+    public void delKeys(String keysPrefix){
+        Set<String> set = stringRedisTemplate.keys(keysPrefix);
+        stringRedisTemplate.delete(set);
+    }
+
+
+
+    /**
+     * 根据指定o获取Object
+     * @param o
+     * @return
+     */
+    public Object getObj(Object o){
+        return valOpsObj.get(o);
+    }
+
+    /**
+     * 设置obj缓存
+     * @param o1
+     * @param o2
+     */
+    public void setObj(Object o1, Object o2){
+        valOpsObj.set(o1, o2);
+    }
+    /**
+     * 设置obj缓存
+     * @param o1
+     * @param o2
+     */
+    public void setObj(Object o1, Object o2,long timeout){
+        valOpsObj.set(o1, o2,timeout);
+    }
+
+
+    /**
+     * 删除Obj缓存
+     * @param o
+     */
+    public void delObj(Object o){
+        redisTemplate.delete(o);
+    }
+
+
+    /**
+     * 自增1
+     * @param key
+     * @return
+     */
+    public long incr(String key){
+        long num = valOpsStr.increment(key,1);
+        return num;
+    }
+    /**
+     * 自增1
+     * @param key
+     * @return
+     */
+    public long incrByExp(String key,long timeout){
+        long num = valOpsStr.increment(key,1);
+        TimeUnit timeUnit=TimeUnit.MILLISECONDS;
+        stringRedisTemplate.expire(key,timeout, timeUnit);
+        return num;
+    }
+    /**
+     * 自增
+     * @param key
+     * @param delta  自增值
+     * @return
+     */
+    public long incrByExp(String key,int delta,long timeout){
+        long num = valOpsStr.increment(key,delta);
+        TimeUnit timeUnit=TimeUnit.MILLISECONDS;
+        stringRedisTemplate.expire(key,timeout, timeUnit);
+        return num;
+    }
+    /**
+     * 自增
+     * @param key
+     * @param delta  自增值
+     * @return
+     */
+    public long incr(String key,long delta){
+        return valOpsStr.increment(key,delta);
+    }
+
+    /**
+     * 获取应用用户ID
+     */
+    public synchronized String getAppUserId() {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMdd");
+        LocalDate currentDate = LocalDate.now();
+        LocalDate yesterday = currentDate.minusDays(1);
+        String todayKey = currentDate.format(formatter);
+        String yesterdayKey = yesterday.format(formatter);
+        if(this.hasKey(todayKey)){
+            this.incr(todayKey);
+            return this.getStr(todayKey);
+        }
+        this.del(yesterdayKey);
+        this.setStr(todayKey, todayKey+"00001");
+        return this.getStr(todayKey);
+    }
+
+    public Long getDitchId(){
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
+        LocalDate currentDate = LocalDate.now();
+        LocalDate yesterday = currentDate.minusDays(1);
+        String todayKey = currentDate.format(formatter);
+        String yesterdayKey = yesterday.format(formatter);
+        String prefix = "Ditch_";
+        if(this.hasKey(prefix+todayKey)){
+            this.incr(prefix+todayKey);
+            return Long.parseLong(this.getStr(prefix+todayKey));
+        }
+        this.del(yesterdayKey);
+        this.setStr(prefix+todayKey, todayKey+"01");
+        return Long.parseLong(this.getStr(prefix+todayKey));
+    }
+
+    /**
+     *
+     * 生成业务单编号
+     * @return
+     */
+    public String generateOrderNo(String key){
+        Calendar cl = Calendar.getInstance();
+        int year = cl.get(Calendar.YEAR);
+        int month = cl.get(Calendar.MONTH) + 1;
+        int day = cl.get(Calendar.DATE);
+        String orderNoPrefix = getTimeStr(year)+getTimeStr(month)+getTimeStr(day);
+        if(this.hasKey(key)){
+            String old = this.getStr(key);
+            String sub = old.substring(0,8);
+            if(sub.equals(orderNoPrefix)){
+                long incr = this.incr(key);
+                return String.valueOf(incr);
+            }
+        }
+        this.setStr(key,orderNoPrefix+"001");
+        return orderNoPrefix+"001";
+    }
+
+    private String getTimeStr(int num){
+        if(num>9) {
+            return String.valueOf(num);
+        }
+        return "0"+num;
+    }
+}

+ 49 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/service/AdService.java

@@ -0,0 +1,49 @@
+package com.ytpm.arithmetic.service;
+
+import com.ytpm.agent.view.AgentAdGroupStaticsVO;
+import com.ytpm.agent.view.AgentTopCountView;
+import com.ytpm.app.param.DyzAdRecordParam;
+import com.ytpm.general.Result;
+import com.ytpm.middle.view.DashboardRankingListVO;
+import com.ytpm.middle.view.DashboardRevenueVO;
+import com.ytpm.middle.view.DashboardRiskVO;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+public interface AdService {
+    /**
+     * 保存广告记录
+     */
+    Result<?> saveRecord(DyzAdRecordParam param);
+
+    /**
+     * 根据应用查询广告数量
+     */
+    Map<String, BigDecimal> getAdCount(String appIds);
+
+    /**
+     * 查询排行榜信息
+     */
+    DashboardRankingListVO queryRankingList(Integer sortBy,Integer limit);
+    /**
+     * 查询各渠道应用收益分时统计
+     */
+    DashboardRevenueVO revenueStatics(String apkIds);
+
+    /**
+     * 查询代理商顶部数据统计
+     */
+    AgentTopCountView getAppTopCount(String appIds);
+
+    /**
+     * 查询用户风控分时数据统计
+     */
+    DashboardRiskVO userStatics(String appIds);
+
+    /**
+     * 查询代理商首页广告数据统计
+     */
+    List<AgentAdGroupStaticsVO> getAgentProfit(String appIds);
+}

+ 18 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/service/AppUserService.java

@@ -0,0 +1,18 @@
+package com.ytpm.arithmetic.service;
+
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.param.WxLoginParam;
+import com.ytpm.app.view.WxLoginResult;
+import com.ytpm.app.view.WxUserInfo;
+
+public interface AppUserService {
+    /**
+     * openid查询用户信息
+     */
+    YtDyzUser selectByOpenid(String openid, Long ditchId);
+
+    /**
+     * 子事务处理用户crud
+     */
+    YtDyzUser crudForNewTrans(WxLoginParam param, WxUserInfo wxUserInfo, WxLoginResult loginResult);
+}

+ 17 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/service/QuestionService.java

@@ -0,0 +1,17 @@
+package com.ytpm.arithmetic.service;
+
+import com.ytpm.app.param.AnswerRecordParam;
+import com.ytpm.app.view.QuestionListView;
+import com.ytpm.general.Result;
+import com.ytpm.general.ResultTable;
+
+public interface QuestionService {
+    /**
+     * 获取题库
+     */
+    ResultTable<QuestionListView> questionList();
+    /**
+     * 回答问题
+     */
+    Result<String> answerQuestion(AnswerRecordParam param);
+}

+ 283 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/service/impl/AdServiceImpl.java

@@ -0,0 +1,283 @@
+package com.ytpm.arithmetic.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.RandomUtil;
+import com.ytpm.advertise.enums.AdPlatformTypeEnum;
+import com.ytpm.agent.enums.UserStatusEnum;
+import com.ytpm.agent.view.AgentAdGroupStaticsVO;
+import com.ytpm.agent.view.AgentTopCountView;
+import com.ytpm.app.model.YtDyzAdRecord;
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.param.DyzAdRecordParam;
+import com.ytpm.arithmetic.dao.AdRecordMapper;
+import com.ytpm.arithmetic.dao.AppUserMapper;
+import com.ytpm.arithmetic.service.AdService;
+import com.ytpm.general.RepMessage;
+import com.ytpm.general.Result;
+import com.ytpm.general.StatusCode;
+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 org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+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;
+
+@Service
+@RefreshScope
+public class AdServiceImpl implements AdService {
+
+    @Autowired
+    private AdRecordMapper adRecordMapper;
+    @Autowired
+    private AppUserMapper appUserMapper;
+
+    @Value(" ${risk.config.banned.tips}")
+    private String tips;
+
+    /**
+     * 保存广告记录
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Result<?> saveRecord(DyzAdRecordParam param) {
+        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()){
+//            Result<?> result = riskFeign.checkAdRisk(user);
+//            if(result.getCode()!=200){
+//                throw new CustomerException(result.getMessage());
+//            }
+//        }
+        return Result.resultOk(RepMessage.SAVE_SUCCESS);
+    }
+
+    private String getTipsMsg(){
+        String[] split = tips.split(",");
+        return split[RandomUtil.randomInt(split.length)];
+    }
+    /**
+     * 根据应用查询广告数量
+     */
+    @Override
+    public Map<String,BigDecimal> getAdCount(String appIds) {
+        Map<String, BigDecimal> map = new HashMap<>();
+        //查询这些app的用户统计广告数量相加
+        int adCount = appUserMapper.getAdCount(appIds);
+        BigDecimal total = appUserMapper.getRevenueCount(appIds);
+        map.put("adCount", BigDecimal.valueOf(adCount));
+        map.put("expectRevenue", total);
+        return map;
+    }
+
+    /**
+     * 查询排行榜信息
+     */
+    @Override
+    public DashboardRankingListVO queryRankingList(Integer sortBy,Integer limit) {
+        //查询前五个应用名称 当日收益  用户数量
+        List<AppRankingListVO> appRankingList = appUserMapper.getAppRanking(sortBy,limit);
+        //查询前五个用户  所属应用  视频数 总收益 ecpm
+        List<UserRankingListVO> userRankingList = appUserMapper.getUserRanking(sortBy,limit);
+        return new DashboardRankingListVO(appRankingList,userRankingList);
+    }
+
+    /**
+     *  查询各渠道应用收益分时统计
+     */
+    @Override
+    public DashboardRevenueVO revenueStatics(String apkIds) {
+        DashboardRevenueVO vo = new DashboardRevenueVO();
+        List<DashboardAppRevenueVO> appRevenueList = new ArrayList<>();
+        //查询出各应用的用户及收益数据
+        List<String> appIdList = Arrays.asList(apkIds.split(","));
+        int index = appIdList.size();
+        // 由于子表数据庞大导致range查询低于const
+        CountDownLatch countDownLatch = new CountDownLatch(index);
+        DashboardAppRevenueVO appRevenueVO;
+        String appId;
+        do {
+            --index;
+            appId = appIdList.get(index);
+            //根据应用ID查询收益数据及个小时数据  1-今日
+            List<AppRevenueHourVO> todayRevenues =  adRecordMapper.getHourRevenue(appId,1);
+            Map<String, BigDecimal> todayHourMap = todayRevenues.stream().collect(Collectors.toMap(AppRevenueHourVO::getHour, AppRevenueHourVO::getRevenue));
+            appRevenueVO = new DashboardAppRevenueVO();
+            appRevenueVO.setAppId(appId);
+            //统计今日数据
+            BigDecimal todayRevenue = todayHourMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
+            appRevenueVO.setTodayHourRevenueMap(todayHourMap);
+            appRevenueVO.setTodayRevenue(todayRevenue);
+            //统计昨日数据
+            List<AppRevenueHourVO> yesterdayRevenues =  adRecordMapper.getHourRevenue(appId,2);
+            Map<String, BigDecimal> yesterdayHourMap = yesterdayRevenues.stream().collect(Collectors.toMap(AppRevenueHourVO::getHour, AppRevenueHourVO::getRevenue));
+            appRevenueVO.setYesterdayHourRevenueMap(yesterdayHourMap);
+            BigDecimal yesterdayRevenue = yesterdayHourMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
+            appRevenueVO.setYesterdayRevenue(yesterdayRevenue);
+            //统计本月数据
+            List<AppRevenueHourVO> monthRevenues =  adRecordMapper.getHourRevenue(appId,3);
+            BigDecimal monthRevenue = monthRevenues.stream().map(AppRevenueHourVO::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add);
+            appRevenueVO.setMonthRevenue(monthRevenue);
+            appRevenueList.add(appRevenueVO);
+            countDownLatch.countDown();
+        }while (index>0);
+        vo.setAppRevenueList(appRevenueList);
+        //直接统计各应用的收益
+        vo.setTodayTotalRevenue(appRevenueList.stream().map(DashboardAppRevenueVO::getTodayRevenue).reduce(BigDecimal.ZERO, BigDecimal::add));
+        vo.setYesterdayTotalRevenue(appRevenueList.stream().map(DashboardAppRevenueVO::getYesterdayRevenue).reduce(BigDecimal.ZERO, BigDecimal::add));
+        vo.setMonthTotalRevenue(appRevenueList.stream().map(DashboardAppRevenueVO::getMonthRevenue).reduce(BigDecimal.ZERO, BigDecimal::add));
+        return vo;
+    }
+
+    /**
+     * 查询代理商顶部数据
+     */
+    @Override
+    public AgentTopCountView getAppTopCount(String appIds) {
+        AgentTopCountView view = new AgentTopCountView();
+        int dyzAdCount = adRecordMapper.countByAppIds(appIds);
+        view.setAdCount(dyzAdCount);
+        int dyzUserCount = appUserMapper.countUserByAppIds(appIds);
+        view.setUserCount(dyzUserCount);
+        BigDecimal totalRevenue = new BigDecimal(0);
+        BigDecimal dyzRevenue = appUserMapper.countRevenueByAppIds(appIds);
+        if(Objects.nonNull(dyzRevenue)){totalRevenue = totalRevenue.add(dyzRevenue);}
+        view.setRevenue(totalRevenue);
+        int bannedCount = appUserMapper.countBannedByAppIds(appIds);
+        view.setRiskCount(bannedCount);
+        return view;
+    }
+
+    /**
+     * 查询用户风控分时数据统计
+     */
+    @Override
+    public DashboardRiskVO userStatics(String appId) {
+        DashboardRiskVO vo = new DashboardRiskVO();
+        vo.setTodayRegistryCount(appUserMapper.countRegistryUser(appId,1));
+        vo.setYesterdayRegistryCount(appUserMapper.countRegistryUser(appId,2));
+        vo.setMonthRegistryCount(appUserMapper.countRegistryUser(appId,3));
+
+        vo.setTodayLoginCount(appUserMapper.countLoginUser(appId,1));
+        vo.setYesterdayLoginCount(appUserMapper.countLoginUser(appId,2));
+        vo.setMonthLoginCount(appUserMapper.countLoginUser(appId,3));
+
+        List<AppUserHourVO> registryHour = appUserMapper.countRegistryHour(appId,1);
+        Map<String, Integer> registryMap = registryHour.stream().collect(Collectors.toMap(AppUserHourVO::getTime, AppUserHourVO::getCount));
+        vo.setTodayRegistryHourMap(registryMap);
+
+        List<AppUserHourVO> loginHour = appUserMapper.countLoginHour(appId,1);
+        Map<String, Integer> loginMap = loginHour.stream().collect(Collectors.toMap(AppUserHourVO::getTime, AppUserHourVO::getCount));
+        vo.setTodayLoginHourMap(loginMap);
+        return vo;
+    }
+
+    /**
+     * 查询代理商首页广告数据统计
+     */
+    @Override
+    public List<AgentAdGroupStaticsVO> getAgentProfit(String appIds) {
+        //查询今日的 按小时 按广告平台分组数据
+        List<AgentAdGroupStaticsVO> vos = new ArrayList<>();
+        // 需要查询各 app 今日的广告记录 然后在内存中按照  广告平台分组 计算平均ecpm值, 并且设置每小时的
+        List<YtDyzAdRecord> records = new ArrayList<>();
+        List<YtDyzAdRecord> dyzRecords = adRecordMapper.getTodayRecord(appIds,1);
+        if(CollUtil.isNotEmpty(dyzRecords)){records.addAll(dyzRecords);}
+        //昨日所有
+        List<YtDyzAdRecord> yestRecords = new ArrayList<>();
+        List<YtDyzAdRecord> dyzYestRecords = adRecordMapper.getTodayRecord(appIds,2);
+        if(CollUtil.isNotEmpty(dyzYestRecords)){yestRecords.addAll(dyzYestRecords);}
+        //本月所有
+        List<YtDyzAdRecord> monthRecords = new ArrayList<>();
+        List<YtDyzAdRecord> dyzMonthRecords = adRecordMapper.getTodayRecord(appIds,3);
+        if(CollUtil.isNotEmpty(dyzMonthRecords)){monthRecords.addAll(dyzMonthRecords);}
+        //广告平台分组
+        Map<String, List<YtDyzAdRecord>> networkMap = records.stream().collect(
+                Collectors.groupingBy(YtDyzAdRecord::getNetworkFormId));
+        //按广告平台分组统计昨天和本月的收益&ecpm
+        BigDecimal yestRevenue = yestRecords.stream().map(YtDyzAdRecord::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add);
+        BigDecimal yestEcpm = yestRecords.stream().map(YtDyzAdRecord::getEcpm).reduce(BigDecimal.ZERO, BigDecimal::add);
+        BigDecimal monthRevenue = monthRecords.stream().map(YtDyzAdRecord::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add);
+        BigDecimal monthEcpm = monthRecords.stream().map(YtDyzAdRecord::getEcpm).reduce(BigDecimal.ZERO, BigDecimal::add);
+        AgentAdGroupStaticsVO vo;
+        for (Map.Entry<String, List<YtDyzAdRecord>> adEntry : networkMap.entrySet()) {
+            vo = new AgentAdGroupStaticsVO();
+            vo.setNetowrkId(adEntry.getKey());
+            vo.setNetowrkName(networkMap.get(adEntry.getKey()).get(0).getNetworkName());
+            //设置收益
+            vo.setTodayRevenue(adEntry.getValue().stream().map(YtDyzAdRecord::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add));
+            vo.setYesterdayRevenue(yestRevenue);
+            vo.setMonthRevenue(monthRevenue);
+            //设置ecpm
+            BigDecimal totalEcpm = adEntry.getValue().stream().map(YtDyzAdRecord::getEcpm).reduce(BigDecimal.ZERO, BigDecimal::add);
+            vo.setTodayEcpm(totalEcpm.divide(new BigDecimal(adEntry.getValue().size()), 2, RoundingMode.HALF_UP));
+            vo.setYesterdayEcpm(yestEcpm.divide(new BigDecimal(yestRecords.size()),2,RoundingMode.HALF_UP));
+            vo.setMonthEcpm(monthEcpm.divide(new BigDecimal(monthRecords.size()),2,RoundingMode.HALF_UP));
+            //设置分小时报表
+            Map<LocalDateTime, List<YtDyzAdRecord>> hourMap = adEntry.getValue().stream().collect(Collectors.groupingBy(s ->
+                    DateUtil.parse(s.getFinishTime(),"yyyy-MM-dd HH").toLocalDateTime()));
+            Map<String, BigDecimal> ecpmMap = new HashMap<>();
+            Map<String, BigDecimal> revenueMap = new HashMap<>();
+            for (Map.Entry<LocalDateTime, List<YtDyzAdRecord>> entry : hourMap.entrySet()) {
+                BigDecimal reduce = entry.getValue().stream().map(YtDyzAdRecord::getEcpm).reduce(BigDecimal.ZERO, BigDecimal::add);
+                ecpmMap.put(entry.getKey().toString(),reduce.divide(new BigDecimal(entry.getValue().size()), RoundingMode.HALF_UP));
+                revenueMap.put(entry.getKey().toString(),hourMap.get(entry.getKey()).stream().map(YtDyzAdRecord::getRevenue).reduce(BigDecimal.ZERO, BigDecimal::add));
+            }
+            vo.setTodayHourEcpmMap(ecpmMap);
+            vo.setTodayHourRevenueMap(revenueMap);
+            vos.add(vo);
+        }
+        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);
+        adRecord.setRecordId(IdUtil.fastSimpleUUID());
+        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);
+    }
+}

+ 157 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/service/impl/AppUserServiceImpl.java

@@ -0,0 +1,157 @@
+package com.ytpm.arithmetic.service.impl;
+
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import com.ytpm.agent.enums.UserStatusEnum;
+import com.ytpm.app.model.YtDyzLoginRecord;
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.param.WxLoginParam;
+import com.ytpm.app.view.WxLoginResult;
+import com.ytpm.app.view.WxUserInfo;
+import com.ytpm.arithmetic.dao.AppUserMapper;
+import com.ytpm.arithmetic.dao.LoginRecordMapper;
+import com.ytpm.arithmetic.dao.QuestionMapper;
+import com.ytpm.arithmetic.redis.RedisService;
+import com.ytpm.arithmetic.service.AppUserService;
+import com.ytpm.constant.StrConstant;
+import com.ytpm.handle.CustomerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.Objects;
+
+@Service
+@RefreshScope
+public class AppUserServiceImpl implements AppUserService {
+
+    @Autowired
+    private AppUserMapper appUserMapper;
+    @Autowired
+    private LoginRecordMapper loginRecordMapper;
+    @Autowired
+    private QuestionMapper questionMapper;
+    @Autowired
+    private RedisService redisService;
+    @Value("${risk.config.banned.tips}")
+    private String tips;
+
+    /**
+     * openid查询用户信息
+     */
+    @Override
+    public YtDyzUser selectByOpenid(String openid,Long ditchId) {
+        return appUserMapper.getYtAppUser(openid,ditchId);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
+    public YtDyzUser crudForNewTrans(WxLoginParam param, WxUserInfo wxUserInfo, WxLoginResult loginResult) {
+        //处理用户注册或登录更新信息
+        YtDyzUser old = appUserMapper.getYtAppUser(loginResult.getOpenid(), param.getDitchId());
+        if(Objects.isNull(old)) {
+            old = new YtDyzUser();
+            registryUser(param,wxUserInfo,loginResult, old);
+        }else{
+            deadWithUserCrud(old,wxUserInfo,param);
+        }
+        //设置最后一次答题问题ID、今日答题数、历史答题数
+        setExtInfo(old,wxUserInfo.getHeadimgurl());
+        // 添加用户登录记录
+        addLoginRecord(param,old.getUserId());
+        return old;
+    }
+    /**
+     * 设置扩展信息
+     */
+    private void setExtInfo(YtDyzUser old, String headimgurl) {
+        old.setHeadImg(headimgurl);
+            old.setLastQuestionId(questionMapper.getLastQuestionId(old.getUserId()));
+        old.setTodayAnswerCount(questionMapper.getAnswerCount(old.getUserId(),1));
+        old.setHistoryAnswerCount(questionMapper.getAnswerCount(old.getUserId(),2));
+        old.setAnswerRecordList(questionMapper.getAnswerRecords(old.getUserId()));
+        old.setLoginRecordList(loginRecordMapper.getLoginRecords(old.getUserId()));
+    }
+
+    /**
+     * 增加用户登录记录
+     */
+    private void addLoginRecord(WxLoginParam param,String userId) {
+        YtDyzLoginRecord loginRecord = new YtDyzLoginRecord();
+        loginRecord.setRecordId(IdUtil.fastSimpleUUID());
+        loginRecord.setUserId(userId);
+        loginRecord.setLoginTime(new Date());
+        loginRecord.setDeviceBrand(param.getBrand());
+        loginRecord.setDeviceModel(param.getModel());
+        loginRecord.setLoginIp(param.getLoginIp());
+        loginRecord.setOperator(param.getIpOperator());
+        loginRecord.setIpAddr(param.getIpLocation());
+        loginRecord.setPhoneJson(param.getPhoneJson());
+        loginRecordMapper.insertOne(loginRecord);
+    }
+
+    private String getTipsMsg(){
+        String[] split = tips.split(",");
+        return split[RandomUtil.randomInt(split.length)];
+    }
+    /**
+     * 处理用户数据
+     */
+    private void deadWithUserCrud(YtDyzUser old, WxUserInfo wxUserInfo, WxLoginParam param) {
+        //处于风控状态的用户不允许登录
+        if(!old.getUserStatus().equals(UserStatusEnum.NORMAL.getCode())){
+            throw new CustomerException(getTipsMsg());
+        }
+        YtDyzUser newUser = new YtDyzUser();
+        newUser.setUserId(old.getUserId());
+        newUser.setNickName(wxUserInfo.getNickname());
+        newUser.setHeadImg(wxUserInfo.getHeadimgurl());
+        newUser.setLastLoginTime(new Date());
+        newUser.setLastLoginIp(param.getLoginIp());
+        newUser.setPhone(param.getPhone());
+        newUser.setDeviceId(param.getDeviceId());
+        newUser.setLoginDays(old.getLoginDays()+1);
+        appUserMapper.updateUser(newUser);
+    }
+
+
+
+    /**
+     * 注册用户
+     */
+    private void registryUser(WxLoginParam param,WxUserInfo wxUserInfo,WxLoginResult loginResult, YtDyzUser old) {
+        old.setUserId(redisService.getAppUserId());
+        old.setPhone(param.getPhone());
+        old.setDeviceId(param.getDeviceId());
+        old.setNickName(wxUserInfo.getNickname());
+        old.setLastLoginTime(new Date());
+        old.setRegistryTime(new Date());
+        old.setLastLoginIp(param.getLoginIp());
+        old.setLoginDays(1);
+        old.setPower(0);
+        old.setTotalVideo(0);
+        old.setTotalIncome(BigDecimal.ZERO);
+        old.setRedPacketAmount(BigDecimal.ZERO);
+        old.setRedPacketBalance(BigDecimal.ZERO);
+        old.setPointsBalance(BigDecimal.ZERO);
+        old.setPointsTotal(BigDecimal.ZERO);
+        old.setWithdrawTotal(BigDecimal.ZERO);
+        old.setDitchId(param.getDitchId());
+        old.setSignDays(0);
+        old.setAppId(param.getAppId());
+        old.setUserStatus(UserStatusEnum.NORMAL.getCode());
+        old.setWxOpenId(loginResult.getOpenid());
+        old.setHeadImg(wxUserInfo.getHeadimgurl());
+        //根据设备ID获取平台ID 获取不到再生成平台ID
+        String platformId = appUserMapper.getByDeviceId(param.getDeviceId(),wxUserInfo.getOpenid());
+        old.setPlatformId(StrUtil.isBlank(platformId)?
+                (StrConstant.PLATFORM_ID_PREFIX + IdUtil.getSnowflakeNextIdStr()):platformId);
+        appUserMapper.addOne(old);
+    }
+}

+ 69 - 0
yt-arithmetic/arithmetic-service/src/main/java/com/ytpm/arithmetic/service/impl/QuestionServiceImpl.java

@@ -0,0 +1,69 @@
+package com.ytpm.arithmetic.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.IdUtil;
+import com.github.pagehelper.PageInfo;
+import com.ytpm.agent.enums.UserStatusEnum;
+import com.ytpm.app.model.YtDyzAnswerRecord;
+import com.ytpm.app.model.YtDyzPowerRecord;
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.param.AnswerRecordParam;
+import com.ytpm.app.view.QuestionListView;
+import com.ytpm.arithmetic.dao.AppUserMapper;
+import com.ytpm.arithmetic.dao.QuestionMapper;
+import com.ytpm.arithmetic.service.QuestionService;
+import com.ytpm.general.RepMessage;
+import com.ytpm.general.Result;
+import com.ytpm.general.ResultTable;
+import com.ytpm.general.StatusCode;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+import java.util.Objects;
+
+@Service
+public class QuestionServiceImpl implements QuestionService {
+
+    @Autowired
+    private QuestionMapper questionMapper;
+    @Autowired
+    private AppUserMapper appUserMapper;
+
+    /**
+     * 获取题库
+     */
+    @Override
+    public ResultTable<QuestionListView> questionList() {
+        return ResultTable.resultTableOk(new PageInfo<QuestionListView>(questionMapper.questionList("AP_202505210009")));
+    }
+    /**
+     * 回答问题
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Result<String> answerQuestion(AnswerRecordParam param) {
+        YtDyzUser user = appUserMapper.selectPrimaryKey(param.getUserId());
+        if(Objects.isNull(user)){
+            return Result.resultOk(RepMessage.TOKEN_EXPIRE);
+        }
+        if(!UserStatusEnum.NORMAL.getCode().equals(user.getUserStatus())){
+            return new Result<>(StatusCode.ACCESS_ERR,"当前用户处于风控中");
+        }
+        YtDyzAnswerRecord record = new YtDyzAnswerRecord();
+        BeanUtil.copyProperties(param,record);
+        record.setRecordId(IdUtil.fastSimpleUUID());
+        record.setAnswerTime(new Date());
+        questionMapper.saveAnswerRecord(record);
+        appUserMapper.subOnePower(param.getUserId());
+        YtDyzPowerRecord powerRecord = new YtDyzPowerRecord();
+        powerRecord.setUserId(param.getUserId());
+        powerRecord.setRecordId(IdUtil.fastSimpleUUID());
+        powerRecord.setAddTime(new Date());
+        powerRecord.setType(1);
+        powerRecord.setRemark("减少体力");
+        appUserMapper.addPowerRecord(powerRecord);
+        return Result.resultOk(RepMessage.SAVE_SUCCESS);
+    }
+}

+ 60 - 0
yt-arithmetic/arithmetic-service/src/main/resources/bootstrap.yml

@@ -0,0 +1,60 @@
+server:
+  port: 25809
+yt:
+  port: 8848
+  local: 127.0.0.1
+  prod_1: 127.0.0.1
+  prod_2: 146.56.196.49
+spring:
+  profiles:
+    active: dev
+---
+spring:
+  profiles: local
+  main:
+    allow-bean-definition-overriding: true
+  application:
+    name: arithmetic-service
+  cloud:
+    nacos:
+      discovery:
+        server-addr: ${yt.local}:${yt.port}
+        namespace: 52439154-ea03-4121-9759-44d0cacc4765
+      config:
+        server-addr: ${yt.local}:${yt.port}
+        file-extension: yml
+        namespace: 52439154-ea03-4121-9759-44d0cacc4765
+---
+spring:
+  profiles: dev
+  main:
+    allow-bean-definition-overriding: true
+  application:
+    name: arithmetic-service
+  cloud:
+    nacos:
+      discovery:
+        server-addr: ${yt.prod_1}:${yt.port}
+        #        ip: ${yt.prod_1}
+        namespace: ad14a1aa-fe06-473a-9050-9afa26ec0020
+      config:
+        server-addr: ${yt.prod_1}:${yt.port}
+        file-extension: yml
+        namespace: ad14a1aa-fe06-473a-9050-9afa26ec0020
+---
+spring:
+  profiles: prod
+  main:
+    allow-bean-definition-overriding: true
+  application:
+    name: arithmetic-service
+  cloud:
+    nacos:
+      discovery:
+        server-addr: ${yt.prod_1}:${yt.port}
+        ip: ${yt.prod_2}
+        namespace: ad14a1aa-fe06-473a-9050-9afa26ec0020
+      config:
+        server-addr: ${yt.prod_1}:${yt.port}
+        file-extension: yml
+        namespace: ad14a1aa-fe06-473a-9050-9afa26ec0020

+ 145 - 0
yt-arithmetic/arithmetic-service/src/main/resources/mapper/AdRecordMapper.xml

@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ytpm.arithmetic.dao.AdRecordMapper">
+
+    <insert id="addOne">
+        insert into yt_dyz_ad_record
+        (
+         record_id,
+         user_id,
+         nick_name,
+         placement_id,
+         ad_source_id,
+         revenue,
+         network_form_id,
+         network_name,
+         network_placement_id,
+         begin_time,
+         finish_time,
+         result_json,
+         ad_source_index,
+         ad_source_type,
+         ecpm,
+         app_id
+        )
+        values
+        (
+         #{recordId},
+         #{userId},
+         #{nickName},
+         #{placementId},
+         #{adSourceId},
+         #{revenue},
+         #{networkFormId},
+         #{networkName},
+         #{networkPlacementId},
+         ifnull(#{beginTime}, now()),
+         ifnull(#{finishTime}, now()),
+         #{resultJson},
+         #{adSourceIndex},
+         #{adSourceType},
+         #{ecpm},
+         #{appId}
+        )
+    </insert>
+    <select id="countByAppIds" resultType="java.lang.Integer">
+        select
+            count(record_id)
+        from yt_dyz_ad_record
+        where ad_source_type = 1 and app_id in
+        <foreach collection="appIds.split(',')" separator="," item="item" open="(" close=")">
+            #{item}
+        </foreach>
+    </select>
+    <select id="getByUserId" resultType="com.ytpm.app.model.YtDyzAdRecord">
+        select
+            record_id, user_id,app_id, nick_name, placement_id, ad_source_id, revenue, network_form_id, network_name, network_placement_id, finish_time, begin_time,result_json,ad_source_type,ad_source_index,ecpm
+        from yt_dyz_ad_record
+        where user_id = #{userId}
+          <if test="adsourceType != null">
+              and ad_source_type = #{adsourceType}
+          </if>
+        order by finish_time desc
+    </select>
+    <select id="getByUserIds" resultType="com.ytpm.app.model.YtDyzAdRecord">
+        select
+            record_id, user_id,app_id, nick_name, placement_id, ad_source_id, revenue, network_form_id, network_name, network_placement_id, finish_time, begin_time,result_json,ad_source_type,ad_source_index,ecpm
+        from yt_dyz_ad_record
+        where user_id in
+              <foreach collection="userIds.split(',')" open="(" close=")" separator="," item="item">
+                  #{item}
+              </foreach>
+        order by finish_time
+    </select>
+    <select id="getHourRevenue" resultType="com.ytpm.middle.view.AppRevenueHourVO">
+        SELECT
+            HOUR(finish_time) `hour`,
+            DATE_FORMAT(finish_time,'%Y-%m-%d %H:00:00') as `time`,
+            sum( revenue ) revenue
+        FROM
+            yt_dyz_ad_record
+        WHERE
+            app_id = #{appId}
+            <if test="type != null and type ==1">
+                and DATE(finish_time) = DATE(now())
+            </if>
+            <if test="type != null and type ==2">
+                and DATE(finish_time) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
+            </if>
+            <if test="type != null and type ==3">
+                and DATE_FORMAT(finish_time, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
+            </if>
+        GROUP BY
+            `hour`
+    </select>
+    <select id="getRevenueByTime" resultType="java.math.BigDecimal">
+        select
+            sum(ar.revenue)
+        from yt_dyz_ad_record ar
+        join yt_dyz_user au on ar.user_id = au.user_id
+        <where>
+            <if test="userId != null and userId !=''">
+                and ar.user_id = #{userId}
+            </if>
+            <if test="nickName != null and nickName !=''">
+                and au.nick_name like concat('%',#{nickName},'%')
+            </if>
+            <if test="ditchId != null">
+                and au.ditch_id = #{ditchId}
+            </if>
+            <if test="userStatus != null">
+                and au.user_status = #{userStatus}
+            </if>
+            <if test="appIds != null and appIds != ''">
+                and ar.app_id in
+                <foreach collection="appIds.split(',')" separator="," item="item" open="(" close=")">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="registryTimeBegin != null ">
+                and DATE_FORMAT(au.registry_time,'%Y-%m-%d') <![CDATA[>=]]> #{registryTimeBegin}
+            </if>
+            <if test="registryTimeEnd != null ">
+                and DATE_FORMAT(au.registry_time,'%Y-%m-%d') <![CDATA[<=]]> #{registryTimeEnd}
+            </if>
+        </where>
+    </select>
+    <select id="getTodayRecord" resultType="com.ytpm.app.model.YtDyzAdRecord">
+        select
+            record_id, user_id, app_id, nick_name, placement_id, ad_source_id, ad_source_type, ad_source_index, revenue, ecpm, network_form_id, network_name, network_placement_id, finish_time, begin_time, result_json
+        from yt_dyz_ad_record
+        where app_id in
+        <foreach collection="appIds.split(',')" separator="," item="item" open="(" close=")">
+            #{item}
+        </foreach>
+        <if test="type != null and type ==1">
+            and DATE(finish_time) = DATE(now())
+        </if>
+        <if test="type != null and type ==2">
+            and DATE(finish_time) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
+        </if>
+        <if test="type != null and type ==3">
+            and DATE_FORMAT(finish_time, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
+        </if>
+    </select>
+</mapper>

+ 625 - 0
yt-arithmetic/arithmetic-service/src/main/resources/mapper/AppUserMapper.xml

@@ -0,0 +1,625 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ytpm.arithmetic.dao.AppUserMapper">
+    <insert id="addOne" parameterType="com.ytpm.app.model.YtDyzUser">
+        insert into yt_dyz_user
+        (
+         user_id,
+         nick_name,
+         head_img,
+         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,
+         platform_id,
+         app_id,
+         power,
+         phone,
+         device_id
+        )
+        values
+        (
+         #{userId},
+         #{nickName},
+         #{headImg},
+         #{registryTime},
+         #{lastLoginTime},
+         #{lastLoginIp},
+         #{loginDays},
+         #{totalVideo},
+         #{totalIncome},
+         #{redPacketBalance},
+         #{redPacketAmount},
+         #{pointsBalance},
+         #{pointsTotal},
+         #{withdrawTotal},
+         #{signDays},
+         #{userStatus},
+         #{riskReason},
+         #{wxOpenId},
+         #{ditchId},
+         #{platformId},
+         #{appId},
+         #{power},
+         #{phone},
+         #{deviceId}
+        )
+    </insert>
+    <insert id="addPowerRecord">
+        insert into yt_dyz_power_record
+        (
+         record_id, user_id, type, remark, add_time
+        )
+        values
+        (
+         #{recordId},#{userId},#{type},#{remark},#{addTime}
+        )
+    </insert>
+    <insert id="saveAppConfig">
+        insert into yt_app_default_config
+        (
+         config_name,
+         open_id,
+         secret,
+         app_id,
+         app_key,
+         app_type
+        )
+        values
+        (
+         #{configName},
+         #{openId},
+         #{secret},
+         #{appId},
+         #{appKey},
+         #{appType}
+        )
+    </insert>
+    <update id="addOnePower">
+        update yt_dyz_user set power = power+1 where user_id = #{userId}
+    </update>
+    <update id="subOnePower">
+        update yt_dyz_user set power = power-1 where user_id = #{userId}
+    </update>
+    <update id="updateUser" parameterType="com.ytpm.app.model.YtDyzUser">
+        update yt_dyz_user
+        <set>
+            <if test="nickName != null">
+                nick_name = #{nickName},
+            </if>
+            <if test="phone != null">
+                phone = #{phone},
+            </if>
+            <if test="deviceId != null">
+                device_id = #{deviceId},
+            </if>
+            <if test="headImg != null">
+                head_img = #{headImg},
+            </if>
+            <if test="power != null">
+                power = #{power},
+            </if>
+            <if test="lastLoginTime != null">
+                last_login_time = #{lastLoginTime},
+            </if>
+            <if test="lastLoginIp != null">
+                last_login_ip = #{lastLoginIp},
+            </if>
+            <if test="loginDays != null">
+                login_days = #{loginDays},
+            </if>
+            <if test="totalVideo != null">
+                total_video = #{totalVideo},
+            </if>
+            <if test="totalIncome != null">
+                total_income = #{totalIncome},
+            </if>
+            <if test="redPacketBalance != null">
+                red_packet_balance = #{redPacketBalance},
+            </if>
+            <if test="redPacketAmount != null">
+                red_packet_amount = #{redPacketAmount},
+            </if>
+            <if test="pointsBalance != null">
+                points_balance = #{pointsBalance},
+            </if>
+            <if test="pointsTotal != null">
+                points_total = #{pointsTotal},
+            </if>
+            <if test="withdrawTotal != null">
+                withdraw_total = #{withdrawTotal},
+            </if>
+            <if test="signDays != null">
+                sign_days = #{signDays},
+            </if>
+            <if test="userStatus != null">
+                user_status = #{userStatus},
+            </if>
+            <if test="riskReason != null">
+                risk_reason = #{riskReason},
+            </if>
+            <if test="wxOpenId != null">
+                wx_open_id = #{wxOpenId},
+            </if>
+            <if test="platformId != null">
+                platform_id = #{platformId}
+            </if>
+        </set>
+        where user_id = #{userId}
+    </update>
+    <delete id="delByAppId">
+        delete from yt_app_default_config where app_id = #{appId}
+    </delete>
+    <select id="getYtAppUser" resultType="com.ytpm.app.model.YtDyzUser">
+        select
+           user_id, app_id,phone,device_id, ditch_id, nick_name,head_img, power, 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, platform_id
+        from yt_dyz_user
+        where wx_open_id = #{openid}
+        and ditch_id = #{ditchId}
+    </select>
+    <select id="queryAll" resultType="com.ytpm.app.view.YtAppUserListView">
+        select
+            user_id,app_id,phone,device_id, ditch_id, nick_name,head_img, power, 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, platform_id
+        from yt_dyz_user
+        where 1 = 1
+        <if test="userId != null and userId !=''">
+            and user_id = #{userId}
+        </if>
+        <if test="nickName != null and nickName !=''">
+            and nick_name like concat('%',#{nickName},'%')
+        </if>
+        <if test="ditchId != null">
+            and ditch_id = #{ditchId}
+        </if>
+        <if test="userStatus != null">
+            and user_status = #{userStatus}
+        </if>
+        <if test="registryTimeBegin != null">
+            and DATE_FORMAT(registry_time, '%Y-%m-%d') <![CDATA[>=]]> DATE_FORMAT(#{registryTimeBegin}, '%Y-%m-%d')
+        </if>
+        <if test="registryTimeEnd != null">
+            and DATE_FORMAT(registry_time, '%Y-%m-%d') <![CDATA[<=]]> DATE_FORMAT(#{registryTimeEnd}, '%Y-%m-%d')
+        </if>
+        <if test="lastLoginTimeBegin != null">
+            and DATE_FORMAT(last_login_time, '%Y-%m-%d') <![CDATA[>=]]> DATE_FORMAT(#{lastLoginTimeBegin}, '%Y-%m-%d')
+        </if>
+        <if test="lastLoginTimeEnd != null">
+            and DATE_FORMAT(last_login_time, '%Y-%m-%d') <![CDATA[<=]]> DATE_FORMAT(#{lastLoginTimeEnd}, '%Y-%m-%d')
+        </if>
+        <if test="appIds != null and appIds != ''">
+            and app_id in
+                <foreach collection="appIds.split(',')" separator="," item="item" open="(" close=")">
+                    #{item}
+                </foreach>
+        </if>
+        order by user_id desc
+    </select>
+    <select id="selectPrimaryKey" resultType="com.ytpm.app.model.YtDyzUser">
+        select
+            user_id,app_id,phone,device_id, ditch_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, platform_id, power
+        from yt_dyz_user
+        where user_id = #{userId}
+    </select>
+    <resultMap id="UserMap" type="com.ytpm.app.model.YtDyzUser">
+        <id column="user_id" property="userId" />
+        <result column="head_img" property="headImg" />
+        <result column="nick_name" property="nickName" />
+        <result column="phone" property="phone" />
+        <result column="device_id" property="deviceId" />
+        <result column="registry_time" property="registryTime" />
+        <result column="last_login_time" property="lastLoginTime" />
+        <result column="last_login_ip" property="lastLoginIp" />
+        <result column="login_days" property="loginDays" />
+        <result column="total_video" property="totalVideo" />
+        <result column="total_income" property="totalIncome" />
+        <result column="red_packet_balance" property="redPacketBalance" />
+        <result column="red_packet_amount" property="redPacketAmount" />
+        <result column="points_balance" property="pointsBalance" />
+        <result column="points_total" property="pointsTotal" />
+        <result column="withdraw_total" property="withdrawTotal" />
+        <result column="sign_days" property="signDays" />
+        <result column="user_status" property="userStatus" />
+        <result column="risk_reason" property="riskReason" />
+        <result column="wx_open_id" property="wxOpenId" />
+        <result column="ditch_id" property="ditchId" />
+        <result column="app_id" property="appId" />
+        <result column="platform_id" property="platformId" />
+        <result column="power" property="power" />
+        <collection property="loginRecordList" ofType="com.ytpm.app.model.YtDyzLoginRecord">
+            <result column="record_id" property="recordId" />
+            <result column="user_id" property="userId" />
+            <result column="login_time" property="loginTime" />
+            <result column="device_brand" property="deviceBrand" />
+            <result column="device_model" property="deviceModel" />
+            <result column="login_ip" property="loginIp" />
+            <result column="operator" property="operator" />
+            <result column="ip_addr" property="ipAddr" />
+        </collection>
+    </resultMap>
+    <select id="getUserList" resultMap="UserMap">
+        select
+            du.user_id,
+            du.head_img,
+            du.nick_name,
+            du.phone,
+            du.device_id,
+            du.registry_time,
+            du.last_login_time,
+            du.last_login_ip,
+            du.login_days,
+            du.total_video,
+            du.total_income,
+            du.red_packet_balance,
+            du.red_packet_amount,
+            du.points_balance,
+            du.points_total,
+            du.withdraw_total,
+            du.sign_days,
+            du.user_status,
+            du.risk_reason,
+            du.wx_open_id,
+            du.ditch_id,
+            du.app_id,
+            du.platform_id,
+            du.power,
+            lr.record_id,
+            lr.login_time,
+            lr.device_brand,
+            lr. device_model,
+            lr.login_ip,
+            lr.operator,
+            lr.ip_addr
+        from yt_dyz_user du
+        left join yt_dyz_login_record lr on du.user_id = lr.user_id
+        <where>
+            <if test="userIds != null and userIds != ''">
+                and du.user_id in
+                <foreach collection="userIds.split(',')" item="item" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </if>
+            <if test="loginTimeBegin != null">
+                and du.last_login_time <![CDATA[>=]]> #{loginTimeBegin}
+            </if>
+            <if test="loginTimeEnd != null">
+                and du.last_login_time <![CDATA[<=]]> #{loginTimeEnd}
+            </if>
+        </where>
+        group by du.user_id
+    </select>
+
+    <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,
+        total_income, red_packet_balance, red_packet_amount,
+        points_balance, points_total, withdraw_total, sign_days,
+        user_status, risk_reason, wx_open_id, platform_id
+        FROM yt_dyz_user
+        <where>
+            <if test="startTime != null">
+                AND registry_time >= #{startTime}
+            </if>
+            <if test="endTime != null">
+                AND registry_time &lt;= #{endTime}
+            </if>
+            <choose>
+                <when test="appIdList == null or appIdList.isEmpty()">
+                    AND 1 = 0
+                </when>
+                <otherwise>
+                    AND app_id IN
+                    <foreach collection="appIdList" item="item"
+                             open="(" close=")" separator=",">
+                        #{item}
+                    </foreach>
+                </otherwise>
+            </choose>
+        </where>
+        ORDER BY registry_time DESC
+    </select>
+    <select id="countUsersByHour" resultType="com.ytpm.app.view.HourCountView">
+        SELECT HOUR(registry_time) AS hour, COUNT(*) AS count
+        FROM yt_dyz_user
+        WHERE registry_time BETWEEN #{startTime} AND #{endTime}
+        <choose>
+            <when test="appIdList == null or appIdList.isEmpty()">
+                AND 1 = 0
+            </when>
+            <otherwise>
+                AND app_id IN
+                <foreach collection="appIdList" item="item"
+                         open="(" close=")" separator=",">
+                    #{item}
+                </foreach>
+            </otherwise>
+        </choose>
+        GROUP BY HOUR(registry_time)
+    </select>
+
+    <select id="queryTodayBanned" resultType="com.ytpm.app.model.YtDyzUser">
+        select user_id from yt_dyz_user
+        where registry_time BETWEEN #{startTime} AND #{endTime}
+        and user_id in
+        <foreach collection="userIdList" item="item"
+                 open="(" close=")" separator=",">
+            #{item}
+        </foreach>
+    </select>
+    <select id="getDefaultConfig" resultType="com.ytpm.app.view.WxDefaultConfig">
+        select
+            config_id, config_name, open_id appId, secret, app_id platformAppId, app_key platformAppSecret, app_type,user_path,login_path,ad_path,answer_path,power_path
+        from yt_app_default_config
+        where app_type = #{appType}
+    </select>
+    <select id="getSecretByAppId" resultType="java.lang.String">
+        select
+           secret
+        from yt_app_default_config
+        where app_id = #{appId}
+    </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
+        from yt_dyz_user
+        where wx_open_id = #{openid}
+    </select>
+    <select id="getByDeviceId" resultType="java.lang.String">
+        select
+            platform_id
+        from yt_dyz_user
+        where device_id = #{deviceId}
+        and wx_open_id = #{openid}
+        limit 1
+    </select>
+    <select id="getConfigByIds" resultType="com.ytpm.app.view.WxDefaultConfig">
+        select
+            config_id, config_name, open_id, secret, app_id, app_key, app_type,user_path,login_path,ad_path,answer_path,power_path
+        from yt_app_default_config
+        where app_id in
+        <foreach collection="appIds.split(',')" item="item" separator="," open="(" close=")">
+            #{item}
+        </foreach>
+    </select>
+    <select id="getAdCount" resultType="java.lang.Integer">
+        select
+            sum(total_video)
+        from yt_dyz_user
+        where app_id in
+        <foreach collection="appIds.split(',')" separator="," item="item" open="(" close=")">
+            #{item}
+        </foreach>
+    </select>
+    <select id="getRevenueCount" resultType="java.math.BigDecimal">
+        select
+        sum(total_income)
+        from yt_dyz_user
+        where app_id in
+        <foreach collection="appIds.split(',')" separator="," item="item" open="(" close=")">
+            #{item}
+        </foreach>
+    </select>
+    <select id="getAppRanking" resultType="com.ytpm.middle.view.AppRankingListVO">
+        SELECT
+            u.app_id,
+            sum( ar.revenue ) totalRevenue
+        FROM
+            yt_dyz_ad_record ar
+            JOIN yt_dyz_user u ON ar.user_id = u.user_id
+        <where>
+            <if test="sortBy != null">
+                <choose>
+                    <when test="sortBy == 1">
+                        DATE_FORMAT( ar.finish_time, '%Y-%m-%d' ) = CURRENT_DATE()
+                    </when>
+                    <when test=" sortBy == 2">
+                        YEARWEEK( ar.finish_time, 1 ) = YEARWEEK(now(),1)
+                    </when>
+                    <otherwise>
+                        DATE_FORMAT( ar.finish_time, '%Y-%m' ) = DATE_FORMAT(NOW(), '%Y-%m')
+                    </otherwise>
+                </choose>
+            </if>
+        </where>
+        GROUP BY
+            u.app_id
+        ORDER BY
+            sum( ar.revenue ) DESC
+        LIMIT #{limit}
+    </select>
+    <select id="getUserRanking" resultType="com.ytpm.middle.view.UserRankingListVO">
+        SELECT
+            u.user_id,
+            u.nick_name,
+            u.head_img,
+            u.total_video,
+            sum( ar.revenue ) totalIncome
+        FROM
+            yt_dyz_ad_record ar
+                JOIN yt_dyz_user u ON ar.user_id = u.user_id
+        <where>
+            <if test="sortBy != null">
+                <choose>
+                    <when test="sortBy == 1">
+                        DATE_FORMAT( ar.finish_time, '%Y-%m-%d' ) = CURRENT_DATE()
+                    </when>
+                    <when test=" sortBy == 2">
+                        YEARWEEK( ar.finish_time, 1 ) = YEARWEEK(now(),1)
+                    </when>
+                    <otherwise>
+                        DATE_FORMAT( ar.finish_time, '%Y-%m' ) = DATE_FORMAT(NOW(), '%Y-%m')
+                    </otherwise>
+                </choose>
+            </if>
+        </where>
+        GROUP BY ar.user_id
+        ORDER BY
+            sum( ar.revenue ) DESC
+        limit #{limit}
+    </select>
+    <select id="countUserByAppIds" resultType="java.lang.Integer">
+        select
+            count(user_id)
+        from yt_dyz_user
+        where app_id in
+        <foreach collection="appIds.split(',')" separator="," item="item" open="(" close=")">
+            #{item}
+        </foreach>
+    </select>
+    <select id="countRevenueByAppIds" resultType="java.math.BigDecimal">
+        select
+            sum(total_income)
+        from yt_dyz_user
+        where app_id in
+        <foreach collection="appIds.split(',')" separator="," item="item" open="(" close=")">
+            #{item}
+        </foreach>
+    </select>
+    <select id="countBannedByAppIds" resultType="java.lang.Integer">
+        select
+            count(user_id)
+        from yt_dyz_user
+        where user_status > 1 and app_id in
+        <foreach collection="appIds.split(',')" separator="," item="item" open="(" close=")">
+            #{item}
+        </foreach>
+    </select>
+    <select id="countRegistryUser" resultType="java.lang.Integer">
+        select
+        count(user_id)
+        from yt_dyz_user
+        where app_id = #{appId}
+        <if test="type != null and type ==1">
+            and DATE(registry_time) = DATE(now())
+        </if>
+        <if test="type != null and type ==2">
+            and DATE(registry_time) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
+        </if>
+        <if test="type != null and type ==3">
+            and DATE_FORMAT(registry_time, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
+        </if>
+    </select>
+    <select id="countLoginUser" resultType="java.lang.Integer">
+        select
+        count(user_id)
+        from yt_dyz_user
+        where app_id = #{appId}
+        <if test="type != null and type ==1">
+            and DATE(last_login_time) = DATE(now())
+        </if>
+        <if test="type != null and type ==2">
+            and DATE(last_login_time) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
+        </if>
+        <if test="type != null and type ==3">
+            and DATE_FORMAT(last_login_time, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
+        </if>
+    </select>
+    <select id="countRegistryHour" resultType="com.ytpm.middle.view.AppUserHourVO">
+        select
+            DATE_FORMAT(registry_time,'%Y-%m-%d %H:00:00') as `time`,
+            count(user_id) `count`
+        from yt_dyz_user
+        where app_id = #{appId}
+        <if test="type != null and type ==1">
+            and DATE(registry_time) = DATE(now())
+        </if>
+        <if test="type != null and type ==2">
+            and DATE(registry_time) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
+        </if>
+        <if test="type != null and type ==3">
+            and DATE_FORMAT(registry_time, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
+        </if>
+        group by `time`
+    </select>
+    <select id="countLoginHour" resultType="com.ytpm.middle.view.AppUserHourVO">
+        select
+        DATE_FORMAT(last_login_time,'%Y-%m-%d %H:00:00') as `time`,
+        count(user_id) `count`
+        from yt_dyz_user
+        where app_id = #{appId}
+        <if test="type != null and type ==1">
+            and DATE(last_login_time) = DATE(now())
+        </if>
+        <if test="type != null and type ==2">
+            and DATE(last_login_time) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
+        </if>
+        <if test="type != null and type ==3">
+            and DATE_FORMAT(last_login_time, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
+        </if>
+        group by `time`
+    </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
+        from yt_dyz_user
+        where user_id in
+        <foreach collection="userIds.split(',')" separator="," item="item"  open="(" close=")">
+            #{item}
+        </foreach>
+    </select>
+    <resultMap id="UserAdMap" type="com.ytpm.agent.view.AgentAuditCheckVO">
+        <id column="user_id" property="userId" />
+        <result column="app_id" property="appId" />
+        <result column="nick_name" property="nickName" />
+        <collection property="adRecordList" ofType="com.ytpm.app.model.YtDyzAdRecord">
+            <result column="record_id" property="recordId" />
+            <result column="placement_id" property="placementId" />
+            <result column="ad_source_id" property="adSourceId" />
+            <result column="ad_source_type" property="adSourceType" />
+            <result column="ad_source_index" property="adSourceIndex" />
+            <result column="revenue" property="revenue" />
+            <result column="ecpm" property="ecpm" />
+            <result column="network_form_id" property="networkFormId" />
+            <result column="network_name" property="networkName" />
+            <result column="network_placement_id" property="networkPlacementId" />
+            <result column="finish_time" property="finishTime" />
+            <result column="begin_time" property="beginTime" />
+        </collection>
+    </resultMap>
+    <select id="queryTodayUserAd" resultMap="UserAdMap">
+        select
+            u.user_id,
+            u.app_id,
+            u.nick_name,
+            ar.record_id,
+            ar.placement_id,
+            ar.ad_source_id,
+            ar.ad_source_type,
+            ar.ad_source_index,
+            ar.revenue,
+            ar.ecpm,
+            ar.network_form_id,
+            ar.network_name,
+            ar.network_placement_id,
+            ar.finish_time,
+            ar.begin_time
+        from yt_dyz_ad_record ar
+        join yt_dyz_user u on ar.user_id = u.user_id
+        where ar.ad_source_type = #{adSourceType} and ar.app_id = #{appId} and ar.user_id in
+        <foreach collection="userIds.split(',')" separator="," item="item" open="(" close=")">
+            #{item}
+        </foreach>
+        and DATE_FORMAT(ar.finish_time, '%Y-%m-%d') = current_date()
+    </select>
+    <update id="unlockUser">
+        update yt_dyz_user
+        set user_status = 1
+        where user_status > 1
+        and user_id in
+        <foreach collection="userIds.split(',')" item="item" separator="," open="(" close=")">
+            #{item}
+        </foreach>
+    </update>
+</mapper>

+ 75 - 0
yt-arithmetic/arithmetic-service/src/main/resources/mapper/LoginRecordMapper.xml

@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ytpm.arithmetic.dao.LoginRecordMapper">
+
+    <insert id="insertOne" parameterType="com.ytpm.app.model.YtDyzLoginRecord">
+        insert into yt_dyz_login_record
+        (
+         record_id,
+         user_id,
+         login_time,
+         device_brand,
+         device_model,
+         login_ip,
+         operator,
+         ip_addr,
+         phone_json
+        )
+        values
+        (
+         #{recordId},
+         #{userId},
+         #{loginTime},
+         #{deviceBrand},
+         #{deviceModel},
+         #{loginIp},
+         #{operator},
+         #{ipAddr},
+         #{phoneJson}
+         );
+    </insert>
+    <select id="getLoginRecords" resultType="com.ytpm.app.model.YtDyzLoginRecord">
+        select
+            record_id, user_id, login_time, device_brand, device_model, login_ip, operator, ip_addr
+        from yt_dyz_login_record
+        where user_id = #{userId}
+    </select>
+
+    <select id="queryLoginCount" resultType="String">
+        select ydlr.user_id
+        from yt_dyz_login_record ydlr
+        left join yt_dyz_user ydu on ydlr.user_id=ydu.user_id
+        <where>
+            <if test="startTime != null">
+                AND ydlr.login_time >= #{startTime}
+            </if>
+            <if test="endTime != null">
+                AND ydlr.login_time &lt;= #{endTime}
+            </if>
+            <choose>
+                <when test="appIdList == null or appIdList.isEmpty()">
+                    AND 1 = 0
+                </when>
+                <otherwise>
+                    AND ydu.app_id IN
+                    <foreach collection="appIdList" item="item"
+                             open="(" close=")" separator=",">
+                        #{item}
+                    </foreach>
+                </otherwise>
+            </choose>
+        </where>
+        group by user_id
+    </select>
+    <select id="getLoginRecordByIds" resultType="com.ytpm.app.model.YtDyzLoginRecord">
+        select
+            record_id, user_id, login_time, device_brand, device_model, login_ip, operator, ip_addr
+        from yt_dyz_login_record
+        where user_id in
+        <foreach collection="userIds.split(',')" open="(" close=")" separator="," item="item">
+            #{item}
+        </foreach>
+    </select>
+
+
+</mapper>

+ 59 - 0
yt-arithmetic/arithmetic-service/src/main/resources/mapper/QuestionMapper.xml

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ytpm.arithmetic.dao.QuestionMapper">
+
+    <resultMap id="questionListMap" type="com.ytpm.app.view.QuestionListView">
+        <id column="question_id" property="questionId" />
+        <result column="question_content" property="questionContent" />
+        <result column="question_reward" property="questionReward" />
+        <result column="correct_item" property="correctItem" />
+        <collection property="itemList" ofType="com.ytpm.app.view.QuestionItemListView">
+            <result column="item_id" property="itemId" />
+            <result column="item_header" property="itemHeader" />
+            <result column="item_content" property="itemContent" />
+            <result column="question_id" property="questionId" />
+        </collection>
+    </resultMap>
+    <insert id="saveAnswerRecord">
+        insert into yt_dyz_answer_record (record_id, question_id, item_id, duration, answer_time, user_id)
+        values (#{recordId},#{questionId},#{itemId},#{duration},#{answerTime},#{userId});
+    </insert>
+    <select id="questionList" resultMap="questionListMap">
+        SELECT
+            ydq.question_id,
+            ydq.question_content,
+            ydq.question_reward,
+            ydq.correct_item,
+            ydqi.item_id,
+            ydqi.item_header,
+            ydqi.item_content,
+            ydqi.question_id
+        FROM yt_dyz_question ydq
+        LEFT JOIN yt_dyz_question_item ydqi
+        ON ydq.question_id = ydqi.question_id
+        where app_id = #{appId}
+    </select>
+    <select id="getLastQuestionId" resultType="java.lang.String">
+        select
+            question_id
+        from yt_dyz_answer_record
+        where user_id = #{userId}
+        order by answer_time desc limit 1
+    </select>
+    <select id="getAnswerCount" resultType="java.lang.Integer">
+        select
+            count(*)
+        from yt_dyz_answer_record
+        where user_id = #{userId}
+        <if test="type != null and type == 1">
+            and DATE_FORMAT(answer_time, '%Y-%m-%d') = current_date()
+        </if>
+    </select>
+    <select id="getAnswerRecords" resultType="com.ytpm.app.model.YtDyzAnswerRecord">
+        select
+            record_id, question_id, item_id, duration, answer_time, user_id
+        from yt_dyz_answer_record
+        where user_id = #{userId}
+        order by answer_time desc
+    </select>
+</mapper>

+ 118 - 0
yt-arithmetic/pom.xml

@@ -0,0 +1,118 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.ytpm</groupId>
+        <artifactId>yt_platform</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>yt-arithmetic</artifactId>
+    <packaging>pom</packaging>
+    <description>算术小达人</description>
+    <modules>
+        <module>arithmetic-feign</module>
+        <module>arithmetic-service</module>
+    </modules>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+            <version>1.4.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>guava</artifactId>
+                    <groupId>com.google.guava</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>jsr305</artifactId>
+                    <groupId>com.google.code.findbugs</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-log4j2</artifactId>
+        </dependency>
+
+        <!--Spring Cloud Alibaba-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>guava</artifactId>
+                    <groupId>com.google.guava</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+
+        <!--database-->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>jsqlparser</artifactId>
+                    <groupId>com.github.jsqlparser</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>com.ytpm</groupId>
+            <artifactId>yt-common</artifactId>
+            <version>1.0-SNAPSHOT</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>guava</artifactId>
+                    <groupId>com.google.guava</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>jsqlparser</artifactId>
+                    <groupId>com.github.jsqlparser</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>jsr305</artifactId>
+                    <groupId>com.google.code.findbugs</groupId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework.cloud</groupId>
+                    <artifactId>spring-cloud-starter-oauth2</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+    </dependencies>
+</project>