ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

大型超市生鲜数据处理系统:Flask+Vue前后端分离实战解析

大型超市生鲜数据处理系统:Flask+Vue前后端分离实战解析 大型超市生鲜数据处理系统说难不难说简单也真不简单。很多人一听到“生鲜处理”就觉得是称重、打标签、收银那点事但真正做进系统里才发现生鲜的数据链路比普通百货长得多而且每个环节都有“时效性”采购得看价格波动库存得盯保质期损耗要按批次算定价还得跟着当日行情走。我最近用 Python Flask 做后端、Vue 做前端完整设计并实现了一套面向大型超市的生鲜数据处理系统借着这篇文章把从架构到落地的关键细节都梳理一遍希望能帮到正在做类似管理系统、尤其是想走前后端分离路线的朋友。这套系统的核心价值是把生鲜从“入库—在库—出库—损耗—销售”的完整数据流打通让管理人员能随时看到每批生鲜“还剩多少、放了几几天、损耗是否异常、该不该降价清库存”。如果没有系统辅助这些数据通常散落在采购单、入库单、电子秤和收银机里月底一盘点全是糊涂账。用 Flask 是因为它足够轻巧接口开发效率高用 Vue 是因为页面交互频繁尤其是数据看板和批量录入场景组件化开发更容易维护。1. 系统整体设计与技术选型1.1 生鲜数据到底要处理什么先别急着写代码得把业务对象理清楚。大型超市里的生鲜品类一般包括蔬菜、水果、肉禽、水产、熟食、面包这几大类每一类都有自己的管理习惯。比如肉禽和水产按批次进货必须记录生产日期和保质期蔬菜水果损耗极大可能早上进 100 斤晚上称重只卖出 80 斤中间“失踪”的 20 斤就是损耗熟食和面包当天生产当天售临期就得打折处理。从数据角度看系统至少得管理以下几类数据商品基础信息商品编码、名称、品类、默认单位、条码、允许损耗率、默认供应商、存储方式。采购与供应商信息供应商编号、联系方式、供货周期、历史进价、信用等级。批次与库存信息采购批次号、进货日期、生产日期、到期日期、进货量、剩余量、当前状态。销售与价格信息实时售价、会员价、促销价、促销开始结束时间、日销量、日销售额。损耗与盘点信息损耗单号、损耗原因、损耗数量、盘点差异、责任人。预警与决策信息临期预警、库存周转天数、损耗异常告警、价格调整建议。这些信息之间存在天然的主从关系一个商品有多个采购批次每个批次产生一批库存记录每次销售和损耗都关联到具体批次的商品。搞清楚了这些实体和关联数据库设计才不会乱。1.2 为什么用 Flask Vue而不是前后端不分离或重型框架我在选型时没有选 Django也没有用传统的 Flask Jinja2 模板渲染方案而是坚持前后端分离后端 Flask 只出 JSON 接口前端用 Vue 独立工程。原因很简单第一生鲜数据处理系统中交互场景非常碎片化。比如采购员录入一张采购单需要动态增加行、选择品类后自动带出供应商和历史进价、填写完数量后实时计算总价店长看数据看板时要频繁切换日期范围、只看某个品类、展开某个批次。这种交互如果用服务端模板页面实现就得不断刷新页面体验很差。第二Vue 的组件化很适合这种系统。我把“批次库存卡片”“价格趋势图”“损耗原因弹窗”“批量改价表格”都封装成组件后续换项目也好复用。第三Flask 足够轻写接口非常直接。生鲜系统本身不是高并发场景日均几百个用户操作Flask 完全扛得住。相比 Spring Boot 那种重框架Python 生态里做数据处理又是天然优势——pandas、sklearn 这些库可以直接用后续做销量预测或者智能化定价决策都方便。1.3 项目目录结构与模块划分我习惯把一个前后端分离项目分成两个独立子工程后端只负责数据管理和算法服务前端只负责展示与交互。目录结构参考fresh-data-system/ ├── backend/ │ ├── app.py # Flask 入口注册蓝图 │ ├── config.py # 配置文件数据库、上传路径、密钥 │ ├── models/ │ │ ├── __init__.py # SQLAlchemy 实例化 │ │ ├── product.py # 商品与品类模型 │ │ ├── batch.py # 批次与库存模型 │ │ └── loss_sale.py # 损耗与销售模型 │ ├── api/ │ │ ├── __init__.py │ │ ├── product_api.py # 商品管理接口 │ │ ├── inventory_api.py # 库存/批次相关接口 │ │ ├── loss_api.py # 损耗登记接口 │ │ └── dashboard_api.py # 看板统计接口 │ ├── services/ │ │ ├── import_service.py # 数据导入清洗 │ │ ├── alert_service.py # 临期/损耗预警 │ │ └── stat_service.py # 统计计算 │ └── requirements.txt ├── frontend/ │ ├── src/ │ │ ├── main.js # Vue 入口 │ │ ├── router/index.js # 路由 │ │ ├── api/ # 封装 axios 请求 │ │ ├── views/ # 页面组件 │ │ ├── components/ # 通用组件 │ │ └── store/ # Pinia 状态管理 │ ├── vite.config.js │ └── package.json └── docs/这样的划分有几个好处接口模块和业务逻辑分离以后换掉数据源或者增加算法不至于动到接口层前端每个页面对应一个视图文件定位问题快另外采购、库存、销售三个人的开发任务可以并行不用相互等待。2. 数据库模型与业务表设计2.1 核心实体关系拆解实体关系是这类系统最需要谨慎的部分。我的做法是“商品档案 批次库存 流水记录”三段式设计。商品档案描述“这是什么”批次库存描述“这一批从哪来、还剩多少”流水记录描述“发生了什么变化”。这样设计的好处是能精确追溯每一笔损耗属于哪个批次而不是笼统地把损耗扣在商品上否则月底時一批菜到底烂了多少根本算不清。核心的表我设计了 6 张category品类表一级分类和二级分类比如“蔬菜-叶菜类”“水果-热带水果”。product商品表商品基础信息关联品类和默认供应商。supplier供应商表。purchase_batch采购批次表一次采购形成一批包含采购日期、供应商、进货成本。inventory批次库存表一个采购批次对应多条库存记录记录剩余数量、当前状态在售/临期/废弃。loss_record损耗记录表每次报损记录数量、原因、操作人。sale_record销售流水表记录每日/每单销售中关联到商品批次的数量和金额。关于销售是否直接关联批次我见很多简单系统为了省事只关联商品编码不关联批次。这在生鲜场景会出问题不同批次的进价不同毛利分析会失真而且无法实现对临期库存优先出库的监控。所以即使录入成本高一点我也坚持在销售明细行记录batch_id。2.2 建表SQL与Flask-SQLAlchemy模型示例以库存批次表为例PostgreSQL 建表语句大致如下CREATE TABLE inventory ( inventory_id SERIAL PRIMARY KEY, batch_id INTEGER NOT NULL REFERENCES purchase_batch(batch_id), product_id INTEGER NOT NULL REFERENCES product(product_id), supplier_id INTEGER REFERENCES supplier(supplier_id), total_quantity NUMERIC(10,3) NOT NULL, remain_quantity NUMERIC(10,3) NOT NULL, unit VARCHAR(10) NOT NULL DEFAULT kg, production_date DATE, expiry_date DATE, storage_type VARCHAR(20), status VARCHAR(10) DEFAULT normal, created_at TIMESTAMP DEFAULT now() ); CREATE INDEX idx_inventory_expiry ON inventory(expiry_date); CREATE INDEX idx_inventory_product_status ON inventory(product_id, status);用 Flask-SQLAlchemy 建模时对应的模型类可以这样写from extension import db class Inventory(db.Model): __tablename__ inventory inventory_id db.Column(db.Integer, primary_keyTrue) batch_id db.Column(db.Integer, db.ForeignKey(purchase_batch.batch_id)) product_id db.Column(db.Integer, db.ForeignKey(product.product_id)) supplier_id db.Column(db.Integer, db.ForeignKey(supplier.supplier_id)) total_quantity db.Column(db.Numeric(10, 3), nullableFalse) remain_quantity db.Column(db.Numeric(10, 3), nullableFalse) unit db.Column(db.String(10), defaultkg) production_date db.Column(db.Date) expiry_date db.Column(db.Date) storage_type db.Column(db.String(20)) status db.Column(db.String(10), defaultnormal) # normal / expiring / expired / sold_out这里有几个容易踩的细节数量全部用NUMERIC(10,3)别用浮点类型否则称重小数累计后会出现 0.30000000000000004 这种脏数据状态字段要留够枚举空间不要只放数字状态码否则前端看值还要猜意思expiry_date必须有索引因为临期预警查询会频繁按日期过滤。2.3 关键字段设计商品编码、批次、保质期、单位换算大型超市生鲜和普通商品最大的不同是单位换算。蔬菜有时按“斤”进、按“份”卖肉类按“公斤”入库、按“盒”上架。如果数据库里只存一个单位过几天就会出乱子。我专门在商品表加了两个字段base_unit基础库存单位比如 kg。sale_unit销售单位比如 盒/g。unit_ratio销售单位与库存单位的换算比例比如 1 盒 0.5 kg。换算比例在批次库存和销售统计里都要用到。计算日销量时我会把销售数量统一折算回基础单位否则盘点时库存数量是公斤销售明细却是盒两边永远对不上。商品编码也要设计得有一点语义。比如用大类(2位)-小类(2位)-商品序号(4位)的结构01-03-0027表示“蔬菜-叶菜类-油麦菜”。这种编码在接收采购单 Excel 时特别好用系统可以根据编码前几位自动判断品类并带出默认损耗率。保质期字段则是预警的基础。生鲜商品的保质期可能只有 3 到 7 天所以系统需要同时存production_date和expiry_date。我还会计算一个衍生字段remain_days不过建议这个值在查询时动态算不要落库因为每天日期都在变落库还要刷数据。3. 后端接口与数据处理逻辑实现3.1 数据导入与多源采集设计生鲜系统的数据源头很杂总部下发的基础资料往往是 Excel 文件供应商交货单可能是 PDF 表格门店自己的 POS 销售数据存在数据库中。我选择优先支持 Excel 导入因为 PDF 识别太不稳定而且不是每个供应商都有接口。数据导入我单独写了一个import_service.py主要流程是接收前端上传的.xlsx文件用pandas.read_excel读取。做字段整理把中文表头映射到数据库字段名比如“商品名称”到product_name。数据校验检查必填字段、格式、编码是否存在单价是否为数字、日期是否合法。清洗转换把“1kg”“2公斤”这种混合单位写法解析成统一数字加单位。写入数据库前先做批量去重避免同一天重复导入。这里分享一个经验不要在接口里一步步循环读取 Excel 后逐条插入通常一次采购表有几百行逐条插会比较慢。先用 pandas 把数据整理成 DataFrame然后一次性db.session.add_all提交实测从 2000 行 Excel 导入到 PostgreSQL 也就一秒钟左右。数据清洗部分会遇到很多现实问题。比如 Excel 里“生产日期”可能是 2024/1/5、2024-01-05、20240105 三种格式需要统一转成 date 类型数量列可能混入空格和中文要先正则清洗同一家供应商的名称这次叫“绿源农业发展有限公司”下次叫“绿源农业”我就用一个清洗函数统一去除“有限公司”后缀再做匹配。3.2 损耗率/保鲜期预警算法这部分是系统的亮点也是让店长真正觉得“有用”的功能。损耗率不能只做事后统计那样等月底看报表已经晚了要结合上架天数、剩余数量、历史损耗规律做动态预期。我先定义了三个核心指标当前损耗率 累计损耗数量 / 批次入库数量。剩余货架天数 到期日期 - 当前日期。预估生命期 根据品类历史平均损耗曲线得到的天数。预警逻辑用几个阈值划分def judge_inventory_status(inv, todayNone): today today or date.today() remain_days (inv.expiry_date - today).days # 如果剩余货架天数小于等于 1 天直接标记“临期” if remain_days 1: return expiring # 如果已经过期 if remain_days 0: return expired # 损耗率阈值按品类配置叶菜类一般是 8%水果 5% threshold get_category_loss_threshold(inv.category_id) current_loss_rate inv.loss_quantity / inv.total_quantity if current_loss_rate threshold * 1.2: return abnormal_loss return normal这个函数只是状态判断真正要出“操作建议”我还会结合库存量和销量做一个销售天数估算预计售罄天数 剩余数量 / 近7日日均销量。如果预计售罄天数大于剩余货架天数就说明这批货大概率卖不完系统会在看板里提示“建议降价促销”或“转移至临期专区”。这个逻辑本质是库存周转和保质期的双因素判断比单独看保质期要合理得多。需要注意的是品类阈值最好做成一个配置表而不是硬编码。因为叶菜和冻品的损耗特征差异太大硬编码做不了后续调优。3.3 基于Flask的RESTful API封装后端接口我采用 Blueprint 拆分模块每个模块维护自己的路由。为了统一返回格式我写了一个响应辅助函数def ok(dataNone, messagesuccess): return jsonify({code: 0, message: message, data: data}) def fail(messageerror, code400): return jsonify({code: code, message: message, data: None}), code库存查询接口看起来大概是这样inventory_api.route(/list, methods[GET]) def list_inventory(): product_id request.args.get(product_id, typeint) status request.args.get(status, defaultnormal) page request.args.get(page, default1, typeint) per_page request.args.get(per_page, default20, typeint) query Inventory.query if product_id: query query.filter(Inventory.product_id product_id) if status: query query.filter(Inventory.status status) total query.count() rows query.order_by(Inventory.expiry_date.asc()) \ .offset((page - 1) * per_page) \ .limit(per_page).all() result [] for inv in rows: result.append({ inventory_id: inv.inventory_id, product_name: inv.product.product_name, remain_quantity: str(inv.remain_quantity), unit: inv.unit, expiry_date: inv.expiry_date.isoformat() if inv.expiry_date else None, status: inv.status }) return ok({total: total, items: result})这个接口有几个值得注意的地方分页参数用typeint做类型转换避免前端传字符串导致比较出问题expiry_date转成isoformat否则前端拿到的是“Fri, 20 Dec 2024”这种非标准格式非常难解析数值型字段用str()转成字符串返回因为前端 JavaScript 处理高精度数值有时会丢精度比如1234.567在 JS 里没太大问题但9999999.999会转成科学计数法稳妥起见后端统一转字符串。所有写操作接口我还会统一做事务提交和异常回滚try: db.session.add(inventory) db.session.commit() except SQLAlchemyError: db.session.rollback() return fail(数据库操作失败, 500)这样即使某个字段写坏也不至于让整个数据库停留在脏状态。3.4 与Vue端的接口联调细节前后端联调最折磨人的不是接口功能而是那些“小摩擦”。我总结几个让联调顺畅的办法时间字段统一用 ISO 字符串传输前端拿到后直接new Date(value)不要自己拼格式。金额和数量统一用后端字符串返回前端显示时直接用计算时再parseFloat。分页接口统一接收page和per_page返回total和items不要一个接口一个风格。统一的错误码约定code0成功非 0 为业务错误。前端可以用 axios 拦截器统一处理。开发环境下我用 Vite 的代理解决跨域生产环境用后端所在服务的 Nginx 反向代理。Flask 侧不用开CORS插件也能正常联动这个后面部署部分细说。4. 前端Vue页面与可视化呈现4.1 项目初始化与路由布局前端我用 Vue 3 Vite Element Plus 这套组合初始化命令就是常规的npm create vitelatest frontend -- --template vue。组件库选 Element Plus 主要因为表格和表单都比较完善生鲜系统里最常用的就是各种数据表格不需要花时间造轮子。路由设计上我按照角色和使用场景划分页面const routes [ { path: /dashboard, name: Dashboard, component: () import(/views/Dashboard.vue), meta: { title: 数据看板, requiresAuth: true, roles: [admin, manager] } }, { path: /product, name: Product, component: () import(/views/Product.vue), meta: { title: 商品管理, requiresAuth: true, roles: [admin, buyer] } }, { path: /inventory, name: Inventory, component: () import(/views/Inventory.vue), meta: { title: 库存批次, requiresAuth: true, roles: [admin, manager, buyer] } }, { path: /loss, name: Loss, component: () import(/views/Loss.vue), meta: { title: 损耗登记, requiresAuth: true, roles: [admin, storekeeper] } } ]路由懒加载是必须的否则首屏会把所有页面都打包进去加载速度会慢很多。meta里的roles字段配合导航守卫做权限跳转比如普通店员访问看板时就重定向到提示页。4.2 数据看板图表实时刷新数据看板是给店长和采购经理看的“驾驶舱”。我默认放了四个核心图表近 7 日生鲜销售额趋势折线图。今日各品类销售占比环形图。当前库存金额 TOP10 品类横向柱状图。临期预警商品列表表格 颜色标记。图表我统一用 ECharts通过vue-echarts封装成组件。数据加载时用watch监听日期范围变化重新请求接口而不是每次手动刷新页面。为了兼顾“实时”我用了 30 秒轮询请求销售额接口。生鲜系统的数据变化不像股票那么频繁轮询完全够用没必要引入 WebSocket 增加复杂度。但轮询有个坑每次拿到数据后如果直接替换图表 option图表会有明显的闪烁。解决方法是调用chart.setOption(option, true)时把notMerge设为 true这样图表会重新渲染而不是增量更新虽然视觉效果稍微生硬但至少不会出现残留数据。看板中还有一个我特别得意的组件临期预警卡片。每张卡片显示商品名、剩余天数、剩余数量和损耗率剩余天数小于 1 天的卡片背景自动变成浅红色同时显示“建议立即处理”的 Tag。这个组件用 Vue 的computed根据后端返回的status动态计算样式代码不多但业务价值很明显。4.3 表格批量编辑与表单校验生鲜数据处理系统里数据录入是一大痛点。比如早上到一批菜采购员需要在 PC 端录入多个商品的数量和进价或者店长想统一把某类商品的价格下调 5%一行一行改太浪费时间。我针对这些场景做了两个功能批量新增采购批次表格里可以动态添加行每行选择商品后自动带出该商品的历史进价作为预填值采购员只需修改实际价格。前端用el-table的组件数据是一个响应式数组rows新增行就是rows.push({})。最终提交前我会在本地做一轮校验数量不能为空、价格必须大于 0、商品编码不能重复。批量改价勾选多个商品后弹出统一改价面板可以选择“直接改为指定价”或“在当前售价基础上增加/减少百分比”。前端把计算逻辑放在浏览器端做提交的是批次商品 ID 列表和价格规则后端再按规则统一更新。这样做的好处是用户能所见即所得看到新售价不用等后端响应。表单校验我习惯在前后端都做一遍。前端校验是为了提升用户体验比如价格输成负数会马上提示后端校验是为了保证数据安全因为接口完全可以绕过前端直接调用。两侧校验规则尽量保持一致我用一个简单约定前端规则写在表单里后端规则用 marshmallow schema 校验两边字段名和错误提示信息和文案保持一致减少扯皮。4.4 权限控制与多角色视角生鲜管理系统一般有三类角色采购员管商品和采购批次店长看数据和调价格库管专员做损耗登记系统管理员管账号权限。前端我做了两级权限控制。第一级是路由守卫进入需要登录的页面判断 token 是否存在然后根据用户角色判断是否有权限router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) return } const role localStorage.getItem(role) if (to.meta.roles !to.meta.roles.includes(role)) { next(/403) return } next() })第二级是按钮级权限比如“删除商品”按钮只有系统管理员能看到普通采购员只能查看和编辑。我封装了一个v-permission自定义指令在按钮上直接v-permissionproduct:delete指令内部根据当前用户权限列表决定是否移除这个 DOM 节点。权限控制容易忽视的一点不能只在前端 hide后端接口也要校验角色。比如删除商品接口如果后端不校验权限任何人抓包都能调用那前端隐藏就毫无意义。所以我在后端写了一个简单的装饰器require_role(admin)在需要权限控制的接口上加装饰器逻辑统一且容易维护。5. 常见问题与部署实践5.1 前后端跨域与代理我在开发环境基本不开 Flask-CORS用的 Vite 代理。vite.config.js里配置export default defineConfig({ plugins: [vue()], server: { proxy: { /api: { target: http://127.0.0.1:5000, changeOrigin: true } } } })这样前端代码里请求/api/inventory/list本地开发时会自动转发到 Flask 的 5000 端口浏览器不会出现 CORS 报错。生产环境我部署时用 Nginx 统一处理把/路径指向前端静态文件/api路径转发到后端 gunicornserver { listen 80; server_name your-domain.com; location / { root /var/www/fresh-frontend/dist; try_files $uri $uri/ /index.html; } location /api { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这个方案比在每个接口上加 CORS 干净得多因为生产环境前后端同源不需要 CORS。如果非要在后端开启 CORS记得supports_credentialsTrue时不能使用通配符*要明确指定允许的源。5.2 大数据量查询性能优化生鲜系统的数据量随着时间增长会越来越大尤其是销售流水表几个月下来可能几十万行。我的优化策略按优先级排列如下索引策略销售表按batch_id、sale_date建复合索引库存表按status、expiry_date建索引商品表按product_code建唯一索引。分页查询所有列表接口强制分页不要一次查出几万行 JSON 给前端。按天聚合统计销售明细表不适合每次都全量聚合我用一个定时任务每天凌晨把前一天的数据按“商品批次小时”聚合到sale_daily汇总表。看板接口只查聚合表性能提升非常明显。避免 N1 查询在 SQLAlchemy 查询时用joinedload或selectinload加载关联商品不要在循环里逐个查商品表。举个例子查库存列表时如果直接这样写for inv in inventories: product_name inv.product.product_name每循环一次都会触发一条 SQL如果有一百条库存记录就会产生一百条查询。改成inventories Inventory.query.options(db.joinedload(Inventory.product)).all()就会把商品信息 JOIN 进来只产生一条 SQL耗时从几秒降到几十毫秒。5.3 项目打包与部署前端打包很简单npm run build后生成dist目录。后端我用 gunicorn 启动gunicorn -w 4 -b 127.0.0.1:8000 app:app-w 4表示 4 个 worker对小型超市管理系统足够。如果是在 Windows 本地跑可以先用 Flask 自带的app.run()调试生产应放到 Linux 环境。数据库方面我生产用 PostgreSQL本地开发用 SQLite 也可以启动但要注意几个差异SQLite 对并发支持不太好多个 worker 写库可能报“database is locked”SQLite 的日期函数和 PostgreSQL 不一样。如果计划上线建议开发时就使用与生产一致的数据库这个坑我踩过一次上线时把 SQLite 迁移到 PostgreSQL光是时间字段的兼容就调了半天。部署时还有一个环境变量管理的细节数据库连接串、密钥、上传目录都不要硬编码在代码里用.env文件存DATABASE_URLpostgresql://user:passwordlocalhost/fresh_db SECRET_KEYyour-secret-key UPLOAD_FOLDER./uploads后端读取环境变量用os.getenv(DATABASE_URL)。这样换环境改配置就行不用改代码。5.4 实际踩坑经历时区、金额精度和单位这一部分我特别想跟做类似系统的人分享因为都是真实项目里会遇到的、测试时还不一定暴露的问题。第一个坑是时间字段的时区问题。我在前端传“2024-12-27”这样一个日期字符串后端用 Flask 解析成 Pythondate没问题但如果传的是“2024-12-27T10:00:0008:00”这种带时区的时间格式直接存到 PostgreSQL 的timestamp字段时不同 worker 或不同数据库驱动可能把它当成 UTC 时间存储导致前端再显示时差了 8 小时。后来我统一约定涉及日期用date类型只存年月日涉及时间用timestamp类型但接口传输统一用 UTC ISO 字符串前端展示时再转本地时区。多花一点心思能省掉后续无数奇怪 bug。第二个坑是金额精度。我在 Python 里用Decimal计算金额Flask 返回 JSON 时却要小心jsonify对Decimal不会直接序列化会报TypeError。我提供一个小工具函数把响应数据里的Decimal全部转成str前端用parseFloat恢复数字。这样安全管理单价和总价避免 JS 浮点误差导致价格显示成 19.9999999。第三个坑是单位混用。有一次采购员导入 Excel 时重量“100kg”被他敲成了“0.1吨”系统没有校验单位导致库存直接多了 10 倍。后来我在商品表里增加allowed_units枚举字段比如蔬菜类只允许 kg、g供应商导入数据时如果单位不在允许列表直接拒绝并提示。录入界面也做了单位下拉选择不再依赖手工输入。这种“脏数据”问题比代码逻辑本身更容易让业务人员失去信任所以必须从入口卡住。整个系统从设计到上线我最深的体会是生鲜数据处理的难点不在技术而在业务细节。损耗怎么定义、批次怎么管理、单位怎么换算、保质期怎么判断这些业务规则如果理不清楚再牛的技术框架也白搭。Flask 和 Vue 只是工具真正让系统产生价值的是这些深入到业务血肉里的逻辑。希望这篇总结能帮你少走几个弯路如果你也正在搭类似的超市管理系统欢迎对照自己的业务场景再细化不建议直接照搬表结构——一定要把自己超市的生鲜品类、损耗规则、岗位权限都摸清楚再动手。
返回列表