Ver Fonte

feat(product): 修正供应商可见性过滤逻辑,按同体系成员关系判断;管理端新增仅体系可见开关,去掉独立供应商字段

- 修正 ProductService.list() 过滤逻辑:vendorVisible=true 商品仅同体系用户可见
- 新增 getMySupplySystemIds() 方法,查询用户所属供应商体系
- teacher/admin 平台角色始终可见所有商品
- 游客和未加入任何体系的用户只可见公开商品
- 管理端 ProductEdit.vue 删除独立供应商字段,功能合并到所属体系
- 管理端新增「仅体系可见」开关(el-switch)
- schema.sql 补充 vendor_visible 列定义
- DatabaseInitializer 添加迁移384注释
E2E Test Bot há 6 dias atrás
pai
commit
0949b7e27a

+ 2 - 1
cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java

@@ -12204,8 +12204,9 @@ log.info("迁移298: 已为无 openid 的 child 账号补齐 family_members 记
         }
     }
 
+    // 迁移384: products 表添加 vendor_visible 列(仅供应商体系内可见开关)
     private void addProductVendorVisibleColumn() {
-        ensureColumn("products", "vendor_visible", "TINYINT(1) DEFAULT NULL COMMENT '是否仅供应商体系内用户可见(列表接口生效,直接分享可bypass)'");
+        ensureColumn("products", "vendor_visible", "TINYINT(1) DEFAULT NULL COMMENT '是否仅供应商体系内用户可见(列表接口生效,直接分享详情页可访问)'");
     }
 
     // 迁移367: products 表添加健康约束字段(基础疾病禁忌/过敏原/药物相互作用标记)

+ 37 - 5
cfc-backend/src/main/java/com/etotem/cfc/service/ProductService.java

@@ -16,10 +16,12 @@ import com.etotem.cfc.entity.Product;
 import com.etotem.cfc.entity.ProductBundleItem;
 import com.etotem.cfc.entity.ProductGiftRule;
 import com.etotem.cfc.entity.ProductSku;
+import com.etotem.cfc.entity.SupplySystemMember;
 import com.etotem.cfc.entity.User;
 import com.etotem.cfc.mapper.ProductBundleItemMapper;
 import com.etotem.cfc.mapper.ProductMapper;
 import com.etotem.cfc.mapper.ProductSkuMapper;
+import com.etotem.cfc.mapper.SupplySystemMemberMapper;
 import com.etotem.cfc.mapper.UserMapper;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -44,6 +46,9 @@ public class ProductService {
     @Resource
     private UserMapper userMapper;
 
+    @Resource
+    private SupplySystemMemberMapper supplySystemMemberMapper;
+
     @Resource
     private MembershipService membershipService;
 
@@ -173,11 +178,25 @@ wrapper.in(Product::getId, productIds)
                 }
             });
         }
-        // 供应商可见过滤:仅对非供应商体系用户生效,供应商体系内用户可查看全部商品
-        boolean isVendor = isVendorUser(userId, role);
-        if (!isVendor && !isGuest(userId)) {
-            // 非供应商用户:只展示 vendorVisible=false 或 NULL 的商品
-            wrapper.and(w -> w.isNull(Product::getVendorVisible).or().eq(Product::getVendorVisible, false));
+        // 供应商可见过滤:vendorVisible=true 的商品仅同体系用户可见
+        // 同体系判定:用户在 supply_system_member 中有记录,且该产品的 distributionSystemId 在用户所属体系中
+        // teacher(规划师)和 admin 作为平台角色始终可见所有商品;游客(未登录)只可见公开商品
+        boolean isPlatform = ("teacher".equals(role) || "admin".equals(role));
+        if (!isPlatform) {
+            // 获取当前用户所属的供应商体系 ID 列表(游客返回空列表)
+            List<Long> mySystemIds = getMySupplySystemIds(userId);
+            if (!mySystemIds.isEmpty()) {
+                // 已加入供应商体系的用户:可见 vendorVisible=false/NULL 的商品,以及自身体系内的 vendorVisible=true 商品
+                wrapper.and(w -> {
+                    w.isNull(Product::getVendorVisible)
+                     .or().eq(Product::getVendorVisible, false)
+                     .or(w2 -> w2.eq(Product::getVendorVisible, true)
+                                .in(Product::getDistributionSystemId, mySystemIds));
+                });
+            } else {
+                // 游客或未加入任何供应商体系:只展示公开商品
+                wrapper.and(w -> w.isNull(Product::getVendorVisible).or().eq(Product::getVendorVisible, false));
+            }
         }
         if (query.getProductType() != null && !query.getProductType().isEmpty()) {
             wrapper.eq(Product::getProductType, query.getProductType());
@@ -595,6 +614,19 @@ wrapper.in(Product::getId, productIds)
         return userId == null;
     }
 
+    /** 获取当前用户所属的供应商体系 ID 列表 */
+    private List<Long> getMySupplySystemIds(Long userId) {
+        if (userId == null) return new ArrayList<>();
+        return supplySystemMemberMapper.selectList(
+                new LambdaQueryWrapper<SupplySystemMember>()
+                        .eq(SupplySystemMember::getUserId, userId)
+                        .eq(SupplySystemMember::getStatus, "active"))
+                .stream()
+                .map(SupplySystemMember::getSystemId)
+                .distinct()
+                .collect(Collectors.toList());
+    }
+
     /** 判断用户是否为供应商体系内用户(审核通过的服务商或 teacher 角色) */
     private boolean isVendorUser(Long userId, String role) {
         if (userId == null) return false;

+ 1 - 0
cfc-backend/src/main/resources/schema.sql

@@ -1195,6 +1195,7 @@ CREATE TABLE IF NOT EXISTS products (
     drug_interaction_flags TINYINT DEFAULT 0 COMMENT '是否命中药物-食物相互作用(1=是,需对照drug_interactions表)',
     is_recommended TINYINT(1) DEFAULT 0 COMMENT '是否推荐商品 0否 1是',
     tags VARCHAR(500) DEFAULT '' COMMENT '商品标签 JSON 数组,用于文章-商品标签匹配推荐',
+    vendor_visible TINYINT(1) DEFAULT NULL COMMENT '是否仅供应商体系内用户可见(列表接口生效,直接分享详情页可访问)',
     created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
     updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';

+ 5 - 5
cfc-web/src/views/admin/ProductEdit.vue

@@ -194,11 +194,9 @@
           </span>
         </el-form-item>
 
-        <el-form-item v-if="isSupplierAdmin" label="供应商">
-          <el-input v-model="form.vendorName" placeholder="自动填写" disabled />
-        </el-form-item>
-        <el-form-item v-else label="供应商">
-          <el-input v-model="form.vendorName" placeholder="供应商名称(管理员创建时填写)" />
+        <el-form-item label="仅体系可见">
+          <el-switch v-model="form.vendorVisible" />
+          <span style="margin-left: 10px; font-size: 12px; color: #999;">开启后仅所属供应体系的用户可在列表中看到该商品,分享商品详情页所有人可访问</span>
         </el-form-item>
 
         <el-form-item label="配送方式">
@@ -397,6 +395,7 @@ export default {
         vendorName: '',
         vendorId: null,
         distributionSystemId: null,
+        vendorVisible: false,
         deliveryMethod: 1,
         memberEligible: 'no',
         minVisibleLevel: '',
@@ -690,6 +689,7 @@ export default {
             vendorName: p.vendorName || '',
             vendorId: p.vendorId || null,
             distributionSystemId: p.distributionSystemId || null,
+            vendorVisible: p.vendorVisible === true,
             deliveryMethod: p.deliveryMethod || 1,
             memberEligible: p.memberEligible || 'no',
             minVisibleLevel: p.minVisibleLevel || '',