153 lines
6.6 KiB
Python
153 lines
6.6 KiB
Python
|
|
"""端点层验收:造临时数据 → 调接口 → 断言 200 且能返回行 → 清理临时数据
|
|||
|
|
|
|||
|
|
用法:
|
|||
|
|
python .tmp-schema-align/endpoint_verify.py dev # 测试环境(pms_dev + test.anzhizhichong.com)
|
|||
|
|
python .tmp-schema-align/endpoint_verify.py prod # 生产环境(pms + app.anzhizhichong.com)
|
|||
|
|
python .tmp-schema-align/endpoint_verify.py dev clean # 只清理
|
|||
|
|
"""
|
|||
|
|
import base64, hashlib, hmac, json, ssl, sys, time, urllib.request, urllib.error, pymysql
|
|||
|
|
|
|||
|
|
ENVS = {
|
|||
|
|
"dev": dict(
|
|||
|
|
db="pms_dev",
|
|||
|
|
base="https://test.anzhizhichong.com/api/pms",
|
|||
|
|
secret="32e8708743c42fe764aff1d40737a63803a46815a9501f4bcb50c41fe3d27108",
|
|||
|
|
admin_phone="19900000001",
|
|||
|
|
admin_role=2,
|
|||
|
|
),
|
|||
|
|
"prod": dict(
|
|||
|
|
db="pms",
|
|||
|
|
base="https://app.anzhizhichong.com/api/pms",
|
|||
|
|
secret="787789e8eb1f3a10e81596f8219a20aa3c34102acacba2a5ed14bbb1d16ec833",
|
|||
|
|
admin_phone="13912391234",
|
|||
|
|
admin_role=2,
|
|||
|
|
# 生产库**不新建账号**:直接复用既有的总管理员账号(id=2)签 token,
|
|||
|
|
# 只造 charge_orders / device_logs 两条临时业务行,验完立刻删除。
|
|||
|
|
existing_admin_id=2,
|
|||
|
|
),
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TAG = "VERIFY-SCHEMA-TMP"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def b64(b):
|
|||
|
|
return base64.urlsafe_b64encode(b).rstrip(b"=").decode()
|
|||
|
|
|
|||
|
|
|
|||
|
|
def sign(secret, sub, phone):
|
|||
|
|
now = int(time.time())
|
|||
|
|
seg = b64(json.dumps({"alg": "HS256", "typ": "JWT"}, separators=(",", ":")).encode()) + "." + \
|
|||
|
|
b64(json.dumps({"sub": sub, "phone": phone, "exp": now + 1800, "iat": now},
|
|||
|
|
separators=(",", ":")).encode())
|
|||
|
|
return seg + "." + b64(hmac.new(secret.encode(), seg.encode(), hashlib.sha256).digest())
|
|||
|
|
|
|||
|
|
|
|||
|
|
def call(base, path, token):
|
|||
|
|
req = urllib.request.Request(base + path, headers={"Authorization": "Bearer " + token})
|
|||
|
|
ctx = ssl.create_default_context()
|
|||
|
|
ctx.check_hostname = False
|
|||
|
|
ctx.verify_mode = ssl.CERT_NONE
|
|||
|
|
try:
|
|||
|
|
with urllib.request.urlopen(req, timeout=25, context=ctx) as r:
|
|||
|
|
return r.status, r.read().decode("utf-8", "replace")
|
|||
|
|
except urllib.error.HTTPError as e:
|
|||
|
|
return e.code, e.read().decode("utf-8", "replace")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
env = ENVS[sys.argv[1]]
|
|||
|
|
only_clean = len(sys.argv) > 2 and sys.argv[2] == "clean"
|
|||
|
|
c = pymysql.connect(host="10.8.0.252", user="root", password="Hbhyg731024@",
|
|||
|
|
database=env["db"], autocommit=True)
|
|||
|
|
cur = c.cursor()
|
|||
|
|
|
|||
|
|
# ---- 清理上一轮遗留 ----
|
|||
|
|
creates_admin = "existing_admin_id" not in env
|
|||
|
|
cur.execute("DELETE FROM device_logs WHERE content LIKE %s", (f"%{TAG}%",))
|
|||
|
|
cur.execute("DELETE FROM charge_orders WHERE battery_id = %s", (TAG,))
|
|||
|
|
if creates_admin:
|
|||
|
|
# 只删自己造的临时账号;复用既有账号时**绝不能**碰 users 表
|
|||
|
|
cur.execute("DELETE FROM users WHERE phone = %s", (env["admin_phone"],))
|
|||
|
|
if only_clean:
|
|||
|
|
print("已清理临时数据(device_logs/charge_orders/users)")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
# ---- 取一个真实拓扑:柜子 + 仓体(满足外键)----
|
|||
|
|
cur.execute(
|
|||
|
|
"SELECT c.id, cb.cabinet_id FROM compartments c "
|
|||
|
|
"JOIN cabin_boards cb ON c.cabin_board_id = cb.id ORDER BY c.id LIMIT 1"
|
|||
|
|
)
|
|||
|
|
row = cur.fetchone()
|
|||
|
|
if not row:
|
|||
|
|
sys.exit("库里没有 compartments,无法造订单(外键需要)")
|
|||
|
|
compartment_id, cabinet_id = row
|
|||
|
|
|
|||
|
|
# ---- 造临时超管(JWT 只带 sub,角色与权限从库里读)----
|
|||
|
|
if creates_admin:
|
|||
|
|
cur.execute(
|
|||
|
|
"INSERT INTO users (phone, name, password, role, organization_id, status) VALUES (%s, %s, 'x', %s, NULL, 1)",
|
|||
|
|
(env["admin_phone"], TAG, env["admin_role"]),
|
|||
|
|
)
|
|||
|
|
admin_id = cur.lastrowid
|
|||
|
|
else:
|
|||
|
|
admin_id = env["existing_admin_id"]
|
|||
|
|
|
|||
|
|
# ---- 造 2 条 charge_orders(status<>0 ⇒ active_flag=NULL,不撞唯一约束)----
|
|||
|
|
cur.execute(
|
|||
|
|
"INSERT INTO charge_orders (compartment_id, cabinet_id, battery_id, start_time, end_time, "
|
|||
|
|
"start_soc, end_soc, energy, reason, status) "
|
|||
|
|
"VALUES (%s, %s, %s, NOW(), NOW(), 850, 1000, 22, 'normal', 1)",
|
|||
|
|
(compartment_id, cabinet_id, TAG),
|
|||
|
|
)
|
|||
|
|
order_id = cur.lastrowid
|
|||
|
|
|
|||
|
|
# ---- 造 2 条 device_logs ----
|
|||
|
|
cur.execute(
|
|||
|
|
"INSERT INTO device_logs (cabinet_id, log_type, content) VALUES (%s, 1, %s)",
|
|||
|
|
(cabinet_id, json.dumps({"tag": TAG, "n": 1})),
|
|||
|
|
)
|
|||
|
|
log_id = cur.lastrowid
|
|||
|
|
|
|||
|
|
print(f"环境={sys.argv[1]} 库={env['db']} 临时数据: users.id={admin_id} "
|
|||
|
|
f"charge_orders.id={order_id} device_logs.id={log_id} (cabinet_id={cabinet_id})")
|
|||
|
|
|
|||
|
|
# ---- 自签 token 调接口 ----
|
|||
|
|
token = sign(env["secret"], admin_id, env["admin_phone"])
|
|||
|
|
results = {}
|
|||
|
|
for path in ("/charge-orders?page=1&page_size=20", "/device-logs?page=1&page_size=20",
|
|||
|
|
"/charge-records?page=1&page_size=5"):
|
|||
|
|
status, body = call(env["base"], path, token)
|
|||
|
|
results[path] = (status, body)
|
|||
|
|
has_tmp = TAG in body
|
|||
|
|
print(f"\n--- GET {path} -> HTTP {status} (临时行出现={has_tmp}) ---")
|
|||
|
|
print(body[:600])
|
|||
|
|
|
|||
|
|
# ---- 断言 ----
|
|||
|
|
ok_orders = results["/charge-orders?page=1&page_size=20"][0] == 200 and TAG in results["/charge-orders?page=1&page_size=20"][1]
|
|||
|
|
ok_logs = results["/device-logs?page=1&page_size=20"][0] == 200 and TAG in results["/device-logs?page=1&page_size=20"][1]
|
|||
|
|
ok_records = results["/charge-records?page=1&page_size=5"][0] == 200
|
|||
|
|
print("\n==== 断言 ====")
|
|||
|
|
print(" /api/charge-orders 200 且返回临时行:", "PASS" if ok_orders else "FAIL")
|
|||
|
|
print(" /api/device-logs 200 且返回临时行:", "PASS" if ok_logs else "FAIL")
|
|||
|
|
print(" /api/charge-records 200 :", "PASS" if ok_records else "FAIL")
|
|||
|
|
|
|||
|
|
print("\n==== 清理临时数据 ====")
|
|||
|
|
cur.execute("DELETE FROM device_logs WHERE content LIKE %s", (f"%{TAG}%",))
|
|||
|
|
print(" device_logs 删除行数:", cur.rowcount)
|
|||
|
|
cur.execute("DELETE FROM charge_orders WHERE battery_id = %s", (TAG,))
|
|||
|
|
print(" charge_orders 删除行数:", cur.rowcount)
|
|||
|
|
if creates_admin:
|
|||
|
|
cur.execute("DELETE FROM users WHERE phone = %s", (env["admin_phone"],))
|
|||
|
|
print(" users 删除行数:", cur.rowcount)
|
|||
|
|
else:
|
|||
|
|
print(" users: 未新建账号(复用既有超管 id=%s),无需删除" % admin_id)
|
|||
|
|
cur.execute("SELECT COUNT(*) FROM charge_orders WHERE battery_id=%s", (TAG,))
|
|||
|
|
print(" 残留 charge_orders:", cur.fetchone()[0])
|
|||
|
|
cur.execute("SELECT COUNT(*) FROM device_logs WHERE content LIKE %s", (f"%{TAG}%",))
|
|||
|
|
print(" 残留 device_logs:", cur.fetchone()[0])
|
|||
|
|
c.close()
|
|||
|
|
sys.exit(0 if (ok_orders and ok_logs and ok_records) else 1)
|
|||
|
|
|
|||
|
|
|
|||
|
|
main()
|