feat(m01-easycalc): usesOf 회사 · 개인 층 조합까지 — M02 층 길 읽기만

- 내 층 /combos/all · 남의 회사 /layers/company/owners · 회사마다 사람 /layers/personal/owners → 층 조합 하나씩 읽어 담은 것만
- EasyUse.where(「회사 · 이름」 · 「개인 · 이름」) · 못 읽은 층은 「일부를 못 읽음」 한 줄
- 시험 — holding 견본(담은 것만 · 자리 글)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KmELpdXFDRZHhfbGuRhSmL
This commit is contained in:
2026-10-01 09:49:18 +09:00
co-authored by Claude Opus 5.5
parent c7f12a0aad
commit a06863e2ec
2 changed files with 134 additions and 5 deletions
+106 -5
View File
@@ -53,9 +53,12 @@ export interface EasySaved {
row: LogicRow;
}
/** 쓰는 곳 한 줄 — 일위대가 조합 · 이 로직을 부르는 다른 로직 */
/** 쓰는 곳 한 줄 — 일위대가 조합 · 이 로직을 부르는 다른 로직
* `where` = 회사 · 개인 층 조합의 자리 글(「회사 · 산림조합」) · 마스터 층 · 로직은 없음
* 층을 못 읽었으면 `key` "" · `name` = 「일부를 못 읽음」 한 줄(지우기 전에 확인하게) */
export interface EasyUse {
kind: "combo" | "logic";
where?: string;
key: string;
name: string;
}
@@ -158,6 +161,14 @@ const TEXT = {
"이 기계는 시간당 값을 셀 자료가 없음 — 다른 기계를 골라 주세요",
"No hourly cost data for this machine — pick another",
],
AtCompany: ["회사 · {name}", "Company · {name}"],
AtPerson: ["개인 · {name}", "Personal · {name}"],
MyCompany: ["내 회사", "My company"],
Me: ["나", "Me"],
Unseen: [
"회사 · 개인 조합 일부를 못 읽음 — 지우기 전에 확인 필요",
"Some company or personal combos could not be read — check before deleting",
],
} as const satisfies Record<string, readonly [string, string]>;
type SayKey = keyof typeof TEXT;
@@ -256,6 +267,26 @@ export function machineCall(m: MachineLogic, code: string): EasyMachineElement {
const rest = needInputs.map((i) => `, ${quote(i.이름)}=${quote(i.이름)}`).join("");
return { element: `로직(${m.key}, ${MACHINE}='${code}'${rest})`, needInputs };
}
/** 층 조합 줄 통째(`담은로직` 포함) */
export interface LayerCombo {
키: string;
이름?: string;
담은로직?: { 로직?: string }[];
}
/** 이 로직을 담은 층 조합 — `layer` = company · personal · `name` = 층 주인 이름 */
export function holding(
combos: LayerCombo[],
key: string,
layer: "company" | "personal",
name: string,
): EasyUse[] {
const where = say(layer === "company" ? "AtCompany" : "AtPerson", { name });
return combos
.filter((c) => (c.담은로직 ?? []).some((one) => one?.로직 === key))
.map((c): EasyUse => ({ kind: "combo", where, key: c.키, name: c.이름 ?? "" }));
}
/* ── 순수 끝 ── */
const OWN = "로직_자체.json"; // 내가 만든 것 한 자리(서버 `Store_Make.OWN`)
@@ -263,10 +294,15 @@ const OWNER = "현장";
const CALC_WAIT_MS = 300;
const MACHINE_FILE = "건설기계"; // 기계 시간당 경비 로직이 든 장 파일(「08장 건설기계」)
async function request<T>(path: string, body?: unknown): Promise<T> {
/** `/api/m01` 길 · M02 층 조합 읽기는 `m02` */
const request = <T>(path: string, body?: unknown): Promise<T> =>
send<T>(`${API_BASE_URL}/m01${path}`, body);
const m02 = <T>(path: string): Promise<T> => send<T>(`${API_BASE_URL}/m02${path}`);
async function send<T>(url: string, body?: unknown): Promise<T> {
let response: Response;
try {
response = await fetch(`${API_BASE_URL}/m01${path}`, {
response = await fetch(url, {
method: body === undefined ? "GET" : "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
@@ -385,13 +421,15 @@ export async function deleteEasy(key: string, version: string): Promise<{ versio
return { version: got.version };
}
/** 쓰는 곳 — 담은 일위대가 조합(마스터 층) + 이 로직을 부르는 내가 만든 것.
/** 쓰는 곳 — 담은 일위대가 조합(마스터 · 회사 · 개인 층) + 이 로직을 부르는 내가 만든 것.
* 지우기 누를 때 부름(무거움) · 서버 지우기 검사는 마스터 층 조합만 막음.
* ponytail: 부르는 로직은 내가 만든 것만 하나씩 받아 훑음(품셈 기본은 내 것을 안 부름) —
* 내 것이 수백이 되면 서버에 「부르는 로직」 길을 더할 것 */
export async function usesOf(key: string): Promise<EasyUse[]> {
const [combos, mine] = await Promise.all([
const [combos, mine, layered] = await Promise.all([
request<{ combos: { 키: string; 이름: string }[] }>(`/logic/combos?${query({ key })}`),
request<{ logics: Summary[] }>(`/logics?${query({ sub: "자체" })}`),
layerUses(key),
]);
const call = `로직(${key}`;
const others = mine.logics.filter((s) => s.키 !== key);
@@ -401,10 +439,73 @@ export async function usesOf(key: string): Promise<EasyUse[]> {
.map((one): EasyUse => ({ kind: "logic", key: one.logic.키, name: one.logic.이름 }));
return [
...combos.combos.map((c): EasyUse => ({ kind: "combo", key: c.키, name: c.이름 })),
...layered,
...callers,
];
}
interface LayerTarget {
layer: "company" | "personal";
owner: string;
name: string;
rows?: { 키: string }[];
}
/** 회사 · 개인 층 조합 가운데 이 로직을 담은 것 — M02 층 길 읽기만(시스템 관리자는 모든 층).
* 내 층 = `/combos/all` · 남의 회사 = `/layers/company/owners` · 회사마다 사람 = `/layers/personal/owners`.
* 못 읽은 층이 있으면 「일부를 못 읽음」 한 줄을 더함.
* ponytail: 층마다 조합 하나씩 받아 훑음 — 조합이 수천이면 서버에 「이 로직을 담은 층 조합」 길을 더할 것 */
async function layerUses(key: string): Promise<EasyUse[]> {
let missed = false;
const soft = <T>(p: Promise<T>, empty: T): Promise<T> =>
p.catch(() => {
missed = true;
return empty;
});
type Own = { layer: string; owner: string | null; 줄: { 키: string }[] };
type Owner = { owner: string; name: string };
const own = await soft(m02<Own[]>("/combos/all"), []);
const targets: LayerTarget[] = [];
let myCompany = "";
for (const one of own) {
if (one.layer === "system" || !one.owner) continue;
const company = one.layer === "company";
if (company) myCompany = one.owner;
targets.push({
layer: company ? "company" : "personal",
owner: one.owner,
name: say(company ? "MyCompany" : "Me"),
rows: one.줄,
});
}
const companies = await soft(m02<Owner[]>("/layers/company/owners"), []);
for (const c of companies) targets.push({ layer: "company", ...c });
const people = await Promise.all(
[myCompany, ...companies.map((c) => c.owner)]
.filter(Boolean)
.map((c) => soft(m02<Owner[]>(`/layers/personal/owners?${query({ company: c })}`), [])),
);
for (const p of people.flat()) targets.push({ layer: "personal", ...p });
const found = await Promise.all(
targets.map(async (t) => {
const at = `/layers/${t.layer}/combos`;
const own = `?${query({ owner: t.owner })}`;
const rows = t.rows ?? (await soft(m02<{ 줄: { 키: string }[] }>(at + own), { 줄: [] })).줄;
const combos = await Promise.all(
rows.map((r) =>
soft(m02<{ combo: LayerCombo }>(`${at}/${encodeURIComponent(r.키)}${own}`), null),
),
);
const full = combos.flatMap((x) => (x ? [x.combo] : []));
return holding(full, key, t.layer, t.name);
}),
);
const out = found.flat();
if (missed) out.push({ kind: "combo", key: "", name: say("Unseen") });
return out;
}
let family: Promise<MachineLogic[]> | null = null;
/** 기계 시간당 경비 로직 묶음 — 한 번 받아 둠(실패하면 다음에 다시) */