feat: 福安德智慧报价平台 - 完整业务模块

基于RuoYi-Vue2构建的智慧采购报价平台,包含:

后端(Spring Boot + MyBatis):
- 物料管理 (BizMaterial)
- 供应商管理 (BizSupplier)
- 报价请求RFQ (BizRfq)
- 供应商报价单 (BizQuotation)
- 智慧比价分析 (BizComparison)
- 采购单 (BizPurchaseOrder)
- 供应商评价 (BizSupplierEvaluation)
- 订单异议 (BizOrderObjection)
- 交易记录 (BizTransaction)
- 租户管理-SaaS数据隔离 (BizTenant)

前端(Vue2 + Element UI):
- 10个业务模块完整页面
- ERPNext风格主题(蓝色系)
- 福安德品牌logo

部署:
- Docker Compose一键部署
- MySQL 8.0 + Redis 7 + Nginx
- 前端端口 10031
This commit is contained in:
2026-05-22 09:36:01 +08:00
parent 7da12b0c07
commit 2941cd23c4
106 changed files with 5511 additions and 92 deletions

View File

@@ -0,0 +1,20 @@
package com.ruoyi.web.controller.bid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.service.bid.IBizComparisonService;
@RestController
@RequestMapping("/bid/comparison")
public class BizComparisonController extends BaseController {
@Autowired private IBizComparisonService service;
@PreAuthorize("@ss.hasPermi('bid:comparison:list')")
@GetMapping("/rfq/{rfqId}")
public AjaxResult compareRfq(@PathVariable Long rfqId) {
return success(service.compareRfq(rfqId, 1L));
}
}

View File

@@ -0,0 +1,57 @@
package com.ruoyi.web.controller.bid;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.bid.BizMaterial;
import com.ruoyi.system.service.bid.IBizMaterialService;
@RestController
@RequestMapping("/bid/material")
public class BizMaterialController extends BaseController {
@Autowired
private IBizMaterialService service;
@PreAuthorize("@ss.hasPermi('bid:material:list')")
@GetMapping("/list")
public TableDataInfo list(BizMaterial query) {
startPage();
List<BizMaterial> list = service.selectBizMaterialList(query);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('bid:material:query')")
@GetMapping("/{id}")
public AjaxResult getInfo(@PathVariable Long id) {
return success(service.selectBizMaterialById(id));
}
@PreAuthorize("@ss.hasPermi('bid:material:add')")
@Log(title = "物料管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BizMaterial record) {
record.setCreateBy(getUsername());
return toAjax(service.insertBizMaterial(record));
}
@PreAuthorize("@ss.hasPermi('bid:material:edit')")
@Log(title = "物料管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BizMaterial record) {
record.setUpdateBy(getUsername());
return toAjax(service.updateBizMaterial(record));
}
@PreAuthorize("@ss.hasPermi('bid:material:remove')")
@Log(title = "物料管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{materialIds}")
public AjaxResult remove(@PathVariable Long[] materialIds) {
return toAjax(service.deleteBizMaterialByIds(materialIds));
}
}

View File

@@ -0,0 +1,52 @@
package com.ruoyi.web.controller.bid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.bid.BizOrderObjection;
import com.ruoyi.system.service.bid.IBizOrderObjectionService;
@RestController
@RequestMapping("/bid/objection")
public class BizOrderObjectionController extends BaseController {
@Autowired private IBizOrderObjectionService service;
@PreAuthorize("@ss.hasPermi('bid:objection:list')")
@GetMapping("/list")
public TableDataInfo list(BizOrderObjection q) {
startPage();
return getDataTable(service.selectBizOrderObjectionList(q));
}
@PreAuthorize("@ss.hasPermi('bid:objection:query')")
@GetMapping("/{objectionId}")
public AjaxResult getInfo(@PathVariable Long objectionId) {
return success(service.selectBizOrderObjectionById(objectionId));
}
@Log(title = "订单异议", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BizOrderObjection record) {
record.setCreateBy(getUsername());
return toAjax(service.insertBizOrderObjection(record));
}
@PreAuthorize("@ss.hasPermi('bid:objection:edit')")
@Log(title = "处理异议", businessType = BusinessType.UPDATE)
@PutMapping("/resolve")
public AjaxResult resolve(@RequestBody BizOrderObjection record) {
record.setStatus("resolved");
return toAjax(service.updateBizOrderObjection(record));
}
@Log(title = "订单异议", businessType = BusinessType.DELETE)
@DeleteMapping("/{objectionIds}")
public AjaxResult remove(@PathVariable Long[] objectionIds) {
return toAjax(service.deleteBizOrderObjectionByIds(objectionIds));
}
}

View File

@@ -0,0 +1,61 @@
package com.ruoyi.web.controller.bid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.bid.BizPurchaseOrder;
import com.ruoyi.system.service.bid.IBizPurchaseOrderService;
@RestController
@RequestMapping("/bid/purchaseorder")
public class BizPurchaseOrderController extends BaseController {
@Autowired private IBizPurchaseOrderService service;
@PreAuthorize("@ss.hasPermi('bid:purchaseorder:list')")
@GetMapping("/list")
public TableDataInfo list(BizPurchaseOrder q) {
startPage();
return getDataTable(service.selectBizPurchaseOrderList(q));
}
@PreAuthorize("@ss.hasPermi('bid:purchaseorder:query')")
@GetMapping("/{poId}")
public AjaxResult getInfo(@PathVariable Long poId) {
return success(service.selectBizPurchaseOrderById(poId));
}
@PreAuthorize("@ss.hasPermi('bid:purchaseorder:add')")
@Log(title = "采购单", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BizPurchaseOrder po) {
po.setCreateBy(getUsername());
return toAjax(service.insertBizPurchaseOrder(po));
}
@PreAuthorize("@ss.hasPermi('bid:purchaseorder:edit')")
@Log(title = "采购单", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BizPurchaseOrder po) {
po.setUpdateBy(getUsername());
return toAjax(service.updateBizPurchaseOrder(po));
}
@PreAuthorize("@ss.hasPermi('bid:purchaseorder:edit')")
@Log(title = "确认采购单", businessType = BusinessType.UPDATE)
@PutMapping("/confirm/{poId}")
public AjaxResult confirm(@PathVariable Long poId) {
return toAjax(service.confirmOrder(poId));
}
@PreAuthorize("@ss.hasPermi('bid:purchaseorder:remove')")
@Log(title = "采购单", businessType = BusinessType.DELETE)
@DeleteMapping("/{poIds}")
public AjaxResult remove(@PathVariable Long[] poIds) {
return toAjax(service.deleteBizPurchaseOrderByIds(poIds));
}
}

View File

@@ -0,0 +1,73 @@
package com.ruoyi.web.controller.bid;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.bid.BizQuotation;
import com.ruoyi.system.service.bid.IBizQuotationService;
@RestController
@RequestMapping("/bid/quotation")
public class BizQuotationController extends BaseController {
@Autowired private IBizQuotationService service;
@PreAuthorize("@ss.hasPermi('bid:quotation:list')")
@GetMapping("/list")
public TableDataInfo list(BizQuotation q) {
startPage();
return getDataTable(service.selectBizQuotationList(q));
}
@PreAuthorize("@ss.hasPermi('bid:quotation:query')")
@GetMapping("/{quotationId}")
public AjaxResult getInfo(@PathVariable Long quotationId) {
return success(service.selectBizQuotationById(quotationId));
}
@PreAuthorize("@ss.hasPermi('bid:quotation:add')")
@Log(title = "报价单", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BizQuotation q) {
q.setCreateBy(getUsername());
return toAjax(service.insertBizQuotation(q));
}
@PreAuthorize("@ss.hasPermi('bid:quotation:edit')")
@Log(title = "报价单", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BizQuotation q) {
return toAjax(service.updateBizQuotation(q));
}
@PreAuthorize("@ss.hasPermi('bid:quotation:edit')")
@Log(title = "提交报价", businessType = BusinessType.UPDATE)
@PutMapping("/submit/{quotationId}")
public AjaxResult submit(@PathVariable Long quotationId) {
return toAjax(service.submitQuotation(quotationId));
}
@PreAuthorize("@ss.hasPermi('bid:quotation:edit')")
@PutMapping("/accept/{quotationId}")
public AjaxResult accept(@PathVariable Long quotationId) {
return toAjax(service.acceptQuotation(quotationId));
}
@PreAuthorize("@ss.hasPermi('bid:quotation:edit')")
@PutMapping("/reject/{quotationId}")
public AjaxResult reject(@PathVariable Long quotationId) {
return toAjax(service.rejectQuotation(quotationId));
}
@PreAuthorize("@ss.hasPermi('bid:quotation:remove')")
@Log(title = "报价单", businessType = BusinessType.DELETE)
@DeleteMapping("/{quotationIds}")
public AjaxResult remove(@PathVariable Long[] quotationIds) {
return toAjax(service.deleteBizQuotationByIds(quotationIds));
}
}

View File

@@ -0,0 +1,69 @@
package com.ruoyi.web.controller.bid;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.bid.BizRfq;
import com.ruoyi.system.domain.bid.BizRfqItem;
import com.ruoyi.system.service.bid.IBizRfqService;
@RestController
@RequestMapping("/bid/rfq")
public class BizRfqController extends BaseController {
@Autowired private IBizRfqService service;
@PreAuthorize("@ss.hasPermi('bid:rfq:list')")
@GetMapping("/list")
public TableDataInfo list(BizRfq query) {
startPage();
return getDataTable(service.selectBizRfqList(query));
}
@PreAuthorize("@ss.hasPermi('bid:rfq:query')")
@GetMapping("/{rfqId}")
public AjaxResult getInfo(@PathVariable Long rfqId) {
return success(service.selectBizRfqById(rfqId));
}
@PreAuthorize("@ss.hasPermi('bid:rfq:query')")
@GetMapping("/{rfqId}/items")
public AjaxResult getItems(@PathVariable Long rfqId) {
return success(service.selectItemsByRfqId(rfqId));
}
@PreAuthorize("@ss.hasPermi('bid:rfq:add')")
@Log(title = "报价请求", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BizRfq rfq) {
rfq.setCreateBy(getUsername());
return toAjax(service.insertBizRfq(rfq));
}
@PreAuthorize("@ss.hasPermi('bid:rfq:edit')")
@Log(title = "报价请求", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BizRfq rfq) {
rfq.setUpdateBy(getUsername());
return toAjax(service.updateBizRfq(rfq));
}
@PreAuthorize("@ss.hasPermi('bid:rfq:publish')")
@Log(title = "发布RFQ", businessType = BusinessType.UPDATE)
@PutMapping("/publish/{rfqId}")
public AjaxResult publish(@PathVariable Long rfqId, @RequestBody Long[] supplierIds) {
return toAjax(service.publishRfq(rfqId, supplierIds));
}
@PreAuthorize("@ss.hasPermi('bid:rfq:remove')")
@Log(title = "报价请求", businessType = BusinessType.DELETE)
@DeleteMapping("/{rfqIds}")
public AjaxResult remove(@PathVariable Long[] rfqIds) {
return toAjax(service.deleteBizRfqByIds(rfqIds));
}
}

View File

@@ -0,0 +1,57 @@
package com.ruoyi.web.controller.bid;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.bid.BizSupplier;
import com.ruoyi.system.service.bid.IBizSupplierService;
@RestController
@RequestMapping("/bid/supplier")
public class BizSupplierController extends BaseController {
@Autowired
private IBizSupplierService service;
@PreAuthorize("@ss.hasPermi('bid:supplier:list')")
@GetMapping("/list")
public TableDataInfo list(BizSupplier query) {
startPage();
List<BizSupplier> list = service.selectBizSupplierList(query);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('bid:supplier:query')")
@GetMapping("/{id}")
public AjaxResult getInfo(@PathVariable Long id) {
return success(service.selectBizSupplierById(id));
}
@PreAuthorize("@ss.hasPermi('bid:supplier:add')")
@Log(title = "供应商管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BizSupplier record) {
record.setCreateBy(getUsername());
return toAjax(service.insertBizSupplier(record));
}
@PreAuthorize("@ss.hasPermi('bid:supplier:edit')")
@Log(title = "供应商管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BizSupplier record) {
record.setUpdateBy(getUsername());
return toAjax(service.updateBizSupplier(record));
}
@PreAuthorize("@ss.hasPermi('bid:supplier:remove')")
@Log(title = "供应商管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{supplierIds}")
public AjaxResult remove(@PathVariable Long[] supplierIds) {
return toAjax(service.deleteBizSupplierByIds(supplierIds));
}
}

View File

@@ -0,0 +1,44 @@
package com.ruoyi.web.controller.bid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.bid.BizSupplierEvaluation;
import com.ruoyi.system.service.bid.IBizSupplierEvaluationService;
@RestController
@RequestMapping("/bid/evaluation")
public class BizSupplierEvaluationController extends BaseController {
@Autowired private IBizSupplierEvaluationService service;
@PreAuthorize("@ss.hasPermi('bid:evaluation:list')")
@GetMapping("/list")
public TableDataInfo list(BizSupplierEvaluation q) {
startPage();
return getDataTable(service.selectBizSupplierEvaluationList(q));
}
@PreAuthorize("@ss.hasPermi('bid:evaluation:query')")
@GetMapping("/{evalId}")
public AjaxResult getInfo(@PathVariable Long evalId) {
return success(service.selectBizSupplierEvaluationById(evalId));
}
@Log(title = "供应商评价", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BizSupplierEvaluation record) {
record.setEvaluator(getUsername());
return toAjax(service.insertBizSupplierEvaluation(record));
}
@Log(title = "供应商评价", businessType = BusinessType.DELETE)
@DeleteMapping("/{evalIds}")
public AjaxResult remove(@PathVariable Long[] evalIds) {
return toAjax(service.deleteBizSupplierEvaluationByIds(evalIds));
}
}

View File

@@ -0,0 +1,57 @@
package com.ruoyi.web.controller.bid;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.bid.BizTenant;
import com.ruoyi.system.service.bid.IBizTenantService;
@RestController
@RequestMapping("/bid/tenant")
public class BizTenantController extends BaseController {
@Autowired
private IBizTenantService service;
@PreAuthorize("@ss.hasPermi('bid:tenant:list')")
@GetMapping("/list")
public TableDataInfo list(BizTenant query) {
startPage();
List<BizTenant> list = service.selectBizTenantList(query);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('bid:tenant:query')")
@GetMapping("/{id}")
public AjaxResult getInfo(@PathVariable Long id) {
return success(service.selectBizTenantById(id));
}
@PreAuthorize("@ss.hasPermi('bid:tenant:add')")
@Log(title = "租户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BizTenant record) {
record.setCreateBy(getUsername());
return toAjax(service.insertBizTenant(record));
}
@PreAuthorize("@ss.hasPermi('bid:tenant:edit')")
@Log(title = "租户管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BizTenant record) {
record.setUpdateBy(getUsername());
return toAjax(service.updateBizTenant(record));
}
@PreAuthorize("@ss.hasPermi('bid:tenant:remove')")
@Log(title = "租户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{tenantIds}")
public AjaxResult remove(@PathVariable Long[] tenantIds) {
return toAjax(service.deleteBizTenantByIds(tenantIds));
}
}

View File

@@ -0,0 +1,29 @@
package com.ruoyi.web.controller.bid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.bid.BizTransaction;
import com.ruoyi.system.service.bid.IBizTransactionService;
@RestController
@RequestMapping("/bid/transaction")
public class BizTransactionController extends BaseController {
@Autowired private IBizTransactionService service;
@PreAuthorize("@ss.hasPermi('bid:transaction:list')")
@GetMapping("/list")
public TableDataInfo list(BizTransaction q) {
startPage();
return getDataTable(service.selectBizTransactionList(q));
}
@PreAuthorize("@ss.hasPermi('bid:transaction:query')")
@GetMapping("/{txId}")
public AjaxResult getInfo(@PathVariable Long txId) {
return success(service.selectBizTransactionById(txId));
}
}

View File

@@ -0,0 +1,50 @@
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
master:
url: ${SPRING_DATASOURCE_URL:jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8}
username: ${SPRING_DATASOURCE_USERNAME:root}
password: ${SPRING_DATASOURCE_PASSWORD:password}
slave:
enabled: false
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
connectTimeout: 30000
socketTimeout: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
maxEvictableIdleTimeMillis: 900000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: false
filter:
stat:
enabled: true
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
redis:
host: ${SPRING_REDIS_HOST:localhost}
port: ${SPRING_REDIS_PORT:6379}
password: ${SPRING_REDIS_PASSWORD:}
database: 0
timeout: 10s
lettuce:
pool:
min-idle: 0
max-idle: 8
max-active: 8
max-wait: -1ms