Преглед на файлове

refactor: adage-service 微信、游客登陆实现抽离公共模板,登陆服务统一调用

hidewnd преди 2 месеца
родител
ревизия
3a7215d7e9

+ 12 - 184
yt-adage/adage-service/src/main/java/com/ytpm/adage/controller/VisitorController.java

@@ -1,43 +1,20 @@
 package com.ytpm.adage.controller;
 
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.core.util.RandomUtil;
-import cn.hutool.core.util.StrUtil;
 import com.ytpm.adage.dao.AppUserMapper;
-import com.ytpm.adage.dao.LoginRecordMapper;
-import com.ytpm.adage.dao.QuestionMapper;
-import com.ytpm.adage.redis.RedisService;
-import com.ytpm.agent.enums.UserStatusEnum;
-import com.ytpm.app.model.YtDyzLoginRecord;
+import com.ytpm.app.enums.LoginType;
 import com.ytpm.app.model.YtDyzUser;
 import com.ytpm.app.param.WxLoginParam;
-import com.ytpm.app.view.WxDefaultConfig;
-import com.ytpm.constant.StrConstant;
-import com.ytpm.feign.RiskFeign;
 import com.ytpm.general.Result;
-import com.ytpm.general.StatusCode;
-import com.ytpm.handle.CustomerException;
+import com.ytpm.handle.LoginServiceFactory;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 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.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 org.springframework.web.bind.annotation.*;
 
+import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
-import java.math.BigDecimal;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
 
 /**
  * @author Marx
@@ -51,177 +28,28 @@ public class VisitorController {
 
     @Autowired
     private AppUserMapper appUserMapper;
-    @Autowired
-    private LoginRecordMapper loginRecordMapper;
-    @Autowired
-    private QuestionMapper questionMapper;
-    @Autowired
-    private RiskFeign riskFeign;
-    @Autowired
-    private RedisService redisService;
-    @Value("${risk.config.banned.tips}")
-    private String tips;
+
+    @Resource
+    private LoginServiceFactory loginServiceFactory;
 
     @PostMapping("/login")
     @ApiOperation("游客登录")
     @Transactional(rollbackFor = Exception.class)
     public Result<YtDyzUser> visitorLogin(@RequestBody WxLoginParam param, HttpServletRequest request) {
-        // 唯一性判断
-        // 1.查询设备ID不存在记录 则根据设备id+渠道id注册用户
-        // 2.查询设备ID存在记录 则根据渠道ID控制唯一性,同步出路风控记录
-        //根据应用获取登陆配置
-        WxDefaultConfig defaultConfig = appUserMapper.getDefaultConfig(param.getAppType());
-        if(Objects.isNull(defaultConfig)){
-            return new Result<>(StatusCode.ACCESS_ERR,"游客登录失败,未找到相应配置!");
-        }
-        param.setAppId(defaultConfig.getPlatformAppId());
-        param.setLoginIp(getClientIp(request));
-        //获取该设备最后一次注册的用户信息(包含查询风控记录)
-        List<YtDyzUser> registryUser = appUserMapper.getLastRegistryUser(param.getDeviceId());
-        //该设备未注册过用户
-        YtDyzUser old;
-        if(CollUtil.isEmpty(registryUser)) {
-            old = new YtDyzUser();
-            registerUser(param, old);
-        }else{
-            //查询当前设备当前渠道是否存在该用户
-            old = appUserMapper.getByDeviceAndDitch(param.getDeviceId(), param.getDitchId());
-            //不存在则说明该渠道为新注册用户,需要风控校验注册是否在规则允许范围内
-            if(Objects.isNull(old)) {
-                YtDyzUser ytDyzUser = registryUser.stream()
-                        .sorted(Comparator.comparing(YtDyzUser::getRegistryTime).reversed())
-                        .collect(Collectors.toList()).get(0);
-                Result<?> result = riskFeign.checkRegRisk(ytDyzUser);
-                if(result.getCode()!=200){
-                    return new Result<>(StatusCode.ACCESS_ERR,result.getMessage());
-                }
-                old = new YtDyzUser();
-                registerUser(param, old);
-            }else{
-                //当前渠道已有用户,校验用户是否处于风控中 & 更新用户信息
-                if(!old.getUserStatus().equals(UserStatusEnum.NORMAL.getCode())){
-                    return new Result<>(StatusCode.ACCESS_ERR,getTipsMsg());
-                }
-                deadWithUserCrud(old,param);
-            }
-        }
-        //设置最后一次答题问题ID、今日答题数、历史答题数
-        setExtInfo(old);
-        //校验指定时间内登录的渠道数
-        Result<?> result = riskFeign.checkLoginRisk(old);
-        if(result.getCode()!=200){
-        // 添加用户登录记录
-        addLoginRecord(param,old.getUserId());
-        return new Result<>(StatusCode.ACCESS_ERR,result.getMessage());
-    }
-        return Result.resultObjOk(old);
+        return loginServiceFactory.login(LoginType.VISITOR, param, request);
     }
 
     @ApiOperation("设备在指定时间内注册的渠道")
     @GetMapping("/getDitchCount")
-    public int getDitchCount(@RequestParam("deviceId")String deviceId,@RequestParam("hours")Integer hours) {
-        return appUserMapper.countDitch(deviceId,hours);
+    public int getDitchCount(@RequestParam("deviceId") String deviceId, @RequestParam("hours") Integer hours) {
+        return appUserMapper.countDitch(deviceId, hours);
     }
 
     @ApiOperation("设备在指定时间内注册的渠道")
     @GetMapping("/getLoginDitchCount")
-    public int getLoginDitchCount(@RequestParam("deviceId")String deviceId,@RequestParam("hours")Integer hours) {
-        return appUserMapper.countLoginDitch(deviceId,hours);
+    public int getLoginDitchCount(@RequestParam("deviceId") String deviceId, @RequestParam("hours") Integer hours) {
+        return appUserMapper.countLoginDitch(deviceId, hours);
     }
 
-    /**
-     * 注册用户
-     */
-    private YtDyzUser registerUser(WxLoginParam param, YtDyzUser old) {
-        old.setUserId(redisService.getAppUserId());
-        old.setPhone(param.getPhone());
-        old.setDeviceId(param.getDeviceId());
-        old.setNickName("visitor_"+RandomUtil.randomString(10));
-        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());
-        String platformId = appUserMapper.getPlatformByDeviceId(param.getDeviceId());
-        old.setPlatformId(StrUtil.isBlank(platformId)?
-                (StrConstant.PLATFORM_ID_PREFIX + IdUtil.getSnowflakeNextIdStr()):platformId);
-        appUserMapper.addOne(old);
-        return 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 String getTipsMsg(){
-        String[] split = tips.split(",");
-        return split[RandomUtil.randomInt(split.length)];
-    }
-
-    /**
-     * 设置扩展信息
-     */
-    private void setExtInfo(YtDyzUser old) {
-        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 deadWithUserCrud(YtDyzUser old, WxLoginParam param) {
-        //处于风控状态的用户不允许登录
-        if(!old.getUserStatus().equals(UserStatusEnum.NORMAL.getCode())){
-            throw new CustomerException(getTipsMsg());
-        }
-        YtDyzUser newUser = new YtDyzUser();
-        newUser.setUserId(old.getUserId());
-        newUser.setLastLoginTime(new Date());
-        newUser.setLastLoginIp(param.getLoginIp());
-        newUser.setPhone(param.getPhone());
-        newUser.setDeviceId(param.getDeviceId());
-        //如果当前登录是本日第一次登录则登录天数+1
-        int loginCount = loginRecordMapper.getTodayLoginCount(old.getUserId());
-        if(loginCount < 1){
-            newUser.setLoginDays(old.getLoginDays()+1);
-        }
-        appUserMapper.updateUser(newUser);
-    }
-
-    /**
-     * 增加用户登录记录
-     */
-    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);
-    }
 }

+ 16 - 86
yt-adage/adage-service/src/main/java/com/ytpm/adage/controller/WxController.java

@@ -2,24 +2,16 @@ package com.ytpm.adage.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.adage.dao.AppUserMapper;
-import com.ytpm.adage.service.AppUserService;
-import com.ytpm.app.enums.AppTypeEnums;
+import com.ytpm.app.enums.LoginType;
 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.feign.RiskFeign;
 import com.ytpm.general.RepMessage;
 import com.ytpm.general.Result;
-import com.ytpm.general.StatusCode;
-import com.ytpm.handle.CustomerException;
+import com.ytpm.handle.LoginServiceFactory;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
@@ -44,97 +36,33 @@ import java.util.Objects;
 @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;
+    private LoginServiceFactory loginServiceFactory;
 
     @PostMapping("/login")
     @ApiOperation("微信登录")
     @Transactional
     public Result<YtDyzUser> wxLogin(@RequestBody WxLoginParam param, HttpServletRequest request) {
-        //根据应用获取配置调用微信接口登录
-        WxDefaultConfig defaultConfig = appUserMapper.getDefaultConfig(param.getAppType());
-        if(Objects.isNull(defaultConfig)){
-            return new Result<>(StatusCode.ACCESS_ERR,"微信登录失败,未找到相应配置!");
-        }
-        param.setAppId(defaultConfig.getPlatformAppId());
-        WxLoginResult loginResult = getWechatLoginInfo(param.getWxCode(),param.getAppType(),defaultConfig.getAppId(),defaultConfig.getSecret());
-        if(Objects.isNull(loginResult)|| StrUtil.isBlank(loginResult.getOpenid())){
-            return new Result<>(StatusCode.ACCESS_ERR,"微信登录失败,请刷新授权码!");
-        }
-        WxUserInfo wxUserInfo = getWechatUserInfo(loginResult.getAccess_token(),loginResult.getOpenid());
-        if(Objects.isNull(wxUserInfo)) {
-            return new Result<>(StatusCode.ACCESS_ERR,"微信用户登录失败");
-        }
-        param.setLoginIp(getClientIp(request));
-        YtDyzUser old = appUserService.crudForNewTrans(param,wxUserInfo,loginResult);
-        //调用风控服务校验默认风控配置
-        old.setRiskCode("313");
-        Result<?> result = riskFeign.checkRisk(old);
-        if(result.getCode()!=200){
-            return new Result<>(StatusCode.ACCESS_ERR,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;
+        return loginServiceFactory.login(LoginType.WX, param, request);
     }
 
     @ApiOperation("获取微信默认配置项")
     @GetMapping("/defaultConfig")
     public Result<WxDefaultConfig> getWxDefaultConfig(int appType) {
         WxDefaultConfig config = appUserMapper.getDefaultConfig(appType);
-        if(Objects.isNull(config)){
+        if (Objects.isNull(config)) {
             return Result.resultErr("应用类型有误!");
         }
-       return Result.resultObjOk(config);
+        return Result.resultObjOk(config);
     }
 
     @ApiOperation("体力增加")
     @GetMapping("/addPower")
     @Transactional(rollbackFor = Exception.class)
-    public Result<YtDyzUser> addPower(@RequestParam("userId")String userId) {
+    public Result<YtDyzUser> addPower(@RequestParam("userId") String userId) {
         appUserMapper.addOnePower(userId);
         YtDyzPowerRecord record = new YtDyzPowerRecord();
         record.setUserId(userId);
@@ -148,32 +76,34 @@ public class WxController {
 
     @ApiOperation("保存应用默认配置")
     @PostMapping("/saveAppConfig")
-    public Result<String> saveAppConfig(@RequestBody YtAppDefaultConfig defaultConfig){
+    public Result<String> saveAppConfig(@RequestBody YtAppDefaultConfig defaultConfig) {
         appUserMapper.saveAppConfig(defaultConfig);
         return Result.resultOk(RepMessage.SAVE_SUCCESS);
     }
 
     @ApiOperation("修改应用默认配置")
     @PostMapping("/updateAppConfig")
-    public Result<String> updateAppConfig(@RequestBody YtAppDefaultConfig defaultConfig){
+    public Result<String> updateAppConfig(@RequestBody YtAppDefaultConfig defaultConfig) {
         appUserMapper.updateAppConfig(defaultConfig);
         return Result.resultOk(RepMessage.SAVE_SUCCESS);
     }
 
     @ApiOperation("根据APP_ID获取配置")
     @GetMapping("/getConfigs")
-    public List<WxDefaultConfig> getConfigs(@RequestParam(name = "appIds")String appIds){
+    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);}
+        if (CollUtil.isNotEmpty(dyzConfigs)) {
+            configs.addAll(dyzConfigs);
+        }
         return configs;
     }
 
     @ApiOperation("删除默认配置")
     @GetMapping("/delDefaultConfig")
-    public void delDefaultConfig(@RequestParam(name = "appId")String appId){
+    public void delDefaultConfig(@RequestParam(name = "appId") String appId) {
         List<WxDefaultConfig> dyzConfig = appUserMapper.getConfigByIds(appId);
-        if(CollUtil.isNotEmpty(dyzConfig)){
+        if (CollUtil.isNotEmpty(dyzConfig)) {
             appUserMapper.delByAppId(appId);
         }
     }

+ 152 - 0
yt-adage/adage-service/src/main/java/com/ytpm/adage/service/impl/VisitorLoginServiceImpl.java

@@ -0,0 +1,152 @@
+package com.ytpm.adage.service.impl;
+
+
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import com.ytpm.adage.dao.AppUserMapper;
+import com.ytpm.adage.dao.LoginRecordMapper;
+import com.ytpm.adage.dao.QuestionMapper;
+import com.ytpm.adage.redis.RedisService;
+import com.ytpm.agent.enums.UserStatusEnum;
+import com.ytpm.app.enums.LoginType;
+import com.ytpm.app.model.YtDyzLoginRecord;
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.param.LoginParam;
+import com.ytpm.app.view.WxDefaultConfig;
+import com.ytpm.constant.StrConstant;
+import com.ytpm.feign.RiskFeign;
+import com.ytpm.general.Result;
+import com.ytpm.handle.AbstractLoginService;
+import com.ytpm.handle.CommonException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author lih
+ * @date 2025/9/4
+ */
+
+@Slf4j
+@Component
+public class VisitorLoginServiceImpl extends AbstractLoginService  {
+
+    @Autowired
+    private AppUserMapper appUserMapper;
+    @Autowired
+    private LoginRecordMapper loginRecordMapper;
+    @Autowired
+    private QuestionMapper questionMapper;
+    @Autowired
+    private RiskFeign riskFeign;
+    @Autowired
+    private RedisService redisService;
+
+    @Override
+    protected WxDefaultConfig getLoginConfig(Integer appType) {
+        if (appType == null) {
+            throw new CommonException("appType is null");
+        }
+        return appUserMapper.getDefaultConfig(appType);
+    }
+
+    @Override
+    protected YtDyzUser queryUserByDeviceAndDitch(String deviceId, Long ditchId) {
+        return appUserMapper.getByDeviceAndDitch(deviceId, ditchId);
+    }
+
+    @Override
+    protected LoginType getLoginType() {
+        return LoginType.VISITOR;
+    }
+
+    @Override
+    protected void validateParams(LoginParam loginParam, HttpServletRequest request) {
+
+    }
+
+    @Override
+    protected List<YtDyzUser> queryDyzUserByParam(LoginParam loginParam, Map<String, Object> thirdPartyLoginInfo) {
+        return appUserMapper.getLastRegistryUser(loginParam.getDeviceId());
+    }
+
+    @Override
+    protected YtDyzUser registryUser(LoginParam loginParam, YtDyzUser user, Map<String, Object> paramMap) {
+        user.setUserId(redisService.getAppUserId());
+        user.setPhone(loginParam.getPhone());
+        user.setDeviceId(loginParam.getDeviceId());
+        user.setNickName("visitor_" + RandomUtil.randomString(10));
+        user.setLastLoginTime(new Date());
+        user.setRegistryTime(new Date());
+        user.setLastLoginIp(loginParam.getLoginIp());
+        user.setLoginDays(1);
+        user.setPower(0);
+        user.setTotalVideo(0);
+        user.setTotalIncome(BigDecimal.ZERO);
+        user.setRedPacketAmount(BigDecimal.ZERO);
+        user.setRedPacketBalance(BigDecimal.ZERO);
+        user.setPointsBalance(BigDecimal.ZERO);
+        user.setPointsTotal(BigDecimal.ZERO);
+        user.setWithdrawTotal(BigDecimal.ZERO);
+        user.setDitchId(loginParam.getDitchId());
+        user.setSignDays(0);
+        user.setAppId(loginParam.getAppId());
+        user.setUserStatus(UserStatusEnum.NORMAL.getCode());
+        String platformId = appUserMapper.getPlatformByDeviceId(loginParam.getDeviceId());
+        user.setPlatformId(StrUtil.isBlank(platformId) ?
+                (StrConstant.PLATFORM_ID_PREFIX + IdUtil.getSnowflakeNextIdStr()) : platformId);
+        appUserMapper.addOne(user);
+        return user;
+    }
+
+    @Override
+    protected void deadWithUserCrud(LoginParam loginParam, YtDyzUser old, Map<String, Object> paramMap) {
+        YtDyzUser newUser = new YtDyzUser();
+        newUser.setUserId(old.getUserId());
+        newUser.setLastLoginTime(new Date());
+        newUser.setLastLoginIp(loginParam.getLoginIp());
+        newUser.setPhone(loginParam.getPhone());
+        newUser.setDeviceId(loginParam.getDeviceId());
+        //如果当前登录是本日第一次登录则登录天数+1
+        int loginCount = loginRecordMapper.getTodayLoginCount(old.getUserId());
+        if (loginCount < 1) {
+            newUser.setLoginDays(old.getLoginDays() + 1);
+        }
+        appUserMapper.updateUser(newUser);
+    }
+
+    @Override
+    protected void saveLoginRecord(YtDyzLoginRecord loginRecord) {
+        loginRecordMapper.insertOne(loginRecord);
+    }
+
+    @Override
+    protected Result<?> remoteCheckLoginRisk(YtDyzUser ytDyzUser) {
+        return riskFeign.checkLoginRisk(ytDyzUser);
+    }
+
+    @Override
+    protected Result<?> remoteCheckRegRisk(YtDyzUser user) {
+        return riskFeign.checkRegRisk(user);
+    }
+
+    /**
+     * 设置扩展信息
+     */
+    @Override
+    protected void setExtInfo(YtDyzUser old, Map<String, Object> paramMap) {
+        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()));
+    }
+
+}

+ 218 - 0
yt-adage/adage-service/src/main/java/com/ytpm/adage/service/impl/WxLoginServiceImpl.java

@@ -0,0 +1,218 @@
+package com.ytpm.adage.service.impl;
+
+
+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.adage.dao.AppUserMapper;
+import com.ytpm.adage.dao.LoginRecordMapper;
+import com.ytpm.adage.dao.QuestionMapper;
+import com.ytpm.adage.redis.RedisService;
+import com.ytpm.adage.service.AppUserService;
+import com.ytpm.agent.enums.UserStatusEnum;
+import com.ytpm.app.enums.AppTypeEnums;
+import com.ytpm.app.enums.LoginType;
+import com.ytpm.app.model.YtDyzLoginRecord;
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.param.LoginParam;
+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.constant.StrConstant;
+import com.ytpm.feign.RiskFeign;
+import com.ytpm.general.Result;
+import com.ytpm.handle.AbstractLoginService;
+import com.ytpm.handle.CommonException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * @author lih
+ * @date 2025/9/4
+ */
+@Slf4j
+@Component
+public class WxLoginServiceImpl extends AbstractLoginService {
+    private final static String GRANT_TYPE = "authorization_code";
+    @Autowired
+    private AppUserMapper appUserMapper;
+    @Autowired
+    private LoginRecordMapper loginRecordMapper;
+    @Autowired
+    private RiskFeign riskFeign;
+    @Autowired
+    private AppUserService appUserService;
+    @Autowired
+    private RedisService redisService;
+    @Autowired
+    private QuestionMapper questionMapper;
+
+    @Override
+    protected WxDefaultConfig getLoginConfig(Integer appType) {
+        return appUserMapper.getDefaultConfig(appType);
+    }
+
+    @Override
+    protected LoginType getLoginType() {
+        return LoginType.WX;
+    }
+
+    @Override
+    protected void validateParams(LoginParam loginParam, HttpServletRequest request) {
+
+    }
+
+    @Override
+    protected Map<String, Object> setThirdPartyLoginInfo(LoginParam loginParam, WxDefaultConfig defaultConfig, HttpServletRequest request) {
+        WxLoginParam param = (WxLoginParam) loginParam;
+        WxLoginResult loginResult = getWechatLoginInfo(param.getWxCode(), param.getAppType(), defaultConfig.getAppId(), defaultConfig.getSecret());
+        if (Objects.isNull(loginResult) || StrUtil.isBlank(loginResult.getOpenid())) {
+            throw new CommonException("微信登录失败,请刷新授权码!");
+        }
+        WxUserInfo wxUserInfo = getWechatUserInfo(loginResult.getAccess_token(), loginResult.getOpenid());
+        if (Objects.isNull(wxUserInfo)) {
+            throw new CommonException("微信用户登录失败");
+        }
+        Map<String, Object> stringObjectMap = super.setThirdPartyLoginInfo(loginParam, defaultConfig, request);
+        stringObjectMap.put("loginResult", loginResult);
+        stringObjectMap.put("wxUserInfo", wxUserInfo);
+        return stringObjectMap;
+    }
+
+    /**
+     * 微信登录
+     */
+    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;
+    }
+
+    /**
+     * 获取微信用户信息
+     */
+    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;
+    }
+
+
+    @Override
+    protected YtDyzUser queryUserByDeviceAndDitch(String deviceId, Long ditchId) {
+        return null;
+    }
+
+
+    @Override
+    protected List<YtDyzUser> queryDyzUserByParam(LoginParam loginParam, Map<String, Object> thirdPartyLoginInfo) {
+        WxLoginParam param = (WxLoginParam) loginParam;
+        WxLoginResult loginResult = (WxLoginResult) thirdPartyLoginInfo.get("loginResult");
+        WxUserInfo wxUserInfo = (WxUserInfo) thirdPartyLoginInfo.get("wxUserInfo");
+        YtDyzUser ytDyzUser = appUserService.crudForNewTrans(param, wxUserInfo, loginResult);
+        return ytDyzUser == null ? Collections.emptyList() : Collections.singletonList(ytDyzUser);
+    }
+
+    @Override
+    protected YtDyzUser registryUser(LoginParam param, YtDyzUser old, Map<String, Object> paramMap) {
+        WxLoginResult loginResult = (WxLoginResult) paramMap.get("loginResult");
+        WxUserInfo wxUserInfo = (WxUserInfo) paramMap.get("wxUserInfo");
+        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());
+        old.setPhoneJson(param.getPhoneJson());
+        //根据设备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);
+        return null;
+    }
+
+    @Override
+    protected void deadWithUserCrud(LoginParam param, YtDyzUser old, Map<String, Object> paramMap) {
+        WxLoginResult loginResult = (WxLoginResult) paramMap.get("loginResult");
+        WxUserInfo wxUserInfo = (WxUserInfo) paramMap.get("wxUserInfo");
+
+        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);
+        newUser.setPhoneJson(param.getPhoneJson());
+        appUserMapper.updateUser(newUser);
+    }
+
+    @Override
+    protected void saveLoginRecord(YtDyzLoginRecord loginRecord) {
+        loginRecordMapper.insertOne(loginRecord);
+    }
+
+    @Override
+    protected Result<?> remoteCheckLoginRisk(YtDyzUser ytDyzUser) {
+        ytDyzUser.setRiskCode("313");
+        return riskFeign.checkRisk(ytDyzUser);
+    }
+
+    @Override
+    protected Result<?> remoteCheckRegRisk(YtDyzUser user) {
+        return null;
+    }
+
+    @Override
+    protected void setExtInfo(YtDyzUser old, Map<String, Object> paramMap) {
+        WxUserInfo wxUserInfo = (WxUserInfo) paramMap.get("wxUserInfo");
+        old.setHeadImg(wxUserInfo.getHeadimgurl());
+        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()));
+    }
+
+
+}

+ 1 - 1
yt-adage/adage-service/src/main/resources/mapper/AppUserMapper.xml

@@ -646,7 +646,7 @@
         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, phone_json
         from yt_dyz_user
-        where device_id = #{deviceId} and ditch_id = #{ditchId}
+        where device_id = #{deviceId} and ditch_id = #{ditchId} limit 1
     </select>
     <select id="countDitch" resultType="java.lang.Integer">
         select

+ 7 - 0
yt-algebraic/algebraic-feign/src/main/java/com/ytpm/algebraic/feign/AlgebraicFeign.java

@@ -30,6 +30,13 @@ import java.util.Map;
 
 @FeignClient(name = "algebraic-service")
 public interface AlgebraicFeign {
+
+    @GetMapping("/visitor/getLoginDitchCount")
+    int getLoginDitchCount(@RequestParam("deviceId")String deviceId,@RequestParam("hours")Integer hours);
+
+    @GetMapping("/visitor/getDitchCount")
+    int getDitchCount(@RequestParam("deviceId")String deviceId,@RequestParam("hours")Integer hours);
+
     @GetMapping("/user/getMonthRegistryUser")
     List<YtDyzUser> getMonthRegistryUser(@RequestParam("appIds")String appIds,@RequestParam("type")Integer type);
     @PostMapping("/user/queryAll")

+ 26 - 0
yt-common/src/main/java/com/ytpm/app/enums/LoginType.java

@@ -0,0 +1,26 @@
+package com.ytpm.app.enums;
+
+
+import lombok.Getter;
+
+/**
+ * 登陆方式
+ * @author lih
+ * @date 2025/9/3
+ */
+@Getter
+public enum LoginType {
+    // ios
+    IOS("IOS"),
+    // 微信
+    WX("微信"),
+    // 游客
+    VISITOR("游客");
+
+    private final String typeName;
+
+    LoginType(String typeName) {
+        this.typeName = typeName;
+    }
+
+}

+ 47 - 0
yt-common/src/main/java/com/ytpm/app/param/LoginParam.java

@@ -0,0 +1,47 @@
+package com.ytpm.app.param;
+
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 基础登陆
+ * @author lih
+ * @date 2025/9/3
+ */
+@Data
+public class LoginParam {
+    @ApiModelProperty(value = "手机号")
+    private String phone;
+
+    @ApiModelProperty(value = "登录IP")
+    private String loginIp;
+
+    @ApiModelProperty(value = "IP运营商")
+    private String ipOperator;
+
+    @ApiModelProperty(value = "IP归属地")
+    private String ipLocation;
+
+    @ApiModelProperty(value = "渠道ID")
+    private Long ditchId;
+
+    @ApiModelProperty(value = "应用类型", required = true)
+    private int appType;
+
+    @ApiModelProperty(value = "所属应用ID", required = false)
+    private String appId;
+
+    @ApiModelProperty(value = "设备ID", required = false)
+    private String deviceId;
+
+    @ApiModelProperty(value = "手机品牌")
+    private String brand;
+
+    @ApiModelProperty(value = "手机型号")
+    private String model;
+
+    @ApiModelProperty(value = "手机信息json")
+    private String phoneJson;
+
+}

+ 1 - 1
yt-common/src/main/java/com/ytpm/app/param/WxLoginParam.java

@@ -6,7 +6,7 @@ import lombok.Data;
 
 @ApiModel("微信登录参数")
 @Data
-public class WxLoginParam {
+public class WxLoginParam extends LoginParam {
     @ApiModelProperty(value = "手机号")
     private String phone;
     @ApiModelProperty(value = "手机品牌")

+ 262 - 0
yt-common/src/main/java/com/ytpm/handle/AbstractLoginService.java

@@ -0,0 +1,262 @@
+package com.ytpm.handle;
+
+
+import cn.hutool.core.collection.CollUtil;
+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.enums.LoginType;
+import com.ytpm.app.model.YtDyzLoginRecord;
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.param.LoginParam;
+import com.ytpm.app.view.WxDefaultConfig;
+import com.ytpm.general.Result;
+import com.ytpm.util.IPUtil;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.util.CollectionUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * <p>登陆模板</p>
+ * 兼容多登陆模式及风控校验
+ * <p>需重写方法:<br>
+ * 1.登陆参数校验 validateParams
+ * 2.根据设备及渠道ID查询用户 queryUserByDeviceAndDitch
+ * 3.获取登陆默认配置 getLoginConfig
+ * 4.根据登陆参数获取系统用户 queryDyzUserByParam
+ * 5.用户新注册逻辑 registryUser
+ * 6.已注册用户处理逻辑 deadWithUserCrud
+ * 7.登陆记录保存 saveLoginRecord
+ * 8.远程调用风控 remoteCheckLoginRisk
+ * 9.远程调用风控 remoteCheckRegRisk
+ * </p>
+ *
+ * <p>回调方法:<br>
+ * 1.用户注册时回调 beforeRegistryUser<br>
+ * 2.用户登陆时回调 beforeDeadWithUserCrud
+ * </p>
+ *
+ * <p>可选重写方法:<br>
+ * 1.第三方登陆调用处理及参数传递 setThirdPartyLoginInfo <br>
+ * 2.登陆成功后调用风控服务校验 ytRiskValidate
+ * 3.登陆用户信息配置额外信息 setExtInfo
+ * </p>
+ *
+ * @author lih
+ * @date 2025/9/4
+ */
+public abstract class AbstractLoginService {
+
+
+    @Value("${risk.config.banned.tips}")
+    private String tips;
+
+    protected abstract LoginType getLoginType();
+
+    /**
+     * 登陆逻辑处理
+     * @param loginType 登陆方式
+     * @param loginParam 登陆参数
+     * @param request request
+     * @return YtDyzUser
+     */
+    public final YtDyzUser loginHandle(LoginType loginType, LoginParam loginParam, HttpServletRequest request) {
+        loginParam.setLoginIp(IPUtil.getClientIp(request));
+        // 获取默认配置
+        WxDefaultConfig defaultConfig = getLoginConfig(loginParam.getAppType());
+        if (Objects.isNull(defaultConfig)) {
+            throw new CommonException(StrUtil.format("{}登录失败,未找到相应配置!", loginType.getTypeName()));
+        }
+        loginParam.setAppId(defaultConfig.getPlatformAppId());
+        // 参数校验
+        validateParams(loginParam, request);
+        // 获取第三方登陆信息 如微信授权码、微信登陆后授权信息
+        Map<String, Object> paramMap = setThirdPartyLoginInfo(loginParam, defaultConfig, request);
+        YtDyzUser ytDyzUser;
+        if (loginType == LoginType.VISITOR) {
+            // 游客模式
+            ytDyzUser = visitorLoginHandle(loginParam, paramMap);
+        } else {
+            ytDyzUser = clientLoginHandle(loginParam, paramMap);
+        }
+        // 配置额外信息
+        setExtInfo(ytDyzUser, paramMap);
+        // 添加用户登录记录
+        addLoginRecord(loginParam,ytDyzUser.getUserId());
+        // 调用风控服务校验默认风控配置
+        ytRiskValidate(ytDyzUser);
+        return ytDyzUser;
+    }
+
+    /**
+     * 登陆参数校验
+     */
+    protected abstract void validateParams(LoginParam loginParam, HttpServletRequest request);
+
+    /**
+     * 微信端/IOS端登陆
+     */
+    private YtDyzUser clientLoginHandle(LoginParam loginParam, Map<String, Object> paramMap) {
+        YtDyzUser ytDyzUser;
+        List<YtDyzUser> ytDyzUsers = queryDyzUserByParam(loginParam, paramMap);
+        if (CollectionUtils.isEmpty(ytDyzUsers)) {
+            beforeRegistryUser(loginParam, paramMap);
+            ytDyzUser = new YtDyzUser();
+            registryUser(loginParam, ytDyzUser, paramMap);
+        } else {
+            ytDyzUser = ytDyzUsers.get(0);
+            beforeDeadWithUserCrud(loginParam, ytDyzUser, paramMap);
+            deadWithUserCrud(loginParam, ytDyzUser, paramMap);
+        }
+        return ytDyzUser;
+    }
+
+    /**
+     * 游客模式登录
+     */
+    private YtDyzUser visitorLoginHandle(LoginParam loginParam, Map<String, Object> paramMap) {
+        // 唯一性判断
+        // 1.查询设备ID不存在记录 则根据设备id+渠道id注册用户
+        // 2.查询设备ID存在记录 则根据渠道ID控制唯一性,同步出路风控记录
+        YtDyzUser ytDyzUser = null;
+        List<YtDyzUser> ytDyzUsers = queryDyzUserByParam(loginParam, paramMap);
+        if(CollUtil.isEmpty(ytDyzUsers)) {
+            beforeRegistryUser(loginParam, paramMap);
+            ytDyzUser = new YtDyzUser();
+            registryUser(loginParam, ytDyzUser, paramMap);
+        } else {
+            // 查询当前设备当前渠道是否存在该用户
+            ytDyzUser = queryUserByDeviceAndDitch(loginParam.getDeviceId(), loginParam.getDitchId());
+            if(ytDyzUser == null) {
+                YtDyzUser user = ytDyzUsers.stream()
+                        .sorted(Comparator.comparing(YtDyzUser::getRegistryTime).reversed())
+                        .collect(Collectors.toList()).get(0);
+                visitorCheckRegRisk(user);
+                ytDyzUser = new YtDyzUser();
+                registryUser(loginParam, ytDyzUser, paramMap);
+            }
+            //当前渠道已有用户,校验用户是否处于风控中 & 更新用户信息
+            beforeDeadWithUserCrud(loginParam, ytDyzUser, paramMap);
+            deadWithUserCrud(loginParam, ytDyzUser, paramMap);
+        }
+        return ytDyzUser;
+    }
+
+    private void visitorCheckRegRisk(YtDyzUser user) {
+        Result<?> result = remoteCheckRegRisk(user);
+        if (result == null) {
+            throw new CommonException("feign invoke Fail!");
+        }
+        if (result.getCode() != 200) {
+            throw new CommonException(result.getMessage());
+        }
+    }
+
+
+
+
+    protected abstract YtDyzUser queryUserByDeviceAndDitch(String deviceId, Long ditchId);
+
+
+    protected Map<String, Object> setThirdPartyLoginInfo(LoginParam loginParam, WxDefaultConfig defaultConfig,
+                                                         HttpServletRequest request) {
+        Map<String, Object> map = new HashMap<>();
+
+        return map;
+    }
+
+
+    // 获取登陆默认配置
+    protected abstract WxDefaultConfig getLoginConfig(Integer appType);
+
+
+
+    /**
+     * 根据登陆配置信息 查询系统用户记录
+     * @param loginParam 登陆参数
+     * @param thirdPartyLoginInfo 第三方登陆西悉尼
+     */
+    protected abstract List<YtDyzUser> queryDyzUserByParam(LoginParam loginParam, Map<String, Object> thirdPartyLoginInfo);
+
+
+    // 注册用户处理器回调
+    protected void beforeRegistryUser(LoginParam loginParam, Map<String, Object> paramMap){
+
+    }
+
+    // 用户注册逻辑
+    protected abstract YtDyzUser registryUser(LoginParam loginParam,YtDyzUser user,  Map<String, Object> paramMap);
+
+    // 已注册用户处理前回调
+    protected void beforeDeadWithUserCrud(LoginParam loginParam, YtDyzUser ytDyzUser, Map<String, Object> paramMap){
+        //当前渠道已有用户,校验用户是否处于风控中 & 更新用户信息
+        if(ytDyzUser != null && !ytDyzUser.getUserStatus().equals(UserStatusEnum.NORMAL.getCode())){
+            throw new CommonException(getTipsMsg());
+        }
+    }
+
+    private String getTipsMsg(){
+        String[] split = tips.split(",");
+        return split[RandomUtil.randomInt(split.length)];
+    }
+
+
+    // 已注册用户处理逻辑
+    protected abstract void deadWithUserCrud(LoginParam loginParam, YtDyzUser old, Map<String, Object> paramMap);
+
+    private void addLoginRecord(LoginParam 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());
+        saveLoginRecord(loginRecord);
+    }
+
+    /**
+     * 保存登陆记录实现
+     */
+    protected abstract void saveLoginRecord(YtDyzLoginRecord loginRecord);
+
+
+    /**
+     * 调用风控服务校验默认风控配置
+     */
+    protected void ytRiskValidate(YtDyzUser ytDyzUser) {
+        Result<?> result = remoteCheckLoginRisk(ytDyzUser);
+        if (result == null || result.getCode() != 200) {
+            throw new CommonException(result == null ? "feign invoke Fail!" : result.getMessage());
+        }
+    }
+
+    /**
+     * 配置相关信息
+     *
+     * @param ytDyzUser
+     * @param paramMap
+     */
+    protected void setExtInfo(YtDyzUser ytDyzUser, Map<String, Object> paramMap) {
+
+    }
+
+    /**
+     * 远程调用校验用户风控,需设置风控编码[riskCode],并重写该方法
+     * @param ytDyzUser 当前用户
+     */
+    protected abstract Result<?> remoteCheckLoginRisk(YtDyzUser ytDyzUser);
+
+    /**
+     * 远程调用校验注册用户
+     * @param user 参数
+     */
+    protected abstract Result<?> remoteCheckRegRisk(YtDyzUser user);
+}

+ 21 - 0
yt-common/src/main/java/com/ytpm/handle/CommonException.java

@@ -0,0 +1,21 @@
+package com.ytpm.handle;
+
+
+/**
+ * @author lih
+ * @date 2025/9/4
+ */
+public class CommonException extends RuntimeException {
+
+    public CommonException(String message) {
+        super(message);
+    }
+
+    public CommonException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public CommonException(Throwable cause) {
+        super(cause);
+    }
+}

+ 59 - 0
yt-common/src/main/java/com/ytpm/handle/LoginServiceFactory.java

@@ -0,0 +1,59 @@
+package com.ytpm.handle;
+
+
+import com.ytpm.app.enums.LoginType;
+import com.ytpm.app.model.YtDyzUser;
+import com.ytpm.app.param.WxLoginParam;
+import com.ytpm.general.RepMessage;
+import com.ytpm.general.Result;
+import com.ytpm.general.StatusCode;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author lih
+ * @date 2025/9/4
+ */
+@Component
+public class LoginServiceFactory {
+
+    private final Map<String, AbstractLoginService> loginServiceMap = new ConcurrentHashMap<>();
+
+
+    /**
+     * 通过构造函数注入所有登录服务
+     */
+    @Autowired
+    public LoginServiceFactory(List<AbstractLoginService> loginServices) {
+        for (AbstractLoginService service : loginServices) {
+            loginServiceMap.put(service.getLoginType().getTypeName(), service);
+        }
+    }
+
+    /**
+     * 根据登录类型获取登录服务
+     */
+    public AbstractLoginService getLoginService(LoginType loginType) {
+        AbstractLoginService service = loginServiceMap.get(loginType.getTypeName());
+        if (service == null) {
+            throw new RuntimeException("不支持的登录类型: " + loginType);
+        }
+        return service;
+    }
+
+    public Result<YtDyzUser> login(LoginType loginType, WxLoginParam param, HttpServletRequest request) {
+        try {
+            AbstractLoginService loginService = getLoginService(loginType);
+            YtDyzUser ytDyzUser = loginService.loginHandle(LoginType.VISITOR, param, request);
+            return Result.resultOk(RepMessage.LOGIN_SUCCESS, ytDyzUser);
+        } catch (CommonException e) {
+            return new Result<>(StatusCode.ACCESS_ERR, e.getMessage());
+        }
+    }
+
+}

+ 19 - 0
yt-common/src/main/java/com/ytpm/util/IPUtil.java

@@ -0,0 +1,19 @@
+package com.ytpm.util;
+
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author lih
+ * @date 2025/9/4
+ */
+public class IPUtil {
+
+    public static String getClientIp(HttpServletRequest request) {
+        String xfHeader = request.getHeader("X-Forwarded-For");
+        if (xfHeader == null) {
+            return request.getRemoteAddr();
+        }
+        return xfHeader.split(",")[0]; // 可能会有多个IP,这里取第一个逗号前的IP
+    }
+}

+ 2 - 0
yt-common/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  com.ytpm.handle.LoginServiceFactory