baseline: 批次A-D 成果 + membership 半成品(测试红)
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# dealerhub 前端选型决策备忘录:Vue 3 vs React(2026-09-08)
|
||||
|
||||
## 问题
|
||||
当前 dealerhub 前端为 Vue 3.5 + Element Plus(今天刚上线),换成 React 会不会更好?
|
||||
|
||||
## 关键事实
|
||||
|
||||
### 1. 当前 Vue 前端规模(实测)
|
||||
- 12 个页面 + MainLayout + api/router 层,**约 1,800 行 / 16 个文件**
|
||||
- 已完成:构建、部署(192.168.5.7:18050 同源托管)、Playwright 全链路验证
|
||||
- 技术栈:Vite 5 + Vue 3.5 + Element Plus + Pinia + Vue Router(hash)
|
||||
|
||||
### 2. 你的 React 项目史(全局记忆,客观事实)
|
||||
| 项目 | 前端 |
|
||||
|---|---|
|
||||
| novel_reader | React 19 + Vite |
|
||||
| 营养师数据查询 | React 18 + Vite + zustand + PWA |
|
||||
| EnglishDrill | React 18 + Vite SPA |
|
||||
| school-meal-web | React + Vite |
|
||||
| **dealerhub** | **Vue 3(唯一一个 Vue 项目)** |
|
||||
|
||||
你的 4 个既有项目全是 React,dealerhub 是第一个 Vue。你本人 review/修改 React 代码的经验远多于 Vue。
|
||||
|
||||
### 3. 生态匹配度(ERP 中后台场景)
|
||||
- **React + Ant Design**:antd 5 的 Table(列筛选/固定列/虚拟滚动/可编辑单元格)对"仿管家婆"这类重表格 ERP 更强;ProComponents 可再省一层 CRUD 封装;本 harness 同时装有 antd MCP(组件文档/示例即时可查)
|
||||
- **Vue + Element Plus**:同样成熟,够用;本 harness 也有 elementplus MCP。两者生态差异不构成决定性理由
|
||||
- 性能、打包体积在此规模下差异可忽略
|
||||
|
||||
## 结论:会更好,但只在特定前提下
|
||||
|
||||
**建议换 React(倾向明确),前提是这个项目前端你会持续自己演进**(加页面、改交互、后续接 AI/报表自定义等):
|
||||
1. 你的主力经验栈是 React,后续每改一页的边际成本更低,agent 生成代码你也更容易把关
|
||||
2. antd 对重表格 ERP 的组件能力更贴合
|
||||
3. 迁移窗口成本最低:只有 1,800 行、API 层独立(axios client 可原样复用)、后端零改动,估计一轮任务即可完成迁移 + Playwright 回归。**越晚迁越贵**
|
||||
|
||||
**可以不换的情形**:前端已接近"够用、少改",主要由 agent 维护而你不直接读改代码——那 Vue 版继续跑完全没问题,沉没成本不值得纠结。
|
||||
|
||||
## 若迁移的执行方案(估算一轮)
|
||||
1. `frontend-react/`:Vite + React 18 + Ant Design 5 + React Router + zustand(沿用 `/api` 代理与部署方式)
|
||||
2. 复用 `client.js`(axios 拦截器逻辑等价改写);按页面迁移:登录 → 大盘 → 三大报表 → 凭证(最复杂)→ 其余列表页
|
||||
3. 部署方式不变(dist → backend/frontend_dist/ → whitenoise),Playwright 回归同一套验证路径
|
||||
4. Vue 版保留 `frontend/` 目录一个版本周期作对照,稳定后删除
|
||||
|
||||
## 决策点(待用户拍板)
|
||||
- A. 保持 Vue,继续开发
|
||||
- B. 迁移 React + Ant Design(推荐)
|
||||
- C. 先试点迁移 1 个复杂页(凭证页)对比后再定
|
||||
@@ -0,0 +1,78 @@
|
||||
# P0 改造实施计划(逐项方案)
|
||||
|
||||
> 依据《差距分析与改造清单.md》的 P0 六项,逐项给出落地方案。实现顺序即优先顺序;每项完成后跑对应测试,最后全量回归。
|
||||
|
||||
## 全局前置:一次模型变更批次
|
||||
|
||||
以下新字段/新模型一次 `makemigrations` 生成(避免反复迁移):
|
||||
|
||||
| App | 变更 |
|
||||
|---|---|
|
||||
| catalog | `Product.is_batch_managed`、`Product.shelf_life_days`、`Product.min_sale_price`;新模型 `UnitConversion(tenant, product, unit, rate)` |
|
||||
| inventory | 新模型 `StockBatch(tenant, warehouse, product, batch_no, production_date, expiry_date, on_hand, locked, unit_cost)`,唯一键 (tenant, warehouse, product, batch_no);`StockMovement.batch_detail`(JSON,记录 FEFO 分摊明细) |
|
||||
| partner | 新模型 `CustomerProductPrice(tenant, customer, product, price)` |
|
||||
| sales | `SalesBill.round_off`(抹零额);`SalesOrderLine/SalesBillLine.source_unit(FK Unit 可空)+source_quantity`(录入单位与数量,quantity 恒为基本单位) |
|
||||
| purchase | `PurchaseBillLine.batch_no/production_date/expiry_date`(批次入库穿透);行加 `source_unit/source_quantity` |
|
||||
| finance | 新模型 `StatementShare(tenant, customer, token UUID, date_from, date_to, expires_at)` |
|
||||
| notify | `AlertRule.RULE_TYPE_CHOICES` 增加 `batch_expiry` |
|
||||
|
||||
**单位语义约定**(#2):`source_quantity × rate = quantity(基本单位)`;有录入单位时 `unit_price` 按**录入单位**计价,`amount = source_quantity × unit_price`;无录入单位时一切按基本单位(现状不变)。
|
||||
|
||||
---
|
||||
|
||||
## #1 批次/效期管理
|
||||
|
||||
- **模型**:见上表。批次账 `StockBatch` 与总账 `Stock` 并行维护,总量恒等。
|
||||
- **服务**(`inventory/services.py`):
|
||||
- `inbound(..., batch_no=None, production_date=None, expiry_date=None)`:商品 `is_batch_managed=True` 时必须带 batch_no,写 `StockBatch`(upsert)+ 总账;否则行为不变。
|
||||
- `outbound(...)`:批次商品按 **FEFO**(expiry_date 升序、空值最后、再按 id)自动分摊到多批次;总账一次扣减;分摊明细写入该次 `StockMovement.batch_detail`。任一批次分摊失败→整单回滚。
|
||||
- 非批次商品路径完全不变(现有测试零影响)。
|
||||
- **采购穿透**:`purchase/services.create_purchase_bill` 行支持 `batch_no/production_date/expiry_date` 落行并传给 `inbound`。
|
||||
- **近效期预警**:`notify` 新增规则 `batch_expiry`(threshold=天数,默认 30);`check_batch_expiry_alerts` 扫描 `on_hand>0 且 expiry_date ≤ today+N` 的批次,按"批次+当日"去重发通知;挂入 `run_all_alert_checks`。
|
||||
- **API**:`GET /api/v1/inventory/batches/`(只读,支持按 warehouse/product 过滤)。
|
||||
- **测试**:批次入库聚合、FEFO 跨批次分摊、不足拒绝、近效期预警去重、采购带批次入库、非批次商品回归不受影响。
|
||||
|
||||
## #2 多单位换算
|
||||
|
||||
- **模型**:`UnitConversion(product, unit, rate)`,rate=1 录入单位等于多少基本单位;唯一键 (tenant, product, unit)。
|
||||
- **服务**:`catalog/services.py::to_base(product, unit, qty)`——unit=base_unit 直接返回;否则查换算表,缺失报错。
|
||||
- **单据**:sales/purchase 的 order/bill 行(4 个模型)支持 `source_unit`(id 或 code) + `source_quantity`;服务内换算写 `quantity`。字段为可空,老调用完全兼容。
|
||||
- **测试**:换算开单(2 箱×24=48 基本单位出库)、无换算率报错、老式行(无单位)回归。
|
||||
|
||||
## #3 开单自动取价
|
||||
|
||||
- **模型**:`CustomerProductPrice`。
|
||||
- **服务**:`partner/services.py::quote_price(tenant, customer, product)`,优先级:
|
||||
1. 客户专属价(`CustomerProductPrice`)
|
||||
2. 价格等级价(`PriceLevel.discount_rate>0` 时 `sale_price × rate`)
|
||||
3. 最近成交价(该客户+商品最近一张已过账销售单行单价)
|
||||
4. 默认售价
|
||||
返回 `{price, source, last_price, min_price, max_price}`(min/max 来自该客户历史成交)。
|
||||
- **接入开单**:`create_sales_bill` 行未传 `unit_price` 时自动取价;换算单位时默认价 = 基本单位价 × rate。
|
||||
- **API**:`GET /api/v1/partner/price-quote/?customer_id=&product_ids=`(批量报价,供前端开单页预填)。
|
||||
- **测试**:四级优先级、last/min/max 正确、开单不传价自动填充。
|
||||
|
||||
## #4 最低售价 + 抹零
|
||||
|
||||
- **校验**:`Product.min_sale_price>0` 且成交价(按基本单位折算)低于它时抛错;行传 `allow_below_min=True` 视为审批放行(记入 remark)。
|
||||
- **抹零**:`SalesBill.round_off`;`create_sales_bill(round_to=0.01|0.1|1)` 按向下取整计算抹零额(如 100.56 元抹到元 → round_off=0.56),`total_amount` 为净额;`round_off > round_to` 拒绝;应收金额=净额。
|
||||
- **测试**:低于最低价拒绝/放行、三种 round_to 的金额计算、应收与 total 一致。
|
||||
|
||||
## #5 信用额度落地
|
||||
|
||||
- **服务**:`partner/services.py::check_credit(tenant, customer, extra_amount)`——outstanding = 该客户 open/partial 应收 balance 之和;`credit_limit>0` 且 `outstanding+extra > limit` 时超限。
|
||||
- **接入过账**:`confirm_sales_bill` 过账前校验,超限抛 `CreditLimitExceeded`(`force=True` 放行并写 warning 通知);占用达 90% 发预警通知。
|
||||
- **测试**:限额内通过、超限拒绝、force 放行+通知、无限额(0)不校验。
|
||||
|
||||
## #6 账龄分析 + 对账单 + 公开分享
|
||||
|
||||
- **账龄**:`finance/services.receivable_aging(tenant, as_of=None, customer=None)`——按 `bill_date` 距 today 天数分桶 0-30/31-60/61-90/91-180/181-365/365+,余额=balance;支持按客户汇总。
|
||||
- **对账单**:`customer_statement(tenant, customer, date_from, date_to)`——期初=open 应收中 bill_date<from 的余额合计;明细=区间内应收单(借)+收款核销(贷,按 receipt.bill_date);逐行滚动余额,期末=期初+借-贷。
|
||||
- **公开分享**:`StatementShare(token=uuid4, expires_at)`;受保护 API `POST /api/v1/finance/statements/share/`(生成)与 `GET /api/v1/finance/statements/share/`(列表/吊销);公开 `GET /api/v1/open/statements/<token>/`(AllowAny,过期 404,租户从 share 记录解析,不依赖请求头)。
|
||||
- **测试**:账龄分桶正确、对账单期初/期末平衡、分享链接可匿名访问且过期失效。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- 每项附专项测试文件 `tests/test_p0_*.py`;全量 `pytest` 通过且不破坏既有 103 例;
|
||||
- `manage.py check` 0 issues;
|
||||
- 非批次/非单位/老式开单路径行为与改造前一致(回归保障)。
|
||||
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>销售单(默认) XS202609100001</title>
|
||||
<style>
|
||||
@page { size: A4 portrait; margin: 12mm 10mm; }
|
||||
* { box-sizing: border-box; }
|
||||
body { font-family: "Microsoft YaHei", "SimSun", sans-serif; font-size: 13px; color: #111; margin: 0; }
|
||||
.doc-head { text-align: center; }
|
||||
.doc-head h1 { margin: 0 0 2px; font-size: 20px; }
|
||||
.doc-head h2 { margin: 6px 0 10px; font-size: 17px; letter-spacing: 8px; }
|
||||
.doc-head .sub { color: #444; font-size: 12px; }
|
||||
table { width: 100%; border-collapse: collapse; margin-top: 6px; }
|
||||
.meta td { padding: 2px 4px; border: none; }
|
||||
.lines th, .lines td { border: 1px solid #333; padding: 4px 6px; text-align: left; }
|
||||
.lines th { background: #f2f2f2; }
|
||||
.lines tfoot td { border: 1px solid #333; font-size: 13px; }
|
||||
.strong { font-weight: 700; font-size: 15px; }
|
||||
.sign { margin-top: 22px; display: flex; justify-content: space-between; }
|
||||
.remark { margin-top: 10px; white-space: pre-wrap; }
|
||||
.foot { margin-top: 14px; color: #555; font-size: 12px; border-top: 1px dashed #999; padding-top: 6px; }
|
||||
.noprint { margin: 10px 0; }
|
||||
@media print { .noprint { display: none; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="noprint"><button onclick="window.print()">打 印</button></div>
|
||||
|
||||
<div class="doc-head">
|
||||
<h1>宏发食品经销部</h1>
|
||||
<div class="sub">电话:0755-12345678 地址:深圳市XX区批发市场A栋12号</div>
|
||||
<h2>销 售 单</h2>
|
||||
</div>
|
||||
<table class="meta">
|
||||
<tr><td>单号:XS202609100001</td><td>日期:2026-09-10</td><td>仓库:主仓</td></tr>
|
||||
<tr><td>客户:开心小卖部</td><td>电话:13800001111</td><td>打印时间:2026-09-10 19:24</td></tr>
|
||||
</table>
|
||||
<table class="lines">
|
||||
<thead><tr><th>#</th><th>编码</th><th>品名</th><th>单位</th><th>数量</th><th>单价</th><th>金额</th></tr></thead>
|
||||
<tbody>
|
||||
|
||||
<tr><td>1</td><td>KL500</td><td>可乐 500ml 1×24瓶</td><td></td><td>5</td><td>96.5000</td><td>482.5000</td></tr>
|
||||
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr><td colspan="4">合计:1 行 / 数量 5</td><td colspan="2">抹零优惠</td><td>0.5000</td></tr>
|
||||
<tr><td colspan="6" class="strong">应收合计(元)</td><td class="strong">482</td></tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<div class="sign">
|
||||
<span>制单:__________</span><span>送货人:__________</span><span>客户签收:__________</span>
|
||||
</div>
|
||||
<div class="remark">备注:抹零 0.5000 元</div>
|
||||
<div class="foot">货已验收,签收后概不退换 开户/账号:工行深圳分行 6222 0000 1234 5678</div>
|
||||
|
||||
<script>window.addEventListener("load", function(){ window.print(); });</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,170 @@
|
||||
# dealerhub 后续改造实施计划(P0 前端配套 + P1/P2/P3)
|
||||
|
||||
> **依据**:《差距分析与改造清单.md》全部 18 项 + 《P0改造实施计划.md》。
|
||||
> **现状快照**(2026-09-10):P0 后端六项(#1 批次效期 / #2 多单位 / #3 自动取价 / #4 最低售价+抹零 / #5 信用额度 / #6 账龄+对账单)已全部落地,专项 38 测试,全量回归 141 passed;P3 #17 打印模板引擎提前落地(+16 测试,共 157 passed)。**P0 引擎已建成但前端 12 页未接入**,竞品差距清单中剩余 P1 三项、P2 四项、P3 四项未启动。
|
||||
> **本计划范围**:把清单剩余项排成四个批次(A→D),每项给出落点、具体内容、验收标准。实现顺序即优先顺序,批次内条目可并行。
|
||||
|
||||
---
|
||||
|
||||
## 〇、批次划分与执行顺序
|
||||
|
||||
```
|
||||
批次A P0前端配套(收尾) ──→ 批次C 商业化基建(#10-13) ──→ 批次D 多端/行业(#14-16,#18)
|
||||
↑ 最快见效(纯前端+少量API) ↑ 收钱的基建 ↑ 拿到客户后扩
|
||||
批次B P1 AI差异化(#7-9) —————— 与A/C并行推进(AI开单演示是销售杠杆,越早越好)
|
||||
```
|
||||
|
||||
| 批次 | 内容 | 预计量级 | 完成标志 |
|
||||
|---|---|---|---|
|
||||
| A | P0 六项的前端接入(开单取价/单位/抹零、信用进度条、账龄+对账单分享、批次页) | 3–5 天 | 前端可见可用,Playwright 冒烟通过 |
|
||||
| B | AI 应收风险预警 / AI 开单 / AI 经营问答 | 1–2 周(可并行) | 官网可演示的三个 AI 卖点 |
|
||||
| C | 套餐计费 → 演示账套 → 营销官网 → 公网上线 | 2 周 + 备案等待 | 最小可售闭环(对食品经销商开口子) |
|
||||
| D | B2B 订货商城 H5 / 移动开单 / 序列号 / 税率 | 按客户驱动 | 行业专版能力 |
|
||||
|
||||
**最小可售闭环 = 批次A + B1(应收风控) + 批次C**:P0 引擎(后端已完成)+ 前端可见 + 套餐计费 + 官网演示账套,即具备对第一个食品经销商开口子的资格。
|
||||
|
||||
---
|
||||
|
||||
## 批次 A · P0 前端配套(引擎"可见可用",预计 3–5 天)
|
||||
|
||||
> P0 后端 API 已就绪,本批次零新模型、零迁移,纯前端 + 个别 API 补口。全部接口现成:
|
||||
|
||||
| API | 用途 |
|
||||
|---|---|
|
||||
| `GET /api/v1/partner/price-quote/?customer_id=&product_ids=` | 批量取价(price/source/last/min/max) |
|
||||
| `GET /api/v1/partner/credit-usage/<customer_id>/` | 信用额度占用 |
|
||||
| `GET /api/v1/inventory/batches/` | 批次列表(product/warehouse/in_stock 过滤) |
|
||||
| `POST/GET /api/v1/finance/statements/share/` | 对账单分享生成/吊销 |
|
||||
| `GET /api/v1/open/statements/<token>/` | 公开对账单(客户侧) |
|
||||
|
||||
### A1 销售开单页改造(`SalesBills.vue`)★★★ 最高优先
|
||||
- 选客户后商品行**自动取价预填**:调 price-quote,行内显示"上次售价/历史最低~最高"参考(智慧记同款体验);单价可改,改后标黄提示偏离。
|
||||
- 行支持**选择录入单位**:下拉列该商品 `UnitConversion`(需后端补一个 `GET /api/v1/catalog/products/<id>/units/` 轻接口,或报价 API 一并返回);数量按录入单位填,提交带 `source_unit/source_quantity`,金额 = 录入单位价 × 录入数量。
|
||||
- **抹零选项**:单据级下拉(不抹/抹分/抹角/抹元),提交带 `round_to`;实时预览"合计 → 抹零后应收"。
|
||||
- **最低售价防呆**:成交价 < min_sale_price 时弹确认框,确认即行带 `allow_below_min=true`(前端"审批放行"话术)。
|
||||
- **信用额度提示**:选客户后调 credit-usage 显示"已用/总额度"进度条;提交后若 402/CreditLimitExceeded,弹"超限,强制过账?"确认框走 `force=true`。
|
||||
- 验收:不传价自动填充可见;2 箱×24 开单;抹零净额展示;超限弹窗流转。`npm run build` 通过。
|
||||
|
||||
### A2 客户档案页(`Receivables.vue` 或客户列表页)
|
||||
- 客户行展开/详情显示**额度占用进度条**(<80% 绿 / 80–99% 橙 / ≥100% 红)+ 账龄六桶汇总(aging API 按 customer 过滤)。
|
||||
- 验收:额度与账龄数字与后端测试口径一致。
|
||||
|
||||
### A3 财务页新增"账龄 + 对账单"(`FinanceReports.vue` 加标签页 或 `Receivables.vue` 扩展)
|
||||
- **账龄标签**:全租户六桶柱状图 + 客户明细表(Element Plus 表格,支持 as_of 切换)。
|
||||
- **对账单**:选客户 + 日期区间 → 预览期初/明细/期末 → "生成分享链接"按钮 → 复制 `GET /api/v1/open/statements/<token>/` 公开 URL(显示有效期,可吊销)。
|
||||
- 验收:分享链接**无痕浏览器**可打开、过期/吊销后 404。
|
||||
|
||||
### A4 库存批次页(`Stocks.vue` 扩展或新 `Batches.vue`)
|
||||
- 批次列表(仓库/商品/在库状态过滤):批次号、生产/到期日期、结存、**距到期天数**着色(≤30 天橙、≤7 天红)。
|
||||
- `Notify.vue`:确认 `batch_expiry` 规则可建可扫,通知类型有图标区分。
|
||||
- 销售单打印模板追加"批次号"列(printing renderer 上下文补 batch_detail 分摊明细)。
|
||||
- 验收:近效期批次一眼可见;打印样张含批次列。
|
||||
|
||||
### A5 采购开单页批次录入(`PurchaseBills.vue`)
|
||||
- 行展开"批次信息":批次号/生产日期/到期日(`is_batch_managed` 商品必填校验与后端一致;到期日留空按保质期自动推算提示)。
|
||||
- 验收:带批次采购单过账后,批次页可见对应批次结存。
|
||||
|
||||
**批次A 总验收**:`npm run build` 通过;Playwright 冒烟(登录→开单取价→抹零→信用提示→打印)全绿;后端 pytest 不受影响。
|
||||
|
||||
---
|
||||
|
||||
## 批次 B · P1 AI 差异化(#7–9,与 A/C 并行,1–2 周)
|
||||
|
||||
> 对外话术:**唯一做应收风控 AI 的进销存**(竞品 AI 全在开单/问答,没人做应收风险——清单问题 4 结论)。
|
||||
> 新增 `apps/ai/`;LLM 依赖做成可配置(`AI_PROVIDER=openai-compatible`,BASE_URL/KEY 走 .env),**无 KEY 时全部功能降级为纯统计版**,不阻塞测试与演示。
|
||||
|
||||
### B1 AI 应收风险预警(#7)★★★ 主打卖点
|
||||
- `ai/risk.py` 风险评分(纯统计,不依赖 LLM):
|
||||
- 回款周期漂移:客户近 N 单实际回款天数 vs 历史均值;
|
||||
- 欠款趋势:outstanding 环比连续上升标记;
|
||||
- 开单频次骤降:连续 X 天无新单但欠款在账;
|
||||
- 三因子加权 → 0–100 风险分 + 高/中/低档。
|
||||
- 落点:`notify` 新增 `risk_score` 规则类型(threshold=分数线),`run_all_alert_checks` 挂入;首页大盘(`Dashboard.vue`)加"应收风险 TOP5"卡片。
|
||||
- LLM 只做增值:有 KEY 时对 TOP 客户生成一句人话催收建议("建议本周上门对账"级别),无 KEY 显示统计理由。
|
||||
- 验收:`tests/test_ai_risk.py`——构造"回款变慢+欠款上升"客户得高分、预警去重、降级路径可用。
|
||||
|
||||
### B2 AI 开单(#8,演示级)
|
||||
- `apps/ai/`:`POST /api/v1/ai/parse-order/`(粘贴/语音转写文本)→ LLM 抽取 `[{name, barcode?, qty}]` → 依次匹配 `barcode 精确 → name 模糊(trigram/包含)` → 返回命中+未命中清单 → 前端确认后生成 SalesOrder 草稿(复用 A1 取价)。
|
||||
- **配额**:`ai/usage.py` 按租户计次(免费版 10 次/月),超限 403 引导升级——为批次C billing 预留 `billing.check_quota(tenant, 'ai_parse')` 接口。
|
||||
- 前端:开单页"AI 录单"按钮 → 粘贴文本 → 确认抽选结果 → 回填表格。
|
||||
- 验收:无 KEY 返回明确错误(不 500);Mock LLM 测试抽取→匹配→草稿全链路;配额拦截。
|
||||
|
||||
### B3 AI 经营问答(#9)
|
||||
- `POST /api/v1/ai/ask/`:预置白名单工具——把 dashboard 既有指标(销售额/应收/库存周转/欠款 TOP)按问题意图取数后交给 LLM 作答;只读、不给 LLM 写权限。
|
||||
- 首页"老板参谋"入口;无 KEY 时按钮隐藏。
|
||||
- 验收:Mock LLM 下问答取数正确;越权(查他租户)拒绝。
|
||||
|
||||
**批次B 总验收**:三个能力各配 `test_ai_*.py`;全量回归通过;官网素材(30 秒演示脚本)产出。
|
||||
|
||||
---
|
||||
|
||||
## 批次 C · P2 商业化基建(#10–13,预计 2 周 + 备案等待)
|
||||
|
||||
> 内部顺序:**C1 计费 → C2 演示账套 → C3 官网 → C4 上线**(官网价格页依赖套餐定义;演示账套是官网的转化入口)。
|
||||
|
||||
### C1 套餐/试用/配额系统(#10,新 `apps/billing/`)
|
||||
- 模型:`Plan(code, name, price_monthly, limits JSON)` / `Subscription(tenant, plan, status[trial/active/expired], trial_ends_at, period_end)`;预置 plan:free(1 用户/100 SKU/30 天全功能)/ basic ¥998 / pro ¥4800(定价依据清单 #10:卡金蝶 698 与 1500 之间偏上,凭"带总账"打差异)。
|
||||
- Enforcement:`billing.quota` 提供 `check_and_count(tenant, kind)`(用户数 / SKU 数 / 月单据量 / AI 次数 / 批次功能开关);拦截点——注册用户、建商品、过账开单、AI 接口;超限 403 + 前端升级引导弹窗(列差距 + 跳价格页)。
|
||||
- 试用到期: Dramatiq cron 每日扫 `trial_ends_at`,到期降级 free 并通知。
|
||||
- 验收:`tests/test_billing.py`——超限拦截/试用降级/升级解除限制/租户隔离。
|
||||
|
||||
### C2 演示账套(#12)
|
||||
- `core/management/commands/seed_demo`:固定演示租户(只读角色 + 预置商品/客户/单据/批次效期数据,突出食品行业场景);重复执行幂等。
|
||||
- 登录页加"免注册体验演示账套"按钮(一次性 token 登录)。
|
||||
- 验收:一键重建;演示租户禁止写操作(写接口 403)。
|
||||
|
||||
### C3 营销官网(#11,新 `website/` 静态站或 Django templates)
|
||||
- 页面清单(对照清单"内容模块改造对照表"):
|
||||
1. **首页**:H1 带 AI("AI 开单 + 应收风控的经销商进销存");6 角色价值区块(老板/财务/销售/仓管/采购/运营,抄网上管家婆结构);演示账套扫码入口。
|
||||
2. **价格页**:free/¥998/¥4800 三列功能对比表(数据源 = C1 Plan.limits,一处定义两处使用)。
|
||||
3. **行业 SEO 落地页 ×3**:食品(批次效期+FEFO+整件散包,A4 批次页截图)、五金(多单位换算+尾差)、汽配(预留 SN,指向 #16 路线)——**每页对应一个已实现特性,不写纸面功能**。
|
||||
4. 打印样张/对账单分享链接作为"产品截图"素材。
|
||||
- 验收:三页 SEO meta/结构化数据;Lighthouse 移动分 ≥80。
|
||||
|
||||
### C4 公网上线(#13)
|
||||
- 域名购买 + 备案(等待期与 C1–C3 并行)→ HTTPS(Nginx/Caddy 反代 192.168.5.7:18050)→ OCR 客户端迁移。
|
||||
- CI 冒烟:ping / 登录 / 开单三接口每日探活。
|
||||
- 验收:公网域名 HTTPS 200;备案号挂页脚。
|
||||
|
||||
---
|
||||
|
||||
## 批次 D · P3 多端与行业扩展(拿到付费客户后,按 #14→#15→#16→#18)
|
||||
|
||||
### D1 B2B 订货商城 H5(#14,新 `apps/storefront/`)
|
||||
- `CustomerProductAuth(tenant, customer, product)` 授权可见 + `CustomerProductPrice` 等级价复用 quote_price;客户 H5 登录(token,不占用户席)→ 只见授权商品 → 自助下单生成 **SalesOrder 草稿**(内部确认转单,不直接过账)→ 接 C1 配额。
|
||||
- 移动端页(Vue3 复用,移动布局)优先,微信小程序(uni-app)后置。
|
||||
- 验收:未授权商品不可见、下单草稿流转、额度超限拦截。
|
||||
|
||||
### D2 业务员移动开单(#15)
|
||||
- H5 复用 D1 框架 + A1 取价/单位/抹零组件:外勤开单、查欠款、收款登记;签到后置。
|
||||
- 验收:手机浏览器全流程走通。
|
||||
|
||||
### D3 行业专版开关(#16,按拿下客户顺序做)
|
||||
- **序列号 SN**(3C):`inventory.Serial(tenant, product, serial_no, status)`,入库登记/出库绑定、SN 追溯查询;开关字段 `Product.is_serial_managed`。
|
||||
- 辅助属性(服装色码)/套装拆子件(建材):同模式后置,每项对应一个行业落地页更新。
|
||||
- 验收:SN 唯一性、出入库追溯、关闭开关回归零影响。
|
||||
|
||||
### D4 多币种/税率兑现(#18)★★ 先解决"纸面功能"风险
|
||||
- **建议只做税率**(国内刚需、发票强相关):`SalesBillLine/PurchaseBillLine.tax_rate`,价内/价外口径与凭证拆分(应交税费科目),抹零在税后。
|
||||
- 多币种从对外话术中删除(无跨境客户前不做)——兑现或删除,二者必居其一(清单问题 7 要求)。
|
||||
- 验收:税率行凭证科目正确、报表含税口径一致;官网话术同步清理。
|
||||
|
||||
### 打印扩展(#17 收尾,顺带做)
|
||||
- 三联送货单/58mm 小票模板、`{{#if}}` 条件块、**对账单分享二维码印上销售单**(客户扫码对账,串起 A3)。
|
||||
|
||||
---
|
||||
|
||||
## 五、里程碑与验收
|
||||
|
||||
| 里程碑 | 内容 | 判定 |
|
||||
|---|---|---|
|
||||
| M-A(+1 周) | 批次A 完成 | 前端接入全部 P0 能力,Playwright 冒烟绿,157+ 测试不回归 |
|
||||
| M-B(+3 周) | 批次B 完成(与 C 并行) | 三个 AI 卖点可演示(含无 KEY 降级路径),30 秒演示视频脚本产出 |
|
||||
| M-C(+5 周) | 批次C 完成 | **最小可售闭环达成**:官网可访问、价格页公开、演示账套可进、free 版配额真实拦截 |
|
||||
| M-D | 按客户驱动 | 订货商城/移动开单随第一个付费客户上线 |
|
||||
|
||||
**全局验收标准**(沿用 P0 惯例):
|
||||
- 每项专项测试 `tests/test_<batch>_<item>.py`,全量 `pytest` 通过且不破坏既有 157 例;
|
||||
- `manage.py check` 0 issues;`npm run build` 通过;
|
||||
- 每批次完成产出 `PROGRESS_<批次>.md` 报告(含变更文件清单与踩坑记录);
|
||||
- **不写纸面功能**:官网/话术只提已实现特性(清单问题 7 教训),未做的从话术删除。
|
||||
@@ -0,0 +1,132 @@
|
||||
# dealerhub 差距分析与改造清单(基于竞品网站调研报告)
|
||||
|
||||
> **依据**:《竞品网站调研报告.md》(2026-09-08 实抓 11 个竞品官网)+ 对 dealerhub 代码的实地核查(backend/apps 各模型、frontend 12 页、5 份 PROGRESS 文档)。
|
||||
> **结论先行**:dealerhub 的**引擎层已经追平甚至超过多数竞品**(完整总账、真实渠道签名对接、状态机、原子库存、103 个测试),真正的差距集中在**四类**:① 商品/价格体系太浅(竞品行业方案的看家本领);② 分销/订货侧空白(竞品标配);③ AI 只有字段预留(2026 年竞品的官网门面);④ 商业化基建为零(套餐、官网、演示账套)。下面逐项展开。
|
||||
|
||||
---
|
||||
|
||||
## 一、当前状态盘点(引擎层:已完成且质量较高)
|
||||
|
||||
| 模块 | 现状 | 对照竞品的相对位置 |
|
||||
|---|---|---|
|
||||
| core(多租户/组织/状态机/审计) | ✅ 完整 + 测试 | 达到金蝶/畅捷通 SaaS 基线 |
|
||||
| catalog 商品中心 | ⚠️ 单级规格/单条码 | **落后**(无批次效期/多单位/序列号/辅助属性) |
|
||||
| inventory 库存 | ✅ 原子操作/加权成本/流水 | 引擎达标,**缺批次维度** |
|
||||
| purchase / sales | ✅ 过账自动写库存+应收应付+凭证 | 业务闭环对标管家婆 |
|
||||
| finance 总账 | ✅ 借贷复式/期间/损益结转/三大报表 | **超过**金蝶云进销存标准版(它要 ¥1500+ 才带总账) |
|
||||
| report / BI | ✅ 大盘+排行+元数据报表 | 接近精斗云高级版 |
|
||||
| channel 渠道 | ✅ 抖音/1688 真实签名拉单+Mock回退 | 超过多数小竞品(但方向是"拉上游单",不是"下游订货") |
|
||||
| notify 预警 | ✅ 3 类规则+站内信 | 有基础,**无账龄/风险评分** |
|
||||
| openapi 开放平台 | ✅ API Key+Scope+限流 | 对标网上管家婆 Open API 的雏形 |
|
||||
| 前端 | ⚠️ Vue3 12 页,桌面后台 | 无移动端、无角色化体验 |
|
||||
|
||||
**技术验证过的事实**(差距分析的依据):
|
||||
- `Product` 模型:只有 `spec/barcode/cost_price/sale_price`;**多币种/税率仍是注释状态**(PLAN 承诺阶段 3 落地,实际未兑现)
|
||||
- `Unit` 模型:只有 `is_base`,**无换算率字段**
|
||||
- `PriceLevel`:只有 `discount_rate` 一个折扣率;`sales/services.py` 里 `unit_price` **纯手填**,无自动取价
|
||||
- `Customer.credit_limit`:字段存在,但**全代码无任何下单校验**(只出现在 serializer 里)
|
||||
- `notify`:规则是"库存低限/应收逾期/呆滞"三种,**无账龄分段、无对账单生成**
|
||||
- 全代码 `grep` 不到:批次(batch)/效期(expiry)/序列号(serial)/抹零/最低售价/小程序/计费(billing)
|
||||
|
||||
---
|
||||
|
||||
## 二、问题分析:六个"竞品有、我们没有"
|
||||
|
||||
### 问题 1|商品体系撑不起"行业纵深"卖点(严重度:★★★★★)
|
||||
竞品报告显示:**所有**竞品都用行业特性做获客页——金蝶食品方案的"效期+多单位换算尾差+整件散包"、3C 的"SN 序列号"、建材的"套装拆子件";智慧记把"库存过期自动提醒"写进口号。而 dealerhub 的 `Product` 是"编码+名称+规格文本"的通用模型,`Stock` 无批次维度。
|
||||
|
||||
→ 后果:规划的"先做垂直行业(食品/五金/汽配)"路线**目前没有产品支撑**,官网没法写行业方案页。
|
||||
|
||||
### 问题 2|价格体系浅,开单体验落后(严重度:★★★★★)
|
||||
金蝶专门宣传"灵活价格策略,取价优先定义:不同客户显示不同价格、老客优先取最近一次价格";智慧记宣传"上次开单价、历史最高/最低价参考"。dealerhub 开单单价靠手工输入,`PriceLevel` 只有一个折扣率——**开单慢、易错、无防呆**,对每天开几十张单的经销商是致命体验差。
|
||||
|
||||
→ 同时缺:最低售价控制、抹零(管家婆 30 年的基础卖点,规划文档 MVP 表里列了但未实现)。
|
||||
|
||||
### 问题 3|分销/订货侧是空白,而它是竞品标配(严重度:★★★★☆)
|
||||
四个竞品都有 B2B 订货入口(金蝶订货商城、网上管家婆"云订货-无需开发"、智慧记云店/微店、秦丝销货宝)。金蝶潘祥记案例宣传的正是"经销商分级定价+商品授权可见+线上自助下单"。dealerhub 的 `channel` 模块只做"从抖店/1688 拉上游单",**没有"让下游客户自助下单"的任何能力**。且 `Customer.credit_limit` 字段存在但下单不校验——信用额度是纸面的。
|
||||
|
||||
### 问题 4|AI 只有一个预留字段,营销层面已输(严重度:★★★★☆)
|
||||
竞品 2026 年官网 H1 全带 AI:金蝶"AI 开单+老板参谋"、秦丝"AI 员工+9 个 AI 工具"、智慧记"AI 进销存(85% 用户选择)"、速达"AI-Code"。dealerhub 只有 `source_channel="ai_agent"` 一个枚举值。经销商老板看官网的第一眼 comparison 就处于下风。
|
||||
|
||||
→ 但反过看:竞品 AI 都集中在"开单+经营问答",**没人做应收风险/催收 AI**——这是 dealerhub 可主打的差异化(notify 已有规则底座)。
|
||||
|
||||
### 问题 5|商业化基建为零(严重度:★★★☆☆)
|
||||
- 无套餐/计费/试用配额(规划了 ¥998-3000 的版本价,但系统里没有任何 enforcement:免费版限 100 SKU、3 用户这种规则无处落地)
|
||||
- 无营销官网(竞品标配:公开价格页+行业方案 SEO 页+分角色价值页+案例页;网上管家婆还有"扫码进演示账套")
|
||||
- 部署在内网 192.168.5.7:18050,无公网域名/HTTPS/备案——规划文档"立刻要做的第 3 件事(建官网+域名备案)"仍未动
|
||||
|
||||
### 问题 6|多端缺失(严重度:★★★☆☆)
|
||||
竞品全员"手机开单"(网上管家婆手机开单+查欠款+对账单分享;智慧记手机查库存;秦丝三端)。dealerhub 只有桌面 Web 后台,业务员外勤场景(PDA/手机开单)为零。规划第二阶段的"业务员外勤开单"未启动。
|
||||
|
||||
### 问题 7(次级)|规划承诺与实现的偏差
|
||||
- 多币种/税率:PLAN 承诺阶段 3 落地,实际仍是 `Product` 里的注释行——**要么兑现,要么从对外话术里删掉**,避免重蹈"纸面功能"
|
||||
- 前端 12 页是"功能堆叠"视角,没有竞品那种"老板/财务/业务员"分角色导航(网上管家婆的 6 角色页是最好的官网模板)
|
||||
|
||||
---
|
||||
|
||||
## 三、改造清单(按优先级,含具体模块落点)
|
||||
|
||||
### P0 · 行业纵深地基(不做就无法讲行业故事,预计 2–3 周量级)
|
||||
|
||||
| # | 改造项 | 落点模块 | 具体内容 | 对标 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | **批次/效期管理** | `inventory`(新 `Batch` 模型 + `StockBatch`) | 出入库可选带批次号+生产日期/保质期;近效期预警接入现有 `notify.AlertRule`(新增 `batch_expiry` 规则类型);先进先出出库建议 | 金蝶食品方案、网上管家婆批次效期 |
|
||||
| 2 | **多单位换算** | `catalog.Unit` + `ProductUnitConvert` | 换算率字段+固定/浮动(尾差)两种模式;单据支持"整件散包"(如 1 箱=24 瓶按 23+1 开单) | 金蝶食品"尾差"、整件散包 |
|
||||
| 3 | **开单自动取价** | `partner`(新 `CustomerProductPrice` 价格本)+ `sales/services` | 取价优先级:客户专属价 → 价格等级价 → 最近成交价 → 默认售价;接口返回"上次售价/历史最高最低";批量调价单+定时生效 | 金蝶取价策略、智慧记上次开单价 |
|
||||
| 4 | **最低售价 + 抹零** | `Product`/`Customer` 字段 + `sales/services` | 低于最低价需审批(接现有 StateMachine guard);抹零规则(分/角/元,四舍五入或抹去) | 管家婆基础能力 |
|
||||
| 5 | **信用额度落地** | `sales/services.confirm_sales_bill` | 下单时校验 `credit_limit - 未收应收 ≥ 本单金额`,超限走审批(guard 拒绝→`notify` 通知);前端客户档案显示"额度占用进度条" | 金蝶案例的经销商信用管控 |
|
||||
| 6 | **账龄分析 + 对账单** | `finance/services` + 新视图 | 应收按 30/60/90/180/365+ 分段统计;客户对账单一键生成(PDF/HTML)+ 公开分享链接(token 化,客户可在线查看) | 智慧记"微信分享对账单" |
|
||||
|
||||
### P1 · 差异化卖点(竞品没人做的,预计 1–2 周)
|
||||
|
||||
| # | 改造项 | 落点模块 | 具体内容 |
|
||||
|---|---|---|---|
|
||||
| 7 | **AI 应收风险预警** | `notify/services` + 新 `ai/` 目录 | 在现有逾期扫描上叠加风险分:结合历史回款周期、客户欠款趋势、开单频次输出"高风险客户"榜单;首页大盘加"应收风险"卡片。**对外话术:唯一做应收风控 AI 的进销存** |
|
||||
| 8 | **AI 开单(演示级)** | 新 `apps/ai/`(调用 LLM API,`source_channel="ai_agent"` 兑现) | 拍照/粘贴文本 → LLM 抽取商品+数量 → 匹配 `barcode/name` → 生成销售单草稿。售前演示利器,成本可控(限免/付费版次数配额) |
|
||||
| 9 | **AI 经营问答(老板参谋平替)** | `report/services` + LLM | 把 dashboard 指标喂给 LLM 做"本月哪个客户欠款最多/哪个商品滞销"问答;复用 openapi 鉴权 |
|
||||
|
||||
### P2 · 商业化与获客基建(不做就没客户进来,预计 2 周 + 持续运营)
|
||||
|
||||
| # | 改造项 | 落点 | 具体内容 |
|
||||
|---|---|---|---|
|
||||
| 10 | **套餐/试用/配额系统** | 新 `apps/billing/` | Plan/Subscription 模型;免费版限 1 用户+100 SKU+30 天全功能试用;超限在 API 层拦截并在前端引导升级。定价对照:基础 ¥998(卡金蝶 698 与 1500 之间偏上,凭"带总账"打差异)→ 专业 ¥4800 |
|
||||
| 11 | **营销官网** | 新 `website/`(静态站或 Django templates) | 必备页:首页(分角色价值主张,抄网上管家婆 6 角色结构)· 价格页(公开标价,金蝶模式)· 行业方案 SEO 落地页 ×3(食品批次效期/五金多单位/汽配——每页对应一个 P0 已实现特性)· 演示账套入口 |
|
||||
| 12 | **演示账套** | `core/management/commands/seed_demo` | 只读演示租户+预置数据,官网扫码直接进(网上管家婆同款打法) |
|
||||
| 13 | **公网上线** | 运维 | 域名备案 + HTTPS + 从 192.168.5.7 反代出公网;ping/登录/开单三接口冒烟进 CI |
|
||||
|
||||
### P3 · 多端与行业扩展(拿到付费客户后)
|
||||
|
||||
| # | 改造项 | 落点 | 具体内容 |
|
||||
|---|---|---|---|
|
||||
| 14 | **B2B 订货商城(H5 优先)** | `channel` 扩展或新 `apps/storefront/` | 客户登录后只见被授权商品(`CustomerProductAuth`)+ 自己的等级价,自助下单生成 SalesOrder 草稿;后期套 uni-app 出微信小程序 |
|
||||
| 15 | **业务员移动开单** | uni-app 小程序/H5 | 手机开单(复用 #3 自动取价)+ 查欠款 + 收款登记;外勤签到后续加 |
|
||||
| 16 | **序列号 SN**(3C)/ **辅助属性色码**(服装)/ **套装拆分**(建材) | `catalog`/`inventory` | 每个对应一个行业专版,按拿下哪个行业客户先做哪个 |
|
||||
| 17 | **打印模板引擎** | 新 `apps/printing/` | 销售单/送货单/标签多模板(智慧记"多种单据都能打"),对接浏览器打印先行,PDA 扫码枪后续 |
|
||||
| 18 | **兑现多币种/税率** | `catalog` 注释字段转正 + 单据行加 `tax_rate` | 或从所有对外话术中删除,避免"纸面功能" |
|
||||
|
||||
### 内容模块(官网/文档)改造对照表
|
||||
|
||||
| 竞品做法 | dealerhub 应产出 |
|
||||
|---|---|
|
||||
| 网上管家婆"分角色价值主张"(老板/财务/销售/仓管/采购/运营) | 官网首页 6 角色区块;前端 Dashboard 加角色切换视图 |
|
||||
| 金蝶"版本价格对比表" | 价格页:免费/基础/专业三列功能对比(billing 系统支持后上线) |
|
||||
| 金蝶/智慧记/秦丝"行业方案页" | 3 个 SEO 落地页,每个绑定一个 P0 特性(批次效期/多单位/序列号) |
|
||||
| 智慧记"AI 进销存 85% 用户选择" | 首页 H1 带 AI(AI 开单/AI 应收风控),配 30 秒演示视频位 |
|
||||
| 金蝶潘祥记"经销商分级+授权+信用"案例 | 拿下种子客户后写成同结构案例(量化:降本 X%) |
|
||||
| 网上管家婆"平台背书墙" | 初期用"兼容管家婆数据迁移/对接 300+平台愿景"替代,客户上来后逐步填 |
|
||||
|
||||
---
|
||||
|
||||
## 四、执行顺序建议(一张图)
|
||||
|
||||
```
|
||||
P0 行业地基(#1-6) ──→ P1 差异化AI(#7-8) ──→ P2 商业化(#10-13) ──→ P3 多端/行业(#14-18)
|
||||
↑ 2-3周 ↑ 可与P0并行 ↑ #11官网可与P0并行
|
||||
产品能讲行业故事 官网/演示有独家卖点 开始收钱+获客 扩行业+移动端
|
||||
```
|
||||
|
||||
**最小可售闭环 = P0(#1–6) + P2(#10–13)**:有了批次效期+自动取价+信用额度+账龄对账单,再配上套餐计费和官网演示账套,dealerhub 才具备"对一个食品经销商开口子"的资格;AI(#7–8) 是把销售对话从"比功能"变成"看演示"的杠杆,越早越好。
|
||||
|
||||
---
|
||||
|
||||
> 附:本清单中所有"竞品有 X"的表述均可在《竞品网站调研报告.md》及 `competitor_research/txt_*.txt` 原文中找到出处;所有"我们没有 Y"均经过对 `backend/apps/` 代码的 grep/阅读核实(2026-09-08)。
|
||||
Reference in New Issue
Block a user