在开始构建批发零售业档案管理系统之前,必须先配置好Python开发环境。本系统基于Python 3.8及以上版本开发,利用Flask作为Web框架,SQLAlchemy作为ORM工具,确保系统轻量且易于维护。
请在终端中执行以下命令创建项目目录并激活虚拟环境:
mkdir wholesale_archive_system
cd wholesale_archive_system
python -m venv venv
对于Windows系统,激活命令为venv\Scripts\activate;对于Mac或Linux系统,激活命令为source venv/bin/activate。
激活环境后,安装核心依赖包。请直接复制以下命令执行:
pip install flask flask-sqlalchemy flask-wtf werkzeug
安装完成后,在项目根目录下创建以下文件夹结构用于存放不同类型的文件:
mkdir static/uploads
mkdir templates
批发零售企业的核心档案通常包括供应商资质、客户信息以及各类经营许可证。我们需要设计两个核心数据表:Enterprise(企业档案表)和Contract(合同文件表)。
在项目根目录下创建models.py文件,并写入以下完整代码。该代码定义了数据库结构,包含统一社会信用代码验证逻辑和文件存储路径:

```python
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
import re
db = SQLAlchemy()
class Enterprise(db.Model):
__tablename__ = 'enterprises'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False) 企业名称
credit_code = db.Column(db.String(18), unique=True, nullable=False) 统一社会信用代码
type = db.Column(db.String(20), nullable=False) 类型:供应商/客户
contact_person = db.Column(db.String(50)) 联系人
phone = db.Column(db.String(20)) 联系电话
address = db.Column(db.String(200)) 地址
status = db.Column(db.String(10), default='有效') 状态
created_at = db.Column(db.DateTime, default=datetime.now) 录入时间
contracts = db.relationship('Contract', backref='enterprise', lazy=True) 关联合同
@staticmethod
def validate_credit_code(code):
简单的18位校验规则
pattern = r'^[0-9A-HJ-NPQ-RTUW-Y]{2}[0-9]{6}[0-9A-HJ-NPQ-RTUW-Y]{10}$'
return re.match(pattern, code) is not None
class Contract(db.Model):
__tablename__ = 'contracts'
id = db.Column(db.Integer, primary_key=True)
enterprise_id = db.Column(db.Integer, db.ForeignKey('enterprises.id'), nullable=False)
title = db.Column(db.String(100), nullable=False) 合同/文件标题
file_path = db.Column(db.String(255), nullable=False) 文件存储路径
upload_time = db.Column(db.DateTime, default=datetime.now) 上传时间
```
创建app.py文件,这是系统的入口文件。这里将配置数据库连接、文件上传路径、表单处理类以及所有的路由逻辑。为了确保读者能直接运行,数据库将自动使用本地SQLite文件,无需额外安装数据库服务。
请在app.py中填入以下代码:
```python
import os
from flask import Flask, render_template, request, redirect, url_for, flash
from werkzeug.utils import secure_filename
from models import db, Enterprise, Contract
app = Flask(__name__)
配置部分
app.config['SECRET_KEY'] = 'dev-secret-key-change-in-prod'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///wholesale_archive.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['UPLOAD_FOLDER'] = os.path.join('static', 'uploads')
app.config['MAX_CONTENT_LENGTH'] = 16 1024 1024 限制上传最大16MB
允许上传的文件类型
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'pdf', 'doc', 'docx'}
db.init_app(app)
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/')
def index():
search_query = request.args.get('q', '')
page = request.args.get('page', 1, type=int)
per_page = 10
query = Enterprise.query
if search_query:
query = query.filter(Enterprise.name.contains(search_query))
pagination = query.order_by(Enterprise.created_at.desc()).paginate(page=page, per_page=per_page, error_out=False)
enterprises = pagination.items
return render_template('index.html', enterprises=enterprises, pagination=pagination, search_query=search_query)
@app.route('/add', methods=['GET', 'POST'])
def add_enterprise():
if request.method == 'POST':
name = request.form.get('name')
credit_code = request.form.get('credit_code')
type_ = request.form.get('type')
contact_person = request.form.get('contact_person')
phone = request.form.get('phone')
address = request.form.get('address')
数据校验
if not name or not credit_code:
flash('企业名称和信用代码为必填项', 'error')
return redirect(url_for('add_enterprise'))
if not Enterprise.validate_credit_code(credit_code):
flash('统一社会信用代码格式不正确', 'error')
return redirect(url_for('add_enterprise'))
new_ent = Enterprise(
name=name, credit_code=credit_code, type=type_,
contact_person=contact_person, phone=phone, address=address
)
db.session.add(new_ent)
db.session.commit()
flash('企业档案添加成功', 'success')
return redirect(url_for('index'))
return render_template('add_enterprise.html')
@app.route('/upload/
def upload_contract(ent_id):
enterprise = Enterprise.query.get_or_404(ent_id)
if request.method == 'POST':
if 'file' not in request.files:
flash('没有文件部分', 'error')
return redirect(request.url)
file = request.files['file']
title = request.form.get('title')
if file.filename == '':
flash('未选择文件', 'error')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
确保上传目录存在
if not os.path.exists(app.config['UPLOAD_FOLDER']):
os.makedirs(app.config['UPLOAD_FOLDER'])
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
new_contract = Contract(
enterprise_id=ent_id,
title=title,
file_path=filename
)
db.session.add(new_contract)
db.session.commit()
flash('文件上传成功', 'success')
return redirect(url_for('index'))
else:
flash('仅允许上传图片或PDF/Word文档', 'error')
return render_template('upload_contract.html', enterprise=enterprise)
if __name__ == '__main__':
with app.app_context():
db.create_all() 自动创建表
app.run(debug=True, port=5000)
```
为了使系统界面美观且易用,我们使用Bootstrap 5框架。请在templates文件夹下创建以下三个HTML文件。
这个文件包含了通用的头部导航和样式引用,减少重复代码:
```html
用于展示所有企业档案,支持分页和搜索功能:
```html
{% extends "base.html" %}
{% block content %}
| ID | 企业名称 | 类型 | 信用代码 | 联系人 | 状态 | 操作 |
|---|---|---|---|---|---|---|
| {{ ent.id }} | {{ ent.name }} | {{ ent.type }} | {{ ent.credit_code }} | {{ ent.contact_person }} {{ ent.phone }} | {{ ent.status }} | 上传合同 |
| 暂无数据 | ||||||
提供表单用于录入新的供应商或客户信息:
```html
{% extends "base.html" %}
{% block content %}