Metadata-Version: 2.5
Name: py-wisharetec-toolkit
Version: 4.1.0
Summary: Wisharetec Python Toolkit / 善数 Python 工具包
Project-URL: HomePage, https://gitee.com/guolei19850528/py_wisharetec_toolkit
Project-URL: Repository, https://gitee.com/guolei19850528/py_wisharetec_toolkit
Author-email: 郭磊 <174000902@qq.com>
License: MIT
License-File: LICENSE
Keywords: api,python,toolkit,wisharetec,善数,物业管理
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: diskcache>=5.6.0
Requires-Dist: httpx<1.0,>=0.24.0
Requires-Dist: pydantic<3.0,>=2.0.0
Requires-Dist: redis>=5.0.0
Provides-Extra: all
Requires-Dist: mkdocs-material>=9.0.0; extra == 'all'
Requires-Dist: mkdocs>=1.5.0; extra == 'all'
Requires-Dist: mypy>=1.10.0; extra == 'all'
Requires-Dist: pre-commit>=3.7.0; extra == 'all'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'all'
Requires-Dist: pytest-cov>=5.0.0; extra == 'all'
Requires-Dist: pytest>=8.0.0; extra == 'all'
Requires-Dist: python-dotenv>=1.0.0; extra == 'all'
Requires-Dist: respx>=0.21.0; extra == 'all'
Requires-Dist: ruff>=0.8.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: mypy>=1.10.0; extra == 'dev'
Requires-Dist: pre-commit>=3.7.0; extra == 'dev'
Requires-Dist: ruff>=0.8.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.0.0; extra == 'docs'
Requires-Dist: mkdocs>=1.5.0; extra == 'docs'
Provides-Extra: test
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'test'
Requires-Dist: pytest-cov>=5.0.0; extra == 'test'
Requires-Dist: pytest>=8.0.0; extra == 'test'
Requires-Dist: python-dotenv>=1.0.0; extra == 'test'
Requires-Dist: respx>=0.21.0; extra == 'test'
Description-Content-Type: text/markdown

# py-wisharetec-toolkit

[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

[中文](./README.md) | [English](./README.en.md)

## 功能

- **善数开放平台 API**：获取并缓存 authorization 令牌，提供同步 `ShanshuClient` 与异步 `ShanshuAsyncClient`，均支持上下文管理器自动关闭 HTTP 连接。
- **分页与详情查询**：覆盖社区、楼栋/房源、空间楼栋、车辆、车位、居民、业主、工单、工单回访、商城订单、服务订单、审批、车场共 13 类接口。
- **authorization 缓存**：内置内存、diskcache、Redis 三种缓存实现，默认使用 diskcache。
- **类型模型**：基于 Pydantic 2 定义分页请求模型与通用响应模型，响应模型允许额外字段。

## 安装

```bash
pip install py-wisharetec-toolkit
```

使用 uv：

```bash
uv add py-wisharetec-toolkit
```

需要开发与测试工具时，从仓库安装：

```bash
uv sync --extra dev --extra test
```

## 包结构

```text
py_wisharetec_toolkit/
├── pyproject.toml
├── README.md
├── README.en.md
├── LICENSE
├── src/
│   └── py_wisharetec_toolkit/
│       ├── __init__.py
│       ├── py.typed
│       └── shanshu/
│           ├── __init__.py
│           ├── _caches.py      # authorization 缓存：内存 / diskcache / Redis
│           ├── _clients.py     # 同步 Client 与异步 AsyncClient
│           └── _models.py      # 分页请求模型与通用响应模型
├── examples/
│   └── shanshu/
│       ├── .env.example
│       ├── get_authorization.py
│       ├── query_approval.py
│       ├── query_building.py
│       ├── query_car.py
│       ├── query_community.py
│       ├── query_mall_order.py
│       ├── query_owner.py
│       ├── query_parking_space.py
│       ├── query_resident.py
│       ├── query_service_order.py
│       ├── query_space_building.py
│       ├── query_work_order.py
│       ├── query_work_order_revisit.py
│       └── query_yard_archive.py
└── tests/
    └── shanshu/
        ├── .env.example
        ├── conftest.py
        └── test_*.py          # 每个业务域拆分为 *_sync.py 与 *_async.py
```

包根导出 `shanshu` 模块。各模块对外公开的名称带有 `Shanshu` 前缀，避免名称冲突；`_` 开头的文件是内部实现模块。测试用例如 `test_query_approval_sync.py` / `test_query_approval_async.py`，每个业务域均提供同步与异步两份用例。

## 使用

### 1. shanshu 模块 - 善数开放平台

善数模块通过账号与密码初始化客户端，`get_authorization` 支持获取并缓存访问令牌，各 `query_*` 接口支持分页与详情查询。

#### 1.1 获取 authorization（同步）

```python
from py_wisharetec_toolkit.shanshu import ShanshuClient

with ShanshuClient(
    account="你的善数账号",
    password="你的善数密码",
) as client:
    token = client.get_authorization()
    print(token)
```

#### 1.2 获取 authorization（异步）

```python
import asyncio

from py_wisharetec_toolkit.shanshu import ShanshuAsyncClient


async def main() -> None:
    async with ShanshuAsyncClient(
        account="你的善数账号",
        password="你的善数密码",
    ) as client:
        token = await client.get_authorization()
        print(token)


asyncio.run(main())
```

同步客户端可用 `with` 管理连接；异步客户端可用 `async with` 管理连接。也可以分别调用 `ShanshuClient.close()` 和 `ShanshuAsyncClient.aclose()` 手动关闭。

#### 1.3 客户端配置

同步客户端 `ShanshuClient` 和异步客户端 `ShanshuAsyncClient` 使用相同的构造参数：

| 参数 | 必填 | 默认值 | 说明 |
|------|------|--------|------|
| `account` | 是 | — | 善数账号，为空时抛出 `ValueError`。 |
| `password` | 是 | — | 善数应用凭证密钥，为空时抛出 `ValueError`。 |
| `cache` | 否 | `ShanshuAuthorizationDiskcacheCache` | authorization 缓存实现。 |
| `base_url` | 否 | `https://saas.wisharetec.com/` | API 基础地址。 |
| `timeout` | 否 | `30.0` | HTTP 请求超时时间，单位为秒。 |
| `**httpx_client_kwargs` | 否 | — | 透传给底层 `httpx.Client` / `httpx.AsyncClient` 的参数。 |

可用的缓存实现：

- `ShanshuAuthorizationMemoryCache`：进程内内存缓存。
- `ShanshuAuthorizationDiskcacheCache`：基于 diskcache，默认目录 `.cache/py_wisharetec_toolkit`。
- `ShanshuAuthorizationRedisCache`：基于 Redis。
- `ShanshuAuthorizationCache`：自定义缓存需继承的抽象基类。

#### 1.4 支持的接口

| 模块 | 分页查询方法 | 详情查询方法 |
|------|-------------|-------------|
| 社区 | `query_community_with_paginator` | `query_community_detail` |
| 楼栋/房源 | `query_building_with_paginator` | `query_property_detail` |
| 空间楼栋 | `query_space_building_with_paginator` | `query_space_building_detail` |
| 车辆 | `query_car_with_paginator` | `query_car_detail` |
| 车位 | `query_parking_space_with_paginator` | `query_parking_space_detail` |
| 居民 | `query_resident_with_paginator` | `query_resident_info` |
| 业主 | `query_owner_with_paginator` | `query_owner_detail_sensitive` |
| 工单 | `query_work_order_with_paginator`、`query_work_order_list_with_status` | `query_work_order_detail` |
| 工单回访 | `query_work_order_revisit_with_paginator` | `query_work_order_revisit_detail` |
| 商城订单 | `query_mall_order_with_paginator` | `query_mall_order_detail` |
| 服务订单 | `query_service_order_with_paginator` | `query_service_order_detail` |
| 审批 | `query_approval_with_paginator` | `query_approval_detail` |
| 车场 | `query_yard_archive_page` | `query_yard_archive_detail` |

分页查询方法接受 `ShanshuQueryWithPaginatorPayload` 或 `dict` 作为请求体参数；详情查询方法按接口要求传入 `id`、`user_id`、`proc_inst_id`、`building_id`、`instance_id` 等参数。

#### 1.5 分页与详情查询示例

```python
from py_wisharetec_toolkit.shanshu import ShanshuClient

payload = {
    "conditions": {
        "fields": [
            {"map": {}, "method": 1, "name": "flag", "value": 0},
        ]
    },
    "count": True,
    "last": True,
    "orderBy": [],
    "pageNum": 1,
    "pageSize": 20,
}

with ShanshuClient(
    account="你的善数账号",
    password="你的善数密码",
) as client:
    resp = client.query_community_with_paginator(payload=payload)
    print(resp.pageNum, resp.pageSize, resp.total, resp.last)

    records = resp.records or []
    if records:
        first_id = str(records[0]["id"])
        detail = client.query_community_detail(id=first_id)
        print(detail)
```

#### 1.6 响应模型

所有接口返回 `ShanshuBaseResponse`：

- `code`：接口返回码，`None` 表示成功。
- `ok`：只读属性，等价于 `code is None`。
- 模型配置为 `extra="allow"`，接口返回的 `pageNum`、`pageSize`、`total`、`last`、`records` 等未显式声明的字段可直接通过属性访问。
- `records` 中的元素当前为原始 `dict`，示例中按下标与键访问。

#### 1.7 使用环境变量运行示例

示例脚本从**仓库根目录**加载 `.env` 文件，且系统中已有的环境变量优先。复制配置模板并填写善数账号与密码：

```bash
cp examples/shanshu/.env.example .env
```

每个 `query_*.py` 同时演示同步与异步用法，覆盖对应业务域：

| 示例脚本 | 业务域 |
|----------|--------|
| `get_authorization.py` | 获取 authorization（含无效凭证场景） |
| `query_community.py` | 社区 |
| `query_building.py` | 楼栋/房源 |
| `query_space_building.py` | 空间楼栋 |
| `query_car.py` | 车辆 |
| `query_parking_space.py` | 车位 |
| `query_resident.py` | 居民 |
| `query_owner.py` | 业主 |
| `query_work_order.py` | 工单（含动态列表） |
| `query_work_order_revisit.py` | 工单回访 |
| `query_mall_order.py` | 商城订单 |
| `query_service_order.py` | 服务订单 |
| `query_approval.py` | 审批 |
| `query_yard_archive.py` | 车场 |

运行示例：

```bash
python examples/shanshu/get_authorization.py
python examples/shanshu/query_community.py
```

`.env` 文件包含账号凭据，请勿提交到版本控制。

#### 1.8 异常处理

- 构造客户端时 `account` 或 `password` 为空，抛出 `ValueError`。
- 接口请求发生网络或 HTTP 错误时，抛出 `ConnectionError`。
- `get_authorization` 在登录失败或网络异常时返回空字符串，不抛出异常，可用 `if not token` 判断。

```python
from py_wisharetec_toolkit.shanshu import ShanshuClient

try:
    with ShanshuClient(account="你的善数账号", password="你的善数密码") as client:
        token = client.get_authorization()
        if not token:
            print("authorization 获取失败")
except (ValueError, ConnectionError) as exc:
    print(f"善数请求失败：{exc}")
```

## 配置与测试

示例脚本读取环境变量；仓库提供 [examples/shanshu/.env.example](examples/shanshu/.env.example) 作为配置模板。将所需变量设置在仓库根目录的 `.env` 中，或在运行脚本前导出环境变量。

测试配置位于 [tests/shanshu/.env.example](tests/shanshu/.env.example)。复制到测试目录的 `.env` 并填入有效凭证：

```bash
cp tests/shanshu/.env.example tests/shanshu/.env
pytest
```

测试按业务域拆分为同步与异步用例，共 30 项，包含有效的 authorization 获取与无效凭证场景。缺少必需环境变量时，相关用例会被跳过。

```bash
pytest tests/shanshu -v -s
```

pytest 默认配置会输出终端、HTML 和 XML 覆盖率报告。

> **注意：**默认测试会向真实善数开放平台发送请求。只有在 `.env` 中配置有效凭据时才会运行真实请求；否则 pytest 会跳过测试。请勿在不适合的设备或环境中执行。

## 开发

```bash
ruff check .
ruff format --check .
python -m compileall -q src examples tests
```

项目支持 Python 3.9 及以上版本。核心依赖包括 httpx、Pydantic、diskcache 和 redis；开发、测试、文档工具可通过 `pyproject.toml` 中的可选依赖组安装。

## 作者信息

- 作者：郭磊
- 邮箱：174000902@qq.com

## 许可证

本项目基于 [MIT 许可证](LICENSE) 开源。
