作为 xiaoqu-agent-service(MCP 服务) 的开发参考。新系统与旧系统零代码耦合, 仅照搬其行级数据权限规则与数据源结构。设计方案见 xiaoqu-franchisee/doc/agent-mcp-service-design.md
458 lines
20 KiB
Markdown
458 lines
20 KiB
Markdown
# 开发文档:出库单上传凭证
|
||
|
||
> **需求**:在「全部出库单」(web_id=211) 每行新增「上传凭证」按钮。
|
||
> - 弹窗可上传图片 / PDF + 填备注(最多 10 个,单文件 ≤ 100MB,仅前端校验);
|
||
> - 打开时若已上传过则回显文件列表 + 备注;
|
||
> - 弹窗内点 ❌ 不真删,点「提交」时才落库;
|
||
> - 「提交」自动判定新增 / 更新。
|
||
> **环境**:dev `192.168.1.181:3306`(root/kaixinjiuhao),PRD 上线需把 `authority_id` 替换为 PRD 实际值后重跑。
|
||
> **涉及库**:`xiaoqu_franchisee_d`(业务数据)+ `xiaoqu_comples_d`(按钮权限)。
|
||
|
||
---
|
||
|
||
## 1. 数据库变更
|
||
|
||
### 1.1 业务表 — `xiaoqu_franchisee_d`
|
||
|
||
#### 1.1.1 新表 `warehouse_out_certificate`(出库单凭证 - 备注)
|
||
|
||
图片本身复用现有 `warehouse_in_out_pic`,本表只保存 **备注**。约定:
|
||
- `warehouse_in_out_pic.aggregate_id = warehouse_out.id`
|
||
- `warehouse_in_out_pic.file_type = 4`(**新枚举值**:出库凭证)
|
||
- `warehouse_in_out_pic.in_out_allocation_type = 99`(**新枚举值**:出库凭证类型,避开 0/1/2/10-14 已用值)
|
||
|
||
```sql
|
||
USE xiaoqu_franchisee_d;
|
||
|
||
CREATE TABLE `warehouse_out_certificate` (
|
||
`id` bigint(8) NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||
`out_order_id` bigint(8) NOT NULL COMMENT '出库单ID(warehouse_out.id)',
|
||
`out_order_no` varchar(50) DEFAULT '' COMMENT '出库单号(冗余,便于查询)',
|
||
`memo` varchar(500) DEFAULT '' COMMENT '备注信息',
|
||
`create_id` bigint(8) DEFAULT '0' COMMENT '创建id',
|
||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||
`modify_id` bigint(8) DEFAULT '0' COMMENT '修改id',
|
||
`modify_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
|
||
`soft_delete` int(4) DEFAULT '0' COMMENT '数据状态',
|
||
PRIMARY KEY (`id`),
|
||
UNIQUE KEY `uk_out_order_id` (`out_order_id`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库单凭证(备注)';
|
||
```
|
||
|
||
> `uk_out_order_id` 唯一索引保证一张出库单只有一条凭证记录;图片可多张挂 `warehouse_in_out_pic`。
|
||
|
||
#### 1.1.2 扩充 `warehouse_in_out_pic` 字段枚举注释(可选 / 可不执行)
|
||
|
||
仅刷注释、便于阅读:
|
||
|
||
```sql
|
||
ALTER TABLE `warehouse_in_out_pic`
|
||
MODIFY COLUMN `file_type` int(4) DEFAULT '0'
|
||
COMMENT '文件类型0:申请图片;1:入库确认图片;2:出库确认图片;3:送货工单签收;4:出库凭证',
|
||
MODIFY COLUMN `in_out_allocation_type` int(4) DEFAULT '0'
|
||
COMMENT '出入库类型(0:入库;1:出库;2:调拨;10:装机工单;11:送货工单;12:取货工单;13:营销客户申请;14:营销单申请;99:出库凭证)';
|
||
```
|
||
|
||
### 1.2 按钮权限(权限库)
|
||
|
||
「上传凭证」是行内按钮(`type=0`),挂在「全部出库单」(`web_id=211`) 上,按钮权限**复用该页面当前已有的授权范围**(不写死 `authority_id`,避免环境差异)。
|
||
|
||
> **环境对应的权限 schema**:
|
||
> - dev → `xiaoqu_comples_d`
|
||
> - test → `test`
|
||
> - prod → `xiaoqu_comples_p`
|
||
>
|
||
> 切环境只需改第一行 `USE`,其余语句通用。
|
||
|
||
> **各环境实测授权范围**(2026-05-14 核对):
|
||
> - dev (`xiaoqu_comples_d`) `web_id=211` → 授权 2 个 authority:`1, 102`
|
||
> - test (`test`) `web_id=211` → 授权 26 个 authority(导自线上)
|
||
> - prod (`xiaoqu_comples_p`) 上线前再用下面 §4 校验脚本现查一次
|
||
|
||
```sql
|
||
USE xiaoqu_comples_d; -- dev;test 改 USE test; prod 改 USE xiaoqu_comples_p;
|
||
|
||
START TRANSACTION;
|
||
|
||
-- ─────────────────────────────────────────────────────────────
|
||
-- ① button:按钮本体
|
||
-- type=0 行内按钮 / type=1 顶部 tableButton
|
||
-- theme: primary(蓝) / danger(红) / info(灰) / 空
|
||
-- 注意:不要硬编码 id,让 AUTO_INCREMENT 决定(不同 schema id 必然不同)
|
||
-- ─────────────────────────────────────────────────────────────
|
||
INSERT INTO `button`
|
||
(web_id, cn_name, en_name, icon, class_name, function_path,
|
||
`type`, `status`, order_by, theme, memo, app_en_name)
|
||
VALUES
|
||
(211, '上传凭证', 'uploadCertificate', '', '', '',
|
||
0, 1, 0, 'primary', '出库单上传凭证(图片+备注)', '');
|
||
|
||
SET @new_button_id := LAST_INSERT_ID();
|
||
SELECT @new_button_id AS new_button_id;
|
||
|
||
-- ─────────────────────────────────────────────────────────────
|
||
-- ② button_webpage_authority:动态复用 web_id=211 当前所有授权组
|
||
-- 这样不管 dev/test/prod,新按钮始终对所有能进该页的用户可见
|
||
-- ─────────────────────────────────────────────────────────────
|
||
INSERT INTO `button_webpage_authority`
|
||
(web_id, authority_id, cn_name, en_name, button_id,
|
||
order_by, check_status, param, icon, `type`, class_name, theme, memo)
|
||
SELECT
|
||
211,
|
||
wa.authority_id,
|
||
'上传凭证',
|
||
'uploadCertificate',
|
||
@new_button_id,
|
||
0, 1, '', '', 0, '', 'primary', ''
|
||
FROM (
|
||
SELECT DISTINCT authority_id
|
||
FROM `webpage_authority`
|
||
WHERE web_id = 211
|
||
AND soft_delete = 0
|
||
) wa;
|
||
|
||
-- ─────────────────────────────────────────────────────────────
|
||
-- ③ 校验
|
||
-- ─────────────────────────────────────────────────────────────
|
||
SELECT id, web_id, cn_name, en_name, `type`, theme FROM `button` WHERE id = @new_button_id;
|
||
SELECT authority_id, COUNT(*) AS cnt FROM `button_webpage_authority`
|
||
WHERE button_id = @new_button_id AND soft_delete=0
|
||
GROUP BY authority_id;
|
||
SELECT COUNT(*) AS total_grants FROM `button_webpage_authority`
|
||
WHERE button_id = @new_button_id AND soft_delete=0;
|
||
-- total_grants 应等于「全部出库单」当前的 webpage_authority 数:
|
||
-- dev=2 / test=26 / prod=待现查
|
||
|
||
COMMIT;
|
||
-- 失败请 ROLLBACK
|
||
```
|
||
|
||
> **2026-05-14 已执行情况**:
|
||
> - dev (`xiaoqu_comples_d`):button.id=789,2 条 button_webpage_authority(authority 1, 102)
|
||
> - test (`test`):button.id=790,26 条 button_webpage_authority(authority 1, 22, 23, 32, 34, 41, 49, 50, 52, 55, 56, 64, 65, 66, 68, 70, 71, 75, 85, 92, 94, 96, 105, 108, 118, 120)
|
||
|
||
---
|
||
|
||
## 2. 后端改动 — `xiaoqu-franchisee`
|
||
|
||
### 2.1 包结构
|
||
|
||
```
|
||
xiaoqu-publicbeanandmap/src/main/java/me/iiv/iivframework/
|
||
├── beans/b2/WarehouseOutCertificate.java # @TableName("warehouse_out_certificate")
|
||
├── mapper/m2/WarehouseOutCertificateMapper.java # extends BaseMapper
|
||
└── service/s2/
|
||
├── WarehouseOutCertificateService.java # interface IService
|
||
└── Impl/WarehouseOutCertificateServiceImpl.java # extends ServiceImpl
|
||
|
||
xiaoqu-franchisee/src/main/java/me/iiv/iivframework/controller/warehouse/
|
||
├── WarehouseOutController.java # 追加 3 个方法
|
||
├── input/OutCertificateSaveInput.java # 新建
|
||
└── output/OutCertificateOutput.java # 新建(位置参考已有 WarehouseInOutPicOut 同包)
|
||
```
|
||
|
||
> 文件 `OutCertificateOutput.java` / `OutCertificateSaveInput.java` 实际路径要与项目既有约定一致,参考 `me.iiv.iivframework.controller.output.WarehouseInOutPicOut` / `me.iiv.iivframework.controller.insert.WarehouseInOutPicInsert`。
|
||
|
||
### 2.2 API(新增 2 个端点)
|
||
|
||
挂在 `WarehouseOutController` 下(`@RequestMapping("/myWarehouseOut")`):
|
||
|
||
| Method | URL | 说明 |
|
||
|---|---|---|
|
||
| GET | `/myWarehouseOut/certificate/get` | 查询出库单的凭证(备注+图片列表) |
|
||
| POST | `/myWarehouseOut/certificate/save` | 保存(自动 upsert 备注 + 真删用户去掉的图片 + 绑定新图片) |
|
||
|
||
#### 2.2.1 `GET /certificate/get`
|
||
|
||
**Query**
|
||
```
|
||
outOrderId: Long
|
||
```
|
||
|
||
**Output**
|
||
```java
|
||
public class OutCertificateOutput {
|
||
private Long outOrderId;
|
||
private String memo; // 无记录则空串
|
||
private List<WarehouseInOutPicOut> picList; // 复用现有 DTO
|
||
}
|
||
```
|
||
|
||
**逻辑**
|
||
```java
|
||
WarehouseOutCertificate cert = certService.getOne(
|
||
Wrappers.<WarehouseOutCertificate>lambdaQuery()
|
||
.eq(WarehouseOutCertificate::getOutOrderId, outOrderId)
|
||
.eq(WarehouseOutCertificate::getSoftDelete, 0)
|
||
.last("limit 1"));
|
||
|
||
List<WarehouseInOutPic> pics = warehouseInOutPicService.list(
|
||
Wrappers.<WarehouseInOutPic>lambdaQuery()
|
||
.eq(WarehouseInOutPic::getAggregateId, outOrderId)
|
||
.eq(WarehouseInOutPic::getFileType, 4)
|
||
.eq(WarehouseInOutPic::getInOutAllocationType, 99)
|
||
.eq(WarehouseInOutPic::getSoftDelete, 0));
|
||
|
||
// for each pic: OSSClientUtil.getNormalImageUrl(bucket, folder, fileName) → 组装 WarehouseInOutPicOut
|
||
```
|
||
|
||
#### 2.2.2 `POST /certificate/save`
|
||
|
||
**Body (JSON)**
|
||
```java
|
||
public class OutCertificateSaveInput {
|
||
private Long outOrderId; // 必填
|
||
private String memo; // ≤500
|
||
private List<Long> fileIds; // 当前保留的图片 fileId 列表(前端 ❌ 掉的不传)
|
||
}
|
||
```
|
||
|
||
**逻辑**(一次性 upsert,幂等)
|
||
```text
|
||
0. 校验:outOrderId 必填、memo ≤500、fileIds 元素必须均归属本出库单或 aggregate_id=0 的孤儿(防越权改别人的图)。
|
||
1. 备注 upsert
|
||
- SELECT … WHERE out_order_id=? AND soft_delete=0
|
||
- 存在 → UPDATE memo / modify_id / modify_time
|
||
- 不存在 → INSERT (out_order_id, out_order_no(冗余取自 warehouse_out), memo, …)
|
||
|
||
2. 真删被去掉的图片
|
||
- dbIds = warehouse_in_out_pic WHERE aggregate_id=outOrderId AND file_type=4 AND in_out_allocation_type=99 AND soft_delete=0
|
||
- toDelete = dbIds - fileIds
|
||
- 对 toDelete:
|
||
a) SET soft_delete=1, modify_time=NOW()
|
||
b) OSSClientUtil.deleteFile(bucket, folder, fileName) // 参考 UploadFileController.deleteFile
|
||
|
||
3. 绑定新上传的图片
|
||
- newOnes = fileIds 中 aggregate_id=0 的(即刚通过 /uploadFile/warehouseCertificate 落库的孤儿)
|
||
- UPDATE warehouse_in_out_pic
|
||
SET aggregate_id=outOrderId, file_type=4, in_out_allocation_type=99,
|
||
modify_id=?, modify_time=NOW()
|
||
WHERE id IN (newOnes) AND aggregate_id=0 AND soft_delete=0
|
||
```
|
||
|
||
> ⚠️ 上传接口 `/uploadFile/warehouseCertificate` 是**通用接口**(同样用于装机/送货/调拨等),落库时不设 `aggregate_id/file_type/in_out_allocation_type`,因此**必须**靠"提交"时绑定。
|
||
> ⚠️ 用户点「返回」不提交时,这次上传的图片在 DB 里 `aggregate_id=0`,会成为孤儿 → 见 §2.4。
|
||
|
||
### 2.3 Service 接口要点
|
||
|
||
```java
|
||
public interface WarehouseOutCertificateService extends IService<WarehouseOutCertificate> {
|
||
OutCertificateOutput getByOutOrderId(Long outOrderId);
|
||
void save(Long userId, OutCertificateSaveInput input);
|
||
}
|
||
```
|
||
|
||
事务:`save()` 加 `@Transactional`(备注 upsert + 图片软删/绑定 必须原子)。
|
||
|
||
### 2.4 孤儿图片清理(可选 / 推荐)
|
||
|
||
用户连续上传多张图后点「返回」会留下 `aggregate_id=0` 的记录。加一个 XXL-Job 定时任务(参考 `task/AutoOutWarehouseTask.java` 同包结构):
|
||
|
||
```text
|
||
每天凌晨:
|
||
SELECT id, ossbucketname, ossfolder, file_name
|
||
FROM warehouse_in_out_pic
|
||
WHERE aggregate_id=0
|
||
AND soft_delete=0
|
||
AND create_time < NOW() - INTERVAL 1 DAY;
|
||
→ 软删 + OSSClientUtil.deleteFile
|
||
```
|
||
|
||
如本期不做,先记录到运维 backlog。
|
||
|
||
---
|
||
|
||
## 3. 前端改动 — `vue-manage-system`
|
||
|
||
### 3.1 API 端点
|
||
|
||
`src/api/api.js` → `outManage` 块追加:
|
||
```js
|
||
certificateGet: '/myWarehouseOut/certificate/get',
|
||
certificateSave: '/myWarehouseOut/certificate/save',
|
||
```
|
||
|
||
`src/api/warehouseService.js` → `outManageService`:
|
||
```js
|
||
certificateGet: (params) => request.get($api.outManage.certificateGet, { params }),
|
||
certificateSave: (data) => request.post($api.outManage.certificateSave, data),
|
||
```
|
||
|
||
### 3.2 新建组件:`outCertificateForm.vue`
|
||
|
||
路径:`src/components/page/warehouseInOutManage/outManage/components/outCertificateForm.vue`
|
||
|
||
props:`outOrderId: Number`;emit:`success`、`cancel`
|
||
|
||
骨架:
|
||
```vue
|
||
<template>
|
||
<div class="cert-form">
|
||
<!-- 上传区:支持图片 + PDF;picList 含图片/PDF 两类,统一用文件名样式 -->
|
||
<el-upload
|
||
class="cert-upload"
|
||
:action="uploadUrl"
|
||
:headers="uploadHeaders"
|
||
name="image"
|
||
:show-file-list="false"
|
||
:limit="10"
|
||
:before-upload="beforeUpload"
|
||
:on-success="handleUploadSuccess"
|
||
accept="image/jpeg,image/jpg,image/png,image/gif,image/bmp,application/pdf"
|
||
>
|
||
<div class="upload-trigger"><i class="el-icon-plus"></i><span>上传</span></div>
|
||
</el-upload>
|
||
<div class="tip"><b>上传凭证</b> 最多10个文件,每个文件大小限制100M</div>
|
||
|
||
<el-form label-width="60px">
|
||
<el-form-item label="备注:">
|
||
<el-input
|
||
v-model="memo"
|
||
type="textarea"
|
||
:rows="6"
|
||
maxlength="500"
|
||
show-word-limit
|
||
placeholder="请输入备注信息(选填)" />
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<el-divider content-position="center">凭证</el-divider>
|
||
<div v-if="picList.length === 0" class="empty">暂无数据</div>
|
||
<div v-else class="pic-list">
|
||
<span v-for="(p, i) in picList" :key="p.fileId" class="pic-item" @click="preview(p)">
|
||
<i :class="isPdf(p.fileName) ? 'el-icon-document' : 'el-icon-picture-outline'"></i>
|
||
{{ p.fileName }}
|
||
<i class="el-icon-error remove" @click.stop="removePic(i)"></i> <!-- ❌ 仅本地移除 -->
|
||
</span>
|
||
</div>
|
||
|
||
<div class="footer">
|
||
<el-button @click="$emit('cancel')">返回</el-button>
|
||
<el-button type="primary" :loading="submitting" @click="onSubmit">提交</el-button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
关键交互:
|
||
- `mounted` → 调 `outManageService.certificateGet({outOrderId})` → 回显 `memo` + `picList`
|
||
- `beforeUpload(file)` 前端校验(仅前端,不走后端兜底):
|
||
- 扩展名必须在 `['jpg','jpeg','png','gif','bmp','pdf']`
|
||
- 大小 ≤ `100 * 1024 * 1024`(100MB)
|
||
- 当前列表 + 待上传不超过 10 个
|
||
- 任一不满足 `this.$message.error('...')` + `return false` 终止上传
|
||
- 上传成功回调把 `{fileId, fileName, url}` push 进 `picList`,**不调保存接口**
|
||
- `removePic(i)` 仅 `splice`,不调接口
|
||
- `preview(p)`:图片走 `el-image-viewer` 大图预览;PDF `window.open(p.url)` 新窗口打开
|
||
- 「提交」:`certificateSave({outOrderId, memo, fileIds: picList.map(p=>p.fileId)})` → 成功 emit `success`
|
||
|
||
### 3.3 `outAllDoc.vue` 接入
|
||
|
||
引入组件、加 dialog、在 `buttonsClickFun` 加 handler:
|
||
```js
|
||
import outCertificateForm from './components/outCertificateForm.vue';
|
||
|
||
components: { ..., outCertificateForm },
|
||
|
||
data() { return {
|
||
...,
|
||
showCertificate: false,
|
||
certificateOrderId: null,
|
||
} },
|
||
|
||
// mounted() 内 buttonsClickFun 追加:
|
||
uploadCertificate: () => {
|
||
this.certificateOrderId = this.currentForm.id;
|
||
this.showCertificate = true;
|
||
}
|
||
```
|
||
|
||
模板末尾:
|
||
```vue
|
||
<el-dialog
|
||
title="上传凭证"
|
||
:visible.sync="showCertificate"
|
||
width="600px"
|
||
v-if="showCertificate"
|
||
:close-on-click-modal="false">
|
||
<out-certificate-form
|
||
:outOrderId="certificateOrderId"
|
||
@success="onCertificateSaved"
|
||
@cancel="showCertificate = false" />
|
||
</el-dialog>
|
||
```
|
||
|
||
```js
|
||
onCertificateSaved() {
|
||
this.showCertificate = false;
|
||
this.$message.success('保存成功');
|
||
}
|
||
```
|
||
|
||
> **复用范围**:本期只在「全部出库单」(`outAllDoc.vue` / web_id=211) 开放;「我的出库单」「待办出库单」暂不上。
|
||
|
||
---
|
||
|
||
## 4. 上线执行顺序
|
||
|
||
1. **DEV (`xiaoqu_franchisee_d` + `xiaoqu_comples_d`)**
|
||
- 业务库执行 §1.1.1(建 `warehouse_out_certificate`)
|
||
- 权限库执行 §1.2(按钮 + 授权,`USE xiaoqu_comples_d`)
|
||
- 后端编码 → `mvn -pl xiaoqu-publicbeanandmap install` → `mvn -pl xiaoqu-franchisee package` → 部署 Tomcat
|
||
- 前端编码 → `yarn build-dev` → 部署
|
||
2. **DEV 冒烟**
|
||
- 管理员(authority=1)打开「全部出库单」→ 每行操作里出现「上传凭证」
|
||
- 点开 → 空态显示「暂无数据」 / 备注空白
|
||
- 上传 1 张 → ❌ → 提交 → 重新进入弹窗 → 凭证为空 ✅(验证"未提交不持久 / 提交才删")
|
||
- 上传 2 张 + 写备注 + 提交 → 重新进入 → 两张图回显 + 备注回显 ✅
|
||
- 改备注 + ❌ 掉 1 张 + 提交 → 重新进入 → 1 张图 + 新备注 ✅
|
||
3. **TEST (`xiaoqu_franchisee_d` + `test`)**
|
||
- 业务表 `warehouse_out_certificate` 已经存在于 `xiaoqu_franchisee_d`(dev/test 共享业务库),**无需再建**
|
||
- 权限库执行 §1.2(按钮 + 授权,`USE test`)
|
||
- §1.2 第二段 INSERT 用动态 SELECT 自动复用 26 个 authority,不需要改 SQL
|
||
- 部署后端 / 前端到 test 环境
|
||
4. **PRD 上线前必查**(仅校验,§1.2 SQL 不用改)
|
||
```sql
|
||
USE xiaoqu_comples_p;
|
||
-- ① 「全部出库单」web_id 是否仍是 211
|
||
SELECT id, title FROM webpage WHERE path='warehouseInOutManage/outAllDoc';
|
||
|
||
-- ② 「全部出库单」当前授权范围(INSERT 后 button_webpage_authority 应等于这个数)
|
||
SELECT COUNT(DISTINCT authority_id) AS total_authorities
|
||
FROM webpage_authority WHERE web_id=211 AND soft_delete=0;
|
||
|
||
-- ③ 防重复执行:确认 uploadCertificate 按钮尚未存在
|
||
SELECT id, web_id, en_name FROM button WHERE en_name='uploadCertificate' AND web_id=211;
|
||
```
|
||
5. **PRD 上线**:§1.2 SQL 把 `USE` 改成 `USE xiaoqu_comples_p` 后**整段照跑**(不需要替换硬编码值,因为已动态化)+ 发包
|
||
|
||
---
|
||
|
||
## 5. 风险与注意
|
||
|
||
| 项 | 风险 | 处理 |
|
||
|---|---|---|
|
||
| ~~`authority_id=1,102` 硬编码~~ | 已用动态 SELECT 替换 | §1.2 INSERT button_webpage_authority 用子查询自动复用 webpage_authority 当前授权范围,dev/test/prod 通用 |
|
||
| `web_id=211` 硬编码 | 各环境 webpage 主键大概率一致,但 prod 上线前仍要校验 | §4 ③ 校验脚本 |
|
||
| 跨环境 button.id 不同 | dev=789, test=790 是不同自增值 | SQL 用 `LAST_INSERT_ID()` 拿,前后端代码不耦合 button.id |
|
||
| `file_type=4` / `in_out_allocation_type=99` 是新枚举 | 与既有数据冲突 | 已查 dev:`file_type` 现仅 0-3、`in_out_allocation_type` 现仅 0-2/10-14,安全 |
|
||
| `/uploadFile/warehouseCertificate` 是通用上传接口 | 不能在那里硬编码 `file_type=4`(会污染装机/送货流程) | 用"上传只落 pic 记录,aggregate_id=0;提交时再补 file_type/aggregate_id/type"两步法 |
|
||
| 用户上传后不提交导致孤儿图 | OSS 浪费 + DB 冗余 | §2.4 加定时清理(推荐);或留 backlog |
|
||
| `uk_out_order_id` 唯一索引 | 同一出库单二次新增会冲突 | service 强制走 `selectOne→update` 路径,禁止裸 INSERT |
|
||
| `warehouse_in_out_pic.aggregate_id` 默认 `0` 而非 `NULL` | 不能用 `IS NULL` 判孤儿 | 全程用 `=0` 判断(已在 §2.2.2 / §2.4 体现) |
|
||
| 文件数 / 大小限制仅前端 | 绕过前端直接调上传接口可超限 | 决策上接受;后续若发现滥用再补后端兜底 |
|
||
| PDF 预览 | 浏览器原生 PDF viewer 在内嵌 dialog 里可能体验差 | 用 `window.open(url)` 新标签页打开;图片走 `el-image-viewer` |
|
||
|
||
---
|
||
|
||
## 6. PM 确认结论(已敲定,2026-05-14)
|
||
|
||
| # | 决议 |
|
||
|---|---|
|
||
| 1 | **复用范围**:本期仅 `outAllDoc.vue`(web_id=211)。我的出库 / 待办出库 暂不上。 |
|
||
| 2 | **列表角标**:**不做**。砍掉 `/certificate/exists` 接口。 |
|
||
| 3 | **文件类型**:图片 + PDF。复用现有 `/uploadFile/warehouseCertificate`(实测 OSSClientUtil.getContentType 已覆盖 jpg/png/gif/bmp/pdf 等 MIME,方法名 `uploadImageStream2OSS` 是历史遗留命名)。 |
|
||
| 4 | **数量/大小限制**:最多 10 个、单个 ≤100MB,**仅前端校验**(`before-upload` 拦截)。 |
|
||
| 5 | **"一键清空"按钮**:**不做**。用户自行 ❌ 每个文件 + 清空备注 + 提交。 |
|
||
|