Files
playwrite/docs/DATABASE_ARCHITECTURE.md
Misaka b88ce96194 docs: add comprehensive database architecture documentation
Create detailed database architecture documentation with Mermaid diagrams including:
- Class hierarchy and design patterns (Factory, DAO, Strategy, Template Method)
- Connection management flows and lifecycle
- Database table structures and entity relationships
- SQL dialect handling for SQL Server and MySQL
- Data flow and batch processing logic
- Code examples and best practices

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 23:14:40 +08:00

1032 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据库架构文档
## 概述
本文档描述了 Playwrite 自动化项目的数据库架构,包括设计模式、组件关系、数据流和数据库表结构。
### 系统简介
本项目是一个 Python 自动化框架,使用 Playwright 与中国 ERP 系统(用友 YonBIP交互。数据库层采用抽象工厂模式和 DAOData Access Object模式支持多种数据库类型。
### 支持的数据库类型
- **SQL Server** (Microsoft SQL Server)
- 驱动ODBC Driver 18 for SQL Server
- 连接库:`pyodbc`
- 占位符:`?`
- **MySQL**
- 连接库:`mysql-connector-python`
- 占位符:`%s`
### 关键特性
- 多数据库支持:通过抽象基类实现统一的接口
- 自动 SQL 转换:处理不同数据库的表名格式差异
- 上下文管理器:支持 `with` 语句自动管理连接生命周期
- 批处理优化:根据数据库参数限制自动分批处理
- 事务管理:自动提交和回滚
---
## 架构设计
### 类层次结构
```mermaid
classDiagram
class BaseDatabaseConnection {
<<abstract>>
+config: Dict
+connection: Connection
+connect() Connection
+disconnect() void
+execute_query(sql, params) List~Dict~
+execute_update(sql, params) int
+get_placeholder() string
}
class SQLServerConnection {
+connect() pyodbc.Connection
+disconnect() void
+execute_query(sql, params) List~Dict~
+execute_update(sql, params) int
+get_placeholder() string
}
class MySQLConnection {
+connect() MySQLConnection
+disconnect() void
+execute_query(sql, params) List~Dict~
+execute_update(sql, params) int
+get_placeholder() string
}
class BaseDAO {
<<abstract>>
#db: BaseDatabaseConnection
#_db_type: DatabaseType
+__enter__() BaseDAO
+__exit__() void
+_convert_sql(sql) string
+_get_placeholder() string
}
class BIPUsersDAO {
+authenticate(username, password) Dict
+get_all_users() List~Dict~
+create_user(username, password, user_type) bool
+update_user_type(username, user_type) bool
+update_password(username, new_password) bool
+delete_user(username) bool
}
class DiscreteMaterialPlanDAO {
+save_dataframe_with_replace(df) Dict
+query_by_plan_number(plan_number) List~Dict~
+query_by_plan_numbers(plan_numbers) List~Dict~
+query_by_source_numbers(source_numbers) List~Dict~
+get_statistics() Dict
}
class ProductionContractDataDAO {
+query_by_总排号(总排号_list) List~Dict~
+get_source_numbers_by_总排号(总排号_list) List~string~
}
class MaterialsToBeDeletedDAO {
+insert_material(material_name, manager_name) bool
+get_all_materials() List~Dict~
+get_materials_by_manager(manager_name) List~Dict~
+update_manager(material_name, old_manager, new_manager) bool
+delete_material(material_name, manager_name) bool
+get_statistics() Dict
}
class ConnectionFactory {
<<factory>>
+create_connection(db_type, config) BaseDatabaseConnection
+create_from_config(database_config) BaseDatabaseConnection
}
class TableNameConverter {
<<utility>>
+to_mysql(table_name) string
+to_sqlserver(table_name) string
+convert_sql(sql, db_type) string
+extract_table_names(sql) List~string~
}
BaseDatabaseConnection <|-- SQLServerConnection
BaseDatabaseConnection <|-- MySQLConnection
BaseDAO <|-- BIPUsersDAO
BaseDAO <|-- DiscreteMaterialPlanDAO
BaseDAO <|-- ProductionContractDataDAO
BaseDAO <|-- MaterialsToBeDeletedDAO
ConnectionFactory ..> BaseDatabaseConnection : creates
BaseDAO ..> BaseDatabaseConnection : uses
BaseDAO ..> TableNameConverter : uses
```
### 设计模式
1. **抽象工厂模式 (Abstract Factory)**
- `ConnectionFactory` 根据配置创建相应的数据库连接实例
- 客户端代码无需关心具体实现类型
2. **DAO 模式 (Data Access Object)**
- `BaseDAO` 提供统一的数据访问接口
- 每个 DAO 类专注于特定表的数据操作
- 封装 SQL 语句和数据库交互细节
3. **策略模式 (Strategy)**
- 不同的数据库连接类实现相同的接口
- 运行时根据配置选择具体实现
4. **模板方法模式 (Template Method)**
- `BaseDAO` 定义通用的数据访问流程
- 子类实现具体的业务逻辑
---
## 连接管理
### 连接工厂流程
```mermaid
flowchart TD
A[Application calls get_connection] --> B[ConfigLoader.load]
B --> C{Database Type?}
C -->|SQL Server| D[ConnectionFactory.create_from_config]
C -->|MySQL| D
D --> E{db_type value}
E -->|sqlserver| F[SQLServerConnection config]
E -->|mysql| G[MySQLConnection config]
F --> H[Connect to database]
G --> H
H --> I[Return connection instance]
I --> J[Execute query/update]
J --> K[Close connection]
```
### 配置加载流程
```mermaid
flowchart LR
A[.env file] --> B[env_loader.py load_env_file]
B --> C[ConfigLoader.load]
C --> D[AppConfig.from_env]
D --> E[DatabaseConfig.from_env]
E --> F[DatabaseType from env]
F --> G{DB_TYPE value}
G -->|sqlserver| H[SQLServerConfig.from_env]
G -->|mysql| I[MySQLConfig.from_env]
H --> J[ConnectionFactory.create_from_config]
I --> J
```
### 连接生命周期
```mermaid
sequenceDiagram
participant App as Application
participant CF as ConnectionFactory
participant DB as DatabaseConnection
participant SQL as SQL Database
App->>CF: get_connection()
CF->>CF: load database config
CF->>DB: create_connection(db_type, config)
DB->>SQL: connect()
SQL-->>DB: connection object
DB-->>CF: connection instance
CF-->>App: return connection
App->>DB: execute_query(sql, params)
DB->>SQL: execute query
SQL-->>DB: results
DB-->>App: return results
App->>DB: disconnect()
DB->>SQL: close connection
```
---
## 数据访问层
### DAO 操作序列图
```mermaid
sequenceDiagram
participant App as Application
participant DAO as BaseDAO
participant CF as ConnectionFactory
participant DB as DatabaseConnection
participant SQL as SQL Database
App->>DAO: with dao:
DAO->>DAO: __enter__()
DAO->>CF: get_connection()
CF->>DB: create_connection()
DB->>SQL: connect()
DB-->>DAO: return connection
DAO->>DAO: _convert_sql(sql)
Note over DAO: TableNameConverter.convert_sql
DAO->>DB: execute_query(sql, params)
DB->>SQL: execute
SQL-->>DB: results
DB-->>DAO: return data
DAO-->>App: return results
App->>DAO: end with block
DAO->>DAO: __exit__()
DAO->>DB: disconnect()
DB->>SQL: close connection
```
### 批处理流程
```mermaid
flowchart TD
A[Batch Data] --> B{Database Type?}
B -->|SQL Server| C[Calculate batch size]
B -->|MySQL| D[Calculate batch size]
C --> E[Max 72 records/batch]
D --> F[Max 2000 records/batch]
E --> G[Split into batches]
F --> G
G --> H[Execute first batch]
H --> I{Has more data?}
I -->|Yes| J[Next batch]
J --> H
I -->|No| K[Complete]
```
**批处理限制说明**
- **SQL Server**: 最多 2100 个参数/查询。以 28 个字段的表为例,每批最多 72 条记录2100 ÷ 28 ≈ 72
- **MySQL**: 默认无硬限制,但建议每批不超过 2000 条记录以优化性能
### 错误处理流程
```mermaid
flowchart TD
A[Execute Query/Update] --> B{Success?}
B -->|Yes| C[Return Results]
B -->|No| D[Catch Exception]
D --> E{Is Update Operation?}
E -->|Yes| F[Rollback Transaction]
E -->|No| G[Skip Rollback]
F --> H[Log Error]
G --> H
H --> I[Raise Exception to Caller]
```
---
## 数据库表结构
### 实体关系图
```mermaid
erDiagram
BIPUsers ||--o{ DiscreteMaterialPlanData : creates
BIPUsers ||--o{ MaterialsTypeToBeDeleted : manages
BIPUsers ||--o{ MaterialsToBeDeleted : tracks
ProductionContractData ||--o{ DiscreteMaterialPlanData : references
BIPUsers {
int ID PK
string UserName
string Password
string UserType
datetime CreateTime
}
DiscreteMaterialPlanData {
int ID PK
string Factory
string MaterialStatus
string PlanNumber
string SourceNumber
string MaterialType
string ProductCode
string ProductName
string ProductUnit
int ProductPlanQuantity
string UseDepartment
string Remark
string Creator
datetime CreateDate
string Approver
datetime ApproveDate
int SequenceNumber
string MaterialCode
string MaterialName
string Specification
string Model
string DrawingNumber
string MaterialQuality
int PlanQuantity
string Unit
datetime RequiredDate
string Warehouse
float UnitUsage
int CumulativeOutputQuantity
string BOMVersion
}
ProductionContractData {
int ID PK
string 总排号
string 生产订单号
int 序号
string 订单号
string 客户名称
string 产品型号
}
MaterialsTypeToBeDeleted {
int ID PK
string MaterialName
string ManagerName
}
MaterialsToBeDeleted {
int ID PK
string MaterialName
string ManagerName
string Reason
datetime CreateDate
}
```
### 表结构说明
#### 1. BIPUsers (用户表)
存储系统用户信息和认证凭据。
| 字段名 | 类型 | 说明 | 约束 |
|--------|------|------|------|
| ID | int | 用户 ID | 主键 |
| UserName | varchar | 用户名 | 唯一 |
| Password | varchar | 密码 | - |
| UserType | varchar | 用户类型 | 'Admin', 'User', 'Guest' |
| CreateTime | datetime | 创建时间 | - |
#### 2. DiscreteMaterialPlanData (离散备料计划数据表)
存储生产备料计划详细信息。
| 字段名 | 类型 | 说明 | 约束 |
|--------|------|------|------|
| ID | int | 记录 ID | 主键 |
| Factory | varchar | 工厂 | - |
| MaterialStatus | varchar | 备料状态 | - |
| PlanNumber | varchar | 备料计划单号 | - |
| SourceNumber | varchar | 来源单号(生产订单号) | 外键关联 |
| MaterialType | varchar | 备料类型 | - |
| ProductCode | varchar | 产品编码 | - |
| ProductName | varchar | 产品名称 | - |
| ProductUnit | varchar | 产品单位 | - |
| ProductPlanQuantity | int | 产品计划数量 | 默认 0 |
| UseDepartment | varchar | 用料部门 | - |
| Remark | varchar | 备注 | - |
| Creator | varchar | 制单人 | 关联 BIPUsers |
| CreateDate | datetime | 制单日期 | - |
| Approver | varchar | 审批人 | - |
| ApproveDate | datetime | 审批日期 | - |
| SequenceNumber | int | 序号 | 默认 0 |
| MaterialCode | varchar | 材料编码 | - |
| MaterialName | varchar | 材料名称 | - |
| Specification | varchar | 规格 | - |
| Model | varchar | 型号 | - |
| DrawingNumber | varchar | 图号 | - |
| MaterialQuality | varchar | 物料材质 | - |
| PlanQuantity | int | 计划数量 | 默认 0 |
| Unit | varchar | 单位 | - |
| RequiredDate | datetime | 需用日期 | - |
| Warehouse | varchar | 发料仓库 | - |
| UnitUsage | float | 单位用量 | 默认 0.0 |
| CumulativeOutputQuantity | int | 累计出库数量 | 默认 0 |
| BOMVersion | varchar | BOM 版本 | - |
#### 3. ProductionContractData (生产合同数据表)
存储生产合同信息26年压力表合同数据
| 字段名 | 类型 | 说明 | 约束 |
|--------|------|------|------|
| ID | int | 记录 ID | 主键 |
| 总排号 | varchar | 总排号 | - |
| 生产订单号 | varchar | 生产订单号 | - |
| 序号 | int | 序号 | - |
| 订单号 | varchar | 订单号 | - |
| 客户名称 | varchar | 客户名称 | - |
| 产品型号 | varchar | 产品型号 | - |
#### 4. MaterialsTypeToBeDeleted (待删除物料类型表)
存储需要删除的物料及其管理员信息。
| 字段名 | 类型 | 说明 | 约束 |
|--------|------|------|------|
| ID | int | 记录 ID | 主键 |
| MaterialName | varchar | 物料名称 | - |
| ManagerName | varchar | 管理员名称 | 关联 BIPUsers |
#### 5. MaterialsToBeDeleted (待删除物料记录表)
存储待删除物料的详细记录。
| 字段名 | 类型 | 说明 | 约束 |
|--------|------|------|------|
| ID | int | 记录 ID | 主键 |
| MaterialName | varchar | 物料名称 | - |
| ManagerName | varchar | 管理员名称 | 关联 BIPUsers |
| Reason | varchar | 删除原因 | - |
| CreateDate | datetime | 创建日期 | - |
---
## SQL 方言处理
### SQL 转换流程
```mermaid
flowchart TD
A[SQL Server Query] --> B{Contains schema dot table format?}
B -->|Yes| C[TableNameConverter.convert_sql]
B -->|No| D[Skip conversion]
C --> E{Target Database Type?}
E -->|MySQL| F[Replace schema.table with schema_table]
E -->|SQL Server| G[Keep original format]
D --> G
F --> H{Placeholder conversion}
G --> H
H --> I{Database Type?}
I -->|MySQL| J[Replace question mark with percent s]
I -->|SQL Server| K[Keep question mark placeholders]
J --> L[Execute query]
K --> L
```
### 表名转换规则
| SQL Server 格式 | MySQL 格式 | 示例 |
|-----------------|------------|------|
| `[dbo].[TableName]` | `dbo_TableName` | `[dbo].[BIPUsers]``dbo_BIPUsers` |
| `[schema].[table name]` | `schema_table name` | `[productionContractData].[26年压力表合同数据]``productionContractData_26年压力表合同数据` |
| `TableName` | `dbo_TableName` | `BIPUsers``dbo_BIPUsers` |
### 占位符差异
| 数据库 | 占位符 | 示例 |
|--------|--------|------|
| SQL Server | `?` | `WHERE UserName = ? AND Password = ?` |
| MySQL | `%s` | `WHERE UserName = %s AND Password = %s` |
### 列名格式差异
| 数据库 | 列名格式 | 示例 |
|--------|----------|------|
| SQL Server | `[ColumnName]` | `SELECT [ID], [UserName] FROM [dbo].[BIPUsers]` |
| MySQL | `ColumnName` | `SELECT ID, UserName FROM dbo_BIPUsers` |
---
## 数据流
### 查询执行流程
```mermaid
flowchart TD
A[User calls DAO method] --> B[DAO enters context with 'with']
B --> C[DAO __enter__]
C --> D[Get connection from factory]
D --> E[Database connect]
E --> F[Convert SQL if needed]
F --> G[Build placeholders]
G --> H[Execute query]
H --> I{Success?}
I -->|Yes| J[Convert results to dicts]
I -->|No| K[Raise exception]
J --> L[Return data to user]
K --> M[DAO __exit__ cleanup]
L --> M
M --> N[Database disconnect]
```
### 保存数据流程
```mermaid
flowchart TD
A[User provides DataFrame] --> B[Check if empty]
B -->|Empty| C[Return zero counts]
B -->|Has data| D[Remove duplicates]
D --> E[Extract unique plan numbers]
E --> F[Delete existing records]
F --> G[Split into batches]
G --> H[Insert batch 1]
H --> I{More batches?}
I -->|Yes| J[Insert next batch]
I -->|No| K[Return statistics]
J --> H
```
### 权限过滤流程
```mermaid
flowchart TD
A[User queries materials] --> B{User authenticated?}
B -->|No| C[Return empty result]
B -->|Yes| D{User type?}
D -->|Admin| E[Return all materials]
D -->|User| F[Get user's materials]
D -->|Guest| G[Return limited materials]
F --> H[Filter by ManagerName]
H --> I[Return filtered result]
G --> I
E --> J[Return unfiltered result]
```
---
## 代码示例
### 连接使用示例
```python
from db.connection import get_connection
# 使用上下文管理器自动管理连接
with get_connection() as db:
results = db.execute_query(
"SELECT * FROM [dbo].[BIPUsers] WHERE UserType = ?",
('Admin',)
)
for user in results:
print(f"User: {user['UserName']}")
# 连接自动关闭
```
### DAO 使用示例
```python
from db.bip_users_dao import BIPUsersDAO
# 用户认证
dao = BIPUsersDAO()
user = dao.authenticate("admin", "password123")
if user:
print(f"Authenticated: {user['username']}")
# 获取所有用户
with dao:
users = dao.get_all_users()
for user in users:
print(f"{user['username']} - {user['user_type']}")
# 创建新用户
success = dao.create_user(
username="newuser",
password="pass123",
user_type="User"
)
```
### 配置示例
#### .env 文件配置
```bash
# 数据库类型 (sqlserver 或 mysql)
DB_TYPE=mysql
# SQL Server 配置
DB_SERVER=192.168.110.114
DB_NAME=CompanyDB
DB_USERNAME=peng
DB_PASSWORD=your_password
DB_SQLSERVER_DRIVER=ODBC Driver 18 for SQL Server
DB_TRUST_SERVER_CERTIFICATE=yes
# MySQL 配置
DB_MYSQL_HOST=192.168.31.83
DB_MYSQL_PORT=3306
DB_MYSQL_CHARSET=utf8mb4
# ERP 配置
ERP_URL=https://68.11.34.30:8082/
ERP_USERNAME=your_username
ERP_PASSWORD=your_password
ERP_HEADLESS=false
```
#### 代码中加载配置
```python
from config.loader import ConfigLoader
# 加载配置
config = ConfigLoader.load()
# 访问数据库配置
db_config = config.database
print(f"Database type: {db_config.db_type}")
print(f"Database name: {db_config.database}")
# 访问 ERP 配置
erp_config = config.erp
print(f"ERP URL: {erp_config.url}")
```
---
## 批处理限制详解
### SQL Server 参数限制
SQL Server 对每个 SQL 查询的参数数量有硬性限制:**最多 2100 个参数**。
#### 计算示例
对于 `DiscreteMaterialPlanData`28 个字段):
```
最大记录数 = floor(2100 / 28) = 72 条记录/批次
```
#### 实现代码
```python
def _batch_insert(self, db, df: pd.DataFrame, batch_size: int = 72) -> int:
"""SQL Server: 最多 72 条记录/批次"""
total_inserted = 0
records = self._convert_df_to_records(df)
for i in range(0, len(records), batch_size):
batch = records[i:i + batch_size]
for record in batch:
db.execute_update(sql, record)
total_inserted += 1
return total_inserted
```
### MySQL 批处理
MySQL 没有硬性参数限制,但建议每批不超过 2000 条记录。
#### 计算示例
```
建议批次大小 = 2000 条记录/批次
```
#### 实现代码
```python
def _batch_insert(self, db, df: pd.DataFrame, batch_size: int = 2000) -> int:
"""MySQL: 最多 2000 条记录/批次"""
total_inserted = 0
records = self._convert_df_to_records(df)
for i in range(0, len(records), batch_size):
batch = records[i:i + batch_size]
for record in batch:
db.execute_update(sql, record)
total_inserted += 1
return total_inserted
```
### IN 子句限制
对于包含 `IN` 子句的查询,也需要批处理:
```python
def _delete_by_plan_numbers(self, db, plan_numbers: List[str]) -> int:
"""分批删除以避免参数限制"""
batch_size = 1000 # 安全限制
total_deleted = 0
for i in range(0, len(plan_numbers), batch_size):
batch = plan_numbers[i:i + batch_size]
placeholders = ','.join(['?' for _ in batch])
sql = f"DELETE FROM {table_name} WHERE PlanNumber IN ({placeholders})"
deleted = db.execute_update(sql, tuple(batch))
total_deleted += deleted
return total_deleted
```
---
## 事务管理
### 自动提交和回滚
```mermaid
flowchart TD
A[Start Transaction] --> B[Execute Query]
B --> C{Success?}
C -->|Yes| D[Commit Transaction]
C -->|No| E[Rollback Transaction]
D --> F[Return Results]
E --> G[Log Error]
G --> H[Raise Exception]
```
### 实现代码
#### SQL Server
```python
def execute_update(self, sql: str, params: Optional[tuple] = None) -> int:
cursor = self.connection.cursor()
try:
if params:
cursor.execute(sql, params)
else:
cursor.execute(sql)
self.connection.commit() # 自动提交
return cursor.rowcount
except pyodbc.Error as e:
self.connection.rollback() # 自动回滚
print(f"执行失败,已回滚: {e}")
raise
finally:
cursor.close()
```
#### MySQL
```python
def execute_update(self, sql: str, params: Optional[tuple] = None) -> int:
cursor = self.connection.cursor()
try:
if params:
cursor.execute(sql, params)
else:
cursor.execute(sql)
self.connection.commit() # 自动提交
return cursor.rowcount
except Error as e:
self.connection.rollback() # 自动回滚
print(f"执行失败,已回滚: {e}")
raise
finally:
cursor.close()
```
---
## 错误处理
### 常见错误类型
| 错误类型 | 原因 | 处理方式 |
|----------|------|----------|
| 连接失败 | 网络问题、凭据错误 | 记录日志,抛出异常 |
| 参数超限 | 超过 2100 参数限制 | 自动分批处理 |
| 表名格式错误 | SQL 方言不匹配 | 自动转换表名 |
| 约束违反 | 重复键、外键约束 | 回滚事务,返回错误 |
| 超时 | 查询执行时间过长 | 增加超时时间或优化查询 |
### 错误处理最佳实践
```python
def safe_database_operation():
try:
with get_connection() as db:
# 执行数据库操作
results = db.execute_query(sql, params)
return results
except pyodbc.Error as e:
# 数据库特定错误
print(f"Database error: {e}")
# 记录到日志文件
log_error(e)
raise
except Exception as e:
# 通用错误
print(f"Unexpected error: {e}")
log_error(e)
raise
```
---
## 性能优化建议
### 1. 使用批处理
对于大量数据插入/更新,始终使用批处理:
```python
# 推荐:批处理
for i in range(0, len(records), batch_size):
batch = records[i:i + batch_size]
db.execute_batch(sql, batch)
# 避免:逐条插入
for record in records:
db.execute_update(sql, record)
```
### 2. 使用索引
确保常用查询字段有索引:
```sql
CREATE INDEX idx_plan_number ON DiscreteMaterialPlanData(PlanNumber)
CREATE INDEX idx_source_number ON DiscreteMaterialPlanData(SourceNumber)
CREATE INDEX idx_manager_name ON MaterialsTypeToBeDeleted(ManagerName)
```
### 3. 使用连接池
对于频繁的数据库操作,考虑使用连接池:
```python
from db.connection_pool import ConnectionPool
pool = ConnectionPool(max_connections=5)
with pool.get_connection() as db:
results = db.execute_query(sql, params)
```
### 4. 优化查询
- 只选择需要的列
- 使用 `WHERE` 子句过滤数据
- 避免使用 `SELECT *`
```python
# 推荐
sql = "SELECT ID, UserName FROM dbo_BIPUsers WHERE UserType = ?"
# 避免
sql = "SELECT * FROM dbo_BIPUsers"
```
---
## 安全考虑
### 1. 参数化查询
始终使用参数化查询防止 SQL 注入:
```python
# 推荐:参数化查询
sql = "SELECT * FROM dbo_BIPUsers WHERE UserName = ?"
results = db.execute_query(sql, (username,))
# 避免:字符串拼接
sql = f"SELECT * FROM dbo_BIPUsers WHERE UserName = '{username}'" # 危险!
```
### 2. 密码管理
- 不要在代码中硬编码密码
- 使用环境变量或配置文件
- 考虑使用密钥管理服务
### 3. 最小权限原则
数据库用户应该只有必要的权限:
```sql
-- 只授予必要的权限
GRANT SELECT, INSERT, UPDATE ON DiscreteMaterialPlanData TO app_user;
GRANT SELECT ON BIPUsers TO app_user;
```
### 4. 连接字符串安全
- 不要在日志中记录连接字符串
- 使用加密存储敏感信息
- 定期轮换凭据
---
## 维护指南
### 添加新 DAO 类
1. 创建新的 DAO 类继承 `BaseDAO`
2. 实现业务方法
3. 使用 `_convert_sql()` 处理表名
4. 使用 `_get_placeholder()` 处理参数
```python
from db.base_dao import BaseDAO
class NewTableDAO(BaseDAO):
def get_by_id(self, record_id: int) -> Dict:
table_name = self._convert_sql('[dbo].[NewTable]')
placeholder = self._get_placeholder()
sql = f"SELECT * FROM {table_name} WHERE ID = {placeholder}"
with get_connection() as db:
results = db.execute_query(sql, (record_id,))
return results[0] if results else None
```
### 添加新数据库支持
1. 创建新的连接类继承 `BaseDatabaseConnection`
2. 实现所有抽象方法
3.`ConnectionFactory` 中注册
4. 更新 `DatabaseType` 枚举
```python
# 1. 创建连接类
class PostgreSQLConnection(BaseDatabaseConnection):
def connect(self):
# 实现连接逻辑
pass
# 实现其他抽象方法...
# 2. 更新枚举
class DatabaseType(str, Enum):
SQLSERVER = "sqlserver"
MYSQL = "mysql"
POSTGRESQL = "postgresql"
# 3. 更新工厂
def create_connection(db_type, config):
if db_type == DatabaseType.POSTGRESQL:
return PostgreSQLConnection(config)
# 其他类型...
```
### 更新文档
当代码变更时,及时更新本文档:
- 更新类图和关系图
- 添加新的表结构说明
- 更新代码示例
- 记录新的限制和最佳实践
---
## 附录
### 相关文件
| 文件路径 | 说明 |
|----------|------|
| `db/base_connection.py` | 数据库连接抽象基类 |
| `db/sqlserver_connection.py` | SQL Server 连接实现 |
| `db/mysql_connection.py` | MySQL 连接实现 |
| `db/base_dao.py` | DAO 基类 |
| `db/connection_factory.py` | 连接工厂 |
| `db/table_name_converter.py` | 表名转换工具 |
| `db/bip_users_dao.py` | 用户 DAO |
| `db/discrete_material_plan_dao.py` | 备料计划 DAO |
| `db/production_contract_data_dao.py` | 合同数据 DAO |
| `db/materials_to_be_deleted_dao.py` | 待删除物料 DAO |
| `db/connection.py` | 连接获取辅助函数 |
| `config/schema.py` | 配置结构定义 |
| `config/env_loader.py` | 环境变量加载器 |
| `config/loader.py` | 配置加载器 |
### 依赖项
```
pyodbc>=4.0.0 # SQL Server 支持
mysql-connector-python>=8.0.0 # MySQL 支持
python-dotenv>=0.19.0 # 环境变量管理
pandas>=1.3.0 # 数据处理
```
### 参考资料
- [SQL Server Documentation](https://docs.microsoft.com/en-us/sql/)
- [MySQL Documentation](https://dev.mysql.com/doc/)
- [pyodbc Documentation](https://github.com/mkleehammer/pyodbc)
- [MySQL Connector/Python](https://dev.mysql.com/doc/connector-python/en/)
- [DAO Pattern](https://en.wikipedia.org/wiki/Data_access_object)
- [Abstract Factory Pattern](https://en.wikipedia.org/wiki/Abstract_factory_pattern)
---
**文档版本**: 1.0
**最后更新**: 2026-02-09
**维护者**: Development Team