feat: 组织隔离(IRIS 单实例)与出料功能基础
本轮之前累积的未提交工作,一并固化:
- 组织隔离:同一份代码部署给不同部门只需改 config 的 ORG_DEPARTMENT 与
MATERIAL_CATEGORY_PREFIX。过滤点在登录/人员列表/物料/MOM 出库单四处,
全部服务端钉死,客户端传什么都放不大。
★ 物料必须用**前缀** LIKE,不能反推成 ILIKE '%IRIS%':MOM 里 LICA 的物料是
`LICA/<中文>`,而本部门分类树里另有 `IRIS/成品/LICA/…`(本就属于本部门),
前缀匹配天然区分得开。
- MOM 出库单只读查询(直连 MOM 库):不走 MOM 现成的 /outbound 接口 ——
那个要 JWT + permission_required,且对非特权账号按 consumer_name 做行级
隔离,服务账号只能拿到自己名下的单。分页必须两段式(先按单号 GROUP BY
分页,再 IN 捞明细),对宽表直接分页会得到明细行数而不是单据数。
- 出料功能:产品 ↔ 出库单存档(product_outbounds)与任务 ↔ 出库明细
(task_outbound_materials),供「这台设备对应 MOM 哪张单」的展示。
⚠️ 快照一律由后端拿 ID 去 MOM 现查,不接受前端传入,否则前端可伪造单据。
This commit is contained in:
48
frontend/src/services/momApi.ts
Normal file
48
frontend/src/services/momApi.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/** MOM 出库单相关接口 —— 任务挂载出库物料时搜索选择用 */
|
||||
import api from "./api";
|
||||
import type { MomOutboundSearchResponse } from "../types/api";
|
||||
|
||||
export interface MomOutboundSearchParams {
|
||||
/** 出库单号 / 物料名称 / 规格型号 / SKU / 领用人,任一命中 */
|
||||
keyword?: string;
|
||||
/** YYYY-MM-DD,含当日 */
|
||||
start_date?: string;
|
||||
/** YYYY-MM-DD,含当日 */
|
||||
end_date?: string;
|
||||
/** 按领用人(中文名)过滤 */
|
||||
consumer?: string;
|
||||
/** 跳过**单据数**(不是明细行数) */
|
||||
skip?: number;
|
||||
/** 返回**单据数**,后端上限 100 */
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索 MOM 出库单(按单据分页,带回每张单的明细)。
|
||||
* 对应后端 GET /api/v1/mom-outbounds
|
||||
*
|
||||
* ⚠️ 可见范围(公司隔离 + 跨部门例外)由后端服务层钉死,本接口**没有任何**
|
||||
* 能放大范围的参数 —— 界面筛选只能收窄。
|
||||
*/
|
||||
export async function searchMomOutbounds(
|
||||
params: MomOutboundSearchParams = {},
|
||||
): Promise<MomOutboundSearchResponse> {
|
||||
const { data } = await api.get<MomOutboundSearchResponse>("/mom-outbounds", { params });
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 本部门出库单里出现过的**领用人姓名**(去重,按出现次数降序)。
|
||||
* 对应后端 GET /api/v1/mom-outbounds/consumers
|
||||
*
|
||||
* ⚠️ 后端**已按权限范围过滤** —— 下拉里不会出现用户看不到的人名。
|
||||
*/
|
||||
export async function listMomOutboundConsumers(): Promise<string[]> {
|
||||
const { data } = await api.get<string[]>("/mom-outbounds/consumers");
|
||||
return data;
|
||||
}
|
||||
|
||||
// 注:原先这里有 addTaskOutboundMaterials / removeTaskOutboundMaterial
|
||||
// 两个**任务级**的读写函数。物料已统一为**设备级**,任务级的三个端点连同
|
||||
// 实现一起删除 —— 挂载/查看/删除/报废一律走 productApi 里的
|
||||
// mountProductOutboundMaterials / removeProductOutboundMaterial。
|
||||
@ -26,6 +26,19 @@ export interface TaskCompletePayload {
|
||||
remark: string | null;
|
||||
}
|
||||
|
||||
export interface TaskCreatePayload {
|
||||
product_id: string;
|
||||
task_name: string;
|
||||
assignee_id?: string | null;
|
||||
remark?: string;
|
||||
/**
|
||||
* 创建时一并挂载的 MOM 出库**明细行** ID(trans_outbound.id)。
|
||||
* 前端按整张出库单勾选,提交时把该单全部明细 ID 带过来。
|
||||
* 不传 = 不挂载。
|
||||
*/
|
||||
mom_line_ids?: number[];
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// API 方法
|
||||
// ============================================================
|
||||
@ -43,6 +56,16 @@ export async function listTasks(productId?: string): Promise<TaskListResponse> {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建任务 — 对应后端 POST /api/v1/tasks/
|
||||
* payload.mom_line_ids 非空时,后端会在**同一事务**里把对应的 MOM 出库明细
|
||||
* 挂到新任务上,不存在「任务建好了但物料没挂上」的中间态。
|
||||
*/
|
||||
export async function createTask(payload: TaskCreatePayload): Promise<TaskResponse> {
|
||||
const { data } = await api.post<TaskResponse>("/tasks/", payload);
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 创建子任务 */
|
||||
export async function createSubtask(
|
||||
parentTaskId: string,
|
||||
|
||||
21
frontend/src/services/userApi.ts
Normal file
21
frontend/src/services/userApi.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/** 用户列表 —— 对接 MOM sys_user,只返回本部门人员 */
|
||||
import api from "./api";
|
||||
|
||||
export interface UserOption {
|
||||
id: string;
|
||||
/** 登录账号,即任务里的 assignee_id 口径 */
|
||||
username: string;
|
||||
/** 中文姓名 */
|
||||
full_name: string;
|
||||
department: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本部门人员列表。
|
||||
* 对应后端 GET /api/v1/users/ —— 部门隔离由服务端按 ORG_DEPARTMENT 钉死,
|
||||
* 客户端传什么都没用。
|
||||
*/
|
||||
export async function listUsers(keyword = "", limit = 200): Promise<UserOption[]> {
|
||||
const { data } = await api.get<UserOption[]>("/users/", { params: { keyword, limit } });
|
||||
return data;
|
||||
}
|
||||
Reference in New Issue
Block a user