This commit is contained in:
zlwu committed 2026-05-24 23:04:33 +08:00
1 parent 13e4c9530a
commit 575e63e7e8
62 files changed
+28759 -9636

No files matched your search

@@ -0,0 +1,633 @@
# 渠道服务订单多次操作流水模型 实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 将渠道服务订单从单次操作模型改造为多次操作流水模型,支持多次充值、退款、购买产品、按份数退订。
**Architecture:** 订单主表(biz_channel_service_order)管汇总,新增操作流水表(biz_channel_service_order_txn)管每笔操作的明细和财务确认流程,产品持仓表(biz_channel_service_order_product)改造为追踪累计购买/退订/当前持有份数。
**Tech Stack:** Python 3.9 / FastAPI / SQLAlchemy / Pydantic v2 / MySQL 5.7 / Vue 3 / Element Plus / TypeScript
**Design Spec:** `docs/superpowers/specs/2026-05-24-channel-service-order-multi-txn-design.md`
---
## 文件结构
### 新增文件
- `docs/sql/channel-service-order-txn-migration.sql` — 数据库迁移 SQL
- `backend/app/models/channel_service_order_txn.py` — 流水 Model(或合并到 models.py)
### 修改文件
- `backend/app/models/models.py` — 新增 BizChannelServiceOrderTxn model,改造 BizChannelServiceOrder/BizChannelServiceOrderProduct
- `backend/app/crud/pilot_service.py` — 新增流水 CRUD + 改造订单 CRUD
- `backend/app/api/pilot_service.py` — 新增流水 API + Pydantic schemas
- `web-admin/src/api/admin.ts` — 新增流水相关 API 调用函数
- `web-admin/src/views/pilot-service/channel-service-order/index.vue` — 订单管理页改造
- `web-admin/src/views/pilot-service/channel-service-order-finance/index.vue` — 财务确认页改造
---
## Task 1: 数据库迁移 — 流水表 + 主表改造 + 产品表改造
**Files:**
- Create: `docs/sql/channel-service-order-txn-migration.sql`
- [ ] **Step 1: 编写迁移 SQL**
```sql
-- ============================================================
-- 渠道服务订单:多次操作流水模型迁移
-- ============================================================
-- 1. 新增操作流水表
CREATE TABLE `biz_channel_service_order_txn` (
`id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '主键ID',
`service_order_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联订单ID',
`txn_type` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'recharge/refund/purchase/unsubscribe',
`amount` decimal(18,2) DEFAULT NULL COMMENT '本次操作金额',
`txn_status` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'dsx' COMMENT 'dsx待生效 dqr待财务确认 yqr已确认 yzf已作废',
`products_json` text COLLATE utf8mb4_unicode_ci COMMENT '产品明细JSON(purchase/unsubscribe类型)',
`remark` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
`finance_submit_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发起确认人ID',
`finance_submit_time` datetime DEFAULT NULL COMMENT '发起确认时间',
`finance_submit_remark` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发起确认备注',
`finance_submit_attachments` text COLLATE utf8mb4_unicode_ci COMMENT '发起确认附件',
`finance_confirm_time` datetime DEFAULT NULL COMMENT '财务确认时间',
`finance_confirm_attachments` text COLLATE utf8mb4_unicode_ci COMMENT '财务确认附件',
`finance_remark` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '财务确认备注',
`balance_before` decimal(18,2) DEFAULT NULL COMMENT '操作前余额(yfk类型)',
`balance_after` decimal(18,2) DEFAULT NULL COMMENT '操作后余额(yfk类型)',
`tenant_no` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
`order_num` int(11) DEFAULT NULL COMMENT '排序号',
`remark` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人id',
`create_dept_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '创建人部门ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新人id',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `idx_txn_order` (`service_order_id`, `del_flag`),
KEY `idx_txn_status` (`txn_status`, `tenant_no`, `del_flag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='渠道服务订单操作流水';
-- 2. 订单主表新增汇总字段
ALTER TABLE `biz_channel_service_order`
ADD COLUMN `total_recharge_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计充值总额' AFTER `channel_agreement_id`,
ADD COLUMN `total_refund_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计退款总额' AFTER `total_recharge_amount`,
ADD COLUMN `current_balance` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '当前余额' AFTER `total_refund_amount`;
-- 3. 产品持仓表新增字段
ALTER TABLE `biz_channel_service_order_product`
ADD COLUMN `total_buy_qty` decimal(18,4) NOT NULL DEFAULT 0.0000 COMMENT '累计购买数量' AFTER `buy_amount`,
ADD COLUMN `total_unsubscribe_qty` decimal(18,4) NOT NULL DEFAULT 0.0000 COMMENT '累计退订数量' AFTER `total_buy_qty`,
ADD COLUMN `current_qty` decimal(18,4) NOT NULL DEFAULT 0.0000 COMMENT '当前持有数量' AFTER `total_unsubscribe_qty`,
ADD COLUMN `total_unsubscribe_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '累计退订金额' AFTER `current_qty`;
-- 4. 迁移现有订单数据到流水表(一次性,保留历史数据)
-- 注意:仅迁移有操作记录的订单(有 finance_submit_time 或 refund_apply_time 的)
INSERT INTO `biz_channel_service_order_txn` (
`id`, `service_order_id`, `txn_type`, `amount`, `txn_status`,
`remark`,
`finance_submit_by`, `finance_submit_time`, `finance_submit_remark`, `finance_submit_attachments`,
`finance_confirm_time`, `finance_confirm_attachments`, `finance_remark`,
`balance_before`, `balance_after`,
`tenant_no`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`
)
SELECT
UUID_SHORT() AS `id`,
o.`id` AS `service_order_id`,
'recharge' AS `txn_type`,
o.`prepay_amount` AS `amount`,
CASE
WHEN o.`finance_confirm_time` IS NOT NULL THEN 'yqr'
WHEN o.`finance_submit_time` IS NOT NULL THEN 'dqr'
ELSE 'dsx'
END AS `txn_status`,
o.`remark`,
o.`finance_submit_by`, o.`finance_submit_time`, o.`finance_submit_remark`, o.`finance_submit_attachments`,
o.`finance_confirm_time`, o.`finance_confirm_attachments`, o.`finance_remark`,
o.`balance_before`, o.`balance_after`,
o.`tenant_no`, o.`create_by`, o.`create_dept_id`, o.`create_time`, o.`update_by`, o.`update_time`, o.`del_flag`
FROM `biz_channel_service_order` o
WHERE o.`finance_submit_time` IS NOT NULL OR o.`prepay_amount` IS NOT NULL;
-- 5. 迁移退款记录到流水表
INSERT INTO `biz_channel_service_order_txn` (
`id`, `service_order_id`, `txn_type`, `amount`, `txn_status`,
`remark`,
`finance_submit_by`, `finance_submit_time`, `finance_submit_remark`, `finance_submit_attachments`,
`finance_confirm_time`, `finance_confirm_attachments`, `finance_remark`,
`tenant_no`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`
)
SELECT
UUID_SHORT() AS `id`,
o.`id` AS `service_order_id`,
'refund' AS `txn_type`,
o.`refund_amount` AS `amount`,
CASE
WHEN o.`refund_finance_confirm_time` IS NOT NULL THEN 'yqr'
ELSE 'dqr'
END AS `txn_status`,
o.`refund_remark` AS `remark`,
o.`refund_apply_by` AS `finance_submit_by`,
o.`refund_apply_time` AS `finance_submit_time`,
o.`refund_remark` AS `finance_submit_remark`,
o.`refund_attachments` AS `finance_submit_attachments`,
o.`refund_finance_confirm_time` AS `finance_confirm_time`,
o.`refund_finance_confirm_attachments` AS `finance_confirm_attachments`,
o.`refund_finance_remark` AS `finance_remark`,
o.`tenant_no`, o.`refund_apply_by` AS `create_by`, o.`create_dept_id`,
COALESCE(o.`refund_apply_time`, o.`update_time`) AS `create_time`,
o.`update_by`, o.`update_time`, o.`del_flag`
FROM `biz_channel_service_order` o
WHERE o.`refund_amount` IS NOT NULL AND o.`refund_amount` > 0;
-- 6. 回填主表汇总字段
UPDATE `biz_channel_service_order` o
SET
`total_recharge_amount` = COALESCE(o.`prepay_amount`, 0),
`total_refund_amount` = COALESCE(o.`refund_amount`, 0),
`current_balance` = COALESCE(o.`prepay_amount`, 0) - COALESCE(o.`refund_amount`, 0)
WHERE o.`order_type` = 'yfk' AND o.`prepay_amount` IS NOT NULL;
-- 7. 回填产品持仓表的累计字段
UPDATE `biz_channel_service_order_product` p
SET
`total_buy_qty` = COALESCE(p.`buy_qty`, 0),
`current_qty` = COALESCE(p.`buy_qty`, 0);
```
- [ ] **Step 2: 在数据库执行迁移**
Run: 手动在 MySQL 中执行上述 SQL,或逐步执行确认无误。
---
## Task 2: 后端 Model — 新增流水 Model + 改造主表/产品 Model
**Files:**
- Modify: `backend/app/models/models.py` (line ~1762, BizChannelServiceOrder class)
- [ ] **Step 1: 在 models.py 中 BizChannelServiceOrder 类新增字段**
在 `BizChannelServiceOrder` 类的 `channel_agreement_id` 字段后面添加三个汇总字段:
```python
total_recharge_amount = Column(Numeric(18, 2), nullable=False, default=0, comment="累计充值总额")
total_refund_amount = Column(Numeric(18, 2), nullable=False, default=0, comment="累计退款总额")
current_balance = Column(Numeric(18, 2), nullable=False, default=0, comment="当前余额")
```
- [ ] **Step 2: 在 BizChannelServiceOrderProduct 类新增字段**
在 `buy_amount` 字段后面添加:
```python
total_buy_qty = Column(Numeric(18, 4), nullable=False, default=0, comment="累计购买数量")
total_unsubscribe_qty = Column(Numeric(18, 4), nullable=False, default=0, comment="累计退订数量")
current_qty = Column(Numeric(18, 4), nullable=False, default=0, comment="当前持有数量")
total_unsubscribe_amount = Column(Numeric(18, 2), nullable=False, default=0, comment="累计退订金额")
```
- [ ] **Step 3: 在 BizChannelServiceOrderProduct 类后面新增 BizChannelServiceOrderTxn 类**
```python
class BizChannelServiceOrderTxn(Base):
"""渠道服务订单操作流水 biz_channel_service_order_txn"""
__tablename__ = "biz_channel_service_order_txn"
id = Column(String(64), primary_key=True, comment="主键ID")
service_order_id = Column(String(64), nullable=False, comment="关联订单ID")
txn_type = Column(String(16), nullable=False, comment="recharge/refund/purchase/unsubscribe")
amount = Column(Numeric(18, 2), nullable=True, comment="本次操作金额")
txn_status = Column(String(16), nullable=False, default="dsx", comment="dsx待生效 dqr待财务确认 yqr已确认 yzf已作废")
products_json = Column(Text, nullable=True, comment="产品明细JSON(purchase/unsubscribe类型)")
remark = Column(String(512), nullable=True, comment="备注")
finance_submit_by = Column(String(64), nullable=True, comment="发起确认人ID")
finance_submit_time = Column(DateTime, nullable=True, comment="发起确认时间")
finance_submit_remark = Column(String(512), nullable=True, comment="发起确认备注")
finance_submit_attachments = Column(Text, nullable=True, comment="发起确认附件")
finance_confirm_time = Column(DateTime, nullable=True, comment="财务确认时间")
finance_confirm_attachments = Column(Text, nullable=True, comment="财务确认附件")
finance_remark = Column(String(512), nullable=True, comment="财务确认备注")
balance_before = Column(Numeric(18, 2), nullable=True, comment="操作前余额")
balance_after = Column(Numeric(18, 2), nullable=True, comment="操作后余额")
tenant_no = Column(String(64), nullable=False, default="zkxw", comment="租户编号")
order_num = Column(Integer, nullable=True, comment="排序号")
remark_col = Column("remark", String(512), nullable=True, comment="备注")
create_by = Column(String(64), nullable=True, comment="创建人id")
create_dept_id = Column(String(64), nullable=False, default="", comment="创建人部门ID")
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
update_by = Column(String(64), nullable=True, comment="更新人id")
update_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间")
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
```
注意:Model 中 `remark` 字段和表里的 `remark` 列名冲突(流水表有两个 remark 用途)—— 实际上流水表只有一个 `remark` 列。Model 中只保留一个 `remark` 即可,去掉上面的 `remark_col`,直接用 `remark`。
- [ ] **Step 4: 验证 import**
确保 `models.py` 文件头部已有 `from sqlalchemy import ... Column, String, Numeric, Text, DateTime, SmallInteger, Integer` 等导入。
---
## Task 3: 后端 CRUD — 流水 CRUD + 订单 CRUD 改造
**Files:**
- Modify: `backend/app/crud/pilot_service.py` (line ~2513+)
- [ ] **Step 1: 新增流水 _row_to_dict 函数**
在 `_row_to_dict_service_order_product` 函数后面添加:
```python
def _row_to_dict_service_order_txn(row, db=None, tenant_no=None) -> Dict[str, Any]:
tn = tenant_no or row.tenant_no
d = {
"id": row.id,
"serviceOrderId": row.service_order_id or "",
"txnType": row.txn_type or "",
"amount": float(row.amount) if row.amount else 0,
"txnStatus": row.txn_status or "",
"productsJson": row.products_json or "",
"remark": row.remark or "",
"financeSubmitBy": row.finance_submit_by or "",
"financeSubmitTime": row.finance_submit_time.strftime("%Y-%m-%d %H:%M:%S") if row.finance_submit_time else "",
"financeSubmitRemark": row.finance_submit_remark or "",
"financeSubmitAttachments": row.finance_submit_attachments or "",
"financeSubmitAttachmentFiles": urls_to_attachment_list(row.finance_submit_attachments, db, tn) if db and row.finance_submit_attachments else [],
"financeConfirmTime": row.finance_confirm_time.strftime("%Y-%m-%d %H:%M:%S") if row.finance_confirm_time else "",
"financeConfirmAttachments": row.finance_confirm_attachments or "",
"financeConfirmAttachmentFiles": urls_to_attachment_list(row.finance_confirm_attachments, db, tn) if db and row.finance_confirm_attachments else [],
"financeRemark": row.finance_remark or "",
"balanceBefore": float(row.balance_before) if row.balance_before else 0,
"balanceAfter": float(row.balance_after) if row.balance_after else 0,
"tenantNo": row.tenant_no or "",
"createBy": row.create_by or "",
"createTime": row.create_time.strftime("%Y-%m-%d %H:%M:%S") if row.create_time else "",
"updateTime": row.update_time.strftime("%Y-%m-%d %H:%M:%S") if row.update_time else "",
}
if row.products_json:
try:
d["products"] = json.loads(row.products_json)
except Exception:
d["products"] = []
else:
d["products"] = []
return d
```
- [ ] **Step 2: 改造 _row_to_dict_service_order,新增汇总字段、移除旧字段**
将 `prepayAmount`、`purchaseTotalAmount` 等保留但标记为兼容字段(从汇总计算),新增:
- `totalRechargeAmount`
- `totalRefundAmount`
- `currentBalance`
- `transactions`(列表,由调用方填充)
- `products`(列表,由调用方填充)
同时在详情查询时附带流水列表和产品持仓列表。
- [ ] **Step 3: 新增流水 CRUD 函数**
```python
def list_service_order_txns(db, *, service_order_id=None, txn_status=None, txn_type=None, page=1, page_size=20, tenant_no=None):
"""查询操作流水列表"""
...
def get_service_order_txn(db, txn_id, *, tenant_no=None):
"""获取单条流水"""
...
def create_recharge_txn(db, order_id, *, amount, remark=None, tenant_no=None, user_id=None):
"""创建充值流水(yfk订单)"""
...
def create_refund_txn(db, order_id, *, amount, remark=None, tenant_no=None, user_id=None):
"""创建退款流水(yfk订单),校验 amount <= current_balance"""
...
def create_purchase_txn(db, order_id, *, products, remark=None, tenant_no=None, user_id=None):
"""创建购买产品流水(gmcp订单),同时创建/更新产品持仓"""
...
def create_unsubscribe_txn(db, order_id, *, products, remark=None, tenant_no=None, user_id=None):
"""创建退订产品流水(gmcp订单),校验退订份数 <= current_qty"""
...
def submit_txn_finance(db, txn_id, *, finance_submit_remark=None, finance_submit_attachments=None, tenant_no=None, user_id=None):
"""发起流水财务确认:dsx → dqr"""
...
def confirm_txn(db, txn_id, *, finance_remark=None, finance_confirm_attachments=None, tenant_no=None, user_id=None):
"""财务确认流水:dqr → yqr,更新主表汇总和产品持仓"""
...
def void_txn(db, txn_id, *, tenant_no=None, user_id=None):
"""作废流水:dsx → yzf"""
...
```
- [ ] **Step 4: 改造 _row_to_dict_service_order_product,新增持仓字段**
在返回字典中新增:
```python
"totalBuyQty": float(row.total_buy_qty) if row.total_buy_qty else 0,
"totalUnsubscribeQty": float(row.total_unsubscribe_qty) if row.total_unsubscribe_qty else 0,
"currentQty": float(row.current_qty) if row.current_qty else 0,
"totalUnsubscribeAmount": float(row.total_unsubscribe_amount) if row.total_unsubscribe_amount else 0,
```
---
## Task 4: 后端 API — 流水接口 + Pydantic Schemas
**Files:**
- Modify: `backend/app/api/pilot_service.py` (line ~1552+)
- [ ] **Step 1: 新增 Pydantic Schemas**
在现有 `RefundConfirmBody` 后面添加:
```python
class TxnRechargeBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
amount: float = Field(..., gt=0, alias="amount")
remark: Optional[str] = Field(None, alias="remark")
class TxnRefundBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
amount: float = Field(..., gt=0, alias="amount")
remark: Optional[str] = Field(None, alias="remark")
class TxnProductItem(BaseModel):
model_config = ConfigDict(populate_by_name=True)
project_code: Optional[str] = Field(None, alias="projectCode")
project_name: Optional[str] = Field(None, alias="projectName")
cell_type: Optional[str] = Field(None, alias="cellType")
spec_text: Optional[str] = Field(None, alias="specText")
agreement_unit_price: Optional[float] = Field(None, alias="agreementUnitPrice")
qty: Optional[float] = Field(None, alias="qty")
amount: Optional[float] = Field(None, alias="amount")
class TxnPurchaseBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
products: List[TxnProductItem] = Field(..., alias="products")
remark: Optional[str] = Field(None, alias="remark")
class TxnUnsubscribeBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
products: List[TxnProductItem] = Field(..., alias="products")
remark: Optional[str] = Field(None, alias="remark")
class TxnFinanceSubmitBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
finance_submit_remark: Optional[str] = Field(None, alias="financeSubmitRemark")
finance_submit_attachments: Optional[str] = Field(None, alias="financeSubmitAttachments")
class TxnFinanceConfirmBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
finance_remark: Optional[str] = Field(None, alias="financeRemark")
finance_confirm_attachments: Optional[str] = Field(None, alias="financeConfirmAttachments")
```
- [ ] **Step 2: 新增流水 API 路由**
```python
@router.get("/channel-service-order-txns")
async def list_txns(...) # 列表查询,支持 status/type/order_id 筛选
@router.get("/channel-service-order-txns/{txn_id}")
async def get_txn_detail(...) # 流水详情
@router.post("/channel-service-orders/{order_id}/recharge")
async def create_recharge(...) # 创建充值流水
@router.post("/channel-service-orders/{order_id}/refund")
async def create_refund(...) # 创建退款流水
@router.post("/channel-service-orders/{order_id}/purchase")
async def create_purchase(...) # 创建购买流水
@router.post("/channel-service-orders/{order_id}/unsubscribe")
async def create_unsubscribe(...) # 创建退订流水
@router.post("/channel-service-order-txns/{txn_id}/submit-finance")
async def submit_txn_finance(...) # 发起财务确认
@router.post("/channel-service-order-txns/{txn_id}/confirm")
async def confirm_txn(...) # 财务确认
@router.put("/channel-service-order-txns/{txn_id}/void")
async def void_txn(...) # 作废流水
```
- [ ] **Step 3: 改造订单详情 API**
修改 `get_channel_service_order_detail` 接口,在返回数据中附带:
- `transactions`: 该订单的所有流水列表
- `products`: 该订单的产品持仓列表(gmcp)
---
## Task 5: 前端 API — 新增流水相关调用函数
**Files:**
- Modify: `web-admin/src/api/admin.ts` (line ~1900+)
- [ ] **Step 1: 新增流水 API 函数**
在现有 `fetchVoidServiceOrder` 后面添加:
```typescript
// ========== 渠道服务订单操作流水 ==========
/** 流水列表 */
export function fetchServiceOrderTxnList(params?: Record<string, any>) {
return request.get({ url: '/web/admin/channel-service-order-txns', params })
}
/** 流水详情 */
export function fetchServiceOrderTxnDetail(txnId: string) {
return request.get({ url: `/web/admin/channel-service-order-txns/${txnId}` })
}
/** 发起充值 */
export function fetchCreateRechargeTxn(orderId: string, data: Record<string, any>) {
return request.post({ url: `/web/admin/channel-service-orders/${orderId}/recharge`, params: data })
}
/** 申请退款 */
export function fetchCreateRefundTxn(orderId: string, data: Record<string, any>) {
return request.post({ url: `/web/admin/channel-service-orders/${orderId}/refund`, params: data })
}
/** 购买产品 */
export function fetchCreatePurchaseTxn(orderId: string, data: Record<string, any>) {
return request.post({ url: `/web/admin/channel-service-orders/${orderId}/purchase`, params: data })
}
/** 退订产品 */
export function fetchCreateUnsubscribeTxn(orderId: string, data: Record<string, any>) {
return request.post({ url: `/web/admin/channel-service-orders/${orderId}/unsubscribe`, params: data })
}
/** 流水发起财务确认 */
export function fetchSubmitTxnFinance(txnId: string, data: Record<string, any>) {
return request.post({ url: `/web/admin/channel-service-order-txns/${txnId}/submit-finance`, params: data })
}
/** 流水财务确认 */
export function fetchConfirmTxn(txnId: string, data: Record<string, any>) {
return request.post({ url: `/web/admin/channel-service-order-txns/${txnId}/confirm`, params: data })
}
/** 作废流水 */
export function fetchVoidTxn(txnId: string) {
return request.put({ url: `/web/admin/channel-service-order-txns/${txnId}/void` })
}
```
- [ ] **Step 2: 保留旧 API 函数但标记废弃**
不删除 `fetchSubmitServiceOrderFinanceConfirm`、`fetchConfirmServiceOrderReceipt`、`fetchSubmitServiceOrderRefund`、`fetchConfirmServiceOrderRefund`,保持向后兼容,但前端页面不再调用。
---
## Task 6: 前端订单管理页改造
**Files:**
- Modify: `web-admin/src/views/pilot-service/channel-service-order/index.vue`
这是改动最大的文件,核心变更:
- [ ] **Step 1: 更新 import**
新增导入流水相关 API 函数:`fetchCreateRechargeTxn`, `fetchCreateRefundTxn`, `fetchCreatePurchaseTxn`, `fetchCreateUnsubscribeTxn`, `fetchSubmitTxnFinance`,移除旧的 `fetchSubmitServiceOrderFinanceConfirm`, `fetchSubmitServiceOrderRefund` 调用。
- [ ] **Step 2: 更新操作按钮**
将操作列的按钮替换为:
- yfk + dsx → 编辑、删除、**发起充值**
- yfk + sxz → **发起充值**、**申请退款**
- gmcp + dsx → 编辑、删除、**购买产品**
- gmcp + sxz → **购买产品**、**退订产品**
- 任意 + yzf → 仅查看
- [ ] **Step 3: 改造详情弹窗(SystemViewDialog)**
在 viewSections computed 中:
- 基本信息section 新增 `currentBalance`(yfk)或产品持仓汇总(gmcp)
- 移除旧的"发起财务确认""财务确认信息""退款信息"平铺 sections
- 新增"操作记录"section,用 inner-table 展示流水列表(类型Tag、金额、状态Tag、时间)
- gmcp 类型新增"产品持仓"section,显示各产品当前持有份数
- [ ] **Step 4: 替换弹窗**
移除旧的"发起财务确认"弹窗和"申请退款"弹窗,新增:
- **发起充值弹窗**:金额输入、备注、凭证上传
- **申请退款弹窗**:金额输入(max=当前余额)、备注、凭证上传
- **购买产品弹窗**:选项目/规格、输入份数、自动算金额(类似现有的新增订单弹窗中产品明细部分)
- **退订产品弹窗**:选已持有项目(下拉,显示当前份数)、输入退订份数(≤持有量)、自动算退款金额
- [ ] **Step 5: 操作按钮点击后,可选择是否立即发起财务确认**
创建流水后,弹出提示"是否立即发起财务确认?",用户点确认则调用 `fetchSubmitTxnFinance`。
---
## Task 7: 前端财务确认页改造
**Files:**
- Modify: `web-admin/src/views/pilot-service/channel-service-order-finance/index.vue`
- [ ] **Step 1: 改为查流水表**
将 `fetchChannelServiceOrderList` 替换为 `fetchServiceOrderTxnList`,参数传递 `txnStatus=dqr`(待处理)或 `txnStatus=yqr`(已处理)。
- [ ] **Step 2: 表格列调整**
列表表格列改为:
- 序号、渠道名称、订单类型Tag、流水类型Tag(充值/退款/购买/退订)、金额、状态Tag、提交人、提交时间、操作
流水类型标签映射:
```typescript
const txnTypeLabel = (t: string) => {
const map: Record<string, string> = { recharge: '充值', refund: '退款', purchase: '购买产品', unsubscribe: '退订产品' }
return map[t] || t
}
```
- [ ] **Step 3: Tab 切换调整**
已有两 tab(待处理/已处理),改为:
- 待处理:`txnStatus=dqr`
- 已处理:`txnStatus=yqr`
- [ ] **Step 4: 确认弹窗改造**
"确认收款"弹窗和"确认退款"弹窗合并为一个"确认操作"弹窗:
- 上半部分展示流水详情(渠道、金额/产品明细、提交人/时间/备注/凭证)
- 下半部分输入财务确认备注 + 上传确认凭证
- 调用 `fetchConfirmTxn(txnId, data)`
- [ ] **Step 5: 详情弹窗**
保留 SystemViewDialog 查看详情,展示流水完整信息。
---
## Task 8: 状态标签注册更新
**Files:**
- Modify: `web-admin/src/utils/status-tag-registry.ts`
- [ ] **Step 1: 新增流水类型标签**
在 `status-tag-registry.ts` 中新增:
```typescript
// 流水类型
export const txnTypeLabel = (type: string): string => {
const map: Record<string, string> = {
recharge: '充值',
refund: '退款',
purchase: '购买产品',
unsubscribe: '退订产品',
}
return map[type] || type
}
export const txnTypeTagType = (type: string): string => {
const map: Record<string, string> = {
recharge: '',
refund: 'danger',
purchase: 'success',
unsubscribe: 'warning',
}
return map[type] || 'info'
}
// 流水状态
export const txnStatusLabel = (status: string): string => {
const map: Record<string, string> = {
dsx: '待生效',
dqr: '待财务确认',
yqr: '已确认',
yzf: '已作废',
}
return map[status] || status
}
export const txnStatusTagType = (status: string): string => {
const map: Record<string, string> = {
dsx: 'info',
dqr: 'warning',
yqr: 'success',
yzf: 'danger',
}
return map[status] || 'info'
}
```
File diff suppressed because it is too large. Load diff