# ============================================================ # 商家端 — 全量端到端测试 # 覆盖 24 个 Controller,约 90 个 API 端点 # ============================================================ import pytest import json import random from api_client import ApiClient from config import SELLER_SHOP_ID # ============================================================ # 商家 — 店铺切换 (SellerShopContextController) # ============================================================ class TestSellerContext: def test_80_switch_shop(self, client, seller_token): """切换店铺上下文""" resp = client.put("/agri/seller/context/shop", json={ "shopId": SELLER_SHOP_ID }) client.assert_ok("切换店铺失败") print(f" [OK] 切换店铺 shopId={SELLER_SHOP_ID}") # ============================================================ # 商家 — 员工管理 (MerchantEmployeeController) # ============================================================ class TestSellerEmployee: def test_81_employee_list(self, client, seller_token): """员工列表""" resp = client.get("/agri/seller/employee/list") client.assert_ok("查询员工列表失败") data = client.extract_data() or {} print(f" [OK] 员工列表 total={data.get('total','?')}") def test_82_employee_quota(self, client, seller_token): """员工名额配额""" resp = client.get("/agri/seller/employee/quota") client.assert_ok("查询员工配额失败") quota = client.extract_data() or {} print(f" [OK] 员工配额: {quota}") def test_83_employee_role_options(self, client, seller_token): """员工角色选项""" resp = client.get("/agri/seller/employee/roleOptions") client.assert_ok("查询角色选项失败") roles = client.extract_data() or [] print(f" [OK] 员工角色选项 count={len(roles) if isinstance(roles, list) else 0}") # ============================================================ # 商家 — 角色管理 (MerchantRoleController) # ============================================================ class TestSellerRole: def test_84_role_list(self, client, seller_token): """角色列表""" resp = client.get("/agri/seller/role/list") client.assert_ok("查询角色列表失败") data = client.extract_data() or {} print(f" [OK] 角色列表 total={data.get('total','?')}") def test_85_role_menu_tree(self, client, seller_token): """角色菜单树""" resp = client.get("/agri/seller/role/menuTree") client.assert_ok("查询菜单树失败") tree = client.extract_data() or [] print(f" [OK] 角色菜单树 nodes={len(tree)}") # ============================================================ # 商家 — 商品管理 (SellerGoodsController) # ============================================================ class TestSellerGoods: def test_86_goods_list(self, client, seller_token): """商家商品列表""" resp = client.get("/agri/seller/goods/list") client.assert_ok("查询商品列表失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 商家商品列表 total={total}") def test_87_goods_category_options(self, client, seller_token): """商品分类选项""" resp = client.get("/agri/seller/goods/categoryOptions") client.assert_ok("查询分类选项失败") cats = client.extract_data() or [] print(f" [OK] 分类选项 count={len(cats)}") if cats: client._test_seller_category_id = cats[0].get("categoryId") or cats[0].get("id") def test_88_goods_service_options(self, client, seller_token): """商品服务选项""" resp = client.get("/agri/seller/goods/serviceOptions") client.assert_ok("查询服务选项失败") svc = client.extract_data() or {} print(f" [OK] 服务选项: {json.dumps(svc, ensure_ascii=False)[:100]}") def test_89_goods_attr_template_options(self, client, seller_token): """属性模板选项""" resp = client.get("/agri/seller/goods/attrTemplateOptions") client.assert_ok("查询属性模板失败") tmpl = client.extract_data() or [] print(f" [OK] 属性模板选项 count={len(tmpl) if isinstance(tmpl, list) else 0}") def test_90_goods_freight_options(self, client, seller_token): """运费模板选项""" resp = client.get("/agri/seller/goods/freightTemplateOptions") client.assert_ok("查询运费模板失败") tmpl = client.extract_data() or [] print(f" [OK] 运费模板选项 count={len(tmpl)}") if tmpl: client._test_seller_freight_id = tmpl[0].get("templateId") or tmpl[0].get("id") def test_91_goods_freight_default(self, client, seller_token): """默认运费模板""" resp = client.get("/agri/seller/goods/freightDefault") client.assert_ok("查询默认运费模板失败") default = client.extract_data() or {} print(f" [OK] 默认运费: {json.dumps(default, ensure_ascii=False)[:80]}") def test_92_goods_shop_category_options(self, client, seller_token): """店铺商品分类选项""" resp = client.get("/agri/seller/goods/shopCategoryOptions", params={"visibleOnly": True}) client.assert_ok("查询店铺分类选项失败") cats = client.extract_data() or [] print(f" [OK] 店铺分类选项 count={len(cats) if isinstance(cats, list) else 0}") def test_93_goods_detail(self, client, seller_token): """商家商品详情""" resp = client.get("/agri/seller/goods/list", params={"pageSize": 1}) data = client.assert_ok() rows = (data.get("data") or {}).get("rows") or [] if not rows: pytest.skip("无商品数据") gid = rows[0].get("goodsId") resp = client.get(f"/agri/seller/goods/{gid}") client.assert_ok("查询商品详情失败") print(f" [OK] 商家商品详情 goodsId={gid}") # ============================================================ # 商家 — 订单管理 (SellerOrderController) # ============================================================ class TestSellerOrder: def test_94_seller_order_list(self, client, seller_token): """商家订单列表""" resp = client.get("/agri/seller/order/list") client.assert_ok("查询订单列表失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 商家订单列表 total={total}") def test_95_seller_order_detail(self, client, seller_token): """商家订单详情""" resp = client.get("/agri/seller/order/list", params={"pageSize": 1}) data = client.assert_ok() rows = (data.get("data") or {}).get("rows") or [] if not rows: pytest.skip("无订单数据") oid = rows[0].get("orderId") resp = client.get(f"/agri/seller/order/{oid}") client.assert_ok("查询订单详情失败") print(f" [OK] 商家订单详情 orderId={oid}") # ============================================================ # 商家 — 发货管理 (SellerShipController) # ============================================================ class TestSellerShip: def test_96_ship_badges(self, client, seller_token): """发货角标""" resp = client.get("/agri/seller/ship/badges") client.assert_ok("查询发货角标失败") badges = client.extract_data() or {} print(f" [OK] 发货角标: {badges}") def test_97_ship_list(self, client, seller_token): """发货列表""" resp = client.get("/agri/seller/ship/list") client.assert_ok("查询发货列表失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 发货列表 total={total}") def test_98_ship_detail(self, client, seller_token): """发货详情""" resp = client.get("/agri/seller/ship/list", params={"pageSize": 1}) data = client.assert_ok() rows = (data.get("data") or {}).get("rows") or [] if not rows: pytest.skip("无发货数据") oid = rows[0].get("orderId") resp = client.get(f"/agri/seller/ship/{oid}") client.assert_ok("查询发货详情失败") print(f" [OK] 发货详情 orderId={oid}") # ============================================================ # 商家 — 售后管理 (SellerAftersaleController) # ============================================================ class TestSellerAftersale: def test_99_aftersale_list(self, client, seller_token): """售后列表""" resp = client.get("/agri/seller/aftersale/list") client.assert_ok("查询售后列表失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 售后列表 total={total}") def test_100_aftersale_detail(self, client, seller_token): """售后详情""" resp = client.get("/agri/seller/aftersale/list", params={"pageSize": 1}) data = client.assert_ok() rows = (data.get("data") or {}).get("rows") or [] if not rows: pytest.skip("无售后数据") aid = rows[0].get("aftersaleId") resp = client.get(f"/agri/seller/aftersale/{aid}") client.assert_ok("查询售后详情失败") print(f" [OK] 售后详情 aftersaleId={aid}") # ============================================================ # 商家 — 评价管理 (SellerReviewController) # ============================================================ class TestSellerReview: def test_101_review_list(self, client, seller_token): """评价列表""" resp = client.get("/agri/seller/review/list") client.assert_ok("查询评价列表失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 评价列表 total={total}") # ============================================================ # 商家 — 运费模板 (SellerFreightTemplateController) # ============================================================ class TestSellerFreight: def test_102_freight_template_list(self, client, seller_token): """运费模板列表""" resp = client.get("/agri/seller/freightTemplate/list") client.assert_ok("查询运费模板失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 运费模板列表 total={total}") def test_103_freight_template_detail(self, client, seller_token): """运费模板详情""" resp = client.get("/agri/seller/freightTemplate/list", params={"pageSize": 1}) data = client.assert_ok() rows = (data.get("data") or {}).get("rows") or [] if not rows: pytest.skip("无运费模板") tid = rows[0].get("templateId") resp = client.get(f"/agri/seller/freightTemplate/{tid}") client.assert_ok("查询运费模板详情失败") print(f" [OK] 运费模板详情 templateId={tid}") # ============================================================ # 商家 — 属性模板 (SellerAttrTemplateController) # ============================================================ class TestSellerAttrTemplate: def test_104_attr_template_list(self, client, seller_token): """属性模板列表""" resp = client.get("/agri/seller/attrTemplate/list") client.assert_ok("查询属性模板失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 属性模板列表 total={total}") def test_105_attr_template_detail(self, client, seller_token): """属性模板详情""" resp = client.get("/agri/seller/attrTemplate/list", params={"pageSize": 1}) data = client.assert_ok() rows = (data.get("data") or {}).get("rows") or [] if not rows: pytest.skip("无属性模板") tid = rows[0].get("templateId") resp = client.get(f"/agri/seller/attrTemplate/{tid}") client.assert_ok("查询属性模板详情失败") print(f" [OK] 属性模板详情 templateId={tid}") # ============================================================ # 商家 — 分类管理 (SellerPlatformCategory + ShopGoodsCategory) # ============================================================ class TestSellerCategory: def test_106_platform_category_options(self, client, seller_token): """平台分类选项""" resp = client.get("/agri/seller/category/platformLevel2Options") client.assert_ok("查询平台分类选项失败") cats = client.extract_data() or [] print(f" [OK] 平台分类选项 count={len(cats) if isinstance(cats, list) else 0}") def test_107_shop_category_list(self, client, seller_token): """店铺分类列表""" resp = client.get("/agri/seller/shopCategory/list") client.assert_ok("查询店铺分类失败") data = client.extract_data() or {} print(f" [OK] 店铺分类列表正常") def test_108_shop_category_scope(self, client, seller_token): """店铺分类使用范围""" resp = client.get("/agri/seller/shopCategory/scope") client.assert_ok("查询店铺分类范围失败") scope = client.extract_data() or {} print(f" [OK] 店铺分类范围: {scope}") # ============================================================ # 商家 — 店铺管理 (SellerShopController) # ============================================================ class TestSellerShop: def test_109_shop_employee_stats(self, client, seller_token): """店铺员工统计""" resp = client.get("/agri/seller/shop/employeeStats") client.assert_ok("查询员工统计失败") stats = client.extract_data() or {} print(f" [OK] 员工统计: {stats}") # ============================================================ # 商家 — 配送设置 (SellerDeliveryController) # ============================================================ class TestSellerDelivery: def test_110_delivery_setting_get(self, client, seller_token): """配送设置查询""" resp = client.get("/agri/seller/delivery") client.assert_ok("查询配送设置失败") setting = client.extract_data() or {} print(f" [OK] 配送设置: {json.dumps(setting, ensure_ascii=False)[:100]}") # ============================================================ # 商家 — 财务管理 (SellerFund / PayAccount / Withdraw) # ============================================================ class TestSellerFinance: def test_111_fund_summary(self, client, seller_token): """资金概览""" resp = client.get("/agri/seller/finance/overview/summary") client.assert_ok("查询资金概览失败") summary = client.extract_data() or {} print(f" [OK] 商家资金概览: {json.dumps(summary, ensure_ascii=False)[:100]}") def test_112_fund_logs(self, client, seller_token): """资金流水""" resp = client.get("/agri/seller/finance/overview/logs") client.assert_ok("查询资金流水失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 资金流水 total={total}") def test_113_pay_account_list(self, client, seller_token): """收款账户列表""" resp = client.get("/agri/seller/finance/payAccount/list") client.assert_ok("查询收款账户失败") accounts = client.extract_data() or [] print(f" [OK] 收款账户 count={len(accounts) if isinstance(accounts, list) else 0}") def test_114_pay_account_options(self, client, seller_token): """收款账户选项""" resp = client.get("/agri/seller/finance/payAccount/options") client.assert_ok("查询账户选项失败") opts = client.extract_data() or [] print(f" [OK] 收款账户选项正常") def test_115_withdraw_balance(self, client, seller_token): """提现余额""" resp = client.get("/agri/seller/finance/withdraw/balance") client.assert_ok("查询提现余额失败") balance = client.extract_data() or {} print(f" [OK] 提现余额: {json.dumps(balance, ensure_ascii=False)[:80]}") def test_116_withdraw_list(self, client, seller_token): """提现记录""" resp = client.get("/agri/seller/finance/withdraw/list") client.assert_ok("查询提现记录失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 提现记录 total={total}") # ============================================================ # 商家 — 库存管理 (5个StockController) # ============================================================ class TestSellerStock: def test_117_stock_query_list(self, client, seller_token): """库存查询""" resp = client.get("/agri/seller/stock/query/list") client.assert_ok("查询库存失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 库存查询 total={total}") def test_118_stock_inbound_list(self, client, seller_token): """入库记录""" resp = client.get("/agri/seller/stock/inbound/list") client.assert_ok("查询入库记录失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 入库记录 total={total}") def test_119_stock_outbound_list(self, client, seller_token): """出库记录""" resp = client.get("/agri/seller/stock/outbound/list") client.assert_ok("查询出库记录失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 出库记录 total={total}") def test_120_stock_adjust_list(self, client, seller_token): """库存调整记录""" resp = client.get("/agri/seller/stock/adjust/list") client.assert_ok("查询库存调整失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 库存调整 total={total}") def test_121_stock_log_list(self, client, seller_token): """库存流水日志""" resp = client.get("/agri/seller/stock/log/list") client.assert_ok("查询库存日志失败") data = client.extract_data() or {} total = data.get("total", 0) if isinstance(data, dict) else 0 print(f" [OK] 库存日志 total={total}") def test_122_stock_inbound_detail(self, client, seller_token): """入库详情""" resp = client.get("/agri/seller/stock/inbound/list", params={"pageSize": 1}) data = client.assert_ok() rows = (data.get("data") or {}).get("rows") or [] if not rows: pytest.skip("无入库记录") iid = rows[0].get("inboundId") resp = client.get(f"/agri/seller/stock/inbound/{iid}") client.assert_ok("查询入库详情失败") print(f" [OK] 入库详情 inboundId={iid}") def test_123_stock_goods_logs(self, client, seller_token): """商品库存流水""" resp = client.get("/agri/seller/stock/query/list", params={"pageSize": 1}) data = client.assert_ok() rows = (data.get("data") or {}).get("rows") or [] if not rows: pytest.skip("无库存数据") gid = rows[0].get("goodsId") resp = client.get(f"/agri/seller/stock/query/{gid}/logs") client.assert_ok("查询商品流水失败") print(f" [OK] 商品库存流水 goodsId={gid}") # ============================================================ # 商家 — 微信支付配置 (SellerShopWechatPayController) # ============================================================ class TestSellerWechat: def test_124_wechat_pay_get(self, client, seller_token): """商家微信支付配置""" resp = client.get("/agri/seller/wechat-pay") client.assert_ok("查询支付配置失败") config = client.extract_data() or {} print(f" [OK] 商家支付配置: {json.dumps(config, ensure_ascii=False)[:100]}")