Quellcode durchsuchen

Merge remote-tracking branch 'origin/企微聊天' into 企微聊天

yh vor 1 Tag
Ursprung
Commit
aa6c1e402d
56 geänderte Dateien mit 584 neuen und 74 gelöschten Zeilen
  1. 118 0
      fs-admin/src/main/java/com/fs/his/task/Task.java
  2. 1 0
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStorePaymentScrmController.java
  3. 1 0
      fs-admin/src/main/java/com/fs/hisStore/task/LiveTask.java
  4. 3 2
      fs-admin/src/main/java/com/fs/web/controller/system/SysLoginController.java
  5. 28 0
      fs-company/src/main/java/com/fs/company/controller/live/LiveMixLiuTestOpenController.java
  6. 1 0
      fs-company/src/main/java/com/fs/framework/config/SecurityConfig.java
  7. 3 1
      fs-company/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java
  8. 25 4
      fs-company/src/main/java/com/fs/user/FsUserAdminController.java
  9. 25 21
      fs-framework/src/main/java/com/fs/framework/web/service/SysLoginService.java
  10. 95 0
      fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java
  11. 3 2
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  12. 4 0
      fs-service/src/main/java/com/fs/his/config/StoreConfig.java
  13. 3 0
      fs-service/src/main/java/com/fs/his/mapper/FsStoreOrderItemMapper.java
  14. 3 0
      fs-service/src/main/java/com/fs/his/service/IFsStoreOrderService.java
  15. 3 0
      fs-service/src/main/java/com/fs/his/service/IFsUserService.java
  16. 5 0
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java
  17. 1 0
      fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java
  18. 39 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  19. 1 1
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreAfterSalesScrmMapper.java
  20. 1 1
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportRefundZMVO.java
  21. 1 1
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportVO.java
  22. 1 1
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportZMVO.java
  23. 1 1
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderPromotionExportVO.java
  24. 1 1
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderVO.java
  25. 1 1
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreProductExportVO.java
  26. 12 0
      fs-service/src/main/java/com/fs/huifuPay/sdk/opps/core/request/V2TradePaymentScanpayQueryRequest.java
  27. 13 1
      fs-service/src/main/java/com/fs/huifuPay/service/impl/HuiFuServiceImpl.java
  28. 18 0
      fs-service/src/main/java/com/fs/im/vo/OpenImResponseUserVO.java
  29. 1 1
      fs-service/src/main/java/com/fs/live/domain/LiveOrder.java
  30. 1 1
      fs-service/src/main/java/com/fs/live/param/LiveOrderSearchParam.java
  31. 10 0
      fs-service/src/main/java/com/fs/live/service/ILiveCompletionPointsRecordService.java
  32. 8 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveCompletionPointsRecordServiceImpl.java
  33. 7 4
      fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  34. 32 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java
  35. 8 1
      fs-service/src/main/java/com/fs/live/service/impl/LiveWatchUserServiceImpl.java
  36. 1 1
      fs-service/src/main/java/com/fs/live/vo/LiveOrderListVo.java
  37. 1 1
      fs-service/src/main/java/com/fs/live/vo/LiveOrderVoZm.java
  38. 1 1
      fs-service/src/main/java/com/fs/live/vo/MergedOrderExportVO.java
  39. 1 1
      fs-service/src/main/java/com/fs/store/vo/FsStoreProductScrmExportVO.java
  40. 3 0
      fs-service/src/main/java/com/fs/store/vo/h5/FsUserPageListVO.java
  41. 1 1
      fs-service/src/main/resources/application-config-druid-bjzm.yml
  42. 2 2
      fs-service/src/main/resources/application-config-druid-hcl.yml
  43. 2 2
      fs-service/src/main/resources/application-config-druid-hzyy.yml
  44. 2 2
      fs-service/src/main/resources/application-config-druid-jzzx.yml
  45. 2 2
      fs-service/src/main/resources/application-config-druid-sczy.yml
  46. 1 1
      fs-service/src/main/resources/application-druid-jnmy-test.yml
  47. 5 5
      fs-service/src/main/resources/application-druid-shdn.yml
  48. 2 1
      fs-service/src/main/resources/mapper/MerchantAppConfigMapper.xml
  49. 8 0
      fs-service/src/main/resources/mapper/his/FsStoreOrderItemMapper.xml
  50. 1 2
      fs-service/src/main/resources/mapper/his/FsUserInformationCollectionMapper.xml
  51. 2 1
      fs-service/src/main/resources/mapper/his/FsUserMapper.xml
  52. 1 1
      fs-service/src/main/resources/mapper/live/LiveAfterSalesMapper.xml
  53. 1 1
      fs-store/src/main/java/com/fs/store/vo/FsStoreProductStoreExcelVO.java
  54. 34 2
      fs-user-app/src/main/java/com/fs/app/controller/FsUserInformationCollectionController.java
  55. 33 0
      fs-user-app/src/main/java/com/fs/app/controller/PackageController.java
  56. 2 2
      fs-user-app/src/main/java/com/fs/app/controller/course/CourseQwController.java

+ 118 - 0
fs-admin/src/main/java/com/fs/his/task/Task.java

@@ -1,6 +1,8 @@
 package com.fs.his.task;
 
+import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.date.DateTime;
+import cn.hutool.http.HttpRequest;
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
@@ -62,9 +64,12 @@ import com.fs.hisStore.service.IFsStorePaymentScrmService;
 import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
 import com.fs.huifuPay.service.HuiFuService;
+import com.fs.im.config.IMConfig;
 import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
 import com.fs.im.service.OpenIMService;
+import com.fs.im.service.impl.OpenIMServiceImpl;
+import com.fs.im.vo.OpenImResponseUserVO;
 import com.fs.qw.domain.QwCompany;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwRestrictionPushRecordMapper;
@@ -78,7 +83,9 @@ import com.fs.system.mapper.SysConfigMapper;
 import com.fs.system.service.ISysConfigService;
 import com.fs.utils.OrderContextHolder;
 import com.google.gson.Gson;
+import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -1148,6 +1155,7 @@ public class Task {
                 V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                 request.setOrgHfSeqId(payment.getTradeNo());
+                request.setAppId(payment.getAppId());
                 HuiFuQueryOrderResult o = null;
                 try {
                     o = huiFuService.queryOrder(request);
@@ -1865,4 +1873,114 @@ public class Task {
         // 等待所有异步任务完成
         CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
     }
+
+
+    /**
+     * 同步Im系统中的用户昵称
+     */
+    public void updateIMUserName() {
+        String adminToken = openIMService.getAdminToken();
+//        String adminToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiJpbUFkbWluIiwiUGxhdGZvcm1JRCI6MTAsImV4cCI6MTc3NDMxNzk3NiwiaWF0IjoxNzY2NTQxOTcxfQ.3LNnzvZQfCdLf5HWc2wvU_Hw0j_JfBGJtc88vwQmzBc";
+        List<OpenIMServiceImpl.UserInfo> userInfos = getImUserId(adminToken);
+        // 过滤出以U开头的用户ID
+        List<String> userIds = userInfos.stream().filter(user -> user.getUserID().startsWith("U"))
+                .map(s -> s.getUserID().length() > 1 ? s.getUserID().substring(1) : "")
+                .filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());
+        log.info("过滤出以U开头的用户ID:{}", userIds);
+        List<FsUser> fsUsers = fsUserMapper.selectUserNameByIds(userIds.toString());
+//        List<FsUser> fsUsers = fsUserMapper.selectlistBylimit(userIds.toString());
+        List<Long> collect = fsUsers.stream().filter(Objects::isNull).map(FsUser::getUserId).collect(Collectors.toList());
+        log.info("查询的用户id:,{}", collect);
+        Map<String, String> userInfosMap = userInfos.stream()
+                .filter(Objects::nonNull)
+                .collect(Collectors.toMap(OpenIMServiceImpl.UserInfo::getUserID, OpenIMServiceImpl.UserInfo::getNickname, (n1, n2) -> n1));
+        log.info("IM的用户Map:{}", userInfosMap);
+        //名字对不上的用户集合
+        List<FsUser> fsUserList = fsUsers.stream().filter(Objects::nonNull)
+                .filter(fsUser -> {
+                    String imName = userInfosMap.get("U" + fsUser.getUserId());
+                    return imName != null && !Objects.equals(fsUser.getNickName(), imName);
+                }).collect(Collectors.toList());
+        log.info("名字对不上的用户集合:{}", fsUserList);
+        requestImUpdate(adminToken, fsUserList);
+    }
+
+
+    /**
+     * 获取Im系统中U开头的用户信息
+     *
+     * @return U开头用户信息集合
+     */
+    private List<OpenIMServiceImpl.UserInfo> getImUserId(String adminToken) {
+        int pageSize = 5000;
+        int pageNumber = 1;
+        List<OpenIMServiceImpl.UserInfo> users;
+        while (true) {
+            // 构建分页查询请求体
+            org.json.JSONObject requestPage = new org.json.JSONObject();
+            org.json.JSONObject pagination = new org.json.JSONObject();
+            pagination.put("pageNumber", pageNumber);
+            pagination.put("showNumber", pageSize);
+            requestPage.put("pagination", pagination);
+            log.info("IM系统URL地址:https://web.jnmyim.ylrzfs.com/api/user/get_users");
+            String result = HttpRequest.post("https://web.jnmyim.ylrzfs.com/api/user/get_users")
+                    .header("operationID", String.valueOf(System.currentTimeMillis()))
+                    .header("token", adminToken)
+                    .body(requestPage.toString())
+                    .execute()
+                    .body();
+            OpenImResponseUserVO responseDTO = JSONUtil.toBean(result, OpenImResponseUserVO.class);
+            users = responseDTO.getData().getUsers();
+
+            if (CollectionUtil.isEmpty(users)) {
+                log.info("数据为空,处理结束");
+                break;
+            }
+            log.info("已处理第 {} 页,共处理用户数:{}", pageNumber, users.size());
+            if (users.size() < pageSize) {
+                log.info("已是最后一页,处理完毕!");
+                break;
+            }
+            pageNumber++; // 下一页
+        }
+        return users;
+    }
+
+
+    /**
+     * 请求Im系统更改用户昵称
+     *
+     * @param adminToken
+     * @param fsUserList 名字对不上的用户集合
+     */
+    private void requestImUpdate(String adminToken, List<FsUser> fsUserList) {
+        if (CollectionUtil.isEmpty(fsUserList)) {
+            return;
+        }
+        OpenImResponseDTO responseDTO = null;
+        UpdateUserInfo updateUserInfo = new UpdateUserInfo();
+        for (FsUser user : fsUserList) {
+            updateUserInfo.setUserID("U" + user.getUserId().toString());
+            updateUserInfo.setNickname(user.getNickName());
+
+            Map<String, Object> bodyMap = new HashMap<>();
+            bodyMap.put("userInfo", updateUserInfo);
+            String jsonBody1 = JSONUtil.toJsonStr(bodyMap);
+            String result2 = HttpRequest.post("https://web.jnmyim.ylrzfs.com/api/user/update_user_info_ex")
+                    .header("operationID", String.valueOf(System.currentTimeMillis()))
+                    .header("token", adminToken)
+                    .body(jsonBody1)
+                    .execute()
+                    .body();
+            responseDTO = JSONUtil.toBean(result2, OpenImResponseDTO.class);
+        }
+        log.info("IM系统响应返回:code:{},msg:{}", responseDTO.getErrCode(), responseDTO.getErrMsg());
+    }
+
+
+    @Data
+    public class UpdateUserInfo {
+        private String userID;
+        private String nickname;
+    }
 }

+ 1 - 0
fs-admin/src/main/java/com/fs/hisStore/controller/FsStorePaymentScrmController.java

@@ -135,6 +135,7 @@ public class FsStorePaymentScrmController extends BaseController
             V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
             request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
             request.setOrgHfSeqId(payment.getTradeNo());
+            request.setAppId(payment.getAppId());
             HuiFuQueryOrderResult o = null;
             try {
                 o = huiFuService.queryOrder(request);

+ 1 - 0
fs-admin/src/main/java/com/fs/hisStore/task/LiveTask.java

@@ -266,6 +266,7 @@ public class LiveTask {
                 V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                 request.setOrgHfSeqId(payment.getTradeNo());
+                request.setAppId(payment.getAppId());
                 HuiFuQueryOrderResult o = null;
                 try {
                     o = huiFuService.queryOrder(request);

+ 3 - 2
fs-admin/src/main/java/com/fs/web/controller/system/SysLoginController.java

@@ -11,6 +11,7 @@ import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.PatternUtils;
 import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.framework.web.service.TokenService;
 import com.fs.his.utils.ConfigUtil;
@@ -216,7 +217,7 @@ public class SysLoginController
 
             String ipAddr = IpUtils.getIpAddr(ServletUtils.getRequest());
             String loginIp = sysUser.getLoginIp();
-            if (com.fs.common.utils.StringUtils.isEmpty(loginIp)) {
+            if (StringUtils.isEmpty(loginIp)) {
                 sysUser.setLoginIp(ipAddr.trim());
             } else {
                 List<String> ipList = Arrays.stream(loginIp.split(","))
@@ -241,7 +242,7 @@ public class SysLoginController
                 return AjaxResult.success(Constants.TOKEN, token);
             }
             return AjaxResult.success("waiting");
-        }else if (com.fs.common.utils.StringUtils.isNotEmpty(status)&&status.startsWith("error:")) {
+        }else if (StringUtils.isNotEmpty(status)&&status.startsWith("error:")) {
             // 把错误返回给前端
             throw new ServiceException(status);
         }

+ 28 - 0
fs-company/src/main/java/com/fs/company/controller/live/LiveMixLiuTestOpenController.java

@@ -0,0 +1,28 @@
+package com.fs.company.controller.live;
+
+import com.fs.live.service.ILiveWatchUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author MixLiu
+ * @date 2025/12/18 下午3:26)
+ */
+
+@RestController
+@RequestMapping("/live/LiveMixLiuTestOpen")
+public class LiveMixLiuTestOpenController {
+
+    @Autowired
+    private ILiveWatchUserService liveWatchUserService;
+
+    @GetMapping("/goToMarkUser/{liveId}")
+    public void goToMarkUser(@PathVariable Long liveId){
+        liveWatchUserService.qwTagMarkByLiveWatchLog(liveId);
+
+
+    }
+}

+ 1 - 0
fs-company/src/main/java/com/fs/framework/config/SecurityConfig.java

@@ -132,6 +132,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 .antMatchers("/druid/**").anonymous()
                 .antMatchers("/qw/data/**").anonymous()
                 .antMatchers("/qw/user/selectCloudByCompany").anonymous()
+                .antMatchers("/live/LiveMixLiuTestOpen/**").anonymous()
                 // 除上面外的所有请求全部需要鉴权认证
                 .anyRequest().authenticated()
                 .and()

+ 3 - 1
fs-company/src/main/java/com/fs/hisStore/controller/FsStoreOrderScrmController.java

@@ -43,6 +43,7 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -182,7 +183,7 @@ public class FsStoreOrderScrmController extends BaseController
                 if (vo.getUserAddress()!=null){
                     vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
                 }
-
+                vo.setCost(BigDecimal.ZERO);
             }
         }
 
@@ -391,6 +392,7 @@ public class FsStoreOrderScrmController extends BaseController
                     catch (Exception e){
                     }
                 }
+                vo.setCost(BigDecimal.ZERO);
             }
         }
         ExcelUtil<FsStoreOrderItemExportVO> util = new ExcelUtil<FsStoreOrderItemExportVO>(FsStoreOrderItemExportVO.class);

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

@@ -9,14 +9,14 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ServletUtils;
-import com.fs.common.utils.StringUtils;
 import com.fs.company.cache.ICompanyUserCacheService;
+import com.fs.course.domain.FsUserCompanyUser;
 import com.fs.course.dto.BatchSendCourseDTO;
 import com.fs.course.param.FsCourseLinkCreateParam;
+import com.fs.course.service.IFsUserCompanyUserService;
 import com.fs.course.service.IFsUserCourseService;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.service.TokenService;
-
 import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.utils.PhoneUtil;
@@ -37,8 +37,6 @@ import org.springframework.web.bind.annotation.*;
 
 import java.util.Date;
 
-import static com.fs.his.utils.PhoneUtil.encryptPhone;
-
 @Api(tags = "会员管理接口")
 @RestController
 @Slf4j
@@ -64,6 +62,9 @@ public class FsUserAdminController extends BaseController {
     @Autowired
     private OpenIMService openIMService;
 
+    @Autowired
+    private IFsUserCompanyUserService fsUserCompanyUserService;
+
     @PreAuthorize("@ss.hasPermi('user:fsUser:list')")
     @PostMapping("/list")
     @ApiOperation("会员列表(与移动端使用的相同查询)")
@@ -146,6 +147,15 @@ public class FsUserAdminController extends BaseController {
         return AjaxResult.success(fsUserService.selectFsUserPageListVOByUserId(userId));
     }
 
+    /**
+     * 获取项目用户详细信息
+     */
+    @GetMapping(value = "/member/{id}")
+    public AjaxResult getMemberInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsUserService.selectFsMemberUserPageListVOById(id));
+    }
+
     /**
      * 修改用户
      */
@@ -157,6 +167,17 @@ public class FsUserAdminController extends BaseController {
         return toAjax(fsUserService.updateFsUser(fsUser));
     }
 
+    /**
+     * 修改用户
+     */
+    @PreAuthorize("@ss.hasPermi('user:fsUser:edit')")
+    @Log(title = "用户", businessType = BusinessType.UPDATE)
+    @PutMapping("/member")
+    public AjaxResult editMemberUser(@RequestBody FsUserCompanyUser fsUser)
+    {
+        return toAjax(fsUserCompanyUserService.updateFsUserCompanyUser(fsUser));
+    }
+
 
     @ApiOperation("后台会员批量发送课程消息")
     @PostMapping("/batchSendCourse")

+ 25 - 21
fs-framework/src/main/java/com/fs/framework/web/service/SysLoginService.java

@@ -36,7 +36,7 @@ import java.util.concurrent.TimeUnit;
 
 /**
  * 登录校验方法
- * 
+ *
 
  */
 @Component
@@ -50,7 +50,7 @@ public class SysLoginService
 
     @Autowired
     private RedisCache redisCache;
-    
+
     @Autowired
     private ISysUserService userService;
 
@@ -70,7 +70,7 @@ public class SysLoginService
 
     /**
      * 登录验证
-     * 
+     *
      * @param username 用户名
      * @param password 密码
      * @param code 验证码
@@ -115,7 +115,7 @@ public class SysLoginService
 
     /**
      * 校验验证码
-     * 
+     *
      * @param username 用户名
      * @param code 验证码
      * @param uuid 唯一标识
@@ -141,25 +141,29 @@ public class SysLoginService
     /**
      * 记录登录信息
      */
-    public void recordLoginInfo(SysUser user)
-    {
+    public void recordLoginInfo(SysUser user) {
         String ipAddr = IpUtils.getIpAddr(ServletUtils.getRequest());
         String loginIp = user.getLoginIp();
-        if (com.fs.common.utils.StringUtils.isEmpty(loginIp)) {
-            user.setLoginIp(ipAddr);
-        } else {
-            List<String> ipList = new ArrayList<>(Arrays.asList(loginIp.split(",")));
-            if (!ipList.contains(ipAddr)) {
-                ipList.add(ipAddr);
-                user.setLoginIp(String.join(",", ipList));
-            }
+
+        List<String> ipList = new ArrayList<>();
+
+        if (com.fs.common.utils.StringUtils.isNotEmpty(loginIp)) {
+            ipList.addAll(Arrays.asList(loginIp.split(",")));
         }
-        user.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
+        ipList.remove(ipAddr);
+        // 如果已满 10 个,清理第一个
+        if (ipList.size() >= 10) {
+            ipList.remove(0);
+        }
+        // 新 IP 放到最后
+        ipList.add(ipAddr);
+        user.setLoginIp(String.join(",", ipList));
         user.setLoginDate(DateUtils.getNowDate());
         userService.updateUserProfile(user);
     }
 
 
+
     public boolean checkIsNeedCheck(String username, String password, String code, String uuid)
     {
         String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
@@ -199,12 +203,12 @@ public class SysLoginService
         LoginUser loginUser = (LoginUser) authentication.getPrincipal();
         //查询当前登录用户信息
         SysUser sysUser = userService.selectUserById(loginUser.getUserId());
-        Long[] userIds = new Long[]{236L, 246L, 247L, 253L,119L};
-        for (Long userId : userIds) {
-            if (userId.equals(sysUser.getUserId())){
-                return false;
-            }
-        }
+//        Long[] userIds = new Long[]{236L, 246L, 247L, 253L,119L};
+//        for (Long userId : userIds) {
+//            if (userId.equals(sysUser.getUserId())){
+//                return false;
+//            }
+//        }
 
         // 判断是否开启了扫码配置
         if (ObjectUtil.isEmpty(isNeedScan) || !isNeedScan){

+ 95 - 0
fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java

@@ -36,7 +36,9 @@ import javax.websocket.*;
 import javax.websocket.server.ServerEndpoint;
 import java.io.EOFException;
 import java.io.IOException;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.concurrent.locks.Lock;
@@ -246,6 +248,9 @@ public class WebSocketServer {
             }
             redisCache.setCacheObject( "live:user:first:entry:" + liveId + ":" + userId, liveUserFirstEntry,1, TimeUnit.HOURS);
 
+            // 推送完课积分倒计时配置信息给前端
+            sendCompletionPointsConfigToUser(session, liveId, userId, live);
+
 
         } else {
             adminRoom.add(session);
@@ -1392,5 +1397,95 @@ public class WebSocketServer {
         }
     }
 
+    /**
+     * 向用户推送完课积分倒计时配置信息
+     * 在用户连接WebSocket时调用,让前端能够显示倒计时
+     * @param session WebSocket会话
+     * @param liveId 直播间ID
+     * @param userId 用户ID
+     * @param live 直播信息
+     */
+    private void sendCompletionPointsConfigToUser(Session session, Long liveId, Long userId, Live live) {
+        try {
+
+            boolean isLiveStarted = false;
+            if (live.getStatus() != null && live.getStatus() == 2) {
+                isLiveStarted = true;
+            } else if (live.getStartTime() != null) {
+                LocalDateTime now = LocalDateTime.now();
+                isLiveStarted = now.isAfter(live.getStartTime()) || now.isEqual(live.getStartTime());
+            }
+
+            if (!isLiveStarted) {
+                // 直播未开始,不推送完课配置
+                log.debug("[完课配置推送] 直播未开始,跳过推送, liveId={}, userId={}", liveId, userId);
+                return;
+            }
+
+            String configJson = live.getConfigJson();
+            if (configJson == null || configJson.isEmpty()) {
+                return;
+            }
+
+            JSONObject jsonConfig = JSON.parseObject(configJson);
+            boolean enabled = jsonConfig.getBooleanValue("enabled");
+            if (!enabled) {
+                return;
+            }
+
+            Integer completionRate = jsonConfig.getInteger("completionRate");
+            if (completionRate == null || completionRate <= 0 || completionRate > 100) {
+                return;
+            }
+
+            // 3. 计算完课所需观看时长
+            Long videoDuration = live.getDuration();
+            if (videoDuration == null || videoDuration <= 0) {
+                return;
+            }
+
+            // 完课所需时长(秒) = 视频总时长 × 完课比例 / 100
+            long requiredDuration = (long) Math.ceil(videoDuration * completionRate / 100.0);
+
+            // 4. 获取用户当前观看时长
+            String hashKey = "live:watch:duration:hash:" + liveId;
+            String userIdField = String.valueOf(userId);
+            Object existingDuration = redisCache.hashGet(hashKey, userIdField);
+            long currentDuration = existingDuration != null ? Long.parseLong(existingDuration.toString()) : 0L;
+
+            // 5. 检查今天是否已有完课记录
+            LocalDate today = LocalDate.now();
+            Date currentDate = Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant());
+            LiveCompletionPointsRecord todayRecord = completionPointsRecordService.selectByUserAndDate(liveId, userId, currentDate);
+
+            boolean hasCompletedToday = (todayRecord != null);
+
+            // 6. 构建配置信息
+            JSONObject configData = new JSONObject();
+            configData.put("videoDuration", videoDuration);  // 视频总时长(秒)
+            configData.put("completionRate", completionRate);  // 完课比例(%)
+            configData.put("requiredDuration", requiredDuration);  // 完课所需时长(秒)
+            configData.put("currentDuration", currentDuration);  // 当前观看时长(秒)
+            configData.put("remainingDuration", Math.max(0, requiredDuration - currentDuration));  // 剩余时长(秒)
+            configData.put("hasCompletedToday", hasCompletedToday);  // 今天是否已完课
+
+            // 7. 推送配置消息
+            SendMsgVo sendMsgVo = new SendMsgVo();
+            sendMsgVo.setLiveId(liveId);
+            sendMsgVo.setUserId(userId);
+            sendMsgVo.setCmd("completionPointsConfig");
+            sendMsgVo.setMsg("完课积分配置");
+            sendMsgVo.setData(configData.toJSONString());
+
+            sendMessage(session, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+
+            log.debug("[完课配置推送] 推送成功, liveId={}, userId={}, 所需时长={}秒, 当前时长={}秒, 剩余={}秒",
+                    liveId, userId, requiredDuration, currentDuration, Math.max(0, requiredDuration - currentDuration));
+
+        } catch (Exception e) {
+            log.error("[完课配置推送] 推送失败, liveId={}, userId={}", liveId, userId, e);
+        }
+    }
+
 }
 

+ 3 - 2
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -2402,7 +2402,8 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
 
         // 记录积分日志
         FsUserIntegralLogs integralLogs = new FsUserIntegralLogs();
-        integralLogs.setIntegral(config.getAnswerIntegral().longValue());
+        long integralValue = config.getAnswerIntegral().longValue();
+        integralLogs.setIntegral(integralValue);
         integralLogs.setUserId(user.getUserId());
         integralLogs.setBalance(userMap.getIntegral());
         integralLogs.setLogType(17);
@@ -2436,7 +2437,7 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
         redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
         redPacketLog.setPeriodId(param.getPeriodId());
         redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
-        return R.ok("积分奖励发放成功").put("rewardType", config.getRewardType());
+        return R.ok("积分奖励发放成功").put("rewardType", config.getRewardType()).put("integralValue",integralValue);
     }
 
     @Override

+ 4 - 0
fs-service/src/main/java/com/fs/his/config/StoreConfig.java

@@ -3,6 +3,7 @@ package com.fs.his.config;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 
 @Data
 public class StoreConfig implements Serializable {
@@ -22,5 +23,8 @@ public class StoreConfig implements Serializable {
     private String API_URL;
     private String HASHCODE;
     private String SECRET_KEY;
+    private String payPriceTip;//支付金额达限提示
+    private BigDecimal yearPayPrice;//年支付金额限制
+    private BigDecimal canPayPrice;//达限后支付限制金额
 
 }

+ 3 - 0
fs-service/src/main/java/com/fs/his/mapper/FsStoreOrderItemMapper.java

@@ -1,5 +1,6 @@
 package com.fs.his.mapper;
 
+import java.math.BigDecimal;
 import java.util.List;
 import com.fs.his.domain.FsStoreOrderItem;
 import com.fs.his.vo.FsStoreOrderItemExcelVO;
@@ -75,4 +76,6 @@ public interface FsStoreOrderItemMapper
     List<FsStoreOrderItemListDVO> selectFsStoreOrderItemListDVOByOrderId(Long orderId);
 
     List<FsStoreOrderItem> selectFsStoreOrderItemListByItemIds(@Param("itemIds") List<Long> itemIds);
+
+    BigDecimal selectPayPriceByYear(@Param("userId") String userId);
 }

+ 3 - 0
fs-service/src/main/java/com/fs/his/service/IFsStoreOrderService.java

@@ -278,4 +278,7 @@ public interface IFsStoreOrderService
     FsStoreOrder confirmOrder(FsPackageOrder packageOrder,Long doctorId);
 
     List<FsStoreOrder> selectOutTimeOrderList(Integer unPayTime);
+
+    BigDecimal selectPayPriceByYear(String userId);
+
 }

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

@@ -188,6 +188,9 @@ public interface IFsUserService
 
     FsUserPageListVO selectFsUserPageListVOByUserId(Long userId);
 
+
+    FsUserPageListVO selectFsMemberUserPageListVOById(Long id);
+
     /**
      * 查询项目会员数据
      *

+ 5 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsStoreOrderServiceImpl.java

@@ -4510,4 +4510,9 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService {
         return fsStoreOrderMapper.selectOutTimeOrderList(unPayTime);
     }
 
+    @Override
+    public BigDecimal selectPayPriceByYear(String userId){
+        return fsStoreOrderItemMapper.selectPayPriceByYear(userId);
+    }
+
 }

+ 1 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java

@@ -389,6 +389,7 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
             V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
             request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(fsStorePayment.getCreateTime()));
             request.setOrgHfSeqId(fsStorePayment.getTradeNo());
+            request.setAppId(fsStorePayment.getAppId());
             HuiFuQueryOrderResult queryOrderResult = null;
             try {
                 queryOrderResult = huiFuService.queryOrder(request);

+ 39 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -1221,6 +1221,45 @@ public class FsUserServiceImpl implements IFsUserService {
         return item;
     }
 
+    @Override
+    public FsUserPageListVO selectFsMemberUserPageListVOById(Long id) {
+        FsUserCompanyUser userCompanyUser = userCompanyUserService.selectFsUserCompanyUserById(id);
+        if(userCompanyUser == null || userCompanyUser.getUserId() == null){
+            return null;
+        }
+
+        FsUser fsUser = fsUserMapper.selectFsUserByUserId(userCompanyUser.getUserId());
+        FsUserPageListVO item = new FsUserPageListVO();
+        BeanUtils.copyProperties(fsUser, item);
+        item.setNickname(fsUser.getNickName());
+        item.setStatus(userCompanyUser.getStatus()); // 取项目会员的状态
+        Map<Long, CompanyTag> tagMap = companyTagCacheService.queryAllTagMap();
+        if (item.getPhone() != null) {
+            item.setPhone(ParseUtils.parsePhone(item.getPhone()));
+        }
+        String userTagByUserId = null;
+        if (item.getUserId() != null && item.getCompanyUserId() != null) {
+            userTagByUserId = companyTagCacheService
+                    .findUserTagByUserId(item.getUserId(), item.getCompanyUserId());
+        }
+        if (StringUtils.isNotEmpty(userTagByUserId)) {
+            String[] split = userTagByUserId.split(",");
+            Set<String> tagNames = new HashSet<>();
+            for (String tag : split) {
+                if (StringUtils.isNotBlank(tag)) {
+                    Long tagL = Long.parseLong(tag);
+                    CompanyTag companyTag = tagMap.get(tagL);
+                    if (companyTag != null) {
+                        tagNames.add(companyTag.getTag());
+                    }
+                }
+            }
+            item.setTagIds(userTagByUserId);
+            item.setTag(String.join(",", tagNames));
+        }
+        return item;
+    }
+
     @Override
     @Transactional
     public void addMoney(FsStoreOrderScrm order) {

+ 1 - 1
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreAfterSalesScrmMapper.java

@@ -255,7 +255,7 @@ public interface FsStoreAfterSalesScrmMapper
             " left join company c on c.company_id=s.company_id " +
             " left join company_user cu on cu.user_id=s.company_user_id " +
             " left join fs_store_payment_scrm fsps on fsps.business_order_id = o.id and fsps.status in (-1,1) " +
-            " where 1=1 and s.status = 4 " +
+            " where 1=1 and s.status = 4 and fsps.bank_transaction_id is not null " +
             "<if test =\"maps.hfOrderCode != null and  maps.hfOrderCode!='' \"> " +
             "and fsps.pay_code = #{maps.hfOrderCode} " +
             "</if>" +

+ 1 - 1
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportRefundZMVO.java

@@ -49,7 +49,7 @@ public class FsStoreOrderItemExportRefundZMVO implements Serializable  {
     @Excel(name = "产品价格",sort =70)
     private String price;
 
-    @Excel(name = "成本价",sort =80)
+    @Excel(name = "成本价",sort =80)
     private String cost;
     @Excel(name = "结算价",sort =90)
     private BigDecimal FPrice;

+ 1 - 1
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportVO.java

@@ -41,7 +41,7 @@ public class FsStoreOrderItemExportVO implements Serializable
     @Excel(name = "产品价格")
     private BigDecimal price;
 
-    @Excel(name = "成本价")
+    @Excel(name = "成本价")
     private BigDecimal cost;
     @Excel(name = "结算价")
     private BigDecimal FPrice;

+ 1 - 1
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportZMVO.java

@@ -46,7 +46,7 @@ public class FsStoreOrderItemExportZMVO implements Serializable  {
     @Excel(name = "产品价格",sort =70)
     private BigDecimal price;
 
-    @Excel(name = "成本价",sort =80)
+    @Excel(name = "成本价",sort =80)
     private BigDecimal cost;
     @Excel(name = "结算价",sort =90)
     private BigDecimal FPrice;

+ 1 - 1
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderPromotionExportVO.java

@@ -132,7 +132,7 @@ public class FsStoreOrderPromotionExportVO implements Serializable
     private String packageTitle;
 
     /** 成本价 */
-    @Excel(name = "订单成本价")
+    @Excel(name = "成本价")
     private BigDecimal cost;
 
     private String phone;

+ 1 - 1
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderVO.java

@@ -194,7 +194,7 @@ public class FsStoreOrderVO implements Serializable
     private Integer isDel;
 
     /** 成本价 */
-    @Excel(name = "成本价")
+    @Excel(name = "成本价")
     private BigDecimal cost;
 
     /** 结算价 */

+ 1 - 1
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreProductExportVO.java

@@ -118,7 +118,7 @@ public class FsStoreProductExportVO implements Serializable {
     private BigDecimal giveIntegral;
 
     /** 成本价 */
-    @Excel(name = "成本价")
+    @Excel(name = "成本价")
     private BigDecimal cost;
 
     /** 是否优品推荐 */

+ 12 - 0
fs-service/src/main/java/com/fs/huifuPay/sdk/opps/core/request/V2TradePaymentScanpayQueryRequest.java

@@ -47,6 +47,10 @@ public class V2TradePaymentScanpayQueryRequest extends BaseRequest {
     @JSONField(name = "party_order_id")
     private String partyOrderId;
 
+
+
+    String appId; //多小程序支付
+
     @Override
     public FunctionCodeEnum getFunctionCode() {
         return FunctionCodeEnum.V2_TRADE_PAYMENT_SCANPAY_QUERY;
@@ -121,4 +125,12 @@ public class V2TradePaymentScanpayQueryRequest extends BaseRequest {
         this.partyOrderId = partyOrderId;
     }
 
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
+
 }

+ 13 - 1
fs-service/src/main/java/com/fs/huifuPay/service/impl/HuiFuServiceImpl.java

@@ -131,7 +131,19 @@ public class HuiFuServiceImpl implements HuiFuService {
 
     @Override
     public HuiFuQueryOrderResult queryOrder(V2TradePaymentScanpayQueryRequest request) throws Exception{
-        doInit(getMerConfig());
+        if (request.getAppId() != null) {
+            FsHfpayConfigMapper fsHfpayConfigMapper = SpringUtils.getBean(FsHfpayConfigMapper.class);
+            FsHfpayConfig fsHfpayConfig = fsHfpayConfigMapper.selectByAppId(request.getAppId());
+            if (fsHfpayConfig != null) {
+                //多汇付支付获取配置
+                doInit(getMerConfig(fsHfpayConfig));
+            } else {
+                //多小程序
+                doInit(getMerConfig());
+            }
+        } else {
+            doInit(getMerConfig());
+        }
         Map<String, Object> response = doExecute(request);
         String jsonString = JSONObject.toJSONString(response);
         HuiFuQueryOrderResult huiFuQueryOrderResult = JSON.parseObject(jsonString, HuiFuQueryOrderResult.class);

+ 18 - 0
fs-service/src/main/java/com/fs/im/vo/OpenImResponseUserVO.java

@@ -0,0 +1,18 @@
+package com.fs.im.vo;
+
+import com.fs.im.service.impl.OpenIMServiceImpl;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+    public class OpenImResponseUserVO {
+        private Integer errCode;
+        private String errMsg;
+        private String errDlt;
+        private UserData data;
+        @Data
+        public static class UserData {
+            private List<OpenIMServiceImpl.UserInfo> users;
+        }
+    }

+ 1 - 1
fs-service/src/main/java/com/fs/live/domain/LiveOrder.java

@@ -145,7 +145,7 @@ public class LiveOrder extends BaseEntity {
     private String isDel;
 
     /** 成本价 */
-    @Excel(name = "成本价")
+    @Excel(name = "成本价")
     private BigDecimal costPrice;
 
     /** 核销码 */

+ 1 - 1
fs-service/src/main/java/com/fs/live/param/LiveOrderSearchParam.java

@@ -131,7 +131,7 @@ public class LiveOrderSearchParam extends BaseEntity {
     private String isDel;
 
     /** 成本价 */
-    @Excel(name = "成本价")
+    @Excel(name = "成本价")
     private BigDecimal costPrice;
 
     /** 核销码 */

+ 10 - 0
fs-service/src/main/java/com/fs/live/service/ILiveCompletionPointsRecordService.java

@@ -2,6 +2,7 @@ package com.fs.live.service;
 
 import com.fs.live.domain.LiveCompletionPointsRecord;
 
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -40,4 +41,13 @@ public interface ILiveCompletionPointsRecordService {
      * @return 完课记录列表
      */
     List<LiveCompletionPointsRecord> getUserRecords(Long liveId, Long userId);
+
+    /**
+     * 根据用户和日期查询完课记录
+     * @param liveId 直播ID
+     * @param userId 用户ID
+     * @param date 日期
+     * @return 完课记录
+     */
+    LiveCompletionPointsRecord selectByUserAndDate(Long liveId, Long userId, Date date);
 }

+ 8 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveCompletionPointsRecordServiceImpl.java

@@ -261,6 +261,14 @@ public class LiveCompletionPointsRecordServiceImpl implements ILiveCompletionPoi
         return recordMapper.selectRecordsByUser(liveId, userId);
     }
 
+    /**
+     * 根据用户和日期查询完课记录
+     */
+    @Override
+    public LiveCompletionPointsRecord selectByUserAndDate(Long liveId, Long userId, Date date) {
+        return recordMapper.selectByUserAndDate(liveId, userId, date);
+    }
+
     /**
      * 从直播配置中获取完课积分配置
      */

+ 7 - 4
fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -3193,7 +3193,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                         });
                         String s = (String) resultMap.get("package");
                         resultMap.put("packageValue", s);
-                        return R.ok().put("payType", param.getPayType()).put("result", resultMap);
+                        return R.ok().put("payType", param.getPayType()).put("result", resultMap).put("type", "hf");
                     } else {
                         return R.error(result.getResp_desc());
                     }
@@ -3532,6 +3532,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                     V2TradePaymentScanpayQueryRequest request = new V2TradePaymentScanpayQueryRequest();
                     request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                     request.setOrgHfSeqId(payment.getTradeNo());
+                    request.setAppId(payment.getAppId());
                     HuiFuQueryOrderResult queryOrderResult = null;
                     try {
                         queryOrderResult = huiFuService.queryOrder(request);
@@ -3624,9 +3625,11 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         }
 
         LiveUserFirstEntry liveUserFirstEntry = liveUserFirstEntryService.selectEntityByLiveIdUserId(liveOrder.getLiveId(), Long.parseLong(liveOrder.getUserId()));
-        liveOrder.setCompanyId(liveUserFirstEntry.getCompanyId());
-        liveOrder.setCompanyUserId(liveUserFirstEntry.getCompanyUserId());
-        liveOrder.setTuiUserId(liveUserFirstEntry.getCompanyUserId());
+        if (ObjectUtil.isNotEmpty(liveUserFirstEntry)) {
+            liveOrder.setCompanyId(liveUserFirstEntry.getCompanyId());
+            liveOrder.setCompanyUserId(liveUserFirstEntry.getCompanyUserId());
+            liveOrder.setTuiUserId(liveUserFirstEntry.getCompanyUserId());
+        }
         String orderSn = OrderCodeUtils.getOrderSn();
 //        String orderSn = "123"; // todo yhq
         log.info("订单生成:"+orderSn);

+ 32 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java

@@ -1,6 +1,7 @@
 package com.fs.live.service.impl;
 
 import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.thread.ThreadUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
@@ -975,7 +976,38 @@ public class LiveServiceImpl implements ILiveService
             redisCache.redisTemplate.opsForZSet().add("live:auto_task:" + live.getLiveId(), JSON.toJSONString(liveAutoTask),liveAutoTask.getAbsValue().getTime());
             redisCache.redisTemplate.expire("live:auto_task:"+live.getLiveId(), 1, TimeUnit.DAYS);
         });
+        String cacheKey = String.format(LiveKeysConstant.LIVE_DATA_CACHE, live.getLiveId());
+        redisCache.deleteObject(cacheKey);
+        String cacheKey2 = String.format(LiveKeysConstant.LIVE_FLAG_CACHE, live.getLiveId());
+        redisCache.deleteObject(cacheKey2);
 
+        // 将开启的直播间信息写入Redis缓存,用于打标签定时任务
+        try {
+            // 获取视频时长
+            Long videoDuration = 0L;
+            List<LiveVideo> videos = liveVideoService.listByLiveId(live.getLiveId(), 1);
+            if (CollUtil.isNotEmpty(videos)) {
+                videoDuration = videos.stream()
+                        .filter(v -> v.getDuration() != null)
+                        .mapToLong(LiveVideo::getDuration)
+                        .sum();
+            }
+
+            // 如果视频时长大于0,将直播间信息存入Redis
+            if (videoDuration > 0 && live.getStartTime() != null) {
+                Map<String, Object> tagMarkInfo = new HashMap<>();
+                tagMarkInfo.put("liveId", live.getLiveId());
+                tagMarkInfo.put("startTime", live.getStartTime().atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli());
+                tagMarkInfo.put("videoDuration", videoDuration);
+
+                String tagMarkKey = String.format(LiveKeysConstant.LIVE_TAG_MARK_CACHE, live.getLiveId());
+                redisCache.setCacheObject(tagMarkKey, JSON.toJSONString(tagMarkInfo), 24, TimeUnit.HOURS);
+                log.info("手动开直播间开启,已加入打标签缓存: liveId={}, startTime={}, videoDuration={}",
+                        live.getLiveId(), live.getStartTime(), videoDuration);
+            }
+        } catch (Exception e) {
+            log.error("手动开写入直播间打标签缓存失败: liveId={}, error={}", live.getLiveId(), e.getMessage(), e);
+        }
 
         return R.ok();
     }

+ 8 - 1
fs-service/src/main/java/com/fs/live/service/impl/LiveWatchUserServiceImpl.java

@@ -863,6 +863,9 @@ public class LiveWatchUserServiceImpl implements ILiveWatchUserService {
         //查询直播间的标签配置
         List<LiveTagItemVO> liveTagConfig = liveTagConfigMapper.getLiveTagListByliveId(liveId);
         log.info("处理直播间打标签: liveTagConfig={}", liveTagConfig);
+        if(null == liveTagConfig || liveTagConfig.isEmpty()){
+            return;
+        }
         /**
          * 8	回放已下单
          * 7	直播已下单
@@ -881,7 +884,10 @@ public class LiveWatchUserServiceImpl implements ILiveWatchUserService {
                 ));
         //查询直播间的看课记录
         List<LiveWatchLog> liveWatchLogs = liveWatchLogMapper.selectLiveWatchLogByLiveId(liveId);
-
+        log.info("处理直播间打标签: liveWatchLogs={}", liveWatchLogs);
+        if(null == liveWatchLogs || liveWatchLogs.isEmpty()){
+            return;
+        }
         //根据配置给每位用户打上标签
         List<HandleUserTagVO> handleUserTagVOS = new ArrayList<>();
         liveWatchLogs.forEach(liveLog -> {
@@ -936,6 +942,7 @@ public class LiveWatchUserServiceImpl implements ILiveWatchUserService {
             }
             handleUserTagVOS.add(addItem);
         });
+        log.info("处理直播间打标签最终打标签:{}",handleUserTagVOS);
         handleUserTags2Qw(handleUserTagVOS);
     }
 

+ 1 - 1
fs-service/src/main/java/com/fs/live/vo/LiveOrderListVo.java

@@ -136,7 +136,7 @@ public class LiveOrderListVo extends BaseEntity {
     private String isDel;
 
     /** 成本价 */
-    @Excel(name = "成本价")
+    @Excel(name = "成本价")
     private BigDecimal costPrice;
 
     /** 核销码 */

+ 1 - 1
fs-service/src/main/java/com/fs/live/vo/LiveOrderVoZm.java

@@ -118,7 +118,7 @@ public class LiveOrderVoZm{
     @Excel(name = "销售价格")
     private BigDecimal price;
 
-    @Excel(name = "成本价")
+    @Excel(name = "成本价")
     private BigDecimal cost;
 
     @Excel(name = "结算价格")

+ 1 - 1
fs-service/src/main/java/com/fs/live/vo/MergedOrderExportVO.java

@@ -56,7 +56,7 @@ public class MergedOrderExportVO implements Serializable
     private BigDecimal price;
 
     /** 成本价 */
-    @Excel(name = "成本价")
+    @Excel(name = "成本价")
     private BigDecimal cost;
 
     /** 商品金额 */

+ 1 - 1
fs-service/src/main/java/com/fs/store/vo/FsStoreProductScrmExportVO.java

@@ -118,7 +118,7 @@ public class FsStoreProductScrmExportVO implements Serializable {
     private BigDecimal giveIntegral;
 
     /** 成本价 */
-    @Excel(name = "成本价")
+    @Excel(name = "成本价")
     private BigDecimal cost;
 
     /** 是否优品推荐 */

+ 3 - 0
fs-service/src/main/java/com/fs/store/vo/h5/FsUserPageListVO.java

@@ -106,4 +106,7 @@ public class FsUserPageListVO {
     @ApiModelProperty(value = "是否购买 1:是 0 否")
     private BigDecimal isBuy;
 
+    // 项目会员 主键
+    private Long id;
+
 }

+ 1 - 1
fs-service/src/main/resources/application-config-druid-bjzm.yml

@@ -92,7 +92,7 @@ headerImg:
 
 ipad:
   ipadUrl: http://aipad.klbycp.com
-  aiApi: 1212121212
+  aiApi: http://49.232.181.28:3000/api
   voiceApi:
   commonApi:
 wx_miniapp_temp:

+ 2 - 2
fs-service/src/main/resources/application-config-druid-hcl.yml

@@ -90,8 +90,8 @@ tmp_secret_config:
 cloud_host:
   company_name: 恒春来
   projectCode: HCL
-  spaceName:
-  volcengineUrl:
+  spaceName: hcl-2114522511
+  volcengineUrl: https://hclvolcengine.ylrztop.com
 #看课授权时显示的头像
 headerImg:
   imgUrl: http://hcl-1b2b.obs.cn-south-1.myhuaweicloud.com/fs/20250815/1755228988455.png

+ 2 - 2
fs-service/src/main/resources/application-config-druid-hzyy.yml

@@ -90,8 +90,8 @@ tmp_secret_config:
 cloud_host:
   company_name: 弘珍医药
   projectCode: HZYY
-  spaceName:
-  volcengineUrl:
+  spaceName: hzyy-2114522511
+  volcengineUrl: https://hzyyvolcengine.ylrztop.com
 #看课授权时显示的头像
 headerImg:
   imgUrl: https://hzyy.obs.cn-north-4.myhuaweicloud.com/fs/20250616/1750067609692.png

+ 2 - 2
fs-service/src/main/resources/application-config-druid-jzzx.yml

@@ -94,8 +94,8 @@ tmp_secret_config:
 cloud_host:
   company_name: 九州在线
   projectCode: JZZX
-  spaceName:
-  volcengineUrl:
+  spaceName: jzzx-2114522511
+  volcengineUrl: https://jzzxvolcengine.ylrztop.com
 headerImg:
   imgUrl: https://jiuzhouzaixian.obs.cn-southwest-2.myhuaweicloud.com/fs/20250623/1750665141214.png
 ipad:

+ 2 - 2
fs-service/src/main/resources/application-config-druid-sczy.yml

@@ -94,8 +94,8 @@ tmp_secret_config:
 cloud_host:
   company_name: 四川致医
   projectCode: SCZY
-  spaceName:
-  volcengineUrl:
+  spaceName: sczy-2114522511
+  volcengineUrl: https://sczytvolcengine.ylrztop.com
 headerImg:
   imgUrl: https://jiuzhouzaixian.obs.cn-southwest-2.myhuaweicloud.com/fs/20250623/1750665141214.png
 ipad:

+ 1 - 1
fs-service/src/main/resources/application-druid-jnmy-test.yml

@@ -232,7 +232,7 @@ isNewWxMerchant: true
 ipad:
     url: http://localhost:8999/dev-api
     companyId: 13
-#wechat:
+wechat:
 #    company:
 #        appid: wxd7c1e221622a0ccf
 #        secret: 70d3ed4f8eb68cca0cf525b8ce07405d

+ 5 - 5
fs-service/src/main/resources/application-druid-shdn.yml

@@ -4,12 +4,12 @@ spring:
         include: config-druid-shdn,common
     # redis 配置
     redis:
-        host: 10.206.0.10
-        port: 6579
+        host: 172.17.0.10
+        port: 6379
         # 数据库索引
         database: 0
         # 密码
-        password:
+        password: !@#123QWe
         # 连接超时时间
         timeout: 30s
         lettuce:
@@ -39,7 +39,7 @@ spring:
             druid:
                 # 主库数据源
                 master:
-                  url: jdbc:mysql://172.17.0.12:65535/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
+                  url: jdbc:mysql://172.17.0.12:3306/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
                   username: root
                   password: QWEqwe123!@#
                 # 从库数据源
@@ -94,7 +94,7 @@ spring:
             druid:
                 # 主库数据源
                 master:
-                    url: jdbc:mysql://172.17.0.12:65535/fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
+                    url: jdbc:mysql://172.17.0.12:3306/fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
                     username: root
                     password: QWEqwe123!@#
                 # 初始连接数

+ 2 - 1
fs-service/src/main/resources/mapper/MerchantAppConfigMapper.xml

@@ -56,9 +56,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <where>
             <if test="merchantId != null and merchantId != ''"> and merchant_id = #{merchantId}</if>
             <if test="merchantType != null  and merchantType != ''"> and merchant_type = #{merchantType}</if>
-            <if test="appId != null  and appId != ''"> and app_id = #{appId}</if>
+            <if test="appId != null  and appId != ''"> and  FIND_IN_SET(#{appId},app_id)  </if>
             <if test="params.beginCreatedTime != null and params.beginCreatedTime != '' and params.endCreatedTime != null and params.endCreatedTime != ''"> and created_time between #{params.beginCreatedTime} and #{params.endCreatedTime}</if>
             <if test="isDeleted != null "> and is_deleted = #{isDeleted}</if>
+             <if test="id != null"> and id = #{id}</if>
         </where>
     </select>
 

+ 8 - 0
fs-service/src/main/resources/mapper/his/FsStoreOrderItemMapper.xml

@@ -47,6 +47,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{itemId}
         </foreach>
     </select>
+    <select id="selectPayPriceByYear" resultType="java.math.BigDecimal">
+        SELECT SUM(pay_price) AS total_pay
+        FROM fs_store_order
+        WHERE status NOT IN (1, -2)
+          AND user_id = #{userId}
+        AND pay_time >= CONCAT(YEAR(CURDATE()), '-01-01')
+        AND pay_time &lt; DATE_ADD(CONCAT(YEAR(CURDATE()), '-01-01'), INTERVAL 1 YEAR);
+    </select>
 
     <insert id="insertFsStoreOrderItem" parameterType="FsStoreOrderItem" useGeneratedKeys="true" keyProperty="itemId">
         insert into fs_store_order_item

+ 1 - 2
fs-service/src/main/resources/mapper/his/FsUserInformationCollectionMapper.xml

@@ -97,12 +97,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectFsUserInformationCollectionByDoctorType2"
             resultType="com.fs.his.domain.FsUserInformationCollection">
         select fui.*,fp.patient_name as patientName from fs_user_information_collection fui left join fs_patient fp on fui.patient_id = fp.patient_id
-            <where>
+            where fui.doctor_confirm = 1
                 <if test="maps.doctorType2Id != null and maps.doctorType2Id != ''"> and fui.doctor_type2_id = #{maps.doctorType2Id}</if>
                 <if test="maps.doctorType2Confirm != null"> and fui.doctor_type2_confirm = #{maps.doctorType2Confirm}</if>
                 <if test="maps.packageOrderCode != null  and maps.packageOrderCode != ''"> and fui.package_order_code = #{maps.packageOrderCode}</if>
                 <if test="maps.patientName != null  and maps.patientName != ''"> and fp.patient_name like concat(#{maps.patientName},"%")</if>
-            </where>
         order by id desc
     </select>
     <select id="selectFsUserInformationCollectionByDoctorType1"

+ 2 - 1
fs-service/src/main/resources/mapper/his/FsUserMapper.xml

@@ -358,9 +358,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         fs_user.nick_name as nickname,
         fs_user.avatar,
         fs_user.phone,
-        fs_user.status,
         fs_user.create_time,
         fs_user.remark,
+        ucu.id,
+        ucu.status,
         ucu.company_user_id,
         ucu.company_id,
         ucu.project_id,

+ 1 - 1
fs-service/src/main/resources/mapper/live/LiveAfterSalesMapper.xml

@@ -131,7 +131,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         left join live_order_item loi on loi.order_id = lo.order_id
         </if>
 
-        where 1=1 and las.status =4
+        where 1=1 and las.status =4 and lop.bank_transaction_id is not null
             <if test="hfOrderCode != null and hfOrderCode != ''"> and lop.pay_code = #{hfOrderCode}</if>
             <if test="liveId != null and liveId != ''"> and las.live_id = #{liveId}</if>
             <if test="companyUserNickName != null and companyUserNickName != ''"> and cu.nick_name like concat(#{companyUserNickName},'%')</if>

+ 1 - 1
fs-store/src/main/java/com/fs/store/vo/FsStoreProductStoreExcelVO.java

@@ -155,7 +155,7 @@ public class FsStoreProductStoreExcelVO extends BaseEntity {
     /**
      * 成本价
      */
-    @Excel(name = "成本价")
+    @Excel(name = "成本价")
     private BigDecimal costPrice;
 
 

+ 34 - 2
fs-user-app/src/main/java/com/fs/app/controller/FsUserInformationCollectionController.java

@@ -21,6 +21,8 @@ import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
 
+import static com.fs.his.utils.PhoneUtil.decryptPhone;
+
 
 /**
  * 用户信息采集Controller
@@ -51,8 +53,38 @@ public class FsUserInformationCollectionController extends AppBaseController
             return R.error("未查询到信息!");
         }
         String userId = getUserId();
-        if (vo.getUserId()!=null && vo.getUserId() != Long.parseLong(userId)){
-            return R.error("未查询您的采集信息!");
+        long nowUserId = Long.parseLong(userId);
+        if (vo.getUserId()!=null){
+            if (vo.getUserId() != nowUserId) {
+                //一对多 创建新的用户采集
+                FsUserInformationCollectionParam info = new FsUserInformationCollectionParam();
+                FsUser fsUser = fsUserService.selectFsUserById(nowUserId);
+                String nickName = fsUser.getNickname();
+                if (StringUtils.isNotBlank(nickName)){
+                    nickName = fsUser.getNickName();
+                }
+                String phone = fsUser.getPhone();
+                if (StringUtils.isNotBlank(phone)){
+                    phone = decryptPhone(phone);
+                }
+                BeanUtils.copyProperties(vo, info);
+                //一对多 这条记录已绑定用户 就重新创建一条
+                info.setId(null);
+                info.setUserId(nowUserId);
+                info.setPayType(null);
+                info.setAmount(null);
+                info.setDoctorSign(null);
+                info.setPackageOrderCode(null);
+                info.setSex(fsUser.getSex());
+                info.setUserName(nickName);
+                info.setUserPhoneFour(phone.substring(phone.length()-4));
+                info.setAllergy(null);
+                info.setRemark(null);
+                info.setPatientId(null);
+                Long newId = fsUserInformationCollectionService.insertFsUserInformationCollection(info);
+                vo = fsUserInformationCollectionService.selectFsUserInformationCollectionVoById(newId);//                return R.error("未查询您的采集信息!");
+            }
+
         }
         if (vo.getDoctorConfirm() == 0){
             vo.setPackageId(null);

+ 33 - 0
fs-user-app/src/main/java/com/fs/app/controller/PackageController.java

@@ -1,6 +1,7 @@
 package com.fs.app.controller;
 
 
+import cn.hutool.json.JSONUtil;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -15,6 +16,7 @@ import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.domain.FsUserCourseFavorite;
 import com.fs.course.param.FsUserCourseFollowUParam;
+import com.fs.his.config.StoreConfig;
 import com.fs.his.domain.*;
 import com.fs.his.param.*;
 import com.fs.his.service.*;
@@ -22,6 +24,7 @@ import com.fs.his.vo.FsDiseaseListUVO;
 import com.fs.his.vo.FsDoctorListUVO;
 import com.fs.his.vo.FsPackageDetailVO;
 import com.fs.his.vo.FsPackageListUVO;
+import com.fs.system.service.ISysConfigService;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.jsonwebtoken.Claims;
@@ -40,6 +43,7 @@ import javax.imageio.ImageIO;
 import javax.servlet.http.HttpServletRequest;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
+import java.math.BigDecimal;
 import java.net.URL;
 import java.util.Date;
 import java.util.List;
@@ -65,6 +69,11 @@ public class PackageController extends AppBaseController {
     private ICompanyUserService companyUserService;
     @Autowired
     private ICompanyService companyService;
+    @Autowired
+    private ISysConfigService configService;
+
+    @Autowired
+    private IFsStoreOrderService storeOrderService;
 
     @ApiOperation("获取套餐分类")
     @GetMapping("/getPackagCateList")
@@ -193,4 +202,28 @@ public class PackageController extends AppBaseController {
         }
     }
 
+    @ApiOperation("校验是否可继续下单")
+    @GetMapping("/checkContinue/{packageId}")
+    public R checkContinue(@PathVariable("packageId") Long packageId){
+        String userId = getUserId();
+        String json = configService.selectConfigByKey("his.store");
+        StoreConfig config = JSONUtil.toBean(json, StoreConfig.class);
+        BigDecimal canPayPrice = config.getCanPayPrice();
+        BigDecimal yearPayPrice = config.getYearPayPrice();
+        if(canPayPrice==null||yearPayPrice==null){
+            return R.ok();
+        }
+        String payPriceTip = config.getPayPriceTip();
+        //查询用户本年内支付处方订单+公私域套餐包总金额
+        FsPackage fsPackage = packageService.selectFsPackageByPackageId(packageId);
+        BigDecimal bigDecimal = storeOrderService.selectPayPriceByYear(userId);
+        if(bigDecimal==null){
+            return R.ok();
+        }
+        if (bigDecimal.compareTo(yearPayPrice)>=0&&fsPackage.getTotalPrice().compareTo(canPayPrice)>=0){
+            return R.ok().put("tip",payPriceTip);
+        }
+        return R.ok();
+    }
+
 }

+ 2 - 2
fs-user-app/src/main/java/com/fs/app/controller/course/CourseQwController.java

@@ -138,13 +138,13 @@ public class CourseQwController extends AppBaseController {
         return R.ok().put("data",course);
     }
 
-//    @Login
+    @Login
     @ApiOperation("h5课程详情加问答")
     @GetMapping("/getH5CourseVideoDetails")
     @UserOperationLog(operationType = FsUserOperationEnum.STUDY)
     public R getCourseVideoDetails(FsUserCourseVideoFinishUParam param)
     {
-//        param.setUserId(Long.parseLong(getUserId()));
+        param.setUserId(Long.parseLong(getUserId()));
         String json = configService.selectConfigByKey("course.config");
         CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
         FsUserCourseVideoH5DVO course = courseService.selectFsUserCourseVideoH5DVOByVideoId(param.getVideoId());