commit 813171deba731bee4bae707af4b156ddd513729e Author: huangjinysf Date: Thu Sep 4 20:56:37 2025 +0800 1st publish diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed61e7a --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +**/__pycache__/ + +*.pyc + +*.log + +*.idea + +myenv/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7d09daf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.11 + +ENV LANG C.UTF-8 +ENV LC_ALL C.UTF-8 +ENV LANGUAGE C.UTF-8 + +WORKDIR /app + +COPY requirements.txt . + +COPY ./app . + +# 如果有requirements.txt,取消下面的注释 + +# 使用 BuildKit 缓存挂载(避免重复下载 pip 包) +RUN --mount=type=cache,target=/root/.cache/pip \ + pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ + # pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ + +CMD ["python", "db_test.py"] \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/app.py b/app/app.py new file mode 100644 index 0000000..ca7f9bd --- /dev/null +++ b/app/app.py @@ -0,0 +1,24 @@ +import logging +import time +import os + +# 确保日志目录存在 +log_dir = 'logs' +os.makedirs(log_dir, exist_ok=True) + +# 配置日志 +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler(f'{log_dir}/app.log'), + logging.StreamHandler() + ] +) + +logger = logging.getLogger(__name__) + +if __name__ == '__main__': + while True: + logger.info("Hello World from Docker Compose!") + time.sleep(5) \ No newline at end of file diff --git a/app/db_test.py b/app/db_test.py new file mode 100644 index 0000000..0875a93 --- /dev/null +++ b/app/db_test.py @@ -0,0 +1,98 @@ +from datetime import datetime +import logging +import time +import os +import sys +import locale +import mysql.connector +from mysql.connector import Error + +# 强制系统使用UTF-8 +sys.stdout.reconfigure(encoding='utf-8') +sys.stderr.reconfigure(encoding='utf-8') +locale.setlocale(locale.LC_ALL, 'C.UTF-8') + +if sys.platform != 'win32': + os.environ['TZ'] = 'Asia/Shanghai' + time.tzset() # 重新加载时区配置 + +# 确保日志目录存在 +log_dir = 'logs' +os.makedirs(log_dir, exist_ok=True) + +# 配置日志 +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler(f'{log_dir}/app.log', encoding='utf-8'), + logging.StreamHandler() + ] +) + +class CSTFormatter(logging.Formatter): + def formatTime(self, record, datefmt=None): + ct = datetime.fromtimestamp(record.created).astimezone() + if datefmt: + s = ct.strftime(datefmt) + else: + t = ct.strftime("%Y-%m-%d %H:%M:%S") + s = "%s,%03d" % (t, record.msecs) + return s + + +logger = logging.getLogger(__name__) + +def get_table_count(db_name): + try: + # 连接到MySQL数据库 + connection = mysql.connector.connect( + host='106.227.91.181', + user='root', # 替换为您的MySQL用户名 + password='cuixing@HuaerdaMySQL', # 替换为您的MySQL密码 + database='huaerdames_cloud' + # host= 'localhost', + # user='root', # 替换为您的MySQL用户名 + # password='root', # 替换为您的MySQL密码 + # database='huaerdames_cloud' + ) + + if connection.is_connected(): + cursor = connection.cursor() + + # 获取所有表名 + cursor.execute(f"SHOW TABLES FROM {db_name}") + tables = cursor.fetchall() + + # 统计每个表的记录数 + table_counts = {} + for table in tables: + table_name = table[0] + cursor.execute(f"SELECT COUNT(*) FROM {table_name}") + count = cursor.fetchone()[0] + table_counts[table_name] = count + + return table_counts + + except Error as e: + logger.error(f"数据库连接错误: {e}") + return None + finally: + if connection.is_connected(): + cursor.close() + connection.close() + +if __name__ == '__main__': + # 统计表数量 + db_name = 'huaerdames_cloud' + table_counts = get_table_count(db_name) + + if table_counts: + logger.info(f"数据库 '{db_name}' 表统计:") + for table, count in table_counts.items(): + logger.info(f"表名: {table}, 记录数: {count}") + logger.info(f"总表数: {len(table_counts)}") + else: + logger.warning("未能获取表统计信息") + + \ No newline at end of file diff --git a/config/__init__.py b/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config/database.py b/config/database.py new file mode 100644 index 0000000..615b0cd --- /dev/null +++ b/config/database.py @@ -0,0 +1,20 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from config.settings import DB_CONFIG +from urllib.parse import quote_plus + +DATABASE_URL = ( + f"mysql+pymysql://{DB_CONFIG['user']}:{quote_plus(DB_CONFIG['password'])}" + f"@{DB_CONFIG['host']}:{DB_CONFIG['port']}/{DB_CONFIG['database']}" +) + +engine = create_engine( + DATABASE_URL, + pool_size=5, # 默认 5,可根据并发调整 + max_overflow=20, # 超过 pool_size 后最大溢出连接数 + pool_timeout=30, # 等待连接的超时时间 + pool_recycle=1800, # 防止 MySQL server timeout + pool_pre_ping=True # 检查连接是否可用 +) + +SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False) diff --git a/config/settings.py b/config/settings.py new file mode 100644 index 0000000..c1f4532 --- /dev/null +++ b/config/settings.py @@ -0,0 +1,7 @@ +DB_CONFIG = { + "user": "root", + "password": "cuixing@HuaerdaMySQL", + "host": "106.227.91.181", + "port": 3306, + "database": "huaerdames_cloud" +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5eb5131 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +version: '3.11' + +services: + python-logger: + build: + context: . + dockerfile: Dockerfile + # 启用 BuildKit 缓存 + args: + - BUILDKIT_INLINE_CACHE=1 # 允许缓存复用 + container_name: my-python-logger + volumes: + - ./logs:/app/logs + working_dir: /app + #restart: unless-stopped + restart: on-failure \ No newline at end of file diff --git a/entity/wms_ingredients_log.py b/entity/wms_ingredients_log.py new file mode 100644 index 0000000..86c68e7 --- /dev/null +++ b/entity/wms_ingredients_log.py @@ -0,0 +1,42 @@ +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import Column, BigInteger, String, DECIMAL, DateTime, Boolean + +Base = declarative_base() + +class WmsIngredientsLog(Base): + __tablename__ = 'wms_ingredients_log' + + id = Column(BigInteger, primary_key=True, autoincrement=True) + net_weight = Column(DECIMAL(24,2)) + is_deleted = Column(Boolean, default=False) + create_time = Column(DateTime) + # 其他字段省略... + code_sn = Column(String(50)) + ingredients_id = Column(BigInteger) + ingredients_name = Column(String(90)) + manufacturer_id = Column(BigInteger) + manufacturer_name = Column(String(90)) + part_number = Column(String(50)) + lot_number = Column(String(50)) + product_id = Column(BigInteger) + product_name = Column(String(90)) + item_specification = Column(String(90)) + item_id = Column(BigInteger) + net_weight = Column(DECIMAL(24,2)) + gross_weight = Column(DECIMAL(24,2)) + tare_weight = Column(DECIMAL(24,2), default=0.00) + measure_id = Column(BigInteger) + measure_name = Column(String(90)) + job_number = Column(String(90)) + status = Column(String(1)) + is_deleted = Column(Boolean, default=False) + version = Column(BigInteger, default=0) + create_by = Column(String(100)) + create_time = Column(DateTime) + update_by = Column(String(100)) + update_time = Column(DateTime) + attr1 = Column(String(255)) + attr2 = Column(BigInteger) + # attr3 JSON 可加 sqlalchemy.dialects.mysql.JSON + order_weight = Column(DECIMAL(24,2)) + is_documents = Column(String(1)) diff --git a/monthly_weight_plot.png b/monthly_weight_plot.png new file mode 100644 index 0000000..2968746 Binary files /dev/null and b/monthly_weight_plot.png differ diff --git a/plot_monthly_weight.py b/plot_monthly_weight.py new file mode 100644 index 0000000..bdf892f --- /dev/null +++ b/plot_monthly_weight.py @@ -0,0 +1,15 @@ +import matplotlib.pyplot as plt +from service.wms_ingredients_service import WmsIngredientsService + +service = WmsIngredientsService() +df = service.get_monthly_weight_df() + +plt.figure(figsize=(12, 6)) +plt.plot(df["month"], df["total_weight"], marker="o") +plt.title("Monthly Net Weight Statistics") +plt.xlabel("Month") +plt.ylabel("Total Net Weight") +plt.grid(True) +plt.xticks(rotation=45) +plt.tight_layout() +plt.show() diff --git a/repository/__init__.py b/repository/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/repository/base_repository.py b/repository/base_repository.py new file mode 100644 index 0000000..87ece7e --- /dev/null +++ b/repository/base_repository.py @@ -0,0 +1,30 @@ +from config.database import SessionLocal +from sqlalchemy import text + +class BaseRepository: + def __init__(self): + self.session = SessionLocal() + + def add(self, obj): + self.session.add(obj) + self.session.commit() + self.session.refresh(obj) + return obj + + def delete(self, obj): + self.session.delete(obj) + self.session.commit() + + def get_by_id(self, entity, id_): + return self.session.query(entity).get(id_) + + def list_all(self, entity): + return self.session.query(entity).all() + + def execute_sql(self, sql, params=None): + """执行原生 SQL""" + result = self.session.execute(text(sql), params or {}) + return result.fetchall() + + def close(self): + self.session.close() diff --git a/repository/wms_ingredients_log_repository.py b/repository/wms_ingredients_log_repository.py new file mode 100644 index 0000000..da49b60 --- /dev/null +++ b/repository/wms_ingredients_log_repository.py @@ -0,0 +1,26 @@ +from repository.base_repository import BaseRepository +from entity.wms_ingredients_log import WmsIngredientsLog + +class WmsIngredientsLogRepository(BaseRepository): + def get_monthly_net_weight(self): + sql = """ + SELECT DATE_FORMAT(create_time, '%Y-%m') AS month, + SUM(net_weight) AS total_weight + FROM wms_ingredients_log + WHERE is_deleted = 0 + AND net_weight IS NOT NULL + AND create_time IS NOT NULL + GROUP BY DATE_FORMAT(create_time, '%Y-%m') + ORDER BY month + """ + return self.execute_sql(sql) + def get_ingredients_log_by_code_sn(self, code_sn: str): + sql = """ + SELECT * + FROM wms_ingredients_log + WHERE code_sn = :code_sn + AND is_deleted = 0 + LIMIT 1 + """ + rows = self.execute_sql(sql, {"code_sn": code_sn}) + return rows[0] if rows else None \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..53c8828 Binary files /dev/null and b/requirements.txt differ diff --git a/service/__init__.py b/service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/service/wms_ingredients_service.py b/service/wms_ingredients_service.py new file mode 100644 index 0000000..4080a1a --- /dev/null +++ b/service/wms_ingredients_service.py @@ -0,0 +1,14 @@ +import pandas as pd +from repository.wms_ingredients_log_repository import WmsIngredientsLogRepository + +class WmsIngredientsService: + def __init__(self): + self.repo = WmsIngredientsLogRepository() + + def get_monthly_weight_df(self): + rows = self.repo.get_monthly_net_weight() + df = pd.DataFrame(rows, columns=["month", "total_weight"]) + df["month"] = pd.to_datetime(df["month"]) + return df + def get_ingredients_log_by_code_sn(self, code_sn: str): + return self.repo.get_ingredients_log_by_code_sn(code_sn) \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..2c1bbbc --- /dev/null +++ b/test.py @@ -0,0 +1,6 @@ +from service.wms_ingredients_service import WmsIngredientsService + +service = WmsIngredientsService() + +record = service.get_ingredients_log_by_code_sn("ITEM_2508290093006") +print(record) diff --git a/wms_ingredients_log_stats.py b/wms_ingredients_log_stats.py new file mode 100644 index 0000000..87f6c7c --- /dev/null +++ b/wms_ingredients_log_stats.py @@ -0,0 +1,65 @@ + + +import mysql.connector +import pandas as pd +import matplotlib.pyplot as plt +from datetime import datetime + +# Database connection +try: + cloud_conn = mysql.connector.connect( + host='106.227.91.181', + database='huaerdames_cloud', + port=3306, + user='root', + password='cuixing@HuaerdaMySQL' + ) + + # Query to get net_weight by month + query = """ + SELECT + DATE_FORMAT(create_time, '%Y-%m') as month, + SUM(net_weight) as total_weight + FROM wms_ingredients_log + WHERE is_deleted = 0 + AND net_weight IS NOT NULL + AND create_time IS NOT NULL + GROUP BY DATE_FORMAT(create_time, '%Y-%m') + ORDER BY month + """ + + # Execute query and load data into DataFrame + df = pd.read_sql(query, cloud_conn) + + # Close database connection + cloud_conn.close() + + # Convert month to datetime for better plotting + df['month'] = pd.to_datetime(df['month']) + + # Create the plot + plt.figure(figsize=(12, 6)) + plt.plot(df['month'], df['total_weight'], marker='o') + + # Customize the plot + plt.title('Monthly Net Weight Statistics', fontsize=14) + plt.xlabel('Month', fontsize=12) + plt.ylabel('Total Net Weight', fontsize=12) + plt.grid(True) + + # Rotate x-axis labels for better readability + plt.xticks(rotation=45) + + # Adjust layout to prevent label cutoff + plt.tight_layout() + + # Save the plot + plt.savefig('monthly_weight_plot.png') + + # Show the plot + plt.show() + +except mysql.connector.Error as err: + print(f"Database error: {err}") +except Exception as e: + print(f"Error: {e}") \ No newline at end of file