Преглед изворни кода

fix: resolve ProductEdit.vue rebase conflict + cleanup supply entities

Sisyphus пре 3 месеци
родитељ
комит
ae0c2272e0

+ 3 - 0
cfc-backend/.classpath

@@ -47,6 +47,9 @@
 	<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
 		<attributes>
 			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+			<attribute name="ignore_optional_problems" value="true"/>
+			<attribute name="m2e-apt" value="true"/>
 			<attribute name="test" value="true"/>
 		</attributes>
 	</classpathentry>

+ 25 - 17
cfc-backend/src/main/java/com/etotem/cfc/controller/admin/SupplySettlementController.java

@@ -1,12 +1,15 @@
 package com.etotem.cfc.controller.admin;
 
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.etotem.cfc.common.Result;
 import com.etotem.cfc.entity.SupplySettlement;
-import com.etotem.cfc.entity.SupplySettlementItem;
+import com.etotem.cfc.entity.SupplySettlementDetail;
 import com.etotem.cfc.service.SupplySettlementService;
+import org.springframework.format.annotation.DateTimeFormat;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import java.util.Date;
 import java.util.List;
 
 @RestController
@@ -16,30 +19,35 @@ public class SupplySettlementController {
     @Resource
     private SupplySettlementService supplySettlementService;
 
-    @PostMapping("/list-by-supplier")
-    public Result<List<SupplySettlement>> listBySupplier(@RequestParam Long supplierId) {
-        return Result.success(supplySettlementService.getSettlementsBySupplier(supplierId));
+    @PostMapping("/list")
+    public Result<Page<SupplySettlement>> list(@RequestParam(required = false) Long systemId,
+                                               @RequestParam(required = false) String status,
+                                               @RequestParam(defaultValue = "1") int page,
+                                               @RequestParam(defaultValue = "10") int size) {
+        return Result.success(supplySettlementService.list(systemId, status, page, size));
     }
 
-    @PostMapping("/list-by-system")
-    public Result<List<SupplySettlement>> listBySystem(@RequestParam Long supplySystemId) {
-        return Result.success(supplySettlementService.getSettlementsBySystem(supplySystemId));
+    @PostMapping("/detail")
+    public Result<SupplySettlement> detail(@RequestParam Long id) {
+        return Result.success(supplySettlementService.detail(id));
     }
 
-    @PostMapping("/items")
-    public Result<List<SupplySettlementItem>> items(@RequestParam Long settlementId) {
-        return Result.success(supplySettlementService.getSettlementItems(settlementId));
+    @PostMapping("/detail-items")
+    public Result<List<SupplySettlementDetail>> detailItems(@RequestParam Long settlementId) {
+        return Result.success(supplySettlementService.detailItems(settlementId));
+    }
+
+    @PostMapping("/create")
+    public Result<SupplySettlement> create(@RequestParam Long systemId,
+                                           @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date periodStart,
+                                           @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date periodEnd,
+                                           @RequestParam(required = false) String remark) {
+        return Result.success(supplySettlementService.createSettlement(systemId, periodStart, periodEnd, remark));
     }
 
     @PostMapping("/confirm")
     public Result<Void> confirm(@RequestParam Long id) {
-        boolean success = supplySettlementService.confirmSettlement(id);
+        boolean success = supplySettlementService.confirm(id);
         return success ? Result.success(null) : Result.error("确认结算失败");
     }
-
-    @PostMapping("/cancel")
-    public Result<Void> cancel(@RequestParam Long id) {
-        boolean success = supplySettlementService.cancelSettlement(id);
-        return success ? Result.success(null) : Result.error("取消结算失败");
-    }
 }

+ 34 - 38
cfc-backend/src/main/java/com/etotem/cfc/controller/admin/SupplySystemController.java

@@ -1,9 +1,9 @@
 package com.etotem.cfc.controller.admin;
 
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.etotem.cfc.common.Result;
-import com.etotem.cfc.entity.Supplier;
-import com.etotem.cfc.entity.SupplyRelationship;
 import com.etotem.cfc.entity.SupplySystem;
+import com.etotem.cfc.entity.SupplySystemMember;
 import com.etotem.cfc.service.SupplySystemService;
 import org.springframework.web.bind.annotation.*;
 
@@ -18,67 +18,63 @@ public class SupplySystemController {
     private SupplySystemService supplySystemService;
 
     @PostMapping("/list")
-    public Result<List<SupplySystem>> list() {
-        return Result.success(supplySystemService.getAllSystems());
+    public Result<Page<SupplySystem>> list(@RequestParam(required = false) String keyword,
+                                           @RequestParam(required = false) String status,
+                                           @RequestParam(defaultValue = "1") int page,
+                                           @RequestParam(defaultValue = "10") int size) {
+        return Result.success(supplySystemService.list(keyword, status, page, size));
     }
 
     @PostMapping("/create")
     public Result<SupplySystem> create(@RequestBody SupplySystem system) {
-        return Result.success(supplySystemService.createSystem(system));
+        return Result.success(supplySystemService.create(system));
     }
 
     @PostMapping("/update")
     public Result<SupplySystem> update(@RequestBody SupplySystem system) {
-        boolean success = supplySystemService.updateSystem(system);
+        boolean success = supplySystemService.update(system);
         return success ? Result.success(system) : Result.error("更新失败");
     }
 
+    @PostMapping("/detail")
+    public Result<SupplySystem> detail(@RequestParam Long id) {
+        return Result.success(supplySystemService.detail(id));
+    }
+
     @PostMapping("/toggle-status")
     public Result<Void> toggleStatus(@RequestParam Long id) {
         boolean success = supplySystemService.toggleStatus(id);
         return success ? Result.success(null) : Result.error("操作失败");
     }
 
-    @PostMapping("/supplier/list")
-    public Result<List<Supplier>> listSuppliers(@RequestParam Long supplySystemId) {
-        return Result.success(supplySystemService.getSuppliers(supplySystemId));
-    }
-
-    @PostMapping("/supplier/create")
-    public Result<Supplier> createSupplier(@RequestBody Supplier supplier) {
-        return Result.success(supplySystemService.createSupplier(supplier));
+    @PostMapping("/list-all")
+    public Result<List<SupplySystem>> listAll() {
+        return Result.success(supplySystemService.listAll());
     }
 
-    @PostMapping("/supplier/update")
-    public Result<Supplier> updateSupplier(@RequestBody Supplier supplier) {
-        boolean success = supplySystemService.updateSupplier(supplier);
-        return success ? Result.success(supplier) : Result.error("更新失败");
-    }
-
-    @PostMapping("/supplier/delete")
-    public Result<Void> deleteSupplier(@RequestParam Long id) {
-        boolean success = supplySystemService.deleteSupplier(id);
-        return success ? Result.success(null) : Result.error("删除失败");
-    }
+    // ========== Member Management ==========
 
-    @PostMapping("/relation/product")
-    public Result<List<SupplyRelationship>> getRelationsByProduct(@RequestParam Long productId) {
-        return Result.success(supplySystemService.getRelationsByProduct(productId));
+    @PostMapping("/member/list")
+    public Result<List<SupplySystemMember>> memberList(@RequestParam Long systemId) {
+        return Result.success(supplySystemService.memberList(systemId));
     }
 
-    @PostMapping("/relation/supplier")
-    public Result<List<SupplyRelationship>> getRelationsBySupplier(@RequestParam Long supplierId) {
-        return Result.success(supplySystemService.getRelationsBySupplier(supplierId));
+    @PostMapping("/member/add")
+    public Result<SupplySystemMember> addMember(@RequestParam Long systemId,
+                                                @RequestParam Long userId) {
+        return Result.success(supplySystemService.addMember(systemId, userId));
     }
 
-    @PostMapping("/relation/create")
-    public Result<SupplyRelationship> createRelation(@RequestBody SupplyRelationship relationship) {
-        return Result.success(supplySystemService.createRelationship(relationship));
+    @PostMapping("/member/remove")
+    public Result<Void> removeMember(@RequestParam Long memberId) {
+        boolean success = supplySystemService.removeMember(memberId);
+        return success ? Result.success(null) : Result.error("移除失败");
     }
 
-    @PostMapping("/relation/remove-by-product")
-    public Result<Void> removeRelationByProduct(@RequestParam Long productId) {
-        supplySystemService.removeRelationshipByProduct(productId);
-        return Result.success(null);
+    @PostMapping("/member/change-role")
+    public Result<Void> changeRole(@RequestParam Long memberId,
+                                   @RequestParam String role) {
+        boolean success = supplySystemService.changeRole(memberId, role);
+        return success ? Result.success(null) : Result.error("角色变更失败");
     }
 }

+ 0 - 24
cfc-backend/src/main/java/com/etotem/cfc/entity/Supplier.java

@@ -1,24 +0,0 @@
-package com.etotem.cfc.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.util.Date;
-
-@Data
-@TableName("supplier")
-public class Supplier implements Serializable {
-    @TableId(type = IdType.AUTO)
-    private Long id;
-    private String name;
-    private String contactName;
-    private String contactPhone;
-    private Long supplySystemId;
-    private BigDecimal commissionRate;
-    private String status;
-    private Date createdAt;
-    private Date updatedAt;
-}

+ 0 - 21
cfc-backend/src/main/java/com/etotem/cfc/entity/SupplyRelationship.java

@@ -1,21 +0,0 @@
-package com.etotem.cfc.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-import java.io.Serializable;
-import java.util.Date;
-
-@Data
-@TableName("supply_relationship")
-public class SupplyRelationship implements Serializable {
-    @TableId(type = IdType.AUTO)
-    private Long id;
-    private Long supplierId;
-    private Long productId;
-    private String relationType;
-    private String status;
-    private Date createdAt;
-    private Date updatedAt;
-}

+ 8 - 10
cfc-backend/src/main/java/com/etotem/cfc/entity/SupplySettlement.java

@@ -5,7 +5,6 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 import java.io.Serializable;
-import java.math.BigDecimal;
 import java.util.Date;
 
 @Data
@@ -13,15 +12,14 @@ import java.util.Date;
 public class SupplySettlement implements Serializable {
     @TableId(type = IdType.AUTO)
     private Long id;
-    private Long supplierId;
-    private Long supplySystemId;
-    private Long orderId;
-    private BigDecimal totalAmount;
-    private BigDecimal platformProfit;
-    private BigDecimal supplierIncome;
-    private BigDecimal commissionAmount;
+    private Long systemId;
+    private Date periodStart;
+    private Date periodEnd;
+    private Integer totalSales;
+    private Integer platformProfit;
+    private Integer supplierPayout;
     private String status;
-    private Date settlementDate;
+    private Date settledAt;
+    private String remark;
     private Date createdAt;
-    private Date updatedAt;
 }

+ 0 - 25
cfc-backend/src/main/java/com/etotem/cfc/entity/SupplySettlementItem.java

@@ -1,25 +0,0 @@
-package com.etotem.cfc.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.util.Date;
-
-@Data
-@TableName("supply_settlement_item")
-public class SupplySettlementItem implements Serializable {
-    @TableId(type = IdType.AUTO)
-    private Long id;
-    private Long settlementId;
-    private Long productId;
-    private String productName;
-    private Integer quantity;
-    private BigDecimal unitPrice;
-    private BigDecimal subtotal;
-    private BigDecimal commissionRate;
-    private BigDecimal commissionAmount;
-    private Date createdAt;
-}

+ 0 - 9
cfc-backend/src/main/java/com/etotem/cfc/mapper/SupplierMapper.java

@@ -1,9 +0,0 @@
-package com.etotem.cfc.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.etotem.cfc.entity.Supplier;
-import org.apache.ibatis.annotations.Mapper;
-
-@Mapper
-public interface SupplierMapper extends BaseMapper<Supplier> {
-}

+ 0 - 9
cfc-backend/src/main/java/com/etotem/cfc/mapper/SupplyRelationshipMapper.java

@@ -1,9 +0,0 @@
-package com.etotem.cfc.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.etotem.cfc.entity.SupplyRelationship;
-import org.apache.ibatis.annotations.Mapper;
-
-@Mapper
-public interface SupplyRelationshipMapper extends BaseMapper<SupplyRelationship> {
-}

+ 0 - 9
cfc-backend/src/main/java/com/etotem/cfc/mapper/SupplySettlementItemMapper.java

@@ -1,9 +0,0 @@
-package com.etotem.cfc.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.etotem.cfc.entity.SupplySettlementItem;
-import org.apache.ibatis.annotations.Mapper;
-
-@Mapper
-public interface SupplySettlementItemMapper extends BaseMapper<SupplySettlementItem> {
-}

+ 71 - 79
cfc-backend/src/main/java/com/etotem/cfc/service/SupplySettlementService.java

@@ -1,14 +1,20 @@
 package com.etotem.cfc.service;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.etotem.cfc.entity.*;
-import com.etotem.cfc.mapper.*;
+import com.etotem.cfc.entity.ProductOrder;
+import com.etotem.cfc.entity.SupplySettlement;
+import com.etotem.cfc.entity.SupplySettlementDetail;
+import com.etotem.cfc.entity.SupplySystem;
+import com.etotem.cfc.mapper.ProductOrderMapper;
+import com.etotem.cfc.mapper.SupplySettlementDetailMapper;
+import com.etotem.cfc.mapper.SupplySettlementMapper;
+import com.etotem.cfc.mapper.SupplySystemMapper;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
-import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
 
@@ -16,10 +22,7 @@ import java.util.List;
 public class SupplySettlementService extends ServiceImpl<SupplySettlementMapper, SupplySettlement> {
 
     @Resource
-    private SupplySettlementItemMapper supplySettlementItemMapper;
-
-    @Resource
-    private SupplierMapper supplierMapper;
+    private SupplySettlementDetailMapper supplySettlementDetailMapper;
 
     @Resource
     private SupplySystemMapper supplySystemMapper;
@@ -27,100 +30,89 @@ public class SupplySettlementService extends ServiceImpl<SupplySettlementMapper,
     @Resource
     private ProductOrderMapper productOrderMapper;
 
-    public List<SupplySettlement> getSettlementsBySupplier(Long supplierId) {
+    public Page<SupplySettlement> list(Long systemId, String status, int page, int size) {
+        Page<SupplySettlement> pageParam = new Page<>(page, size);
         LambdaQueryWrapper<SupplySettlement> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(SupplySettlement::getSupplierId, supplierId)
-               .orderByDesc(SupplySettlement::getCreatedAt);
-        return this.list(wrapper);
+        if (systemId != null) {
+            wrapper.eq(SupplySettlement::getSystemId, systemId);
+        }
+        if (status != null && !status.isEmpty()) {
+            wrapper.eq(SupplySettlement::getStatus, status);
+        }
+        wrapper.orderByDesc(SupplySettlement::getId);
+        return this.page(pageParam, wrapper);
     }
 
-    public List<SupplySettlement> getSettlementsBySystem(Long supplySystemId) {
-        LambdaQueryWrapper<SupplySettlement> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(SupplySettlement::getSupplySystemId, supplySystemId)
-               .orderByDesc(SupplySettlement::getCreatedAt);
-        return this.list(wrapper);
+    public SupplySettlement detail(Long id) {
+        return this.getById(id);
     }
 
-    public List<SupplySettlementItem> getSettlementItems(Long settlementId) {
-        LambdaQueryWrapper<SupplySettlementItem> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(SupplySettlementItem::getSettlementId, settlementId);
-        return supplySettlementItemMapper.selectList(wrapper);
+    public List<SupplySettlementDetail> detailItems(Long settlementId) {
+        LambdaQueryWrapper<SupplySettlementDetail> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SupplySettlementDetail::getSettlementId, settlementId);
+        return supplySettlementDetailMapper.selectList(wrapper);
     }
 
-    /**
-     * Calculate and create a settlement for a completed order.
-     * Called when an order is marked as completed/confirmed.
-     */
     @Transactional
-    public SupplySettlement createSettlementForOrder(Long orderId) {
-        ProductOrder order = productOrderMapper.selectById(orderId);
-        if (order == null || order.getSupplierId() == null) {
-            return null;
-        }
+    public SupplySettlement createSettlement(Long systemId, Date periodStart, Date periodEnd, String remark) {
+        SupplySystem system = supplySystemMapper.selectById(systemId);
+        if (system == null) return null;
 
-        Supplier supplier = supplierMapper.selectById(order.getSupplierId());
-        if (supplier == null || supplier.getSupplySystemId() == null) {
-            return null;
-        }
+        // Query all completed orders in the period for this system
+        LambdaQueryWrapper<ProductOrder> orderWrapper = new LambdaQueryWrapper<>();
+        orderWrapper.eq(ProductOrder::getSupplySystemId, systemId)
+                    .ge(ProductOrder::getPaidAt, periodStart)
+                    .le(ProductOrder::getPaidAt, periodEnd)
+                    .ne(ProductOrder::getStatus, "CANCELLED");
+        List<ProductOrder> orders = productOrderMapper.selectList(orderWrapper);
 
-        SupplySystem system = supplySystemMapper.selectById(supplier.getSupplySystemId());
-        if (system == null) {
-            return null;
-        }
+        if (orders.isEmpty()) return null;
 
-        BigDecimal totalAmount = BigDecimal.valueOf(order.getTotalAmount()).divide(BigDecimal.valueOf(100));
-        BigDecimal platformRate = system.getPlatformProfitRate() != null
-                ? system.getPlatformProfitRate() : BigDecimal.ZERO;
-        BigDecimal platformProfit = totalAmount.multiply(platformRate);
-        BigDecimal supplierIncome = totalAmount.subtract(platformProfit);
-        BigDecimal commissionAmount = BigDecimal.valueOf(
-                order.getSupplyCommission() != null ? order.getSupplyCommission() : 0)
-                .divide(BigDecimal.valueOf(100));
+        int totalSales = 0;
+        int platformProfit = 0;
 
         SupplySettlement settlement = new SupplySettlement();
-        settlement.setSupplierId(order.getSupplierId());
-        settlement.setSupplySystemId(supplier.getSupplySystemId());
-        settlement.setOrderId(orderId);
-        settlement.setTotalAmount(totalAmount);
-        settlement.setPlatformProfit(platformProfit);
-        settlement.setSupplierIncome(supplierIncome);
-        settlement.setCommissionAmount(commissionAmount);
-        settlement.setStatus("PENDING");
+        settlement.setSystemId(systemId);
+        settlement.setPeriodStart(periodStart);
+        settlement.setPeriodEnd(periodEnd);
+        settlement.setStatus("pending");
+        settlement.setRemark(remark);
         settlement.setCreatedAt(new Date());
-        settlement.setUpdatedAt(new Date());
-        this.save(settlement);
 
-        SupplySettlementItem item = new SupplySettlementItem();
-        item.setSettlementId(settlement.getId());
-        item.setProductId(order.getProductId());
-        item.setProductName(order.getProductName());
-        item.setQuantity(order.getQuantity());
-        item.setUnitPrice(totalAmount.divide(BigDecimal.valueOf(order.getQuantity()), BigDecimal.ROUND_HALF_UP));
-        item.setSubtotal(totalAmount);
-        item.setCommissionRate(supplier.getCommissionRate());
-        item.setCommissionAmount(commissionAmount);
-        item.setCreatedAt(new Date());
-        supplySettlementItemMapper.insert(item);
+        for (ProductOrder order : orders) {
+            int orderAmount = order.getTotalAmount() != null ? order.getTotalAmount() : 0;
+            int profitRate = system.getPlatformProfitRate() != null ? system.getPlatformProfitRate().multiply(java.math.BigDecimal.valueOf(10)).intValue() : 0;
+            int orderProfit = (orderAmount * profitRate) / 1000;
+
+            totalSales += orderAmount;
+            platformProfit += orderProfit;
+
+            SupplySettlementDetail detail = new SupplySettlementDetail();
+            detail.setSettlementId(settlement.getId());
+            detail.setOrderId(order.getId());
+            detail.setOrderNo(order.getOrderNo());
+            detail.setProductName(order.getProductName());
+            detail.setAmount(orderAmount);
+            detail.setPlatformFee(orderProfit);
+            detail.setSupplierId(order.getSupplierId());
+            detail.setPayout(orderAmount - orderProfit);
+            supplySettlementDetailMapper.insert(detail);
+        }
 
-        return settlement;
-    }
+        settlement.setTotalSales(totalSales);
+        settlement.setPlatformProfit(platformProfit);
+        settlement.setSupplierPayout(totalSales - platformProfit);
+        this.save(settlement);
 
-    @Transactional
-    public boolean confirmSettlement(Long id) {
-        SupplySettlement settlement = this.getById(id);
-        if (settlement == null) return false;
-        settlement.setStatus("SETTLED");
-        settlement.setSettlementDate(new Date());
-        settlement.setUpdatedAt(new Date());
-        return this.updateById(settlement);
+        return settlement;
     }
 
     @Transactional
-    public boolean cancelSettlement(Long id) {
+    public boolean confirm(Long id) {
         SupplySettlement settlement = this.getById(id);
         if (settlement == null) return false;
-        settlement.setStatus("CANCELLED");
-        settlement.setUpdatedAt(new Date());
+        settlement.setStatus("settled");
+        settlement.setSettledAt(new Date());
         return this.updateById(settlement);
     }
 }

+ 71 - 62
cfc-backend/src/main/java/com/etotem/cfc/service/SupplySystemService.java

@@ -1,13 +1,14 @@
 package com.etotem.cfc.service;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.etotem.cfc.entity.Supplier;
-import com.etotem.cfc.entity.SupplyRelationship;
 import com.etotem.cfc.entity.SupplySystem;
-import com.etotem.cfc.mapper.SupplierMapper;
-import com.etotem.cfc.mapper.SupplyRelationshipMapper;
+import com.etotem.cfc.entity.SupplySystemMember;
+import com.etotem.cfc.entity.User;
 import com.etotem.cfc.mapper.SupplySystemMapper;
+import com.etotem.cfc.mapper.SupplySystemMemberMapper;
+import com.etotem.cfc.mapper.UserMapper;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -19,30 +20,48 @@ import java.util.List;
 public class SupplySystemService extends ServiceImpl<SupplySystemMapper, SupplySystem> {
 
     @Resource
-    private SupplierMapper supplierMapper;
+    private SupplySystemMemberMapper supplySystemMemberMapper;
 
     @Resource
-    private SupplyRelationshipMapper supplyRelationshipMapper;
+    private UserMapper userMapper;
 
-    public List<SupplySystem> getAllSystems() {
-        return this.list();
+    public Page<SupplySystem> list(String keyword, String status, int page, int size) {
+        Page<SupplySystem> pageParam = new Page<>(page, size);
+        LambdaQueryWrapper<SupplySystem> wrapper = new LambdaQueryWrapper<>();
+        if (keyword != null && !keyword.isEmpty()) {
+            wrapper.like(SupplySystem::getName, keyword);
+        }
+        if (status != null && !status.isEmpty()) {
+            wrapper.eq(SupplySystem::getStatus, status);
+        }
+        wrapper.orderByDesc(SupplySystem::getId);
+        return this.page(pageParam, wrapper);
     }
 
-    public List<SupplySystem> getActiveSystems() {
-        LambdaQueryWrapper<SupplySystem> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(SupplySystem::getStatus, "ACTIVE");
-        return this.list(wrapper);
+    public SupplySystem detail(Long id) {
+        return this.getById(id);
     }
 
     @Transactional
-    public SupplySystem createSystem(SupplySystem system) {
+    public SupplySystem create(SupplySystem system) {
+        system.setStatus("active");
         system.setCreatedAt(new Date());
         system.setUpdatedAt(new Date());
         this.save(system);
+
+        // 创建者自动成为 admin 成员
+        SupplySystemMember member = new SupplySystemMember();
+        member.setSystemId(system.getId());
+        member.setUserId(system.getAdminId());
+        member.setRole("admin");
+        member.setStatus("active");
+        member.setJoinedAt(new Date());
+        supplySystemMemberMapper.insert(member);
+
         return system;
     }
 
-    public boolean updateSystem(SupplySystem system) {
+    public boolean update(SupplySystem system) {
         system.setUpdatedAt(new Date());
         return this.updateById(system);
     }
@@ -50,69 +69,59 @@ public class SupplySystemService extends ServiceImpl<SupplySystemMapper, SupplyS
     public boolean toggleStatus(Long id) {
         SupplySystem system = this.getById(id);
         if (system == null) return false;
-        system.setStatus("ACTIVE".equals(system.getStatus()) ? "INACTIVE" : "ACTIVE");
+        system.setStatus("active".equals(system.getStatus()) ? "disabled" : "active");
         system.setUpdatedAt(new Date());
         return this.updateById(system);
     }
 
-    // ========== Supplier Management ==========
-
-    public List<Supplier> getSuppliers(Long supplySystemId) {
-        LambdaQueryWrapper<Supplier> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(Supplier::getSupplySystemId, supplySystemId);
-        return supplierMapper.selectList(wrapper);
+    public List<SupplySystem> listAll() {
+        LambdaQueryWrapper<SupplySystem> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SupplySystem::getStatus, "active");
+        return this.list(wrapper);
     }
 
-    @Transactional
-    public Supplier createSupplier(Supplier supplier) {
-        supplier.setStatus("ACTIVE");
-        supplier.setCreatedAt(new Date());
-        supplier.setUpdatedAt(new Date());
-        supplierMapper.insert(supplier);
-        return supplier;
-    }
+    // ========== Member Management ==========
 
-    public boolean updateSupplier(Supplier supplier) {
-        supplier.setUpdatedAt(new Date());
-        return supplierMapper.updateById(supplier) > 0;
+    public List<SupplySystemMember> memberList(Long systemId) {
+        LambdaQueryWrapper<SupplySystemMember> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SupplySystemMember::getSystemId, systemId)
+               .eq(SupplySystemMember::getStatus, "active");
+        return supplySystemMemberMapper.selectList(wrapper);
     }
 
     @Transactional
-    public boolean deleteSupplier(Long id) {
-        // Also remove associated relationships
-        LambdaQueryWrapper<SupplyRelationship> relWrapper = new LambdaQueryWrapper<>();
-        relWrapper.eq(SupplyRelationship::getSupplierId, id);
-        supplyRelationshipMapper.delete(relWrapper);
-        return supplierMapper.deleteById(id) > 0;
-    }
-
-    // ========== Product-Supplier Relationship ==========
-
-    public List<SupplyRelationship> getRelationsByProduct(Long productId) {
-        LambdaQueryWrapper<SupplyRelationship> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(SupplyRelationship::getProductId, productId);
-        return supplyRelationshipMapper.selectList(wrapper);
-    }
-
-    public List<SupplyRelationship> getRelationsBySupplier(Long supplierId) {
-        LambdaQueryWrapper<SupplyRelationship> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(SupplyRelationship::getSupplierId, supplierId);
-        return supplyRelationshipMapper.selectList(wrapper);
+    public SupplySystemMember addMember(Long systemId, Long userId) {
+        // Check if already exists
+        LambdaQueryWrapper<SupplySystemMember> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SupplySystemMember::getSystemId, systemId)
+               .eq(SupplySystemMember::getUserId, userId);
+        SupplySystemMember existing = supplySystemMemberMapper.selectOne(wrapper);
+        if (existing != null) return existing;
+
+        SupplySystemMember member = new SupplySystemMember();
+        member.setSystemId(systemId);
+        member.setUserId(userId);
+        member.setRole("member");
+        member.setStatus("active");
+        member.setJoinedAt(new Date());
+        supplySystemMemberMapper.insert(member);
+        return member;
     }
 
     @Transactional
-    public SupplyRelationship createRelationship(SupplyRelationship relationship) {
-        relationship.setStatus("ACTIVE");
-        relationship.setCreatedAt(new Date());
-        relationship.setUpdatedAt(new Date());
-        supplyRelationshipMapper.insert(relationship);
-        return relationship;
+    public boolean removeMember(Long memberId) {
+        SupplySystemMember member = supplySystemMemberMapper.selectById(memberId);
+        if (member == null) return false;
+        member.setStatus("inactive");
+        member.setJoinedAt(member.getJoinedAt());
+        return supplySystemMemberMapper.updateById(member) > 0;
     }
 
     @Transactional
-    public void removeRelationshipByProduct(Long productId) {
-        LambdaQueryWrapper<SupplyRelationship> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(SupplyRelationship::getProductId, productId);
-        supplyRelationshipMapper.delete(wrapper);
+    public boolean changeRole(Long memberId, String role) {
+        SupplySystemMember member = supplySystemMemberMapper.selectById(memberId);
+        if (member == null) return false;
+        member.setRole(role);
+        return supplySystemMemberMapper.updateById(member) > 0;
     }
 }

+ 20 - 2
cfc-web/src/router/index.js

@@ -292,10 +292,28 @@ const routes = [
       },
       {
         path: 'supply-system',
-        name: 'SupplySystem',
-        component: () => import('@/views/admin/SupplySystem.vue'),
+        name: 'SupplySystemList',
+        component: () => import('@/views/admin/supply-system/List.vue'),
         meta: { title: '供应商体系', perm: 'system:supply' }
       },
+      {
+        path: 'supply-system/create',
+        name: 'SupplySystemCreate',
+        component: () => import('@/views/admin/supply-system/Form.vue'),
+        meta: { title: '新建体系', perm: 'system:supply' }
+      },
+      {
+        path: 'supply-system/:id',
+        name: 'SupplySystemDetail',
+        component: () => import('@/views/admin/supply-system/Detail.vue'),
+        meta: { title: '体系详情', perm: 'system:supply' }
+      },
+      {
+        path: 'supply-system/:id/edit',
+        name: 'SupplySystemEdit',
+        component: () => import('@/views/admin/supply-system/Form.vue'),
+        meta: { title: '编辑体系', perm: 'system:supply' }
+      },
       {
         path: 'zodiac-configs',
         name: 'ZodiacConfigs',

+ 35 - 2
cfc-web/src/views/admin/ProductEdit.vue

@@ -87,6 +87,15 @@
           <el-input v-model="form.vendorName" placeholder="供应商名称(管理员创建时填写)" />
         </el-form-item>
 
+        <el-form-item label="所属体系">
+          <el-select v-model="form.distributionSystemId" placeholder="不属于任何体系" clearable style="width: 100%" @change="handleSystemChange">
+            <el-option v-for="s in supplySystems" :key="s.id" :label="s.name" :value="s.id"></el-option>
+          </el-select>
+          <span v-if="selectedSystem" style="font-size: 12px; color: #999; margin-left: 8px">
+            平台留利: {{ selectedSystem.platformProfitRate }}% → {{ selectedSystem.platformProfitRate * 10 }}‰
+          </span>
+        </el-form-item>
+
         <el-form-item label="配送方式">
           <el-radio-group v-model="form.deliveryMethod">
             <el-radio :label="1">快递</el-radio>
@@ -122,7 +131,7 @@
 </template>
 
 <script>
-import { getProduct, createProduct, updateProduct } from '@/api/admin'
+import { getProduct, createProduct, updateProduct, listAllSupplySystems } from '@/api/admin'
 import { saveDimensionWeights, getDimensionWeights } from '@/api/dimension-weight'
 import DimensionWeightPicker from '@/components/DimensionWeightPicker.vue'
 import ProductSkuEditor from './components/ProductSkuEditor.vue'
@@ -146,11 +155,13 @@ export default {
         stock: null,
         vendorName: '',
         vendorId: null,
+        distributionSystemId: null,
         deliveryMethod: 1,
         memberEligible: 'no',
         dimensionWeights: []
       },
       imageList: [],
+      supplySystems: [],
       rules: {
         name: [{ required: true, message: '请输入商品名称', trigger: 'blur' }],
         productType: [{ required: true, message: '请选择商品类型', trigger: 'change' }],
@@ -191,12 +202,16 @@ export default {
     uploadHeaders() {
       const token = localStorage.getItem('token')
       return { Authorization: 'Bearer ' + token }
+    },
+    selectedSystem() {
+      if (!this.form.distributionSystemId || !this.supplySystems.length) return null
+      return this.supplySystems.find(s => s.id === this.form.distributionSystemId)
     }
   },
   mounted() {
-    // Set editor upload config using computed values
     this.editorConfig.MENU_CONF.uploadImage.server = this.uploadActionUrl
     this.editorConfig.MENU_CONF.uploadImage.headers = this.uploadHeaders
+    this.loadSupplySystems()
     if (this.isEdit) {
       this.loadProduct(this.$route.query.id)
     }
@@ -205,6 +220,23 @@ export default {
     handleEditorCreated(editor) {
       this.editorInstance = editor
     },
+    async loadSupplySystems() {
+      try {
+        const res = await listAllSupplySystems()
+        if (res.data) {
+          this.supplySystems = res.data
+        }
+      } catch (e) {
+        console.error(e)
+      }
+    },
+    handleSystemChange(systemId) {
+      if (!systemId) return
+      const system = this.supplySystems.find(s => s.id === systemId)
+      if (system && system.platformProfitRate != null) {
+        this.form.profitRate = Math.round(system.platformProfitRate * 10)
+      }
+    },
     async loadProduct(id) {
       this.pageLoading = true
       try {
@@ -224,6 +256,7 @@ export default {
             stock: (p.stock != null) ? p.stock : null,
             vendorName: p.vendorName || '',
             vendorId: p.vendorId || null,
+            distributionSystemId: p.distributionSystemId || null,
             deliveryMethod: p.deliveryMethod || 1,
             memberEligible: p.memberEligible || 'no',
             dimensionWeights: []

+ 0 - 277
cfc-web/src/views/admin/SupplySystem.vue

@@ -1,277 +0,0 @@
-<template>
-  <div class="supply-system admin-page">
-    <!-- System List -->
-    <el-card>
-      <div slot="header">
-        <span>供应商体系管理</span>
-        <el-input v-model="keyword" placeholder="搜索供应商体系..." prefix-icon="el-icon-search" style="width:200px" clearable @keyup.enter.native="handleSearch" @clear="handleSearch" />
-        <el-button style="float: right" type="primary" size="small" @click="handleCreateSystem">+ 添加体系</el-button>
-      </div>
-      <el-table :data="filteredData" stripe>
-        <el-table-column prop="id" label="ID" width="80"></el-table-column>
-        <el-table-column prop="name" label="名称" width="150"></el-table-column>
-        <el-table-column prop="description" label="描述"></el-table-column>
-        <el-table-column prop="settlementPeriodDays" label="结算周期(天)" width="120"></el-table-column>
-        <el-table-column prop="platformProfitRate" label="平台利润率" width="120">
-          <template slot-scope="scope">
-            {{ scope.row.platformProfitRate ? (scope.row.platformProfitRate * 100).toFixed(1) + '%' : '-' }}
-          </template>
-        </el-table-column>
-        <el-table-column prop="status" label="状态" width="100">
-          <template slot-scope="scope">
-            <el-tag :type="scope.row.status === 'ACTIVE' ? 'success' : 'danger'">
-              {{ scope.row.status === 'ACTIVE' ? '启用' : '停用' }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" width="220">
-          <template slot-scope="scope">
-            <el-button size="mini" type="primary" @click="handleManageSupplier(scope.row)">供应商</el-button>
-            <el-button size="mini" @click="handleEditSystem(scope.row)">编辑</el-button>
-            <el-button size="mini" :type="scope.row.status === 'ACTIVE' ? 'warning' : 'success'" @click="toggleSystemStatus(scope.row)">
-              {{ scope.row.status === 'ACTIVE' ? '停用' : '启用' }}
-            </el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-    </el-card>
-
-    <!-- System Dialog -->
-    <el-dialog :visible.sync="systemDialogVisible" :title="isEditSystem ? '编辑供应商体系' : '添加供应商体系'" width="500px">
-      <el-form :model="systemForm" label-width="120px">
-        <el-form-item label="名称">
-          <el-input v-model="systemForm.name" placeholder="如: 优选供应商体系"></el-input>
-        </el-form-item>
-        <el-form-item label="结算周期(天)">
-          <el-input-number v-model="systemForm.settlementPeriodDays" :min="1" :max="90"></el-input-number>
-        </el-form-item>
-        <el-form-item label="平台利润率">
-          <el-input-number v-model="systemForm.platformProfitRate" :min="0" :max="1" :step="0.01"></el-input-number>
-        </el-form-item>
-        <el-form-item label="描述">
-          <el-input v-model="systemForm.description" type="textarea"></el-input>
-        </el-form-item>
-      </el-form>
-      <div slot="footer">
-        <el-button @click="systemDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="handleSystemSubmit">保存</el-button>
-      </div>
-    </el-dialog>
-
-    <!-- Supplier Management Dialog -->
-    <el-dialog :visible.sync="supplierDialogVisible" title="供应商管理" width="700px" @close="handleSupplierDialogClose">
-      <div>
-        <div style="margin-bottom: 15px;">
-          <strong>供应商体系:</strong>{{ currentSystem?.name }}
-          <el-button style="float: right" type="primary" size="small" @click="handleCreateSupplier">+ 添加供应商</el-button>
-        </div>
-        <el-table :data="suppliers" stripe>
-          <el-table-column prop="id" label="ID" width="60"></el-table-column>
-          <el-table-column prop="name" label="供应商名称" width="140"></el-table-column>
-          <el-table-column prop="contactName" label="联系人" width="100"></el-table-column>
-          <el-table-column prop="contactPhone" label="联系电话" width="130"></el-table-column>
-          <el-table-column prop="commissionRate" label="佣金比例" width="100">
-            <template slot-scope="scope">
-              {{ scope.row.commissionRate ? (scope.row.commissionRate * 100).toFixed(1) + '%' : '-' }}
-            </template>
-          </el-table-column>
-          <el-table-column prop="status" label="状态" width="80">
-            <template slot-scope="scope">
-              <el-tag :type="scope.row.status === 'ACTIVE' ? 'success' : 'danger'" size="mini">
-                {{ scope.row.status === 'ACTIVE' ? '启用' : '停用' }}
-              </el-tag>
-            </template>
-          </el-table-column>
-          <el-table-column label="操作" width="120">
-            <template slot-scope="scope">
-              <el-button size="mini" @click="handleEditSupplier(scope.row)">编辑</el-button>
-              <el-button size="mini" type="danger" @click="handleDeleteSupplier(scope.row)">删除</el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-      </div>
-    </el-dialog>
-
-    <!-- Supplier Form Dialog -->
-    <el-dialog :visible.sync="supplierFormVisible" :title="isEditSupplier ? '编辑供应商' : '添加供应商'" width="450px">
-      <el-form :model="supplierForm" label-width="100px">
-        <el-form-item label="供应商名称">
-          <el-input v-model="supplierForm.name" placeholder="如: XX供货商"></el-input>
-        </el-form-item>
-        <el-form-item label="联系人">
-          <el-input v-model="supplierForm.contactName"></el-input>
-        </el-form-item>
-        <el-form-item label="联系电话">
-          <el-input v-model="supplierForm.contactPhone"></el-input>
-        </el-form-item>
-        <el-form-item label="佣金比例">
-          <el-input-number v-model="supplierForm.commissionRate" :min="0" :max="1" :step="0.01"></el-input-number>
-        </el-form-item>
-      </el-form>
-      <div slot="footer">
-        <el-button @click="supplierFormVisible = false">取消</el-button>
-        <el-button type="primary" @click="handleSupplierSubmit">保存</el-button>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import { getSupplySystemList, createSupplySystem, updateSupplySystem, toggleSupplySystemStatus,
-         getSupplierList, createSupplier, updateSupplier, deleteSupplier } from '@/api/admin'
-
-export default {
-  name: 'SupplySystem',
-  data() {
-    return {
-      systems: [],
-      keyword: '',
-      systemDialogVisible: false,
-      isEditSystem: false,
-      systemForm: {
-        id: null,
-        name: '',
-        description: '',
-        settlementPeriodDays: 30,
-        platformProfitRate: 0,
-        status: 'ACTIVE'
-      },
-      supplierDialogVisible: false,
-      currentSystem: null,
-      suppliers: [],
-      supplierFormVisible: false,
-      isEditSupplier: false,
-      supplierForm: {
-        id: null,
-        name: '',
-        contactName: '',
-        contactPhone: '',
-        commissionRate: 0,
-        supplySystemId: null
-      }
-    }
-  },
-  computed: {
-    filteredData() {
-      if (!this.keyword) return this.systems
-      const kw = this.keyword.toLowerCase()
-      return this.systems.filter(item => item.name?.toLowerCase().includes(kw))
-    }
-  },
-  created() {
-    this.loadData()
-  },
-  methods: {
-    async loadData() {
-      try {
-        const res = await getSupplySystemList()
-        if (res.data) {
-          this.systems = res.data
-        }
-      } catch (e) {
-        console.error(e)
-      }
-    },
-    handleCreateSystem() {
-      this.isEditSystem = false
-      this.systemForm = { id: null, name: '', description: '', settlementPeriodDays: 30, platformProfitRate: 0, status: 'ACTIVE' }
-      this.systemDialogVisible = true
-    },
-    handleEditSystem(row) {
-      this.isEditSystem = true
-      this.systemForm = { ...row }
-      this.systemDialogVisible = true
-    },
-    async toggleSystemStatus(row) {
-      try {
-        const res = await toggleSupplySystemStatus(row.id)
-        if (res.code === 'OK') {
-          this.$message.success('状态已更新')
-          this.loadData()
-        }
-      } catch (e) {
-        this.$message.error('更新失败')
-      }
-    },
-    async handleSystemSubmit() {
-      try {
-        if (this.isEditSystem) {
-          await updateSupplySystem(this.systemForm)
-        } else {
-          await createSupplySystem(this.systemForm)
-        }
-        this.$message.success('保存成功')
-        this.systemDialogVisible = false
-        this.loadData()
-      } catch (e) {
-        this.$message.error('保存失败')
-      }
-    },
-    async handleManageSupplier(system) {
-      this.currentSystem = system
-      this.supplierDialogVisible = true
-      try {
-        const res = await getSupplierList(system.id)
-        if (res.data) {
-          this.suppliers = res.data
-        }
-      } catch (e) {
-        console.error(e)
-      }
-    },
-    handleSupplierDialogClose() {
-      this.currentSystem = null
-      this.suppliers = []
-    },
-    handleCreateSupplier() {
-      this.isEditSupplier = false
-      this.supplierForm = { id: null, name: '', contactName: '', contactPhone: '', commissionRate: 0, supplySystemId: this.currentSystem.id }
-      this.supplierFormVisible = true
-    },
-    handleEditSupplier(row) {
-      this.isEditSupplier = true
-      this.supplierForm = { ...row }
-      this.supplierFormVisible = true
-    },
-    async handleDeleteSupplier(row) {
-      try {
-        await this.$confirm('确定删除该供应商吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
-        await deleteSupplier(row.id)
-        this.$message.success('删除成功')
-        const res = await getSupplierList(this.currentSystem.id)
-        if (res.data) {
-          this.suppliers = res.data
-        }
-      } catch (e) {
-        if (e !== 'cancel') {
-          this.$message.error('删除失败')
-        }
-      }
-    },
-    async handleSupplierSubmit() {
-      try {
-        if (this.isEditSupplier) {
-          await updateSupplier(this.supplierForm)
-        } else {
-          await createSupplier(this.supplierForm)
-        }
-        this.$message.success('保存成功')
-        this.supplierFormVisible = false
-        const res = await getSupplierList(this.currentSystem.id)
-        if (res.data) {
-          this.suppliers = res.data
-        }
-      } catch (e) {
-        this.$message.error('保存失败')
-      }
-    },
-    handleSearch() {}
-  }
-}
-</script>
-
-<style scoped>
-.supply-system {
-  padding: 20px;
-}
-</style>