Explorar o código

Merge branch 'master' into 会员关联项目

# Conflicts:
#	fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java
#	fs-service-system/src/main/java/com/fs/store/domain/FsUser.java
#	fs-service-system/src/main/java/com/fs/store/mapper/FsUserMapper.java
#	fs-service-system/src/main/java/com/fs/store/service/impl/FsUserServiceImpl.java
#	fs-service-system/src/main/resources/mapper/store/FsUserMapper.xml
#	fs-user-app/src/main/java/com/fs/app/controller/UserController.java
#	fs-user-app/src/main/java/com/fs/app/controller/WxCompanyUserController.java
Long hai 1 semana
pai
achega
f26c756a4e
Modificáronse 45 ficheiros con 767 adicións e 282 borrados
  1. 9 20
      fs-admin/src/main/java/com/fs/core/security/filter/JwtAuthenticationTokenFilter.java
  2. 15 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java
  3. 0 6
      fs-admin/src/main/java/com/fs/web/controller/system/SysLoginController.java
  4. 4 0
      fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java
  5. 12 0
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  6. 26 4
      fs-company/src/main/java/com/fs/user/FsUserAdminController.java
  7. 11 0
      fs-service-system/src/main/java/com/fs/course/config/CourseConfig.java
  8. 2 0
      fs-service-system/src/main/java/com/fs/course/domain/FsCourseWatchComment.java
  9. 2 0
      fs-service-system/src/main/java/com/fs/course/domain/FsUserCourseComplaintRecord.java
  10. 2 0
      fs-service-system/src/main/java/com/fs/course/domain/FsUserCourseComplaintType.java
  11. 1 1
      fs-service-system/src/main/java/com/fs/course/mapper/FsCourseAnswerLogsMapper.java
  12. 2 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseVideoRedPackageMapper.java
  13. 23 0
      fs-service-system/src/main/java/com/fs/course/param/BatchCompanyRedPackageParam.java
  14. 7 0
      fs-service-system/src/main/java/com/fs/course/service/IFsCourseLinkService.java
  15. 7 0
      fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseVideoRedPackageService.java
  16. 2 0
      fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java
  17. 101 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java
  18. 18 35
      fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseQuestionBankServiceImpl.java
  19. 50 8
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoRedPackageServiceImpl.java
  20. 68 22
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  21. 9 5
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserWatchCourseStatisticsServiceImpl.java
  22. 2 0
      fs-service-system/src/main/java/com/fs/crm/domain/CrmCustomer.java
  23. 2 0
      fs-service-system/src/main/java/com/fs/qw/domain/QwMsg.java
  24. 2 1
      fs-service-system/src/main/java/com/fs/qw/domain/QwSession.java
  25. 14 0
      fs-service-system/src/main/java/com/fs/store/domain/FsUser.java
  26. 5 0
      fs-service-system/src/main/java/com/fs/store/mapper/FsUserMapper.java
  27. 5 0
      fs-service-system/src/main/java/com/fs/store/param/FsUserEditParam.java
  28. 4 0
      fs-service-system/src/main/java/com/fs/store/param/h5/FsUserPageListParam.java
  29. 3 0
      fs-service-system/src/main/java/com/fs/store/service/IFsUserService.java
  30. 23 34
      fs-service-system/src/main/java/com/fs/store/service/impl/FsStorePaymentServiceImpl.java
  31. 86 6
      fs-service-system/src/main/java/com/fs/store/service/impl/FsUserServiceImpl.java
  32. 2 0
      fs-service-system/src/main/java/com/fs/system/domain/SysKeyword.java
  33. 10 10
      fs-service-system/src/main/java/com/fs/system/mapper/SysDictTypeMapper.java
  34. 3 3
      fs-service-system/src/main/resources/application-common.yml
  35. 2 1
      fs-service-system/src/main/resources/mapper/company/CompanyUserChangeApplyMapper.xml
  36. 2 0
      fs-service-system/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml
  37. 9 1
      fs-service-system/src/main/resources/mapper/course/FsUserCourseVideoRedPackageMapper.xml
  38. 1 1
      fs-service-system/src/main/resources/mapper/course/FsVideoResourceMapper.xml
  39. 1 1
      fs-service-system/src/main/resources/mapper/store/FsUserCompanyUserMapper.xml
  40. 106 33
      fs-service-system/src/main/resources/mapper/store/FsUserMapper.xml
  41. 54 15
      fs-user-app/src/main/java/com/fs/app/controller/AppBaseController.java
  42. 0 7
      fs-user-app/src/main/java/com/fs/app/controller/CourseWxH5Controller.java
  43. 22 1
      fs-user-app/src/main/java/com/fs/app/controller/UserController.java
  44. 29 67
      fs-user-app/src/main/java/com/fs/app/controller/WxCompanyUserController.java
  45. 9 0
      fs-user-app/src/main/java/com/fs/app/param/LoginMaWxParam.java

+ 9 - 20
fs-admin/src/main/java/com/fs/core/security/filter/JwtAuthenticationTokenFilter.java

@@ -1,24 +1,21 @@
 package com.fs.core.security.filter;
 
-import java.io.IOException;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import com.fs.common.core.redis.RedisCache;
-import com.fs.core.exception.FSException;
+import com.fs.common.utils.StringUtils;
 import com.fs.core.security.LoginUser;
+import com.fs.core.security.SecurityUtils;
+import com.fs.core.web.service.TokenService;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
 import org.springframework.stereotype.Component;
 import org.springframework.web.filter.OncePerRequestFilter;
-import com.fs.core.security.SecurityUtils;
-import com.fs.common.utils.StringUtils;
-import com.fs.core.web.service.TokenService;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 
 /**
  * token过滤器 验证token有效性
@@ -30,8 +27,6 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
 {
     @Autowired
     private TokenService tokenService;
-    @Autowired
-    private RedisCache redisCache;
     @Override
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
             throws ServletException, IOException
@@ -39,12 +34,6 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
         LoginUser loginUser = tokenService.getLoginUser(request);
         if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
         {
-            //如果REDIS中的TOKEN与请求的TOKEN不一致,抛出异常
-            String requestToken = tokenService.getHeaderToken(request).substring(7);
-            String token=redisCache.getCacheObject("token-pc:"+loginUser.getUsername());
-            if(!token.equals(requestToken)){
-                throw new FSException("Token失效,请重新登录", HttpStatus.UNAUTHORIZED.value());
-            }
             tokenService.verifyToken(loginUser);
             UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
             authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

+ 15 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java

@@ -10,6 +10,7 @@ import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.domain.FsUserCoursePeriodDays;
 import com.fs.course.domain.FsUserCourseVideoRedPackage;
+import com.fs.course.param.BatchCompanyRedPackageParam;
 import com.fs.course.param.CompanyRedPacketParam;
 import com.fs.course.param.FsBatchPeriodRedPackageParam;
 import com.fs.course.param.PeriodCountParam;
@@ -27,6 +28,7 @@ import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.HashMap;
@@ -213,6 +215,19 @@ public class FsUserCoursePeriodController extends BaseController {
         return R.ok();
     }
 
+    @PreAuthorize("@ss.hasPermi('course:period:setCompanyRedPacket')")
+    @ApiOperation("按公司批量保存设置红包金额")
+    @PostMapping("/batchRedPacket/byCompany")
+    public R batchRedPacketByCompany(@Validated @RequestBody BatchCompanyRedPackageParam param) {
+        try {
+            fsUserCourseVideoRedPackageService.batchRedPacketByCompany(param);
+        } catch (Exception e) {
+            logger.error("按恭送批量保存设置红包金额-失败!,入参:{}", param);
+            return R.error("保存失败!");
+        }
+        return R.ok();
+    }
+
     @PostMapping("/periodCount")
     @ApiOperation("营期统计")
     public R periodCourseCount(@RequestBody PeriodCountParam param) {

+ 0 - 6
fs-admin/src/main/java/com/fs/web/controller/system/SysLoginController.java

@@ -5,7 +5,6 @@ import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.entity.SysMenu;
 import com.fs.common.core.domain.entity.SysUser;
-import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.PatternUtils;
 import com.fs.common.utils.ServletUtils;
 import com.fs.core.security.LoginBody;
@@ -32,9 +31,6 @@ import java.util.Set;
 @RestController
 public class SysLoginController
 {
-    @Autowired
-    private RedisCache redisCache;
-
     @Autowired
     private SysLoginService loginService;
 
@@ -65,8 +61,6 @@ public class SysLoginController
             // 生成令牌
             String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
                     loginBody.getUuid());
-            //写入用户TOKEN
-            redisCache.setCacheObject("token-pc:"+loginBody.getUsername(),token);
             return R.ok().put(Constants.TOKEN, token);
         }
         catch (Exception e){

+ 4 - 0
fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java

@@ -123,6 +123,10 @@ public class FsUserController extends AppBaseController {
         param.setUserId(Long.parseLong(getUserId()));
         PageHelper.startPage(param.getPageNum(), param.getPageSize());
         List<CompanyUserTagListVO> tagList = userProjectTagService.getTagList(param);
+        CompanyUserTagListVO noTag = new CompanyUserTagListVO();
+        noTag.setTagId(0L);
+        noTag.setTagName("无标签");
+        tagList.add(0, noTag);
         PageInfo<CompanyUserTagListVO> pageInfo = new PageInfo<>(tagList);
         return ResponseResult.ok(pageInfo);
     }

+ 12 - 0
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -234,5 +234,17 @@ public class FsUserCourseVideoController extends AppBaseController {
         return fsUserCourseVideoService.setWatchCourseTime(collect);
     }
 
+    /**
+     * 获取跳转微信小程序的链接地址
+     * @param linkStr
+     * @return
+     */
+    @Login
+    @GetMapping("/getGotoWxAppLink")
+    @ApiOperation("获取跳转微信小程序的链接地址")
+    public ResponseResult<String> getGotoWxAppLink(String linkStr,String appid) {
+        return ResponseResult.ok(courseLinkService.getGotoWxAppLink(linkStr,appid));
+    }
+
 
 }

+ 26 - 4
fs-company/src/main/java/com/fs/user/FsUserAdminController.java

@@ -1,14 +1,18 @@
 package com.fs.user;
 
+import com.fs.common.annotation.Log;
 import com.fs.common.constant.HttpStatus;
 import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
 import com.fs.company.cache.ICompanyUserCacheService;
 import com.fs.core.security.LoginUser;
 import com.fs.core.web.service.TokenService;
+import com.fs.store.domain.FsUser;
 import com.fs.store.param.h5.FsUserPageListParam;
 import com.fs.store.service.IFsUserService;
 import com.fs.store.vo.h5.FsUserPageListVO;
@@ -17,10 +21,7 @@ import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
-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;
+import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
 
@@ -76,5 +77,26 @@ public class FsUserAdminController extends BaseController {
         return R.error();
     }
 
+    /**
+     * 获取用户详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('store:user:query')")
+    @GetMapping(value = "/{userId}")
+    public AjaxResult getInfo(@PathVariable("userId") Long userId)
+    {
+        return AjaxResult.success(fsUserService.selectFsUserById(userId));
+    }
+
+    /**
+     * 修改用户
+     */
+    @PreAuthorize("@ss.hasPermi('store:user:edi')")
+    @Log(title = "用户", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsUser fsUser)
+    {
+        return toAjax(fsUserService.updateFsUser(fsUser));
+    }
+
 
 }

+ 11 - 0
fs-service-system/src/main/java/com/fs/course/config/CourseConfig.java

@@ -22,6 +22,7 @@ public class CourseConfig implements Serializable {
     private String mpAppId;//看课公众号APPID
     private String registerDomainName;//注册域名
     private String courseDomainName;//链接域名
+    private String miniprogramAppid;//链接域名
     private Integer rewardType; // 奖励类型 1红包 2积分
     private Integer redPacketMode;//红包模式 1总公司 2销售公司
     private BigDecimal moneyPri;//充值手续费百分比
@@ -31,6 +32,16 @@ public class CourseConfig implements Serializable {
     private String courseLogo;//课程Logo
     private Integer openCommentStatus; //开启评论/弹幕
     private Integer viewCommentNum; // 查看历史评论数量
+    /**
+     * 小程序授权头像昵称方式(目前仅会员看课有效)
+     * 1:小程序原生授权 2:跳转H5服务号授权
+     */
+    private Integer miniAppAuthType;
+    /**
+     * 会员看课
+     * 小程序授权头像昵称,跳转H5服务号授权域名
+     */
+    private String userCourseAuthDomain;
 
     @Data
     public static class DisabledTimeVo{

+ 2 - 0
fs-service-system/src/main/java/com/fs/course/domain/FsCourseWatchComment.java

@@ -1,5 +1,6 @@
 package com.fs.course.domain;
 
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import io.swagger.annotations.ApiModelProperty;
@@ -17,6 +18,7 @@ import lombok.EqualsAndHashCode;
 public class FsCourseWatchComment extends BaseEntity{
 
     /** 评论id */
+    @TableId
     private Long commentId;
 
     /** 用户id */

+ 2 - 0
fs-service-system/src/main/java/com/fs/course/domain/FsUserCourseComplaintRecord.java

@@ -1,6 +1,7 @@
 package com.fs.course.domain;
 
 import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import io.swagger.annotations.ApiModelProperty;
@@ -30,6 +31,7 @@ public class FsUserCourseComplaintRecord extends BaseEntity{
     private String nickName;
 
     /** 投诉记录id */
+    @TableId
     private Long recordId;
 
     /** 用户id,关联fs_user */

+ 2 - 0
fs-service-system/src/main/java/com/fs/course/domain/FsUserCourseComplaintType.java

@@ -1,5 +1,6 @@
 package com.fs.course.domain;
 
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.TreeEntity;
 import lombok.Data;
@@ -16,6 +17,7 @@ import lombok.EqualsAndHashCode;
 public class FsUserCourseComplaintType extends TreeEntity{
 
     /** 投诉类型id */
+    @TableId
     private Long complaintTypeId;
 
     /** 投诉类型名称 */

+ 1 - 1
fs-service-system/src/main/java/com/fs/course/mapper/FsCourseAnswerLogsMapper.java

@@ -103,6 +103,6 @@ public interface FsCourseAnswerLogsMapper
      * @param videoId  小节ID
      * @return count
      */
-    @Select("select count(log_id) from fs_course_red_packet_log where user_id = #{userId} and video_id = #{videoId}")
+    @Select("select count(log_id) from fs_course_red_packet_log where user_id = #{userId} and video_id = #{videoId} and status = 1")
     Long selectRedStatus(@Param("userId") Long userId, @Param("videoId") Long videoId);
 }

+ 2 - 0
fs-service-system/src/main/java/com/fs/course/mapper/FsUserCourseVideoRedPackageMapper.java

@@ -108,4 +108,6 @@ public interface FsUserCourseVideoRedPackageMapper
             "</foreach>" +
             "</script>")
     int updateBatchDelFlag(@Param("ids") Long [] ids, @Param("delFlag") Integer delFlag);
+
+    Integer selectRedPacketByCompanyCount(@Param("videoId") Long videoId,@Param("companyId") Long companyId, @Param("periodId") Long periodId);
 }

+ 23 - 0
fs-service-system/src/main/java/com/fs/course/param/BatchCompanyRedPackageParam.java

@@ -0,0 +1,23 @@
+package com.fs.course.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.DecimalMin;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class BatchCompanyRedPackageParam {
+
+    @NotNull(message = "营期ID不能为空")
+    private Long periodId;
+
+    @NotEmpty(message = "公司ID不能为空")
+    private List<Long> companyIds;
+
+    @NotNull(message = "红包金额不能为空")
+    @DecimalMin(value = "0.1", message = "红包金额必须大于0.1")
+    private BigDecimal redPacketMoney;
+}

+ 7 - 0
fs-service-system/src/main/java/com/fs/course/service/IFsCourseLinkService.java

@@ -79,4 +79,11 @@ public interface IFsCourseLinkService
     R createRoomLinkUrl(FsCourseLinkCreateParam param);
 
     R getRealLinkH5(String link);
+
+    /**
+     * 获取跳转微信小程序的链接地址
+     * @param linkStr
+     * @return
+     */
+    String getGotoWxAppLink(String linkStr,String appid);
 }

+ 7 - 0
fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseVideoRedPackageService.java

@@ -1,6 +1,7 @@
 package com.fs.course.service;
 
 import com.fs.course.domain.FsUserCourseVideoRedPackage;
+import com.fs.course.param.BatchCompanyRedPackageParam;
 import com.fs.course.param.FsBatchPeriodRedPackageParam;
 
 import java.util.List;
@@ -92,4 +93,10 @@ public interface IFsUserCourseVideoRedPackageService
     void batchRedPacketByPeriod(List<FsBatchPeriodRedPackageParam> periodRedPackageList);
 
     List<FsUserCourseVideoRedPackage> selectByRuleIds(List<Long> ruleIds);
+
+    /**
+     * 按照公司批量设置红包
+     * @param param 入参
+     */
+    void batchRedPacketByCompany(BatchCompanyRedPackageParam param);
 }

+ 2 - 0
fs-service-system/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java

@@ -175,4 +175,6 @@ public interface IFsUserCourseVideoService
     ResponseResult<Boolean> setWatchCourseTime(List<FsWatchCourseTimeParam> paramList);
 
     FsUserCourseVideoQVO selectFsUserCourseVideoByVideoIdVO(Long videoId);
+
+    R checkUserInfo(Long userId);
 }

+ 101 - 0
fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseLinkServiceImpl.java

@@ -1,8 +1,10 @@
 package com.fs.course.service.impl;
 
+import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
@@ -23,17 +25,29 @@ import com.fs.course.service.IFsCourseLinkService;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.system.service.ISysConfigService;
+import com.fs.wx.miniapp.config.WxMaConfiguration;
 import lombok.Synchronized;
 import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
 import org.checkerframework.checker.units.qual.A;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.security.SecureRandom;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -289,6 +303,93 @@ public class FsCourseLinkServiceImpl implements IFsCourseLinkService
         }
     }
 
+    /**
+     * 获取跳转微信小程序的链接地址
+     * @param linkStr
+     * @return
+     */
+    @Override
+    public String getGotoWxAppLink(String linkStr,String appId) {
+        CloseableHttpClient client = null;
+        try {
+            client = HttpClients.createDefault();
+            String[] split = linkStr.split("\\?");
+            if (split.length == 2 && split[0].length() > 0 && split[1].length() > 0) {
+                //处理页面路径
+                String pageUrl = split[0];
+                if (pageUrl.startsWith("/")) {
+                    pageUrl = pageUrl.substring(1);
+                }
+                //处理参数
+                String query = split[1];
+                query = URLEncoder.encode(query, StandardCharsets.UTF_8.toString());
+                String json = configService.selectConfigByKey("course.config");
+                CourseConfig config = JSON.parseObject(json, CourseConfig.class);
+                String miniprogramAppid = config.getMiniprogramAppid();
+                if (StringUtils.isBlank(miniprogramAppid)) {
+                    return "未配置点播小程序id";
+                }
+//                if(StringUtils.isBlank(code)){
+//                    return "参数错误,请传入code";
+//                }
+                //获取微信token
+                final WxMaService wxService = WxMaConfiguration.getMaService(appId);
+                String token = wxService.getAccessToken();
+                HttpPost httpPost = new HttpPost("https://api.weixin.qq.com/wxa/generate_urllink?access_token=" + token);
+                JSONObject bodyObj = new JSONObject();
+                bodyObj.put("path", pageUrl);
+                bodyObj.put("query", query);
+                log.info("微信小程序请求参数打印:{}", bodyObj.toJSONString());
+                StringEntity entity = new StringEntity(bodyObj.toJSONString(),"UTF-8");
+                httpPost.setEntity(entity);
+                httpPost.setHeader("Content-type", "application/json");
+                HttpEntity response = client.execute(httpPost).getEntity();
+                String responseString = EntityUtils.toString(response);
+                log.info("微信小程序接口响应数据:{}", responseString);
+                JSONObject jsonObject = JSONObject.parseObject(responseString);
+                if(null != jsonObject && !jsonObject.isEmpty() && jsonObject.containsKey("url_link")){
+                    return jsonObject.getString("url_link");
+                }
+            } else {
+                return "页面链接错误,获取失败";
+            }
+
+        } catch (WxErrorException e) {
+            throw new RuntimeException(e);
+        } catch (ClientProtocolException e) {
+            throw new RuntimeException(e);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+//        String json = configService.selectConfigByKey("course.config");
+//        CourseConfig config = JSON.parseObject(json, CourseConfig.class);
+//        String miniprogramAppid = config.getMiniprogramAppid();
+//        if (StringUtils.isBlank(miniprogramAppid)) {
+//                    return "未配置点播小程序id";
+//                }
+////        String envVersion = "trial";
+////        String envVersion = version;
+//        if (StringUtils.isNotBlank(linkStr)) {
+//            //解析pageLink
+//            String[] split = linkStr.split("\\?");
+//            if (split.length == 2 && split[0].length() > 0 && split[1].length() > 0) {
+//                //处理页面路径
+//                String pageUrl =split[0];
+//                if(pageUrl.startsWith("/")){
+//                    pageUrl = pageUrl.substring(1);
+//                }
+//                //处理参数
+//                String query = split[1];
+////                query = query.replace("\\u003d", "=");
+//                String wxAppLink = getWxAppLink(miniprogramAppid, pageUrl, query);
+//                return wxAppLink;
+//            } else {
+//                return "页面链接错误,获取失败";
+//            }
+//        }
+        return "";
+    }
+
     @Override
     public R createLinkUrlWc(FsCourseLinkCreateParam param) {
 

+ 18 - 35
fs-service-system/src/main/java/com/fs/course/service/impl/FsCourseQuestionBankServiceImpl.java

@@ -166,49 +166,32 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
         new FsCourseAnswerLogs();
         FsCourseAnswerLogs rightLog;
         //判断短链类型
-        if (param.getLinkType()!=null&&param.getLinkType()==1){
-            rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideo(param.getVideoId(), param.getUserId(),null);
-            if (rightLog!=null){
+
+        FsCourseWatchLog log = courseWatchLogMapper.getWatchLogByFsUser(param.getVideoId(), param.getUserId(), param.getCompanyUserId());
+        if (log==null){
+            return R.error("无记录");
+        }
+        if (log.getLogType()!=2){
+            return R.error("未完课");
+        }
+        logId = log.getLogId();
+
+        rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideo(param.getVideoId(), param.getUserId(), param.getQwUserId());
+        if (rightLog != null) {
+            if (log.getRewardType() != null) {
                 // 增加判断,去查询红包记录是否已发送成功,如果成功,则返回当前提示,否则返回答题成功(让其可以继续答题,直到红包领取完成)
                 FsCourseRedPacketLog fsCourseRedPacketLog = redPacketLogMapper.selectUserFsCourseRedPacketLog(param.getVideoId(), param.getUserId(),param.getPeriodId());
-                if(fsCourseRedPacketLog != null && fsCourseRedPacketLog.getStatus() == 1){
+                if(fsCourseRedPacketLog != null && fsCourseRedPacketLog.getStatus() == 1) {
                     return R.error("该课程已答题完成,不可重复答题");
-                }
-            }
-            errorCount = courseAnswerLogsMapper.selectErrorCountByCourseVideo(param.getVideoId(), param.getUserId(),null);
-
-        }else {
-            FsCourseWatchLog log;
-            if(isH5User){
-               log = courseWatchLogMapper.getWatchLogByFsUser(param.getVideoId(), param.getUserId(), param.getCompanyUserId());
-            } else {
-                log = courseWatchLogMapper.getWatchCourseVideo(param.getUserId(), param.getVideoId(), param.getQwUserId(), param.getQwExternalId());
-            }
-            if (log==null){
-                return R.error("无记录");
-            }
-            if (log.getLogType()!=2){
-                return R.error("未完课");
-            }
-            logId = log.getLogId();
-
-            rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideo(param.getVideoId(), param.getUserId(), param.getQwUserId());
-            if (rightLog != null) {
-                if (log.getRewardType() != null) {
-                    // 增加判断,去查询红包记录是否已发送成功,如果成功,则返回当前提示,否则返回答题成功(让其可以继续答题,直到红包领取完成)
-                    FsCourseRedPacketLog fsCourseRedPacketLog = redPacketLogMapper.selectUserFsCourseRedPacketLog(param.getVideoId(), param.getUserId(),param.getPeriodId());
-                    if(fsCourseRedPacketLog != null && fsCourseRedPacketLog.getStatus() == 1) {
-                        return R.error("该课程已答题完成,不可重复答题");
-                    } else {
-                        return R.ok("答题成功");
-                    }
                 } else {
                     return R.ok("答题成功");
                 }
-//                return R.error("该课程已答题完成,不可重复答题");
+            } else {
+                return R.ok("答题成功");
             }
-            errorCount = courseAnswerLogsMapper.selectErrorCountByCourseVideo(param.getVideoId(), param.getUserId(),param.getQwUserId());
         }
+        errorCount = courseAnswerLogsMapper.selectErrorCountByCourseVideo(param.getVideoId(), param.getUserId(),param.getQwUserId());
+
 
 
         if (errorCount >= config.getAnswerErrorCount()) {

+ 50 - 8
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoRedPackageServiceImpl.java

@@ -1,26 +1,28 @@
 package com.fs.course.service.impl;
 
-import java.util.*;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.StringUtils;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.domain.FsUserCoursePeriodDays;
+import com.fs.course.domain.FsUserCourseVideoRedPackage;
 import com.fs.course.mapper.FsUserCoursePeriodDaysMapper;
 import com.fs.course.mapper.FsUserCoursePeriodMapper;
+import com.fs.course.mapper.FsUserCourseVideoRedPackageMapper;
+import com.fs.course.param.BatchCompanyRedPackageParam;
 import com.fs.course.param.FsBatchPeriodRedPackageParam;
+import com.fs.course.service.IFsUserCourseVideoRedPackageService;
 import com.google.common.collect.Lists;
 import org.apache.ibatis.session.ExecutorType;
 import org.apache.ibatis.session.SqlSession;
 import org.apache.ibatis.session.SqlSessionFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import com.fs.course.mapper.FsUserCourseVideoRedPackageMapper;
-import com.fs.course.domain.FsUserCourseVideoRedPackage;
-import com.fs.course.service.IFsUserCourseVideoRedPackageService;
-import org.springframework.test.annotation.Rollback;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
 /**
  * 课程公司红包Service业务层处理
  *
@@ -244,5 +246,45 @@ public class FsUserCourseVideoRedPackageServiceImpl implements IFsUserCourseVide
         return fsUserCourseVideoRedPackageMapper.selectByRuleIds(ruleIds);
     }
 
+    /**
+     * 按照公司批量设置红包
+     * @param param 入参
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void batchRedPacketByCompany(BatchCompanyRedPackageParam param) {
+        FsUserCoursePeriod period = userCoursePeriodMapper.selectFsUserCoursePeriodById(param.getPeriodId());
+        if (Objects.isNull(period)) {
+            throw new CustomException("营期不存在");
+        }
+
+        Set<Long> targetCompanyIds = new HashSet<>(param.getCompanyIds());
+        Set<Long> companyIds = Arrays.stream(period.getCompanyId().split(","))
+                .filter(StringUtils::isNotBlank)
+                .map(Long::valueOf)
+                .filter(targetCompanyIds::contains)
+                .collect(Collectors.toSet());
+
+        if (companyIds.isEmpty()) {
+            throw new CustomException("当前营期不包含所选公司");
+        }
+
+        List<FsUserCoursePeriodDays> periodVideos = userCoursePeriodDaysMapper.selectCourseVideoList(Collections.singleton(period.getPeriodId()));
+
+        List<FsUserCourseVideoRedPackage> fsRedPackageList = companyIds.stream()
+            .flatMap(companyId -> periodVideos.stream().map(video -> {
+                FsUserCourseVideoRedPackage redPkg = new FsUserCourseVideoRedPackage();
+                redPkg.setCompanyId(companyId);
+                redPkg.setVideoId(video.getVideoId());
+                redPkg.setRedPacketMoney(param.getRedPacketMoney());
+                redPkg.setPeriodId(period.getPeriodId());
+                redPkg.setDataType(2);
+                return redPkg;
+            }))
+            .collect(Collectors.toList());
+
+        this.batchSaveCompanyRedPackage(fsRedPackageList);
+    }
+
 
 }

+ 68 - 22
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -826,19 +826,49 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         packetParam.setRedPacketMode(config.getRedPacketMode());
         packetParam.setCompanyId(param.getCompanyId());
 
+        //2025.7.11 红包金额为0的时候
+        if (amount.compareTo(BigDecimal.ZERO)>0){
+            // 发送红包
+            R sendRedPacket = paymentService.sendRedPacket(packetParam);
+            if (sendRedPacket.get("code").equals(200)) {
+                FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+                TransferBillsResult transferBillsResult;
+                if (sendRedPacket.get("isNew").equals(1)){
+                    transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
+                    redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
+                    redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+                }else {
+                    redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                }
+                // 添加红包记录
+                redPacketLog.setCourseId(param.getCourseId());
+//            redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                redPacketLog.setCompanyId(param.getCompanyId());
+                redPacketLog.setUserId(param.getUserId());
+                redPacketLog.setVideoId(param.getVideoId());
+                redPacketLog.setStatus(0);
+                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+                redPacketLog.setCompanyUserId(param.getCompanyUserId());
+                redPacketLog.setCreateTime(new Date());
+                redPacketLog.setAmount(amount);
+                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+                redPacketLog.setPeriodId(param.getPeriodId());
+
+                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+
+                // 更新观看记录的奖励类型
+//            if (param.getLinkType() == null || param.getLinkType() == 0) {
+                log.setRewardType(config.getRewardType());
+                courseWatchLogMapper.updateFsCourseWatchLog(log);
+//            }
+                return sendRedPacket;
+            } else {
+                return R.error("奖励发送失败,请联系客服");
+            }
 
-        // 发送红包
-        R sendRedPacket = paymentService.sendRedPacket(packetParam);
-        if (sendRedPacket.get("code").equals(200)) {
+        } else {
+            // 发送红包
             FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
-            TransferBillsResult transferBillsResult;
-            if (sendRedPacket.get("isNew").equals(1)){
-                transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
-                redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
-                redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
-            }else {
-                redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
-            }
             // 添加红包记录
             redPacketLog.setCourseId(param.getCourseId());
 //            redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
@@ -849,20 +879,15 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
             redPacketLog.setCompanyUserId(param.getCompanyUserId());
             redPacketLog.setCreateTime(new Date());
-            redPacketLog.setAmount(amount);
+            redPacketLog.setAmount(BigDecimal.ZERO);
             redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
             redPacketLog.setPeriodId(param.getPeriodId());
-
             redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
 
             // 更新观看记录的奖励类型
-//            if (param.getLinkType() == null || param.getLinkType() == 0) {
             log.setRewardType(config.getRewardType());
             courseWatchLogMapper.updateFsCourseWatchLog(log);
-//            }
-            return sendRedPacket;
-        } else {
-            return R.error("奖励发送失败,请联系客服");
+            return R.ok("红包发送成功");
         }
     }
 
@@ -1160,8 +1185,8 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 //
         if (courseVideoDetails != null && courseVideoDetails.getDuration() != null){
             // 查询视频是否设置了红包,没有就不提示
-            FsUserCourseVideoRedPackage fsUserCourseVideoRedPackage = fsUserCourseVideoRedPackageMapper.selectRedPacketByCompanyId(param.getVideoId(), null, param.getPeriodId());
-            if(fsUserCourseVideoRedPackage != null){
+            Integer fsUserCourseVideoRedPackage = fsUserCourseVideoRedPackageMapper.selectRedPacketByCompanyCount(param.getVideoId(), null, param.getPeriodId());
+            if(fsUserCourseVideoRedPackage>0){
                 tipsTime = courseVideoDetails.getDuration() / 3;
                 tipsTime2 = (courseVideoDetails.getDuration() * 2) / 3;
             }
@@ -1240,10 +1265,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
         // 从Redis中获取观看时长
         String redisKey = "h5wxuser:watch:duration:" + param.getUserId() + ":" + param.getVideoId() + ":" + param.getCompanyUserId();
-        log.info("看课redis缓存key:{}", redisKey);
+//        log.info("看课redis缓存key:{}", redisKey);
         try {
             String durationStr = redisCache.getCacheObject(redisKey);
-            log.info("看课记录:{}", durationStr);
+//            log.info("看课记录:{}", durationStr);
             long duration = durationStr != null ? Long.parseLong(durationStr) : 0L;
 
             // 更新Redis中的观看时长
@@ -1443,6 +1468,27 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         return fsUserCourseVideoQVO;
     }
 
+    @Override
+    public R checkUserInfo(Long userId) {
+        FsUser user=fsUserService.selectFsUserById(userId);
+        if (user==null) {
+            return R.error(404,"用户不存在!");
+        }
+        //读取配置给前端返回
+        String json = sysConfigService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        if (org.apache.commons.lang3.StringUtils.isEmpty(user.getNickname()) || org.apache.commons.lang3.StringUtils.isEmpty(user.getAvatar())){
+            Map<String,Object> map = new HashMap<>();
+            map.put("authType",config.getMiniAppAuthType());
+            if (config.getMiniAppAuthType()==2){
+                map.put("domain",config.getUserCourseAuthDomain());
+            }
+            map.put("code",500);
+            return R.ok(map);
+        }
+        return R.ok().put("user",user);
+    }
+
     /**
      * 生成唯一键
      */

+ 9 - 5
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserWatchCourseStatisticsServiceImpl.java

@@ -6,6 +6,7 @@ import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.stream.Collectors;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.course.mapper.FsUserCoursePeriodDaysMapper;
 import com.fs.store.mapper.FsUserMapper;
@@ -161,11 +162,14 @@ public class FsUserWatchCourseStatisticsServiceImpl extends ServiceImpl<FsUserWa
             if(userTotalDataList != null && !userTotalDataList.isEmpty()){
                 // 获取过滤时间后的销售会员数量
                 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
-
-                int userNum = userTotalDataList.stream()
-                        .filter(v -> v.getUserCreateDate().before(data.getCourseStartDateTime())).mapToInt(FsUserWatchCourseStatistics::getUserNum).sum();
-                int newUserNum = userTotalDataList.stream()
-                        .filter(v -> sdf.format(v.getUserCreateDate()).equals(sdf.format(data.getCourseStartDateTime()))).mapToInt(FsUserWatchCourseStatistics::getUserNum).sum();
+                int userNum =0;
+                int newUserNum =0;
+                if (ObjectUtil.isNotEmpty(data.getCourseStartDateTime())){
+                    userNum = userTotalDataList.stream()
+                            .filter(v -> v.getUserCreateDate().before(data.getCourseStartDateTime())).mapToInt(FsUserWatchCourseStatistics::getUserNum).sum();
+                    newUserNum = userTotalDataList.stream()
+                            .filter(v -> sdf.format(v.getUserCreateDate()).equals(sdf.format(data.getCourseStartDateTime()))).mapToInt(FsUserWatchCourseStatistics::getUserNum).sum();
+                }
                 vo.setUserNum(userNum);
                 vo.setNewUserNum(newUserNum);
             } else {

+ 2 - 0
fs-service-system/src/main/java/com/fs/crm/domain/CrmCustomer.java

@@ -1,5 +1,6 @@
 package com.fs.crm.domain;
 
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
@@ -21,6 +22,7 @@ public class CrmCustomer extends BaseEntity
     private static final long serialVersionUID = 1L;
 
     /** $column.columnComment */
+    @TableId
     private Long customerId;
 
     /** 组织机构代码 */

+ 2 - 0
fs-service-system/src/main/java/com/fs/qw/domain/QwMsg.java

@@ -1,5 +1,6 @@
 package com.fs.qw.domain;
 
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
@@ -18,6 +19,7 @@ public class QwMsg extends BaseEntity implements Serializable
 
     /** $column.columnComment */
 
+    @TableId
     private Long msgId;
 
     /** 会话id */

+ 2 - 1
fs-service-system/src/main/java/com/fs/qw/domain/QwSession.java

@@ -1,5 +1,6 @@
 package com.fs.qw.domain;
 
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
@@ -17,7 +18,7 @@ public class QwSession extends BaseEntity implements Serializable
 {
 
     /** 会话ID */
-
+    @TableId
     private Long sessionId;
 
     /** 会话标识 */

+ 14 - 0
fs-service-system/src/main/java/com/fs/store/domain/FsUser.java

@@ -173,6 +173,12 @@ public class FsUser extends BaseEntity
         this.projectId = projectId;
     }
 
+
+
+    @ApiModelProperty(value = "是否点过注册链接 0:否 1:是")
+    @Excel(name = "是否点过注册链接 0:否 1:是")
+    private Integer isBecomeMember;
+
     public Integer getQwRepeat() {
         return qwRepeat;
     }
@@ -659,4 +665,12 @@ public class FsUser extends BaseEntity
     public void setCompanyName(String companyName) {
         this.companyName = companyName;
     }
+
+    public Integer getIsBecomeMember() {
+        return isBecomeMember;
+    }
+
+    public void setIsBecomeMember(Integer isBecomeMember) {
+        this.isBecomeMember = isBecomeMember;
+    }
 }

+ 5 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsUserMapper.java

@@ -183,6 +183,9 @@ public interface FsUserMapper
     @Select("select * from fs_user where union_id=#{unionId}")
     FsUser selectFsUserByUnionId(String unionId);
 
+    @Select("select * from fs_user where course_ma_open_id=#{openId}")
+    FsUser selectFsUserByCourseMaOpenId(String openId);
+
     Long selectCrmCustomerCount(int type, Long companyId);
 
     @Select("select * from fs_user where user_code=#{userCode}")
@@ -320,4 +323,6 @@ public interface FsUserMapper
     List<FsUser> selectFsUserListByJointUserNameKey(String userNameKey);
 
     List<FSUserVO> selectFsUserVOListByProject(@Param("maps") FsUser fsUser);
+
+    Map<String, Long> countUserCourse2(UserStatisticsCommonParam param);
 }

+ 5 - 0
fs-service-system/src/main/java/com/fs/store/param/FsUserEditParam.java

@@ -2,15 +2,20 @@ package com.fs.store.param;
 
 import lombok.Data;
 
+import javax.validation.constraints.NotBlank;
 import java.io.Serializable;
 
 @Data
 public class FsUserEditParam implements Serializable
 {
 
+    @NotBlank(message = "用户昵称不能为空!")
     private String nickname;
 
+    @NotBlank(message = "用户头像不能为空!")
     private String avatar;
 
+    private Long userId;
+
 
 }

+ 4 - 0
fs-service-system/src/main/java/com/fs/store/param/h5/FsUserPageListParam.java

@@ -69,6 +69,10 @@ public class FsUserPageListParam implements Serializable {
     private Long pcLoginUserId;
 
     private Boolean isAdmin;
+    /**
+     * 判断是否选择了无标签
+     */
+    private Boolean isNullTag =false;
 
     /**
      * 项目ID

+ 3 - 0
fs-service-system/src/main/java/com/fs/store/service/IFsUserService.java

@@ -125,6 +125,9 @@ public interface IFsUserService
     FsUser selectFsUserByUnionId(String unionId);
 
 
+    FsUser selectFsUserByCourseMaOpenId(String openId);
+
+
     FsUser selectFsUserByUserCode(String userCode);
 
     //CRM客户绑定 小程序客户之 查询小程序客户

+ 23 - 34
fs-service-system/src/main/java/com/fs/store/service/impl/FsStorePaymentServiceImpl.java

@@ -488,55 +488,44 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService
         String  json = configService.selectConfigByKey("redPacket.config");
         config = JSONUtil.toBean(json, RedPacketConfig.class);
 
-        param.setSource(1);
-        param.setOpenId("ooXAA1Fw5ekSqCT-WLKpoA0cDVDo");
+        param.setSource(2);
+//        param.setOpenId("ooXAA1Fw5ekSqCT-WLKpoA0cDVDo");
         param.setAmount(new BigDecimal(0.1));
 
 
         WxPayConfig payConfig = new WxPayConfig();
         BeanUtils.copyProperties(config, payConfig);
-//        payConfig.setPublicKeyId(null);
-//        payConfig.setPublicKeyPath(null);
-//        payConfig.setCertSerialNo("63AC73F33E0A21973BB1DE533421A2337FD91C20");
+        payConfig.setAppId(config.getMiniappId());
         WxPayService wxPayService = new WxPayServiceImpl();
         wxPayService.setConfig(payConfig);
         TransferService transferService = wxPayService.getTransferService();
 
-        TransferBillsRequest request = new TransferBillsRequest();
-        request.setAppid(config.getAppId());
-        System.out.println("appid:"+config.getAppId());
-        request.setOpenid(param.getOpenId());
-
-        String code = String.valueOf(IdUtil.getSnowflake(0, 0).nextId());
-        request.setOutBillNo("fsCourse" + code);
-
-        Integer amount = WxPayUnifiedOrderRequest.yuanToFen(param.getAmount() != null ? param.getAmount().toString() : "0.1");
-        request.setTransferAmount(amount);
-        request.setTransferRemark("活动奖励");
-        request.setUserRecvPerception("活动奖励");
+        TransferBatchesRequest request = new TransferBatchesRequest();
+        request.setAppid(config.getMiniappId());
+        String code = IdUtil.getSnowflake(0, 0).nextIdStr();
+        request.setOutBatchNo("fsCourse" + code);
+        request.setBatchRemark("课堂答题奖励");
+        request.setBatchName("课堂答题奖励");
+        Integer amount = WxPayUnifiedOrderRequest.yuanToFen(param.getAmount().toString());
+        request.setTotalAmount(amount);
+        request.setTotalNum(1);
         request.setNotifyUrl(config.getNotifyUrl());
-        request.setTransferSceneId("1000");
-
-        // 设置场景信息
-        List<TransferBillsRequest.TransferSceneReportInfo> transferSceneReportInfos = new ArrayList<>();
-        TransferBillsRequest.TransferSceneReportInfo info1 = new TransferBillsRequest.TransferSceneReportInfo();
-        info1.setInfoType("活动名称");
-        info1.setInfoContent("新会员有礼");
-        transferSceneReportInfos.add(info1);
 
-        TransferBillsRequest.TransferSceneReportInfo info2 = new TransferBillsRequest.TransferSceneReportInfo();
-        info2.setInfoType("奖励说明");
-        info2.setInfoContent("注册会员抽奖一等奖");
-        transferSceneReportInfos.add(info2);
-        request.setTransferSceneReportInfos(transferSceneReportInfos);
+        ArrayList<TransferBatchesRequest.TransferDetail> transferDetailList = new ArrayList<>();
+        TransferBatchesRequest.TransferDetail transferDetail = new TransferBatchesRequest.TransferDetail();
+        transferDetail.setOpenid(param.getOpenId());
+        String code1 = IdUtil.getSnowflake(0, 0).nextIdStr();
+        transferDetail.setOutDetailNo("fsCourse" + code1);
+        transferDetail.setTransferAmount(amount);
+        transferDetail.setTransferRemark("恭喜同学,认真听课,奖励你一朵小红花!");
+        transferDetailList.add(transferDetail);
+        request.setTransferDetailList(transferDetailList);
 
         try {
-            TransferBillsResult transferBillsResult = transferService.transferBills(request);
-            logger.info("商家转账支付完成:[msg:{}]", transferBillsResult);
-            return R.ok("发送红包成功").put("data", transferBillsResult);
+            TransferBatchesResult transferBatchesResult = transferService.transferBatches(request);
+            return R.ok("发送红包成功").put("orderCode", transferBatchesResult.getOutBatchNo());
         } catch (WxPayException e) {
             e.printStackTrace();
-            logger.info("商家转账支付失败:[msg:{}]", e.getMessage());
             return R.error("发送失败");
         }
     }

+ 86 - 6
fs-service-system/src/main/java/com/fs/store/service/impl/FsUserServiceImpl.java

@@ -196,6 +196,12 @@ public class FsUserServiceImpl implements IFsUserService
     @Override
     public int updateFsUser(FsUser fsUser)
     {
+        String nickname = fsUser.getNickname();
+        if (StringUtils.isNotBlank(nickname)){
+            if (nickname.length()>100){
+                return 0;
+            }
+        }
         fsUser.setUpdateTime(DateUtils.getNowDate());
         return fsUserMapper.updateFsUser(fsUser);
     }
@@ -423,6 +429,11 @@ public class FsUserServiceImpl implements IFsUserService
         return fsUserMapper.selectFsUserByUnionId(unionId);
     }
 
+    @Override
+    public FsUser selectFsUserByCourseMaOpenId(String openId) {
+        return fsUserMapper.selectFsUserByCourseMaOpenId(openId);
+    }
+
     @Override
     public FsUser selectFsUserByUserCode(String userCode) {
         return fsUserMapper.selectFsUserByUserCode(userCode);
@@ -466,7 +477,7 @@ public class FsUserServiceImpl implements IFsUserService
 
     @Override
     public PageInfo<FsUserPageListVO> selectFsUserPageList(FsUserPageListParam param) {
-
+        System.out.println("参数:" + Arrays.toString(param.getTagIds()));
         CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getUserId());
         if (companyUser!=null && companyUser.isAdmin()){
             param.setUserId(0L);
@@ -478,7 +489,14 @@ public class FsUserServiceImpl implements IFsUserService
         }
 
         PageHelper.startPage(param.getPageNum(), param.getPageSize());
-
+        if (param.getTagIds() != null && param.getTagIds().length > 0) {
+            boolean containsZero = Arrays.asList(param.getTagIds()).contains("0");
+            // 如果包含 "0",则设置 isNullTag 为 true
+            System.out.println("是否包含 0:" + containsZero + ",参数:" + Arrays.toString(param.getTagIds()));
+            if (containsZero) {
+                param.setIsNullTag(true);
+            }
+        }
         List<FsUserPageListVO> fsUserPageListVOS = fsUserMapper.selectFsUserPageList(param);
 
         // 项目
@@ -529,11 +547,11 @@ public class FsUserServiceImpl implements IFsUserService
     public TableDataInfo selectFsUserPageListNew(FsUserPageListParam param) {
         // 找出下级销售
         String companyUserId = param.getCompanyUserId();
-        if(companyUserId != null) {
+        if(StringUtils.isNotBlank(companyUserId)) {
             Long companyUser = Long.parseLong(companyUserId);
             Set<Long> userIds = companyUserCacheService.selectUserAllCompanyUserId(companyUser);
             if (userIds != null || userIds.size() <= 1) {
-                if (param.getIsAdmin()) {
+                if (param.getIsAdmin() != null && param.getIsAdmin()) {
                     List<CompanyUser> companyUsers = companyUserMapper.selectCompanyUserByCompanyId(param.getCompanyId());
                     userIds = companyUsers.stream().map(CompanyUser::getUserId).collect(Collectors.toSet());
                 }
@@ -545,7 +563,10 @@ public class FsUserServiceImpl implements IFsUserService
         Map<Long, CompanyTag> tagMap = companyTagCacheService.queryAllTagMap();
         //获取会员的最新的看课状态和最后看课时间
         Set<Long> userIds = fsUserPageListVOS.stream().map(FsUserPageListVO::getUserId).collect(Collectors.toSet());
-        List<FsUserLastCount> fsUserCourseCounts = fsUserCourseCountMapper.selectUserLastCount(userIds);
+        List<FsUserLastCount> fsUserCourseCounts = new ArrayList<>();
+        if (userIds.size() > 0) {
+            fsUserCourseCounts = fsUserCourseCountMapper.selectUserLastCount(userIds);
+        }
         Map<Long, FsUserLastCount> countMap = fsUserCourseCounts.stream().collect(Collectors.toMap(FsUserLastCount::getUserId, Function.identity()));
 
         for (FsUserPageListVO item : fsUserPageListVOS) {
@@ -775,7 +796,8 @@ public class FsUserServiceImpl implements IFsUserService
 
     @Override
     public FsUserStatisticsVO userStatisticsDetails(UserStatisticsCommonParam param) {
-        FsUserStatisticsVO userStatisticsVO = getUserStatistics(param);
+//        FsUserStatisticsVO userStatisticsVO = getUserStatistics(param);
+        FsUserStatisticsVO userStatisticsVO = getUserStatistics2(param);
 
         // 判断是否是管理员
         CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getUserId());
@@ -792,6 +814,53 @@ public class FsUserServiceImpl implements IFsUserService
         return userStatisticsVO;
     }
 
+    private FsUserStatisticsVO getUserStatistics2(UserStatisticsCommonParam param) {
+        FsUserStatisticsVO fsUserStatisticsVO = new FsUserStatisticsVO();
+
+        // 判断是否是管理员
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(param.getUserId());
+        if (companyUser != null && companyUser.isAdmin()){
+            param.setUserId(0L);
+            param.setCompanyId(companyUser.getCompanyId());
+        }
+        // 获取课程统计
+        Map<String, Long> couserMap = fsUserMapper.countUserCourse2(param);
+        if (couserMap != null) {
+            //看课人数
+            fsUserStatisticsVO.setCourseWatchNum(couserMap.get("courseWatchNum").intValue()).setCourseCompleteNum(couserMap.get("courseCompleteNum").intValue());
+
+            if (couserMap.get("courseCompleteNum") != null && couserMap.get("courseWatchNum") > 0) {
+                int courseCompleteRate = BigDecimal.valueOf(couserMap.get("courseCompleteNum"))
+                        .divide(BigDecimal.valueOf(couserMap.get("courseWatchNum")), 2, RoundingMode.HALF_UP)
+                        .multiply(BigDecimal.valueOf(100))
+                        .intValue();
+                fsUserStatisticsVO.setCourseCompleteRate(courseCompleteRate);
+            }
+        }
+
+        // 获取答题统计
+        Map<String, Long> answerMap = fsUserMapper.countUserAnswer(param);
+        if (answerMap != null) {
+            fsUserStatisticsVO.setAnswerNum(answerMap.get("answerNum").intValue()).setAnswerRightNum(answerMap.get("answerRightNum").intValue());
+
+            if (answerMap.get("answerRightNum") != null && answerMap.get("answerNum") > 0) {
+                int answerCompleteRate = BigDecimal.valueOf(answerMap.get("answerRightNum"))
+                        .divide(BigDecimal.valueOf(answerMap.get("answerNum")), 2, RoundingMode.HALF_UP)
+                        .multiply(BigDecimal.valueOf(100))
+                        .intValue();
+                fsUserStatisticsVO.setAnswerRightRate(answerCompleteRate);
+            }
+        }
+
+        // 获取红包统计
+        Map<String, Object> redPacketMap = fsUserMapper.countUserRedPacket(param);
+        if(redPacketMap != null && redPacketMap.get("redPacketNum") != null && redPacketMap.get("redPacketAmount") != null) {
+            fsUserStatisticsVO.setRedPacketNum(Integer.parseInt(redPacketMap.get("redPacketNum").toString()))
+                    .setRedPacketAmount(new BigDecimal(redPacketMap.get("redPacketAmount").toString()));
+        }
+        return fsUserStatisticsVO;
+    }
+
     @Override
     public List<FsUserRankingVO> userRanking(Long userId, String startTime, String endTime, String periodId, String videoId, String order, Integer type) {
         List<FsUserRankingVO> listVO = Collections.emptyList();
@@ -941,6 +1010,17 @@ public class FsUserServiceImpl implements IFsUserService
             return ResponseResult.fail(406,"该用户已成为其他销售会员");
         }
 
+        //2025.7.10 解决在不知销售已经审核的情况下 多次点击
+        if (fsUser.getIsBecomeMember() == 1){
+            if(fsUser.getStatus() == 0){
+                //禁用
+                return ResponseResult.fail(402, "已成功注册,待管理审核");
+            } else {
+                return ResponseResult.ok(Boolean.TRUE); //已经成为会员
+            }
+        }
+        fsUser.setIsBecomeMember(1);
+
         // 特殊(需求设计:需要根据公司是否开启黑名单来设置会员初始化的状态)
         Company company = companyMapper.selectCompanyById(param.getCompanyId());
         // isDefaultBlack 值为1 ,表示需要加入小黑屋,否则不加

+ 2 - 0
fs-service-system/src/main/java/com/fs/system/domain/SysKeyword.java

@@ -1,6 +1,7 @@
 package com.fs.system.domain;
 
 import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
@@ -23,6 +24,7 @@ public class SysKeyword extends BaseEntity{
     private Integer pageSize;
 
     /** 关键字id */
+    @TableId
     private Long keywordId;
 
     /** 关键字 */

+ 10 - 10
fs-service-system/src/main/java/com/fs/system/mapper/SysDictTypeMapper.java

@@ -6,7 +6,7 @@ import com.fs.common.core.domain.entity.SysDictType;
 
 /**
  * 字典表 数据层
- * 
+ *
 
  */
 @Mapper
@@ -14,7 +14,7 @@ public interface SysDictTypeMapper
 {
     /**
      * 根据条件分页查询字典类型
-     * 
+     *
      * @param dictType 字典类型信息
      * @return 字典类型集合信息
      */
@@ -22,14 +22,14 @@ public interface SysDictTypeMapper
 
     /**
      * 根据所有字典类型
-     * 
+     *
      * @return 字典类型集合信息
      */
     public List<SysDictType> selectDictTypeAll();
 
     /**
      * 根据字典类型ID查询信息
-     * 
+     *
      * @param dictId 字典类型ID
      * @return 字典类型
      */
@@ -37,7 +37,7 @@ public interface SysDictTypeMapper
 
     /**
      * 根据字典类型查询信息
-     * 
+     *
      * @param dictType 字典类型
      * @return 字典类型
      */
@@ -45,7 +45,7 @@ public interface SysDictTypeMapper
 
     /**
      * 通过字典ID删除字典信息
-     * 
+     *
      * @param dictId 字典ID
      * @return 结果
      */
@@ -53,7 +53,7 @@ public interface SysDictTypeMapper
 
     /**
      * 批量删除字典类型信息
-     * 
+     *
      * @param dictIds 需要删除的字典ID
      * @return 结果
      */
@@ -61,7 +61,7 @@ public interface SysDictTypeMapper
 
     /**
      * 新增字典类型信息
-     * 
+     *
      * @param dictType 字典类型信息
      * @return 结果
      */
@@ -69,7 +69,7 @@ public interface SysDictTypeMapper
 
     /**
      * 修改字典类型信息
-     * 
+     *
      * @param dictType 字典类型信息
      * @return 结果
      */
@@ -77,7 +77,7 @@ public interface SysDictTypeMapper
 
     /**
      * 校验字典类型称是否唯一
-     * 
+     *
      * @param dictType 字典类型
      * @return 结果
      */

+ 3 - 3
fs-service-system/src/main/resources/application-common.yml

@@ -41,7 +41,7 @@ server:
 # 日志配置
 logging:
   level:
-    com.fs: debug
+    com.fs: info
     org.springframework: warn
 # Spring配置
 spring:
@@ -58,9 +58,9 @@ spring:
   servlet:
     multipart:
       # 单个文件大小
-      max-file-size:  200MB
+      max-file-size:  3GB
       # 设置总上传的文件大小
-      max-request-size:  200MB
+      max-request-size:  3GB
   #       enabled: false
   # 服务模块
   devtools:

+ 2 - 1
fs-service-system/src/main/resources/mapper/company/CompanyUserChangeApplyMapper.xml

@@ -30,6 +30,7 @@
                 and cuca.company_id = #{map.companyId}
             </if>
         </where>
+        order by cuca.apply_time desc
     </select>
 
     <!-- 查询申请详情 -->
@@ -37,4 +38,4 @@
         <include refid="applySQL"/>
         where cuca.id = #{id}
     </select>
-</mapper>
+</mapper>

+ 2 - 0
fs-service-system/src/main/resources/mapper/course/FsUserCoursePeriodMapper.xml

@@ -225,6 +225,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 AND fvrp.period_id = #{periodId}
                 AND fvrp.company_id = #{companyId}
                 AND fvrp.data_type = 2
+                and fvrp.del_flag = 0
         WHERE
             a.video_id IN (
                 SELECT
@@ -237,6 +238,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                     a.period_id = #{periodId}
             )
           and a.period_id = #{periodId}
+          and a.del_flag = 0
     </select>
 
     <select id="selectFsUserCoursePeriodsByIds" resultType="FsUserCoursePeriod">

+ 9 - 1
fs-service-system/src/main/resources/mapper/course/FsUserCourseVideoRedPackageMapper.xml

@@ -147,6 +147,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <if test="companyId != null "> and company_id = #{companyId}</if>
         <if test="periodId != null "> and period_id = #{periodId}</if>
         </where>
+        order by id desc limit 1
+    </select>
+    <select id="selectRedPacketByCompanyCount" resultType="java.lang.Integer">
+        select count(0) from fs_user_course_video_red_package
+        <where>
+            <if test="videoId != null "> and video_id =#{videoId}</if>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="periodId != null "> and period_id = #{periodId}</if>
+        </where>
     </select>
-
 </mapper>

+ 1 - 1
fs-service-system/src/main/resources/mapper/course/FsVideoResourceMapper.xml

@@ -24,6 +24,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <if test="params.typeSubId != null">
             and rr.type_sub_id = #{params.typeSubId}
         </if>
-        order by rr.id
+        order by rr.resource_name
     </select>
 </mapper>

+ 1 - 1
fs-service-system/src/main/resources/mapper/store/FsUserCompanyUserMapper.xml

@@ -95,7 +95,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where id = #{id}
     </update>
     <update id="transfer">
-        update fs_user_company_user set company_user_id=#{targetCompanyUserId}
+        update fs_user_company_user set company_user_id=#{param.targetCompanyUserId}
         <where>
             <if test="param.userIds != null and param.userIds.size() > 0">
                 user_id in

+ 106 - 33
fs-service-system/src/main/resources/mapper/store/FsUserMapper.xml

@@ -47,6 +47,7 @@
         <result property="qwRepeat" column="qw_repeat"/>
         <result property="userRepeat" column="user_repeat"/>
         <result property="payOrder" column="pay_order"/>
+        <result property="isBecomeMember" column="is_become_member"/>
     </resultMap>
 
     <sql id="selectFsUserVo">
@@ -92,7 +93,8 @@
                user_code,
                qw_repeat,
                user_repeat,
-               pay_order
+               pay_order,
+               is_become_member
         from fs_user
     </sql>
 
@@ -272,36 +274,38 @@
         LEFT JOIN company_user ON company_user.user_id = u.company_user_id
         LEFT JOIN company on company.company_id = company_user.company_id
         <where>
-            1 = 1
-            <if test = "maps.nickname != null and  maps.nickname !='' " >
-                AND u.nickname LIKE CONCAT("%",#{maps.nickname},"%")
-            </if >
-            <if test = "maps.phone != null   and  maps.phone !='' " >
-                AND u.phone LIKE CONCAT("%",#{maps.phone},"%")
-            </if >
-            <if test = "maps.startCreateTime != null and maps.endCreateTime != null" >
-                AND (DATE_FORMAT( u.create_time, "%Y-%m-%d" ) &gt;= DATE_FORMAT(#{maps.startCreateTime}, "%Y-%m-%d")
+        <if test = "maps.userId != null" >
+            AND u.user_id = #{maps.userId}
+        </if >
+        <if test = "maps.nickname != null and  maps.nickname !='' " >
+            AND u.nickname LIKE CONCAT("%",#{maps.nickname},"%")
+        </if >
+        <if test = "maps.phone != null   and  maps.phone !='' " >
+        AND u.phone LIKE CONCAT("%",#{maps.phone},"%")
+        </if >
+        <if test = "maps.startCreateTime != null and maps.endCreateTime != null" >
+        AND (DATE_FORMAT( u.create_time, "%Y-%m-%d" ) &gt;= DATE_FORMAT(#{maps.startCreateTime}, "%Y-%m-%d")
                 and DATE_FORMAT( u.create_time, "%Y-%m-%d" ) &lt;= DATE_FORMAT(#{maps.endCreateTime}, "%Y-%m-%d")
-                )
-            </if >
-            <if test = "maps.registerCode != null  and  maps.registerCode !=''  " >
-                AND u.register_code = #{maps.registerCode}
-            </if >
-            <if test = "maps.status != null" >
-                AND u.STATUS = #{maps.status}
-            </if >
-            <if test = "maps.companyUserNickName != null and maps.companyUserNickName != '' " >
-                AND company_user.nick_name like CONCAT ("%",#{maps.companyUserNickName},"%")
-            </if >
-            <if test = "maps.companyName != null and maps.companyName != '' " >
-                AND company.company_name like CONCAT ("%",#{maps.companyName},"%")
-            </if >
-            <if test = "maps.level != null  and  maps.level !=''" >
-                AND u.LEVEL = #{maps.level}
-            </if >
-            <if test = "maps.isPromoter != null  and  maps.isPromoter !=''" >
-                AND u.is_promoter = #{maps.isPromoter}
-            </if >
+            )
+        </if >
+        <if test = "maps.registerCode != null  and  maps.registerCode !=''  " >
+        AND u.register_code = #{maps.registerCode}
+        </if >
+        <if test = "maps.status != null" >
+        AND u.STATUS = #{maps.status}
+        </if >
+        <if test = "maps.companyUserNickName != null and maps.companyUserNickName != '' " >
+        AND company_user.nick_name like CONCAT ("%",#{maps.companyUserNickName},"%")
+        </if >
+        <if test = "maps.companyName != null and maps.companyName != '' " >
+            AND company.company_name like CONCAT ("%",#{maps.companyName},"%")
+        </if >
+        <if test = "maps.level != null  and  maps.level !=''" >
+        AND u.LEVEL = #{maps.level}
+        </if >
+        <if test = "maps.isPromoter != null  and  maps.isPromoter !=''" >
+        AND u.is_promoter = #{maps.isPromoter}
+        </if >
         </where>
         ORDER BY
         user_id DESC
@@ -426,6 +430,7 @@
             <if test="qwRepeat != null">qw_repeat,</if>
             <if test="userRepeat != null">user_repeat,</if>
             <if test="payOrder != null">pay_order,</if>
+            <if test="isBecomeMember != null">is_become_member,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="username != null">#{username},</if>
@@ -470,6 +475,7 @@
             <if test="qwRepeat != null">#{qwRepeat},</if>
             <if test="userRepeat != null">#{userRepeat},</if>
             <if test="payOrder != null">#{payOrder},</if>
+            <if test="isBecomeMember != null">#{isBecomeMember},</if>
         </trim>
     </insert>
 
@@ -518,6 +524,7 @@
             <if test="qwRepeat != null">qw_repeat = #{qwRepeat},</if>
             <if test="userRepeat != null">user_repeat = #{userRepeat},</if>
             <if test="payOrder != null">pay_order = #{payOrder},</if>
+            <if test="isBecomeMember != null">is_become_member = #{isBecomeMember},</if>
         </trim>
         where user_id = #{userId}
     </update>
@@ -561,6 +568,10 @@
         left join fs_user_company_user ucu on ucu.user_id = fs_user.user_id
         <where>
             fs_user.is_del = 0
+        where fs_user.is_del = 0
+            <if test="userId != null">
+                AND fs_user.user_id = #{userId}
+            </if>
             <if test="companyId != null">
                 AND ucu.company_id = #{companyId}
             </if>
@@ -588,7 +599,6 @@
             <if test="projectId != null">
                 AND ucu.project_id = #{projectId}
             </if>
-        </where>
         limit ${(pageNum-1)*pageSize},${pageSize}
     </select>
 
@@ -648,10 +658,21 @@
         <if test="registerEndTime != null and registerEndTime !='' ">
             AND fs_user_company_user.create_time &lt;= #{registerEndTime}
         </if>
-        <if test="tagIds != null and tagIds.length > 0">
+        <if test="isNullTag">
+            and (upt.tag_id is null
+            <if test="tagIds != null and tagIds.length > 0">
+                or upt.tag_id in
+                <foreach collection="tagIds" item="item" index="index" open="(" separator="," close=")">
+                    #{item}
+                </foreach>
+            </if>
+            )
+        </if>
+
+        <if test="!isNullTag and tagIds != null and tagIds.length > 0">
             AND upt.tag_id in
             <foreach collection="tagIds" item="item" index="index" open="(" separator="," close=")">
-                 #{item}
+                #{item}
             </foreach>
         </if>
         <if test="tabValue != null and tabValue !='' ">
@@ -1825,5 +1846,57 @@
         </where>
         order by user_id desc
     </select>
+    <select id="countUserCourse2" resultType="java.util.Map">
+        SELECT
+        (
+        SELECT
+        count(1)
+        FROM
+        fs_course_watch_log l
+        LEFT JOIN company_user ON l.company_user_id = company_user.user_id
+        where
+            l.log_type != 3 and send_type = 1
+            <if test="userId != null and userId != 0 ">
+                and (l.company_user_id = #{userId} OR company_user.parent_id = #{userId} )
+            </if>
+            <if test="userId != null and userId == 0 ">
+                and l.company_id = #{companyId}
+            </if>
+            <if test="periodId != null and periodId != ''">
+                AND l.period_id = #{periodId}
+            </if>
+            <if test="videoId != null and videoId != ''">
+                AND l.video_id = #{videoId}
+            </if>
+            <if test="companyUserId != null and companyUserId != ''">
+                AND l.user_id = #{companyUserId}
+            </if>
+        ) as courseWatchNum,
+        (
+        SELECT
+        count(1)
+        FROM
+        fs_course_watch_log l
+        LEFT JOIN company_user ON l.company_user_id = company_user.user_id
+        where
+        l.log_type = 2 and send_type = 1
+        <if test="userId != null and userId != 0 ">
+            and (l.company_user_id = #{userId} OR company_user.parent_id = #{userId} )
+        </if>
+        <if test="userId != null and userId == 0 ">
+            and l.company_id = #{companyId}
+        </if>
+        <if test="periodId != null and periodId != ''">
+            AND l.period_id = #{periodId}
+        </if>
+        <if test="videoId != null and videoId != ''">
+            AND l.video_id = #{videoId}
+        </if>
+        -- 单独通过销售id查询
+        <if test="companyUserId != null and companyUserId != ''">
+            AND l.user_id = #{companyUserId}
+        </if>
+        ) as courseCompleteNum
+    </select>
 
 </mapper>

+ 54 - 15
fs-user-app/src/main/java/com/fs/app/controller/AppBaseController.java

@@ -42,24 +42,63 @@ public class AppBaseController {
 	@Autowired
 	RedisCache redisCache;
 
-	public String getUserId()
-	{
-		String headValue =  ServletUtils.getRequest().getHeader("APPToken");
-		if(StringUtils.isNotEmpty(headValue)){
-			Claims claims=jwtUtils.getClaimByToken(headValue);
-			if(StringUtils.isNotEmpty(claims)){
-				String userId = claims.getSubject().toString();
-				return userId;
-			}
-			else{
-				return null;
-			}
+//	public String getUserId()
+//	{
+//		String headValue =  ServletUtils.getRequest().getHeader("APPToken");
+//		if(StringUtils.isNotEmpty(headValue)){
+//			Claims claims=jwtUtils.getClaimByToken(headValue);
+//			if(StringUtils.isNotEmpty(claims)){
+//				String userId = claims.getSubject().toString();
+//				return userId;
+//			}
+//			else{
+//				return null;
+//			}
+//
+//		}
+//		else{
+//			return null;
+//		}
+//
+//	}
 
+	public enum TokenStrategy {
+		DEFAULT,            // 默认策略:优先 APPToken,回退 UserToken
+		USER_TOKEN_ONLY,    // 仅使用 UserToken
+		// 未来可扩展其他策略,如 BACKUP_TOKEN_ONLY
+	}
+
+	/**
+	 * 获取用户ID(默认优先 APPToken,传参可覆盖策略)
+	 * @param strategy 可选策略,默认 DEFAULT(APPToken优先)
+	 */
+	public String getUserId(TokenStrategy strategy) {
+		if (strategy == TokenStrategy.USER_TOKEN_ONLY) {
+			return parseToken("UserToken"); // 仅 UserToken
+		} else {
+			// 默认策略:优先 APPToken
+			return parseToken("APPToken");
 		}
-		else{
-			return null;
-		}
+	}
 
+	// 不传参数时,默认走 DEFAULT 策略
+	public String getUserId() {
+		return getUserId(TokenStrategy.DEFAULT);
+	}
+
+
+	/**
+	 * 通用方法:从指定 Token 获取 userId(私有方法,复用逻辑)
+	 */
+	private String parseToken(String tokenHeaderName) {
+		String token = ServletUtils.getRequest().getHeader(tokenHeaderName);
+		if (StringUtils.isNotEmpty(token)) {
+			Claims claims = jwtUtils.getClaimByToken(token);
+			if (claims != null && StringUtils.isNotEmpty(claims.getSubject())) {
+				return claims.getSubject();
+			}
+		}
+		return null;
 	}
 
 

+ 0 - 7
fs-user-app/src/main/java/com/fs/app/controller/CourseWxH5Controller.java

@@ -1,23 +1,18 @@
 package com.fs.app.controller;
 
 
-import cn.hutool.json.JSONUtil;
 import com.fs.app.annotation.Login;
 import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
-import com.fs.course.config.CourseConfig;
-import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.param.FsCourseQuestionAnswerUParam;
 import com.fs.course.param.FsCourseSendRewardUParam;
 import com.fs.course.param.FsUserCourseVideoFinishUParam;
 import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
-import com.fs.course.param.newfs.FsUserCourseBeMemberParam;
 import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
 import com.fs.course.param.newfs.FsUserCourseVideoUParam;
 import com.fs.course.service.*;
 import com.fs.course.vo.FsUserCourseVideoH5VO;
-import com.fs.course.vo.newfs.FsUserCourseVideoDetailsVO;
 import com.fs.course.vo.newfs.FsUserCourseVideoLinkDetailsVO;
 import com.fs.store.service.IFsUserService;
 import com.fs.system.service.ISysConfigService;
@@ -56,8 +51,6 @@ public class CourseWxH5Controller extends AppBaseController {
     @Autowired
     private IFsUserService fsUserService;
 
-
-    @RepeatSubmit
     @Login
     @ApiOperation("判断是否添加客服(是否关联销售)")
     @PostMapping("/isAddKf")

+ 22 - 1
fs-user-app/src/main/java/com/fs/app/controller/UserController.java

@@ -13,6 +13,9 @@ import com.fs.common.exception.CustomException;
 import com.fs.common.param.BaseQueryParam;
 import com.fs.common.utils.OrderUtils;
 import com.fs.course.param.newfs.FsUserCourseBeMemberParam;
+import com.fs.course.config.CourseConfig;
+import com.fs.course.service.IFsUserCourseService;
+import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.store.domain.FsStoreOrder;
 import com.fs.store.domain.FsUser;
 import com.fs.store.domain.FsUserBill;
@@ -30,6 +33,8 @@ import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -66,6 +71,8 @@ public class UserController extends  AppBaseController {
     @Autowired
     private IFsUserPromoterApplyService userPromoterApplyService;
     @Autowired
+    private IFsUserCourseVideoService courseVideoService;
+    @Autowired
     private IFsUserService fsUserService;
 
     /**
@@ -91,6 +98,15 @@ public class UserController extends  AppBaseController {
         }
     }
 
+
+    @Login
+    @ApiOperation("判断用户是否存在头像昵称")
+    @GetMapping("/checkUserInfo")
+    public R checkUserInfo(){
+        log.info("【判断判断用户是否存在头像昵称】:{}",getUserId());
+        return courseVideoService.checkUserInfo(Long.parseLong(getUserId()));
+    }
+
     @Login
     @ApiOperation("检测是否登录")
     @GetMapping("/checkLogin")
@@ -285,7 +301,12 @@ public class UserController extends  AppBaseController {
     @Login
     @ApiOperation("修改用户信息")
     @PostMapping("/editUser")
-    public R editUser(@RequestBody FsUserEditParam param, HttpServletRequest request){
+    public R editUser(@RequestBody @Valid FsUserEditParam param, HttpServletRequest request){
+        param.setUserId(Long.parseLong(getUserId()));
+        log.info("【修改用户头像昵称】:{}",param);
+        if (param.getNickname().length()>50){
+            return R.error("请授权正确的昵称!");
+        }
         FsUser user=new FsUser();
         user.setUserId(Long.parseLong(getUserId()));
         user.setAvatar(param.getAvatar());

+ 29 - 67
fs-user-app/src/main/java/com/fs/app/controller/WxCompanyUserController.java

@@ -76,6 +76,9 @@ public class WxCompanyUserController extends AppBaseController {
         if (StringUtils.isBlank(param.getCode())) {
             return R.error("code不存在");
         }
+//        if (param.getNickname().length()>100){
+//            return R.error("昵称不符合标准!");
+//        }
         //获取第二个小程序配置,序号从0开始
         final WxMaService wxService = WxMaConfiguration.getMaService(maProperties.getConfigs().get(1).getAppid());
         try {
@@ -83,63 +86,21 @@ public class WxCompanyUserController extends AppBaseController {
             this.logger.info(session.getSessionKey());
             this.logger.info(session.getOpenid());
             this.logger.info(session.getUnionid());
-            // 解密
-            WxMaPhoneNumberInfo phoneNoInfo = wxService.getUserService().getPhoneNoInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
-//            WxMaUserInfo userInfo = wxService.getUserService().getUserInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
-            FsUser user = userService.selectFsUserByPhone(phoneNoInfo.getPhoneNumber());
-            //以下暂时注释,不需要往销售表添加数据
-//            CompanyUser companyUser = companyUserService.getCompanyUserByOpenId(session.getOpenid());
-//            String ip = IpUtil.getRequestIp();
-//
-////            // 如果公司id为空(表示可能是该公司的第一位销售管理员),则需要根据电话号码判断是否存在销售,如果不存在则提示
-////            if (param.getCompanyId() == null) {
-////                if (checkPhone == null) {
-////                    throw new CustomException("由于不是管理员,不能直接登录", 401);
-////                }
-////            }
-//            if (companyUser == null) {
-//                CompanyUser checkPhone = companyUserService.getCompanyUserByPhone(phoneNoInfo.getPhoneNumber());
-//                if (checkPhone != null) {
-//                    if (checkPhone.getMaOpenId() == null) {
-//                        companyUser = checkPhone;
-//                        companyUser.setMaOpenId(session.getOpenid());
-//                        companyUser.setUserId(companyUser.getUserId());
-//                        companyUser.setUpdateTime(new DateTime());
-//                        companyUser.setLoginIp(ip);
-//                        companyUserService.updateUserProfile(companyUser);
-//                    } else {
-//                        throw new CustomException("此手机号用户已存在");
-//                    }
-//                } else {
-//                    //新增
-//                    companyUser = new CompanyUser();
-//                    companyUser.setUserName(phoneNoInfo.getPhoneNumber());
-//                    companyUser.setNickName(userInfo.getNickName() == null ? "微信用户" : userInfo.getNickName());
-//                    companyUser.setPhonenumber(phoneNoInfo.getPhoneNumber());
-//                    companyUser.setSex(userInfo.getGender());
-//                    //密码初始化为123456
-//                    String pw = "123456";
-//                    companyUser.setPassword(SecurityUtils.encryptPassword(param.getPassword() == null ? pw : param.getPassword()));
-//                    companyUser.setCreateTime(new Date());
-//                    companyUser.setCompanyId(param.getCompanyId());
-//                    companyUser.setParentId(param.getParentCompanyUseId());
-//                    companyUser.setMaOpenId(session.getOpenid());
-//
-//                    //部门信息
-//                    CompanyDept dept = companyDeptService.getDefaultCompanyDeptByCompanyId(param.getCompanyId());
-//                    if (Objects.nonNull(dept)) {
-//                        companyUser.setDeptId(dept.getDeptId());
-//                    }
-//                    companyUserService.insertUser(companyUser);
-//                }
-//            } else {
-//                CompanyUser companyUserMp = new CompanyUser();
-//                companyUserMp.setPhonenumber(phoneNoInfo.getPhoneNumber());
-//                companyUserMp.setUserId(companyUser.getUserId());
-//                companyUserMp.setUpdateTime(new DateTime());
-//                companyUserMp.setLoginIp(ip);
-//                companyUserService.updateUserProfile(companyUser);
-//            }
+
+            if (StringUtils.isEmpty(session.getOpenid())){
+                return R.error("登陆失败,openid未授权,请稍后再试!");
+            }
+
+            // 手机号信息
+            WxMaPhoneNumberInfo phoneNoInfo = new WxMaPhoneNumberInfo();
+            FsUser user = null;
+            if (param.getAuthType()==1){
+                phoneNoInfo = wxService.getUserService().getPhoneNoInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
+                if (StringUtils.isEmpty(phoneNoInfo.getPhoneNumber())){
+                    return R.error("授权失败,请联系客服!");
+                }
+                user = userService.selectFsUserByPhone(phoneNoInfo.getPhoneNumber());
+            }
 
             // 特殊(需求设计:需要根据公司是否开启黑名单来设置会员初始化的状态)
             Company company = null;
@@ -170,8 +131,8 @@ public class WxCompanyUserController extends AppBaseController {
                     userMap.setMaOpenId(session.getOpenid());
                     userMap.setUnionId(session.getUnionid());
                     userMap.setUpdateTime(new DateTime());
-                    userMap.setNickname(param.getNickname() != null ? param.getNickname() : "微信用户");
-                    userMap.setAvatar(param.getAvatar() != null ? param.getAvatar() : null);
+//                    userMap.setNickname(param.getNickname() != null ? param.getNickname() : "微信用户");
+//                    userMap.setAvatar(param.getAvatar() != null ? param.getAvatar() : null);
                     userMap.setPhone(phoneNoInfo.getPhoneNumber());
                     // 逻辑调整:如果会员已经绑定了销售,直接提示,不让注册-2025年6月16日14点53分
                     FsUserCompanyUser userCompanyUser = userCompanyUserService.selectByUserIdAndProjectId(user.getUserId(), param.getProjectId());
@@ -189,8 +150,9 @@ public class WxCompanyUserController extends AppBaseController {
                 } else {
                     //新增
                     user = new FsUser();
-                    user.setNickname(param.getNickname() != null ? param.getNickname() : "微信用户");
-                    user.setAvatar(param.getAvatar() != null ? param.getAvatar() : null);
+//                    user.setNickname(param.getNickname() != null ? param.getNickname() : "微信用户");
+//                    user.setAvatar(param.getAvatar() != null ? param.getAvatar() : null);
+                    user.setStatus((company != null ? company.getFsUserIsDefaultBlack() : 0) == 1 ? 0 : 1);
                     user.setMaOpenId(session.getOpenid());
                     user.setUnionId(session.getUnionid());
                     user.setCreateTime(new Date());
@@ -243,12 +205,12 @@ public class WxCompanyUserController extends AppBaseController {
      *
      * @return 用户id
      */
-    public String getUserId() {
-        String headValue = ServletUtils.getRequest().getHeader("UserToken");
-        Claims claims = jwtUtils.getClaimByToken(headValue);
-        String userId = claims.getSubject().toString();
-        return userId;
-    }
+//    public String getUserId() {
+//        String headValue = ServletUtils.getRequest().getHeader("UserToken");
+//        Claims claims = jwtUtils.getClaimByToken(headValue);
+//        String userId = claims.getSubject().toString();
+//        return userId;
+//    }
 
 
 }

+ 9 - 0
fs-user-app/src/main/java/com/fs/app/param/LoginMaWxParam.java

@@ -28,9 +28,11 @@ public class LoginMaWxParam implements Serializable {
     @ApiModelProperty(value = "销售id")
     private Long companyUserId;
 
+//    @NotBlank(message = "用户昵称不能为空")
     @ApiModelProperty(value = "用户昵称")
     private String nickname;
 
+//    @NotBlank(message = "用户头像不能为空")
     @ApiModelProperty(value = "用户头像")
     private String avatar;
 
@@ -50,4 +52,11 @@ public class LoginMaWxParam implements Serializable {
 //    @ApiModelProperty(value = "用户密码")
 //    private String password;
 
+    /**
+     * 0:静默授权  1:手机号授权
+     */
+    @NotNull(message = "授权类型缺失")
+    @ApiModelProperty(value = "小程序授权类型")
+    private Integer authType;
+
 }