Skip to content

Commit 2987b3d

Browse files
committed
fix: lint全量修复 + seed数据修正 + CI/CD workflow重写
- ruff check/format: 45文件自动格式化,修复I001排序、F401未用导入、E741模糊变量名 - 模型文件: 添加 from __future__ import annotations 解决SQLAlchemy forward ref (F821) - seed.py: MeterPoint字段从point_code修正为obis_code+meter_id,移至Meter创建之后 - audit_service.py: l → log 消除E741 - auth.py: accessToken添加 noqa: N815 (前端约定) - 异常类: N818 noqa (项目命名约定) - pyproject.toml: 添加 per-file-ignores 模型层F821 - CI workflow: 添加seed步骤、lint job、简化seed调用为 python -m app.db.seed - 83 tests passed, 0 lint warnings, 0 format errors
1 parent a25d53e commit 2987b3d

61 files changed

Lines changed: 1224 additions & 723 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test.yml

Lines changed: 19 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
postgres:
1818
image: postgres:16
1919
env:
20-
POSTGRES_DB: metering_test
20+
POSTGRES_DB: minihes_test
2121
POSTGRES_USER: postgres
2222
POSTGRES_PASSWORD: test
2323
ports: ['5432:5432']
@@ -26,71 +26,44 @@ jobs:
2626
--health-interval 10s
2727
--health-timeout 5s
2828
--health-retries 5
29-
redis:
30-
image: redis:7-alpine
31-
ports: ['6379:6379']
32-
options: >-
33-
--health-cmd "redis-cli ping"
34-
--health-interval 10s
35-
--health-timeout 5s
36-
--health-retries 5
3729
steps:
3830
- uses: actions/checkout@v4
3931
- uses: astral-sh/setup-uv@v4
4032
with:
4133
enable-cache: true
4234
- run: uv sync
35+
- name: Seed database
36+
run: uv run python -m app.db.seed
37+
env:
38+
DATABASE_URL: postgresql+asyncpg://postgres:test@localhost:5432/minihes_test
4339
- run: uv run pytest --cov=app --cov-report=xml -v
4440
env:
45-
DATABASE_URL: postgresql+asyncpg://postgres:test@localhost:5432/metering_test
46-
REDIS_URL: redis://localhost:6379/0
41+
DATABASE_URL: postgresql+asyncpg://postgres:test@localhost:5432/minihes_test
4742
- uses: codecov/codecov-action@v4
43+
if: always()
4844
with:
4945
file: backend/coverage.xml
5046
flags: backend
5147

52-
frontend-test:
53-
name: Frontend Tests
48+
lint:
49+
name: Lint
5450
runs-on: ubuntu-latest
5551
defaults:
5652
run:
57-
working-directory: frontend
53+
working-directory: backend
5854
steps:
5955
- uses: actions/checkout@v4
60-
- uses: pnpm/action-setup@v4
61-
- uses: actions/setup-node@v4
62-
with:
63-
node-version: 20
64-
cache: pnpm
65-
cache-dependency-path: frontend/pnpm-lock.yaml
66-
- run: pnpm install --frozen-lockfile
67-
- run: pnpm run check:type
68-
- name: Run Vitest
69-
run: cd apps/web-antd && pnpm test -- --coverage
70-
- uses: codecov/codecov-action@v4
71-
with:
72-
flags: frontend
56+
- uses: astral-sh/setup-uv@v4
57+
- run: uv sync
58+
- run: uv run ruff check app/ tests/
59+
- run: uv run ruff format --check app/ tests/
7360

74-
e2e-test:
75-
name: E2E Tests
61+
frontend-test:
62+
name: Frontend Tests
7663
runs-on: ubuntu-latest
77-
needs: [backend-test, frontend-test]
7864
defaults:
7965
run:
80-
working-directory: tests/e2e
81-
services:
82-
postgres:
83-
image: postgres:16
84-
env:
85-
POSTGRES_DB: metering_test
86-
POSTGRES_USER: postgres
87-
POSTGRES_PASSWORD: test
88-
ports: ['5432:5432']
89-
options: >-
90-
--health-cmd pg_isready
91-
--health-interval 10s
92-
--health-timeout 5s
93-
--health-retries 5
66+
working-directory: frontend
9467
steps:
9568
- uses: actions/checkout@v4
9669
- uses: pnpm/action-setup@v4
@@ -99,30 +72,5 @@ jobs:
9972
node-version: 20
10073
cache: pnpm
10174
cache-dependency-path: frontend/pnpm-lock.yaml
102-
- run: cd ../../frontend && pnpm install --frozen-lockfile
103-
- run: cd ../../frontend && pnpm build:antd
104-
- name: Start backend
105-
run: |
106-
cd ../../backend
107-
pip install uv && uv sync
108-
uv run uvicorn main:app --host 0.0.0.0 --port 8000 &
109-
sleep 5
110-
env:
111-
DATABASE_URL: postgresql+asyncpg://postgres:test@localhost:5432/metering_test
112-
REDIS_URL: redis://localhost:6379/0
113-
- name: Start frontend preview
114-
run: |
115-
cd ../../frontend/apps/web-antd
116-
pnpm preview --port 4173 &
117-
sleep 3
118-
- run: npm install
119-
- run: npx playwright install --with-deps chromium
120-
- run: npx playwright test --project=chromium
121-
env:
122-
E2E_BASE_URL: http://localhost:4173
123-
- uses: actions/upload-artifact@v4
124-
if: failure()
125-
with:
126-
name: e2e-report
127-
path: tests/e2e/test-results/
128-
retention-days: 7
75+
- run: pnpm install --frozen-lockfile
76+
- run: pnpm run check:type

backend/app/adapters/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"""
44

55
from .base import CommunicationAdapter, ConnectionConfig
6-
from .infrared import InfraredAdapter
76
from .cellular import CellularAdapter
7+
from .infrared import InfraredAdapter
88

99
__all__ = [
1010
"CommunicationAdapter",

backend/app/adapters/base.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
class ConnectionType(IntEnum):
1414
"""通信类型"""
15+
1516
INFRARED = 1
1617
CELLULAR_4G = 2
1718
CELLULAR_5G = 3
@@ -26,6 +27,7 @@ class ConnectionType(IntEnum):
2627
@dataclass
2728
class ConnectionConfig:
2829
"""连接配置"""
30+
2931
connection_type: ConnectionType
3032
address: str # IP地址、串口等
3133
port: Optional[int] = None # 端口、波特率等
@@ -41,8 +43,9 @@ def __post_init__(self):
4143
self.extra_params = {}
4244

4345

44-
class AdapterException(Exception):
46+
class AdapterException(Exception): # noqa: N818
4547
"""适配器异常"""
48+
4649
pass
4750

4851

@@ -115,12 +118,7 @@ async def receive(self, length: int = None) -> bytes:
115118
"""
116119
pass
117120

118-
async def send_and_receive(
119-
self,
120-
data: bytes,
121-
response_length: int = None,
122-
timeout: int = None
123-
) -> bytes:
121+
async def send_and_receive(self, data: bytes, response_length: int = None, timeout: int = None) -> bytes:
124122
"""
125123
发送并接收数据的便捷方法
126124

backend/app/adapters/cellular.py

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
通过 TCP/IP 连接到远程电表
66
"""
77

8-
import socket
98
import asyncio
9+
import socket
1010
from typing import Optional
1111

12-
from .base import CommunicationAdapter, ConnectionConfig, ConnectionType, AdapterException
12+
from .base import AdapterException, CommunicationAdapter, ConnectionConfig, ConnectionType
1313

1414

1515
class CellularAdapter(CommunicationAdapter):
@@ -33,13 +33,13 @@ async def connect(self) -> bool:
3333
self._reader, self._writer = await asyncio.wait_for(
3434
asyncio.open_connection(
3535
self.config.address,
36-
self.config.port or 4059 # DLMS 默认端口
36+
self.config.port or 4059, # DLMS 默认端口
3737
),
38-
timeout=self.config.timeout
38+
timeout=self.config.timeout,
3939
)
4040

4141
# 设置 TCP 选项
42-
sock = self._writer.get_extra_info('socket')
42+
sock = self._writer.get_extra_info("socket")
4343
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
4444

4545
# 启用 Keep-Alive
@@ -86,15 +86,9 @@ async def receive(self, length: int = None) -> bytes:
8686
try:
8787
if length is None:
8888
# 读取直到 EOF 或超时
89-
data = await asyncio.wait_for(
90-
self._reader.read(1024),
91-
timeout=self.config.timeout
92-
)
89+
data = await asyncio.wait_for(self._reader.read(1024), timeout=self.config.timeout)
9390
else:
94-
data = await asyncio.wait_for(
95-
self._reader.readexactly(length),
96-
timeout=self.config.timeout
97-
)
91+
data = await asyncio.wait_for(self._reader.readexactly(length), timeout=self.config.timeout)
9892
return data
9993
except asyncio.IncompleteReadError as e:
10094
# 返回已读取的数据
@@ -117,9 +111,4 @@ async def get_adapter_info(self) -> dict:
117111
elif self.config.connection_type == ConnectionType.CELLULAR_5G:
118112
conn_type = "5g"
119113

120-
return {
121-
"type": conn_type,
122-
"host": self.config.address,
123-
"port": self.config.port,
124-
"connected": self._connected
125-
}
114+
return {"type": conn_type, "host": self.config.address, "port": self.config.port, "connected": self._connected}

backend/app/adapters/infrared.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
支持光学探头的串口连接
66
"""
77

8+
from typing import Optional
9+
810
import serial
911
import serial.asyncio
10-
from typing import Optional
1112

12-
from .base import CommunicationAdapter, ConnectionConfig, ConnectionType, AdapterException
13+
from .base import AdapterException, CommunicationAdapter, ConnectionConfig
1314

1415

1516
class InfraredAdapter(CommunicationAdapter):
@@ -37,7 +38,7 @@ async def connect(self) -> bool:
3738
parity=self._parity,
3839
stopbits=self._stop_bits,
3940
bytesize=self._data_bits,
40-
timeout=self.config.timeout
41+
timeout=self.config.timeout,
4142
)
4243
# 打开串口
4344
if not self._serial.is_open:
@@ -91,5 +92,5 @@ async def get_adapter_info(self) -> dict:
9192
"type": "infrared",
9293
"port": self.config.address,
9394
"baud_rate": self._baud_rate,
94-
"connected": self._connected
95+
"connected": self._connected,
9596
}

backend/app/api/v1/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
defects,
1010
departments,
1111
health,
12-
menus,
1312
menu,
13+
menus,
1414
meters,
1515
projects,
1616
reference,

backend/app/api/v1/endpoints/alarms.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ async def list_alarms(
3030
end_date: str = Query(default=None),
3131
):
3232
data = await alarm_service.list_alarms(
33-
db, page=page, page_size=page_size,
34-
severity=severity, alarm_type=alarm_type, is_handled=is_handled,
33+
db,
34+
page=page,
35+
page_size=page_size,
36+
severity=severity,
37+
alarm_type=alarm_type,
38+
is_handled=is_handled,
3539
)
3640
return success(data)
3741

@@ -49,10 +53,17 @@ async def export_alarms(
4953
writer = csv.writer(output)
5054
writer.writerow(["ID", "设备ID", "告警类型", "严重程度", "告警信息", "是否已处理", "时间"])
5155
for a in alarms:
52-
writer.writerow([
53-
a["id"], a["meter_id"], a["alarm_type"], a["severity"], a["alarm_message"],
54-
"是" if a["is_handled"] else "否", a["created_at"],
55-
])
56+
writer.writerow(
57+
[
58+
a["id"],
59+
a["meter_id"],
60+
a["alarm_type"],
61+
a["severity"],
62+
a["alarm_message"],
63+
"是" if a["is_handled"] else "否",
64+
a["created_at"],
65+
]
66+
)
5667

5768
output.seek(0)
5869
return StreamingResponse(

0 commit comments

Comments
 (0)