feat: 为MES和工作站服务添加启动后自动打开浏览器功能
优化WMS客户端URL处理逻辑,移除尾斜杠避免请求404错误 更新工作站配置默认工位号为1 新增数据库字段支持工位页签配置 删除WMS端未完成的权限树形化和范围验证测试文件 删除物料档案对齐方案文档 新增自动打开浏览器的跨平台实现,服务启动后等待1秒后打开管理后台,提升使用便捷性
This commit is contained in:
@@ -1,58 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""验证:权限树形化(parent_code) - seed 回填 + /permissions 返回父子结构"""
|
||||
import json, urllib.request, urllib.error
|
||||
|
||||
BASE = "http://127.0.0.1:8902"
|
||||
TOKEN = None
|
||||
|
||||
def req(method, path, payload=None):
|
||||
url = BASE + path
|
||||
data = json.dumps(payload).encode() if payload is not None else None
|
||||
r = urllib.request.Request(url, data=data, method=method)
|
||||
r.add_header("Content-Type", "application/json")
|
||||
if TOKEN:
|
||||
r.add_header("Authorization", "Bearer " + TOKEN)
|
||||
try:
|
||||
with urllib.request.urlopen(r, timeout=20) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
except urllib.error.HTTPError as e:
|
||||
return {"_http": e.code, "_body": e.read().decode()[:300]}
|
||||
|
||||
def main():
|
||||
global TOKEN
|
||||
d = req("POST", "/api/auth/login", {"username": "admin", "password": "123456"})
|
||||
TOKEN = (d.get("data") or {}).get("token")
|
||||
assert TOKEN, f"登录失败: {d}"
|
||||
print("✓ 登录成功")
|
||||
|
||||
# 执行 seed 回填 parent_code
|
||||
sd = req("POST", "/api/rbac/seed")
|
||||
print("✓ seed 完成:", sd.get("data") or sd)
|
||||
|
||||
# 拉取权限,校验 parent_code
|
||||
pl = req("GET", "/api/permissions")
|
||||
listp = (pl.get("data") or {}).get("list") or []
|
||||
menus = [p for p in listp if p["type"] == "MENU"]
|
||||
buttons = [p for p in listp if p["type"] == "BUTTON"]
|
||||
print(f"权限总数 {len(listp)}:菜单 {len(menus)},按钮 {len(buttons)}")
|
||||
|
||||
# 校验每个按钮的 parent_code 是否为有效菜单 code 或为空(全局)
|
||||
menu_codes = {m["code"] for m in menus}
|
||||
bad = [p for p in buttons if p.get("parentCode") and p["parentCode"] not in menu_codes]
|
||||
global_btns = [p for p in buttons if not p.get("parentCode")]
|
||||
print(f" 按钮中全局(无父)数: {len(global_btns)} -> {[p['code'] for p in global_btns]}")
|
||||
print(f" 按钮 parent_code 有误(指向不存在菜单): {[p['code'] for p in bad]}")
|
||||
assert not bad, "存在 parent_code 指向无效菜单"
|
||||
print("✓ 所有按钮 parent_code 均有效(或为全局空)")
|
||||
|
||||
# 展示各菜单下挂的按钮(模拟前端树)
|
||||
for m in menus:
|
||||
kids = [p["name"] for p in buttons if p.get("parentCode") == m["code"]]
|
||||
print(f" [{m['name']}] 挂按钮: {kids if kids else '(无)'}")
|
||||
|
||||
passed = not bad and len(global_btns) >= 1
|
||||
print("\n==== 验证 %s ====" % ("通过" if passed else "失败"))
|
||||
return passed
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(0 if main() else 1)
|
||||
@@ -1,83 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""端到端验证:问题四(备料台账无单选全部) + 问题六(盘点范围 + 物料分布)"""
|
||||
import json, urllib.request, urllib.parse, urllib.error
|
||||
|
||||
BASE = "http://127.0.0.1:8902"
|
||||
TOKEN = None
|
||||
|
||||
def req(method, path, payload=None, params=None):
|
||||
url = BASE + path
|
||||
if params:
|
||||
qs = urllib.parse.urlencode({k: v for k, v in params.items() if v not in (None, "")})
|
||||
if qs:
|
||||
url += "?" + qs
|
||||
data = json.dumps(payload).encode() if payload is not None else None
|
||||
r = urllib.request.Request(url, data=data, method=method)
|
||||
r.add_header("Content-Type", "application/json")
|
||||
if TOKEN:
|
||||
r.add_header("Authorization", "Bearer " + TOKEN)
|
||||
try:
|
||||
with urllib.request.urlopen(r, timeout=20) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
except urllib.error.HTTPError as e:
|
||||
return {"_http": e.code, "_body": e.read().decode()[:300]}
|
||||
|
||||
def login():
|
||||
global TOKEN
|
||||
d = req("POST", "/api/auth/login", {"username": "admin", "password": "123456"})
|
||||
TOKEN = d.get("data", {}).get("token")
|
||||
assert TOKEN, f"登录失败: {d}"
|
||||
print("✓ 登录成功 admin")
|
||||
|
||||
passed = []
|
||||
def check(name, cond, extra=""):
|
||||
passed.append(bool(cond))
|
||||
print(("✓ " if cond else "✗ ") + name + (f" {extra}" if extra and not cond else ""))
|
||||
|
||||
def main():
|
||||
login()
|
||||
# ---------- 问题四:备料台账无单号查全部 ----------
|
||||
d = req("GET", "/api/ledger/query", params={"page": 1, "pageSize": 20})
|
||||
t4 = d.get("data", {}) or d
|
||||
check("ledger/query 无 orderNo 返回 total+list", "list" in t4 and "total" in t4,
|
||||
f"got {list(t4.keys())}")
|
||||
|
||||
# ---------- 问题六:盘点范围按物料过滤 ----------
|
||||
# 先看库存有哪些物料可盘点,选一个真实物料
|
||||
st = req("GET", "/api/stock/query", params={"page": 1, "pageSize": 10})
|
||||
stock_data = st.get("data", {}) or st
|
||||
rows = stock_data.get("list") or []
|
||||
if rows:
|
||||
mat = rows[0]["materialCode"]
|
||||
# 按该物料发起盘点
|
||||
d = req("POST", "/api/stocktake/start", {"operator": "e2e", "materialCodes": [mat]})
|
||||
data = d.get("data", {}) or d
|
||||
check("startStocktake 按物料过滤成功", bool(data.get("stocktakeNo")),
|
||||
f"got {data}")
|
||||
check("按物料盘点 totalTargets>0", data.get("totalTargets", 0) > 0,
|
||||
f"totalTargets={data.get('totalTargets')}")
|
||||
# 按类型过滤发起
|
||||
d2 = req("POST", "/api/stocktake/start", {"operator": "e2e", "manageMode": 1})
|
||||
data2 = d2.get("data", {}) or d2
|
||||
check("startStocktake 按类型(结构件)成功", bool(data2.get("stocktakeNo")),
|
||||
f"got {data2}")
|
||||
else:
|
||||
check("stock/query 有数据(需先造数才能验盘点范围)", False, "库存为空")
|
||||
|
||||
# ---------- 问题六:物料分布数据源(聚合含 zoneCode) ----------
|
||||
d = req("GET", "/api/stock/query", params={"page": 1, "pageSize": 50})
|
||||
dist = d.get("data", {}) or d
|
||||
dl = dist.get("list") or []
|
||||
check("stock/query 返回 list(分布图数据源)", len(dl) > 0, f"len={len(dl)}")
|
||||
if dl:
|
||||
r0 = dl[0]
|
||||
check("聚合行含 zoneCode/materialCode/totalQty",
|
||||
all(k in r0 for k in ("zoneCode", "materialCode", "totalQty")),
|
||||
f"keys={list(r0.keys())}")
|
||||
|
||||
print("\n==== 结果:%d/%d 通过 ====" % (sum(passed), len(passed)))
|
||||
return all(passed)
|
||||
|
||||
if __name__ == "__main__":
|
||||
ok = main()
|
||||
raise SystemExit(0 if ok else 1)
|
||||
Reference in New Issue
Block a user