Selaa lähdekoodia

课程留言和介绍图片

yuhongqi 3 viikkoa sitten
vanhempi
commit
9bc984718c
32 muutettua tiedostoa jossa 396 lisäystä ja 22 poistoa
  1. 1 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseCommentController.java
  2. 72 0
      fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java
  3. 3 1
      fs-service/src/main/java/com/fs/course/enums/MiniProgramAgreementEnum.java
  4. 12 2
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseCommentMapper.java
  5. 1 1
      fs-service/src/main/java/com/fs/course/service/IFsUserCourseCommentService.java
  6. 65 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  7. 2 2
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseCommentServiceImpl.java
  8. 4 0
      fs-service/src/main/java/com/fs/course/vo/CourseStatisticsUserDetailVO.java
  9. 5 0
      fs-service/src/main/java/com/fs/erp/dto/ShopOrderDTO.java
  10. 56 0
      fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java
  11. 2 0
      fs-service/src/main/java/com/fs/his/config/AgreementConfig.java
  12. 2 0
      fs-service/src/main/java/com/fs/his/enums/FsUserIntegralLogTypeEnum.java
  13. 5 0
      fs-service/src/main/java/com/fs/hisStore/config/StoreConfig.java
  14. 3 0
      fs-service/src/main/java/com/fs/hisStore/domain/FsStoreOrderScrm.java
  15. 6 2
      fs-service/src/main/java/com/fs/hisStore/domain/FsStoreProductScrm.java
  16. 1 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderItemScrmMapper.java
  17. 5 0
      fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java
  18. 2 0
      fs-service/src/main/java/com/fs/hisStore/param/FsStoreProductAddEditParam.java
  19. 3 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsExpressScrmServiceImpl.java
  20. 44 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java
  21. 27 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  22. 3 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportVO.java
  23. 3 0
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderItemExportZMVO.java
  24. 1 1
      fs-service/src/main/java/com/fs/hisStore/vo/FsStoreOrderVO.java
  25. 1 1
      fs-service/src/main/resources/mapper/company/CompanyWithdrawDetailMapper.xml
  26. 12 1
      fs-service/src/main/resources/mapper/course/FsUserCourseCommentMapper.xml
  27. 12 0
      fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml
  28. 5 0
      fs-service/src/main/resources/mapper/hisStore/FsStoreProductScrmMapper.xml
  29. 13 5
      fs-user-app/src/main/java/com/fs/app/controller/CourseCommentController.java
  30. 6 0
      fs-user-app/src/main/java/com/fs/app/controller/H5Controller.java
  31. 13 5
      fs-user-app/src/main/java/com/fs/app/controller/store/CourseCommentScrmController.java
  32. 6 0
      fs-user-app/src/main/java/com/fs/app/controller/store/IndexScrmController.java

+ 1 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCourseCommentController.java

@@ -279,6 +279,7 @@ public class FsUserCourseCommentController extends BaseController
         FsUserCourseComment param = new FsUserCourseComment();
         param.setCourseId(courseId);
         param.setType(3L);
+        param.setIsDel(0);
         List<FsUserCourseComment> list = fsUserCourseCommentService.selectFsUserCourseCommentList(param);
         return getDataTable(list);
     }

+ 72 - 0
fs-admin/src/main/java/com/fs/hisStore/task/MallStoreTask.java

@@ -28,10 +28,13 @@ import com.fs.his.utils.ConfigUtil;
 import com.fs.hisStore.domain.*;
 import com.fs.hisStore.dto.DateComparisonConfigDTO;
 import com.fs.hisStore.enums.ShipperCodeEnum;
+import com.fs.his.enums.FsUserIntegralLogTypeEnum;
 import com.fs.hisStore.mapper.FsStoreOrderItemScrmMapper;
 import com.fs.hisStore.mapper.FsStoreOrderScrmMapper;
 import com.fs.hisStore.mapper.FsStorePaymentScrmMapper;
 import com.fs.hisStore.mapper.FsStoreProductAttrValueScrmMapper;
+import com.fs.hisStore.mapper.FsUserScrmMapper;
+import com.fs.hisStore.mapper.FsUserIntegralLogsScrmMapper;
 import com.fs.hisStore.param.*;
 import com.fs.hisStore.service.*;
 import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
@@ -43,6 +46,7 @@ import com.fs.pay.pay.dto.OrderQueryDTO;
 import com.fs.pay.service.IPayService;
 import com.fs.store.config.StoreConfig;
 import com.fs.system.service.ISysConfigService;
+import cn.hutool.json.JSONUtil;
 import com.fs.ybPay.domain.OrderResult;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
@@ -154,6 +158,12 @@ public class MallStoreTask
     @Autowired
     private ConfigUtil configUtil;
 
+    @Autowired
+    private FsUserScrmMapper fsUserScrmMapper;
+
+    @Autowired
+    private FsUserIntegralLogsScrmMapper fsUserIntegralLogsScrmMapper;
+
     @Autowired
     private FsCourseRedPacketLogMapper fsCourseRedPacketLogMapper;
 
@@ -748,4 +758,66 @@ public class MallStoreTask
         }
     }
 
+    /**
+     * 购物积分发放定时任务
+     * 查询条件:订单状态为已收货(2)或交易完成(3),超过7天,创建时间在2026-05-1以后,
+     *           是否领取购物积分=0,已支付,商城订单(order_type=1)
+     * 积分规则:用户实际支付金额(pay_money) 1:1 向下取整折算积分(eg: 19.9元=19积分)
+     */
+    public void grantShoppingPoints() {
+        // 检查配置开关
+        String storeConfigJson = configService.selectConfigByKey("his.store");
+        if (StringUtils.isBlank(storeConfigJson)) {
+            return;
+        }
+        com.fs.hisStore.config.StoreConfig storeConfig = JSONUtil.toBean(storeConfigJson, com.fs.hisStore.config.StoreConfig.class);
+        if (storeConfig == null || !Boolean.TRUE.equals(storeConfig.getEnableShoppingPoints())) {
+            return;
+        }
+
+        // 查询符合条件的订单(SQL直接过滤)
+        String cutoffDate = "2026-05-01 00:00:00";
+        String sevenDaysAgo = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(DateUtils.addDays(new Date(), -7));
+        List<FsStoreOrderScrm> orders = fsStoreOrderMapper.selectShoppingPointsPendingOrders(cutoffDate, sevenDaysAgo);
+
+        int count = 0;
+        for (FsStoreOrderScrm order : orders) {
+            // 用户ID为空跳过
+            if (order.getUserId() == null) continue;
+
+            // 计算积分:pay_money 向下取整
+            long points = order.getPayMoney().setScale(0, BigDecimal.ROUND_DOWN).longValue();
+            if (points <= 0) continue;
+
+            try {
+                // 发放积分
+                fsUserScrmMapper.incrIntegral(order.getUserId(), BigDecimal.valueOf(points));
+
+                // 记录积分日志 logType=5 购物积分
+                FsUserIntegralLogsScrm logs = new FsUserIntegralLogsScrm();
+                logs.setUserId(order.getUserId());
+                logs.setIntegral(BigDecimal.valueOf(points));
+                logs.setLogType(FsUserIntegralLogTypeEnum.TYPE_30.getValue()); // 购物积分发放
+                logs.setStatus(1);
+                logs.setBusinessId(String.valueOf(order.getId()));
+                logs.setBusinessType(1); // 商城订单
+                logs.setCreateTime(new Date());
+                FsUserScrm user = fsUserScrmMapper.selectFsUserByUserId(order.getUserId());
+                logs.setBalance(user != null && user.getIntegral() != null ? BigDecimal.valueOf(user.getIntegral()) : BigDecimal.valueOf(points));
+                fsUserIntegralLogsScrmMapper.insertFsUserIntegralLogs(logs);
+
+                // 更新订单为已领取
+                FsStoreOrderScrm updateOrder = new FsStoreOrderScrm();
+                updateOrder.setId(order.getId());
+                updateOrder.setShoppingPointsClaimed(1);
+                fsStoreOrderMapper.updateFsStoreOrder(updateOrder);
+
+                count++;
+            } catch (Exception e) {
+                log.error("购物积分发放异常,订单ID={}", order.getId(), e);
+            }
+        }
+        log.info("购物积分发放完成,本次发放{}笔订单", count);
+    }
+
 }

+ 3 - 1
fs-service/src/main/java/com/fs/course/enums/MiniProgramAgreementEnum.java

@@ -9,7 +9,9 @@ public enum MiniProgramAgreementEnum {
     USER_HEALTH("userHealth", "健康客服协议"),
     VIP_SERVICE("vipService", "会员服务协议"),
     VIP_AUTOMATIC_SERVICE("vipAutomaticService", "会员自动续费协议"),
-    USER_REMOVE_SERVICE("userRemoveService", "用户注销协议");
+    USER_REMOVE_SERVICE("userRemoveService", "用户注销协议"),
+    APP_USER_AGREEMENT("appUserAgreement", "APP用户协议"),
+    APP_PRIVACY_AGREEMENT("appPrivacyAgreement", "APP隐私协议");
     private String code;
     private String message;
     MiniProgramAgreementEnum(String code, String message) {

+ 12 - 2
fs-service/src/main/java/com/fs/course/mapper/FsUserCourseCommentMapper.java

@@ -98,8 +98,13 @@ public interface FsUserCourseCommentMapper
             "<if test=\"sortType != null and sortType == 2\"> order by c.comment_id desc </if>" +
             "</script>"})
     List<FsUserCourseCommentListUVO> selectFsUserCourseCommentListUVO(FsUserCourseCommentUParam param);
-    @Select("select c.*,u.nick_name,u.avatar,tu.nick_name to_nick_name from fs_user_course_comment c LEFT JOIN fs_user u ON c.user_id=u.user_id LEFT  JOIN fs_user tu ON tu.user_id =c.to_user_id where c.user_id=#{userId}")
-    List<FsUserCourseCommentListUVO> selectFsUserCourseCommentMyListUVO(Long userId);
+    @Select({"<script> " +
+            "select c.*,u.nick_name,u.avatar,tu.nick_name to_nick_name from fs_user_course_comment c LEFT JOIN fs_user u ON c.user_id=u.user_id LEFT  JOIN fs_user tu ON tu.user_id =c.to_user_id " +
+            "where c.user_id=#{userId} " +
+            "<if test='courseId != null'> and c.course_id = #{courseId}</if>" +
+            " order by c.create_time desc" +
+            "</script>"})
+    List<FsUserCourseCommentListUVO> selectFsUserCourseCommentMyListUVO(FsUserCourseCommentUParam param);
 
     @Update("update fs_user_course_comment set likes=likes+1 where comment_id=#{commentId}")
     int addLikes(Long commentId);
@@ -147,4 +152,9 @@ public interface FsUserCourseCommentMapper
     List<FsUserCourseCommentListUVO> selectFsUserCourseCommentListUVOAll(FsUserCourseCommentUParam param);
 
     List<FsUserCourseComment> selectFsUserCourseCommentListByTypeAndCourseId(@Param("type") int type,@Param("courseId") Long courseId);
+
+    /**
+     * 按课程ID和用户ID列表批量查询评论(type=1,未删除)
+     */
+    List<FsUserCourseComment> selectCommentsByCourseIdAndUserIds(@Param("courseId") Long courseId, @Param("userIds") List<Long> userIds);
 }

+ 1 - 1
fs-service/src/main/java/com/fs/course/service/IFsUserCourseCommentService.java

@@ -75,7 +75,7 @@ public interface IFsUserCourseCommentService
 
     List<FsUserCourseCommentReplyListUVO> selectFsUserCourseCommentReplyListUVO(Long commentId, String userId);
 
-    List<FsUserCourseCommentListUVO> selectFsUserCourseCommentMyListUVO(Long userId);
+    List<FsUserCourseCommentListUVO> selectFsUserCourseCommentMyListUVO(FsUserCourseCommentUParam param);
 
     int updateLikes(Long commentId, Integer type);
 

+ 65 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java

@@ -189,6 +189,9 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
     @Autowired
     private FsCourseAnswerLogsMapper fsCourseAnswerLogsMapper;
 
+    @Autowired
+    private FsUserCourseCommentMapper fsUserCourseCommentMapper;
+
     @Autowired
     private FsUserMapper userMapper;
 
@@ -1915,6 +1918,8 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         }
         List<CourseStatisticsUserDetailVO> list = fsCourseWatchLogMapper.selectCourseStatisticsUserDetailExportList(param);
         applyCourseRatingAnswerDisplay(list);
+        // 查询用户留言并填充
+        applyUserComments(list, param.getVideoId());
         return list;
     }
 
@@ -1934,6 +1939,66 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         }
     }
 
+    /**
+     * 查询用户留言并填充到导出VO中
+     * 1. 通过videoId获取courseId
+     * 2. 收集所有userId,批量查询评论
+     * 3. 按userId分组,拼接评论内容填充到对应VO
+     */
+    private void applyUserComments(List<CourseStatisticsUserDetailVO> list, Long videoId) {
+        if (CollectionUtils.isEmpty(list)) {
+            return;
+        }
+        // 通过videoId获取courseId
+        FsUserCourseVideo video = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId(videoId);
+        if (video == null || video.getCourseId() == null) {
+            return;
+        }
+        Long courseId = video.getCourseId();
+
+        // 收集所有userId
+        List<Long> userIds = list.stream()
+                .map(CourseStatisticsUserDetailVO::getUserId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+        if (userIds.isEmpty()) {
+            return;
+        }
+
+        // 批量查询评论(type=1 评论,is_del=0 未删除)
+        List<FsUserCourseComment> comments = fsUserCourseCommentMapper.selectCommentsByCourseIdAndUserIds(courseId, userIds);
+        if (CollectionUtils.isEmpty(comments)) {
+            return;
+        }
+
+        // 按userId分组,拼接评论内容
+        Map<Long, List<FsUserCourseComment>> commentMap = comments.stream()
+                .collect(Collectors.groupingBy(FsUserCourseComment::getUserId));
+
+        for (CourseStatisticsUserDetailVO vo : list) {
+            if (vo == null || vo.getUserId() == null) {
+                continue;
+            }
+            List<FsUserCourseComment> userComments = commentMap.get(vo.getUserId());
+            if (userComments != null && !userComments.isEmpty()) {
+                SimpleDateFormat commentFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+                String joined = userComments.stream()
+                        .map(c -> {
+                            String content = c.getContent();
+                            if (content == null || content.trim().isEmpty()) {
+                                return "";
+                            }
+                            String timePrefix = c.getCreateTime() != null ? "[" + commentFmt.format(c.getCreateTime()) + "] " : "";
+                            return timePrefix + content.trim();
+                        })
+                        .filter(s -> !s.isEmpty())
+                        .collect(Collectors.joining(";"));
+                vo.setUserComment(joined);
+            }
+        }
+    }
+
     @Override
     public List<AppWatchLogReportVO> selectUserAppWatchLogReportVO(FsCourseWatchLogStatisticsListParam param) {
         if (StringUtils.isNotEmpty(param.getUserPhone())) {

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

@@ -128,8 +128,8 @@ public class FsUserCourseCommentServiceImpl implements IFsUserCourseCommentServi
     }
 
     @Override
-    public List<FsUserCourseCommentListUVO> selectFsUserCourseCommentMyListUVO(Long userId) {
-        return fsUserCourseCommentMapper.selectFsUserCourseCommentMyListUVO(userId);
+    public List<FsUserCourseCommentListUVO> selectFsUserCourseCommentMyListUVO(FsUserCourseCommentUParam param) {
+        return fsUserCourseCommentMapper.selectFsUserCourseCommentMyListUVO(param);
     }
 
     @Override

+ 4 - 0
fs-service/src/main/java/com/fs/course/vo/CourseStatisticsUserDetailVO.java

@@ -37,4 +37,8 @@ public class CourseStatisticsUserDetailVO implements Serializable {
      */
     @Excel(name = "课程评分", width = 60)
     private String courseRating;
+
+    /** 用户留言(该课程下该用户的所有评论拼接) */
+    @Excel(name = "用户留言", width = 80)
+    private String userComment;
 }

+ 5 - 0
fs-service/src/main/java/com/fs/erp/dto/ShopOrderDTO.java

@@ -26,6 +26,11 @@ public class ShopOrderDTO implements Serializable {
      */
     private String soId;
 
+    /**
+     * 最晚发货时间;发货前可更新
+     */
+    private String planDeliveryDate;
+
     /**
      * 订单日期
      */

+ 56 - 0
fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java

@@ -271,6 +271,7 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
 
         // 订单商品项列表
         List<OrderItemDTO> itemDTOList = new ArrayList<>();
+        String presaleShippingTime = null; // 预售最晚发货时间
 
         List<FsStoreOrderItemVO> fsStoreOrderItemVOS = fsStoreOrderItemScrmService.selectFsStoreOrderItemListByOrderId(fsStoreOrder.getId());
         log.info("fsStoreOrderItemVOS==========>{}",fsStoreOrderItemVOS);
@@ -296,9 +297,36 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
             orderItemDTO.setQty(item.getNum().intValue());
             orderItemDTO.setOuterOiId(String.format("%s%s",fsStoreOrder.getOrderCode(),item.getItemId()));
             itemDTOList.add(orderItemDTO);
+
+            // 检查商品是否有presale标签,收集最晚发货时间
+            try {
+                if (StringUtils.isNotBlank(fsStoreProduct.getTagInfo())) {
+                    JSONObject tagObj = JSON.parseObject(fsStoreProduct.getTagInfo());
+                    Object typeObj = tagObj.get("type");
+                    boolean isPresale = false;
+                    if (typeObj instanceof com.alibaba.fastjson.JSONArray) {
+                        isPresale = ((com.alibaba.fastjson.JSONArray) typeObj).contains("presale");
+                    } else if (typeObj instanceof String) {
+                        isPresale = "presale".equals(typeObj);
+                    }
+                    if (isPresale && tagObj.getString("shippingTime") != null) {
+                        String shippingTime = tagObj.getString("shippingTime");
+                        if (presaleShippingTime == null || shippingTime.compareTo(presaleShippingTime) > 0) {
+                            presaleShippingTime = shippingTime;
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                log.error("检查商品presale标签失败:{}", e.getMessage());
+            }
         }
         shopOrderDTO.setItems(itemDTOList);
 
+        // 如果有预售发货时间,设置到planDeliveryDate
+        if (presaleShippingTime != null) {
+            shopOrderDTO.setPlanDeliveryDate(presaleShippingTime);
+        }
+
         // 实际支付金额
         PaymentDTO paymentDTO = new PaymentDTO();
         paymentDTO.setAmount(fsStoreOrder.getPayMoney().doubleValue());
@@ -397,6 +425,7 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
 
         // 订单商品项列表
         List<OrderItemDTO> itemDTOList = new ArrayList<>();
+        String presaleShippingTime = null; // 预售最晚发货时间
 
         List<LiveOrderItem> liveOrderItems = liveOrderItemMapper.selectLiveOrderItemByOrderId(liveOrder.getOrderId());
         log.info("liveOrderItems==========>{}",liveOrderItems);
@@ -422,9 +451,36 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
             orderItemDTO.setQty(item.getNum().intValue());
             orderItemDTO.setOuterOiId(String.format("%s%s",liveOrder.getOrderCode(),item.getItemId()));
             itemDTOList.add(orderItemDTO);
+
+            // 检查商品是否有presale标签,收集最晚发货时间
+            try {
+                if (StringUtils.isNotBlank(fsStoreProduct.getTagInfo())) {
+                    JSONObject tagObj = JSON.parseObject(fsStoreProduct.getTagInfo());
+                    Object typeObj = tagObj.get("type");
+                    boolean isPresale = false;
+                    if (typeObj instanceof com.alibaba.fastjson.JSONArray) {
+                        isPresale = ((com.alibaba.fastjson.JSONArray) typeObj).contains("presale");
+                    } else if (typeObj instanceof String) {
+                        isPresale = "presale".equals(typeObj);
+                    }
+                    if (isPresale && tagObj.getString("shippingTime") != null) {
+                        String shippingTime = tagObj.getString("shippingTime");
+                        if (presaleShippingTime == null || shippingTime.compareTo(presaleShippingTime) > 0) {
+                            presaleShippingTime = shippingTime;
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                log.error("检查商品presale标签失败:{}", e.getMessage());
+            }
         }
         shopOrderDTO.setItems(itemDTOList);
 
+        // 如果有预售发货时间,设置到planDeliveryDate
+        if (presaleShippingTime != null) {
+            shopOrderDTO.setPlanDeliveryDate(presaleShippingTime);
+        }
+
         // 实际支付金额
         PaymentDTO paymentDTO = new PaymentDTO();
         paymentDTO.setAmount(liveOrder.getPayMoney().doubleValue());

+ 2 - 0
fs-service/src/main/java/com/fs/his/config/AgreementConfig.java

@@ -12,6 +12,8 @@ public class AgreementConfig implements Serializable {
     String vipService;
     String vipAutomaticService;
     String userRemoveService;
+    String appUserAgreement;
+    String appPrivacyAgreement;
     String doctorRegister;
     String doctorFiling;
 

+ 2 - 0
fs-service/src/main/java/com/fs/his/enums/FsUserIntegralLogTypeEnum.java

@@ -37,6 +37,8 @@ public enum FsUserIntegralLogTypeEnum {
     TYPE_27(27, "积分订单取消退回积分"),
     TYPE_28(28, "首次下载APP获取积分"),
     TYPE_29( 29,"玩游戏获取积分"),
+    TYPE_30(30, "购物积分发放"),
+    TYPE_31(31, "购物积分退款扣除"),
     ;
 
 

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/config/StoreConfig.java

@@ -59,4 +59,9 @@ public class StoreConfig implements Serializable {
      * 是否开启积分订单手机号解密
      */
     private Boolean enableIntegralOrderPhoneDecrypt;
+
+    /**
+     * 是否开启购物积分(用户实际支付金额1:1向下取整折算积分)
+     */
+    private Boolean enableShoppingPoints;
 }

+ 3 - 0
fs-service/src/main/java/com/fs/hisStore/domain/FsStoreOrderScrm.java

@@ -413,4 +413,7 @@ public class FsStoreOrderScrm extends BaseEntity
 
     @TableField(exist = false)
     private String auditReasonName;
+
+    /** 是否领取购物积分 0-未领取 1-已领取 */
+    private Integer shoppingPointsClaimed;
 }

+ 6 - 2
fs-service/src/main/java/com/fs/hisStore/domain/FsStoreProductScrm.java

@@ -2,6 +2,7 @@ package com.fs.hisStore.domain;
 
 import java.math.BigDecimal;
 import java.util.Date;
+import java.util.List;
 
 import com.baomidou.mybatisplus.annotation.FieldStrategy;
 import com.baomidou.mybatisplus.annotation.TableField;
@@ -381,7 +382,10 @@ public class FsStoreProductScrm extends BaseEntity
     /** 过滤商品id */
     private Long[] excludeProductIds;
 
-    /** 标签信息(JSON格式,存储标签类型及预售发货时间,如 {"type":"presale","shippingTime":"2024-01-15 12:00:00"}) */
-    @Excel(name = "标签信息")
+    /** 标签信息(JSON格式,存储标签类型及预售发货时间,如 {"type":["no_commission","presale"],"shippingTime":"2026-05-17 00:00:00"} */
+//    @Excel(name = "标签信息")
     private String tagInfo;
+
+    /** 商品类型多选查询(用于IN查询) */
+    private List<Integer> productTypes;
 }

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

@@ -80,6 +80,7 @@ public interface FsStoreOrderItemScrmMapper
             " o.order_code, o.pay_price, o.pay_money, o.deduction_price,o.pay_delivery, o.order_type,psps.price " +
             ", CASE o.is_audit WHEN 1 THEN '是' ELSE '否' END AS isAudit " +
             ", sas.audit_remark as auditRemark, sas.reason_level1_text as reasonValue1, sas.reason_level2_text as reasonValue2 " +
+            ", o.delivery_type" +
             " from fs_store_order_item_scrm i " +
             " left join fs_store_order_scrm o on o.id=i.order_id" +
             " left join fs_user u on o.user_id=u.user_id  " +

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/FsStoreOrderScrmMapper.java

@@ -53,6 +53,11 @@ public interface FsStoreOrderScrmMapper
      * @return 订单集合
      */
     public List<FsStoreOrderScrm> selectFsStoreOrderList(FsStoreOrderScrm fsStoreOrder);
+    
+        /**
+         * 查询待发放购物积分的订单列表
+         */
+        List<FsStoreOrderScrm> selectShoppingPointsPendingOrders(@Param("cutoffDate") String cutoffDate, @Param("sevenDaysAgo") String sevenDaysAgo);
 
     /**
      * 新增订单

+ 2 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreProductAddEditParam.java

@@ -309,4 +309,6 @@ public class FsStoreProductAddEditParam implements Serializable
 
     /** 商品标签ID列表(最多3个) */
     private List<Long> tagIds;
+
+    private String tagInfo;
 }

+ 3 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsExpressScrmServiceImpl.java

@@ -157,6 +157,9 @@ public class FsExpressScrmServiceImpl implements IFsExpressScrmService
 
             //根据公司业务处理返回的信息......
             ExpressInfoDTO dto=JSONUtil.toBean(result,ExpressInfoDTO.class);
+            if (!dto.isSuccess()) {
+                logger.error("查询快递信息失败:{}:{}", result, requestData);
+            }
             return dto;
         } catch (Exception e) {
             throw  new CustomException(e.getMessage());

+ 44 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java

@@ -36,6 +36,7 @@ import com.fs.his.domain.*;
 import com.fs.his.enums.FsStoreAfterSalesStatusEnum;
 import com.fs.his.enums.FsStoreOrderLogEnum;
 import com.fs.his.enums.FsStoreOrderStatusEnum;
+import com.fs.his.enums.FsUserIntegralLogTypeEnum;
 import com.fs.his.mapper.*;
 import com.fs.his.service.IFsStoreOrderLogsService;
 import com.fs.his.utils.ConfigUtil;
@@ -157,6 +158,12 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
     @Autowired
     private ISysConfigService configService;
 
+    @Autowired
+    private FsUserScrmMapper fsUserScrmMapper;
+
+    @Autowired
+    private FsUserIntegralLogsScrmMapper fsUserIntegralLogsScrmMapper;
+
     @Autowired
     private RedisCache redisCache;
 
@@ -928,6 +935,43 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
         order.setRefundStatus(OrderInfoEnum.REFUND_STATUS_2.getValue());
         orderService.updateFsStoreOrder(order);
 
+        // 购物积分扣除逻辑:如果该订单已领取购物积分,退款时需扣除
+        if (order.getShoppingPointsClaimed() != null && order.getShoppingPointsClaimed() == 1
+                && order.getPayMoney() != null && order.getPayMoney().compareTo(BigDecimal.ZERO) > 0) {
+            try {
+                // 计算应扣除的购物积分(与发放时一致:pay_money向下取整)
+                long pointsToDeduct = order.getPayMoney().setScale(0, BigDecimal.ROUND_DOWN).longValue();
+                if (pointsToDeduct > 0) {
+                    // 查询用户当前积分,积分不足则扣除全部积分
+                    FsUserScrm user = fsUserScrmMapper.selectFsUserByUserId(order.getUserId());
+                    long currentIntegral = (user != null && user.getIntegral() != null) ? user.getIntegral() : 0L;
+                    long actualDeduct = Math.min(pointsToDeduct, currentIntegral);
+
+                    if (actualDeduct > 0) {
+                        // 扣除用户积分
+                        fsUserScrmMapper.decIntegral(order.getUserId(), actualDeduct);
+
+                        // 记录积分日志 logType=6 购物积分退款扣除
+                        FsUserIntegralLogsScrm logs = new FsUserIntegralLogsScrm();
+                        logs.setUserId(order.getUserId());
+                        logs.setIntegral(BigDecimal.valueOf(-actualDeduct));
+                        logs.setLogType(FsUserIntegralLogTypeEnum.TYPE_31.getValue()); // 购物积分退款扣除
+                        logs.setStatus(1);
+                        logs.setBusinessId(String.valueOf(order.getId()));
+                        logs.setBusinessType(1);
+                        logs.setCreateTime(new Date());
+                        long newBalance = Math.max(currentIntegral - actualDeduct, 0);
+                        logs.setBalance(BigDecimal.valueOf(newBalance));
+                        fsUserIntegralLogsScrmMapper.insertFsUserIntegralLogs(logs);
+
+                        logger.info("购物积分退款扣除,订单ID={},应扣={},实扣={}", order.getId(), pointsToDeduct, actualDeduct);
+                    }
+                }
+            } catch (Exception e) {
+                logger.error("购物积分退款扣除异常,订单ID={}", order.getId(), e);
+            }
+        }
+
         BigDecimal refundAmount = param.getRefundAmount();
         if (Objects.isNull(refundAmount)) {
             throw new CustomException("退款金额不能为空");

+ 27 - 1
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -2644,7 +2644,33 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
             //写入公司余额 条件是只有全款订单才分,非全款后台导入
             if (order.getCompanyId() != null && order.getCompanyId() > 0 && order.getPayDelivery().compareTo(new BigDecimal(0)) == 0) {
                 if (order.getTuiMoneyStatus() == null || order.getTuiMoneyStatus() != 1) {
-                    companyService.addCompanyMoney(order);
+                    // 检查商品是否有"不分润"标签,有则不增加公司余额
+                    boolean noCommission = false;
+                    try {
+                        if (StringUtils.isNotBlank(order.getItemJson())) {
+                            com.alibaba.fastjson.JSONArray itemArray = JSON.parseArray(order.getItemJson());
+                            if (itemArray != null && !itemArray.isEmpty()) {
+                                Long productId = itemArray.getJSONObject(0).getLong("productId");
+                                if (productId != null) {
+                                    FsStoreProductScrm product = productService.selectFsStoreProductById(productId);
+                                    if (product != null && StringUtils.isNotBlank(product.getTagInfo())) {
+                                        JSONObject tagObj = JSON.parseObject(product.getTagInfo());
+                                        Object typeObj = tagObj.get("type");
+                                        if (typeObj instanceof com.alibaba.fastjson.JSONArray) {
+                                            noCommission = ((com.alibaba.fastjson.JSONArray) typeObj).contains("no_commission");
+                                        } else if (typeObj instanceof String) {
+                                            noCommission = "no_commission".equals(typeObj);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.error("检查商品不分润标签失败:{},订单号:{}", e.getMessage(), order.getOrderCode());
+                    }
+                    if (!noCommission) {
+                        companyService.addCompanyMoney(order);
+                    }
                 }
             }
             //套餐包赠送积分

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

@@ -123,6 +123,9 @@ public class FsStoreOrderItemExportVO implements Serializable
     @Excel(name = "是否审核")
     private String isAudit;
 
+    @Excel(name = "物流跟踪状态" , dictType = "store_order_delivery_type")
+    private String deliveryType;
+
 
     private BigDecimal payPrice;// 应付金额
     private BigDecimal deductionPrice;// 应付金额

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

@@ -104,6 +104,9 @@ public class FsStoreOrderItemExportZMVO implements Serializable {
     @Excel(name = "银行交易流水号")
     private String bankTransactionId;
 
+    @Excel(name = "物流跟踪状态" , dictType = "store_order_delivery_type")
+    private String deliveryType;
+
 
     /** 商品分类(与 MergedOrderVO 一致,不导出) */
 

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

@@ -172,7 +172,7 @@ public class FsStoreOrderVO implements Serializable
     private String deliveryName;
 
     /** 发货类型 */
-    @Excel(name = "发货类型")
+    @Excel(name = "物流跟踪状态", dictType = "store_order_delivery_type")
     private String deliveryType;
 
     /** 快递单号/手机号 */

+ 1 - 1
fs-service/src/main/resources/mapper/company/CompanyWithdrawDetailMapper.xml

@@ -98,7 +98,7 @@
     </sql>
 
     <sql id="withdrawDetailBaseWhere">
-        WHERE l.create_time &gt;= '2026-03-27 00:00:00'
+        WHERE l.create_time &gt;= '2026-04-01 00:00:00'
           AND l.logs_type IN (1, 2, 3, 4, 5, 6, 7, 8)
           AND NOT (l.logs_type = 5 AND IFNULL(l.remark, '') = '订单佣金冻结')
     </sql>

+ 12 - 1
fs-service/src/main/resources/mapper/course/FsUserCourseCommentMapper.xml

@@ -34,6 +34,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="content != null  and content != ''"> and content = #{content}</if>
             <if test="replyCount != null "> and reply_count = #{replyCount}</if>
             <if test="likes != null "> and likes = #{likes}</if>
+            <if test="isDel != null "> and is_del = #{isDel}</if>
         </where>
     </select>
 
@@ -105,6 +106,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </update>
 
     <select id="selectFsUserCourseCommentListByTypeAndCourseId" resultType="com.fs.course.domain.FsUserCourseComment">
-        select * from fs_user_course_comment where type = #{type} and course_id = #{courseId}
+        select * from fs_user_course_comment where type = #{type} and course_id = #{courseId} and is_del = 0 order by create_time desc
+    </select>
+
+    <select id="selectCommentsByCourseIdAndUserIds" resultMap="FsUserCourseCommentResult">
+        <include refid="selectFsUserCourseCommentVo"/>
+        WHERE course_id = #{courseId} AND type = 1 AND is_del = 0
+        AND user_id IN
+        <foreach item="userId" collection="userIds" open="(" separator="," close=")">
+            #{userId}
+        </foreach>
+        ORDER BY user_id, create_time ASC
     </select>
 </mapper>

+ 12 - 0
fs-service/src/main/resources/mapper/hisStore/FsStoreOrderScrmMapper.xml

@@ -93,6 +93,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="backendEditProductType"    column="backend_edit_product_type"    />
         <result property="virtualPhone"    column="virtual_phone"    />
         <result property="groupBuyId"    column="group_buy_id"    />
+        <result property="shoppingPointsClaimed"    column="shopping_points_claimed"    />
     </resultMap>
 
     <sql id="selectFsStoreOrderVo">
@@ -164,6 +165,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </where>
     </select>
 
+    <select id="selectShoppingPointsPendingOrders" resultMap="FsStoreOrderResult">
+        <include refid="selectFsStoreOrderVo"/>
+        WHERE  paid = 1
+          AND status = 4
+          AND (shopping_points_claimed IS NULL OR shopping_points_claimed = 0)
+          AND create_time &gt;= #{cutoffDate}
+          AND create_time &lt;= #{sevenDaysAgo}
+          AND pay_money &gt; 0
+    </select>
+
     <select id="selectFsStoreOrderById" parameterType="Long" resultMap="FsStoreOrderResult">
         <include refid="selectFsStoreOrderVo"/>
         where id = #{id}
@@ -469,6 +480,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="courseId != null">course_id = #{courseId},</if>
             <if test="virtualPhone != null">virtual_phone = #{virtualPhone},</if>
             <if test="groupBuyId != null">group_buy_id = #{groupBuyId},</if>
+            <if test="shoppingPointsClaimed != null">shopping_points_claimed = #{shoppingPointsClaimed},</if>
         </trim>
         where id = #{id}
     </update>

+ 5 - 0
fs-service/src/main/resources/mapper/hisStore/FsStoreProductScrmMapper.xml

@@ -152,6 +152,11 @@
             <if test="isIntegral != null "> and is_integral = #{isIntegral}</if>
             <if test="integral != null "> and integral = #{integral}</if>
             <if test="productType != null "> and product_type = #{productType}</if>
+            <if test="productTypes != null and productTypes.size() > 0"> and product_type IN
+                <foreach item="pt" collection="productTypes" open="(" separator="," close=")">
+                    #{pt}
+                </foreach>
+            </if>
             <if test="prescribeCode != null  and prescribeCode != ''"> and prescribe_code = #{prescribeCode}</if>
             <if test="prescribeSpec != null  and prescribeSpec != ''"> and prescribe_spec = #{prescribeSpec}</if>
             <if test="prescribeFactory != null  and prescribeFactory != ''"> and prescribe_factory = #{prescribeFactory}</if>

+ 13 - 5
fs-user-app/src/main/java/com/fs/app/controller/CourseCommentController.java

@@ -129,9 +129,16 @@ public class CourseCommentController extends AppBaseController
     {
         param.setUserId(Long.parseLong(getUserId()));
         PageHelper.startPage(param.getPageNum(), param.getPageSize());
-        List<FsUserCourseCommentListUVO> list = fsUserCourseCommentService.selectFsUserCourseCommentMyListUVO(Long.parseLong(getUserId()));
+        List<FsUserCourseCommentListUVO> list = fsUserCourseCommentService.selectFsUserCourseCommentMyListUVO(param);
         PageInfo<FsUserCourseCommentListUVO> listPageInfo=new PageInfo<>(list);
-        return R.ok().put("data",listPageInfo);
+        List<FsUserCourseComment> fsUserCourseComments = new ArrayList<>();
+        PageInfo<FsUserCourseComment> defaultPageInfo= new PageInfo<>(fsUserCourseComments);
+        try {
+            PageHelper.startPage(param.getPageNum(), param.getPageSize());
+            fsUserCourseComments = fsUserCourseCommentService.selectFsUserCourseCommentListByTypeAndCourseId(3, param.getCourseId());
+            defaultPageInfo = new PageInfo<>(fsUserCourseComments);
+        } catch (Exception ignore) {}
+        return R.ok().put("data",listPageInfo).put("defaultPageInfo",defaultPageInfo);
     }
     /**
      * 新增课堂评论
@@ -249,6 +256,9 @@ public class CourseCommentController extends AppBaseController
                     String path = "comment/video/" + datePath + "/" + uuid + suffix;
                     CloudStorageService storage = OSSFactory.build();
                     String url = storage.upload(videoFile.getBytes(), path);
+                    if (StringUtils.isNotEmpty(cloudHostProper.getCommentContentReplaceFrom())) {
+                        url = url.replace(cloudHostProper.getCommentContentReplaceFrom(), cloudHostProper.getCommentContentReplaceTo());
+                    }
                     content = (content == null ? "" : content) + "\n" + url;
                 }
             }
@@ -256,9 +266,7 @@ public class CourseCommentController extends AppBaseController
             FsUserCourseComment fsUserCourseComment = new FsUserCourseComment();
             fsUserCourseComment.setUserId(Long.parseLong(getUserId()));
             fsUserCourseComment.setCourseId(courseId);
-            if (StringUtils.isNotEmpty(cloudHostProper.getCommentContentReplaceFrom())) {
-                content = content.replace(cloudHostProper.getCommentContentReplaceFrom(), cloudHostProper.getCommentContentReplaceTo());
-            }
+
             fsUserCourseComment.setContent(content);
             fsUserCourseComment.setType(type);
             if (type == 1) {

+ 6 - 0
fs-user-app/src/main/java/com/fs/app/controller/H5Controller.java

@@ -163,6 +163,12 @@ public class H5Controller
                 case DOCTOR_FILING:
                     data = config.getDoctorFiling();
                     break;
+                case APP_USER_AGREEMENT:
+                    data = config.getAppUserAgreement();
+                    break;
+                case APP_PRIVACY_AGREEMENT:
+                    data = config.getAppPrivacyAgreement();
+                    break;
                 default:
                     agreementType="notFound";
                     data="<div class=\"error-message\"><h3>暂未找到该协议内容</h3><p>请确认协议类型是否正确,或联系管理员配置相关协议内容。</p></div>";

+ 13 - 5
fs-user-app/src/main/java/com/fs/app/controller/store/CourseCommentScrmController.java

@@ -108,9 +108,16 @@ public class CourseCommentScrmController extends AppBaseController
     {
         param.setUserId(Long.parseLong(getUserId()));
         PageHelper.startPage(param.getPageNum(), param.getPageSize());
-        List<FsUserCourseCommentListUVO> list = fsUserCourseCommentService.selectFsUserCourseCommentMyListUVO(Long.parseLong(getUserId()));
+        List<FsUserCourseCommentListUVO> list = fsUserCourseCommentService.selectFsUserCourseCommentMyListUVO(param);
         PageInfo<FsUserCourseCommentListUVO> listPageInfo=new PageInfo<>(list);
-        return R.ok().put("data",listPageInfo);
+        List<FsUserCourseComment> fsUserCourseComments = new ArrayList<>();
+        PageInfo<FsUserCourseComment> defaultPageInfo= new PageInfo<>(fsUserCourseComments);
+        try {
+            PageHelper.startPage(param.getPageNum(), param.getPageSize());
+            fsUserCourseComments = fsUserCourseCommentService.selectFsUserCourseCommentListByTypeAndCourseId(3, param.getCourseId());
+            defaultPageInfo = new PageInfo<>(fsUserCourseComments);
+        } catch (Exception ignore) {}
+        return R.ok().put("data",listPageInfo).put("defaultPageInfo",defaultPageInfo);
     }
     /**
      * 新增课堂评论
@@ -228,6 +235,9 @@ public class CourseCommentScrmController extends AppBaseController
                     String path = "comment/video/" + datePath + "/" + uuid + suffix;
                     CloudStorageService storage = OSSFactory.build();
                     String url = storage.upload(videoFile.getBytes(), path);
+                    if (StringUtils.isNotEmpty(cloudHostProper.getCommentContentReplaceFrom())) {
+                        url = url.replace(cloudHostProper.getCommentContentReplaceFrom(), cloudHostProper.getCommentContentReplaceTo());
+                    }
                     content = (content == null ? "" : content) + "\n" + url;
                 }
             }
@@ -235,9 +245,7 @@ public class CourseCommentScrmController extends AppBaseController
             FsUserCourseComment fsUserCourseComment = new FsUserCourseComment();
             fsUserCourseComment.setUserId(Long.parseLong(getUserId()));
             fsUserCourseComment.setCourseId(courseId);
-            if (StringUtils.isNotEmpty(cloudHostProper.getCommentContentReplaceFrom())) {
-                content = content.replace(cloudHostProper.getCommentContentReplaceFrom(), cloudHostProper.getCommentContentReplaceTo());
-            }
+
             fsUserCourseComment.setContent(content);
             fsUserCourseComment.setType(type);
             if (type == 1) {

+ 6 - 0
fs-user-app/src/main/java/com/fs/app/controller/store/IndexScrmController.java

@@ -377,6 +377,12 @@ public class IndexScrmController extends AppBaseController {
 			case "userRemoveService":
 				data = config.getUserRemoveService();
 				break;
+			case "appUserAgreement":
+				data = config.getAppUserAgreement();
+				break;
+			case "appPrivacyAgreement":
+				data = config.getAppPrivacyAgreement();
+				break;
 			default:
 				break;
 		}