# 资产管理系统
**Repository Path**: zhangjun76/asset-management
## Basic Information
- **Project Name**: 资产管理系统
- **Description**: 基于 python tornado layui 的资产管理系统
企业资产管理软件和解决方案
借助我们的云端软件,开启智慧资产管理变革,有效管理企业实体资产的完整生命周期
设备管理云端解决方案
运用即时洞察、IoT、AI以及预测性分析,维护并管理实体设备效能
移动办公,超越传统资产全流程管理
随时随地管理资产和设备信息、支持扫码和RFID盘点,执行巡检、工单,全面提升管理效率
- **Primary Language**: Python
- **License**: Artistic-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 20
- **Created**: 2023-12-03
- **Last Updated**: 2024-05-30
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
资产全生命周期管理EAM
基于Tornado的MVC管理后台,演示地址
#### 项目简介
**易点易动**是国内领先的企业资产管理SaaS平台(EAM),公司以自研的固定资产PaaS平台为核心,致力于为各行业客户提供智能、灵活的固定资产和设备解决方案。
通过易点易动的在线帮助可以让用户快速了解如何使用系统,并指导用户设置基础数据,管理固定资产、设备、库存、采购,以及财务折旧和处置。 在易点易动EAM系统中,实物资产管理分为两种模式:
**固定资产/设备管理:** 是指实物资产,按单品管理,每一个都是独立管理的,比如电脑等设备;
**库存管理:** 是指实物资产按品类管理的,每个品类中个体没有差异的,比如打印纸,硒鼓等。
系统包括多个客户端版本:
网页端(通过浏览器、企业微信、飞书、钉钉使用)
- 桌面端
- iOS APP
- Android APP
- 员工端(通过浏览器、企业微信、飞书、微信小程序和钉钉登录使用)
用户可以使用同一个账号登陆不同的客户端,数据互通。**其中,员工端由普通员工使用,其他客户端由管理员使用。**
系统通过对资产设置唯一的资产编码(标签)进行管理,通过PC端打印资产标签,粘贴到设备上,通过Android和iOS端进行日常管理(入库、领用、退库、借用、盘点、巡检点检、维修、保养等)。
对于第一次使用易点固定资产云系统,根据使用的模块,建议按如下顺序进行初始化:
1. **系统管理** → 组织架构 → 员工档案 → 系统角色 → 系统用户 → 表单和流程配置
2. **资产管理** → 相关设置 → 资产入库 → 日常管理(领用、调拨、变更、盘点等)
3. **设备管理**(巡检点检、设备维修、工单管理、设备保养等)
4. **库存管理** → 相关设置 → 日常管理(入库、出库、调拨、盘点等)
5. **软件管理** → 相关设置 → 日常管理(软件登记、分发、维保、变更等)
6. **采购管理**(采购申请、订单、收货、付款等)
7. **财务管理**(折旧、处置等)
>Tornado Admin 基于 python框架 Tornado + Layui 开发的后台管理系统,基于本系统即可快速构建你的功能业务
>
>项目旨在为python开发者提供一个后台管理系统的模板,成为您构建信息管理系统,物联网后台....等等应用时灵活,简单的工具
>
> gii_model 可用于快速反向生成model
>
> gii_crud 快速生成 增删改查前后端代码, 简单调整下, 即可使用
#### 内置功能
- [x] 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
- [x] 权限管理:配置系统菜单,操作权限,按钮权限标识等。
- [x] 角色管理:角色菜单权限分配。
- [x] 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
- [x] 登录日志:系统登录日志记录查询包含登录异常。
- [x] 服务监控:监视当前系统CPU、内存、磁盘、python版本,运行时长等相关信息。
- [x] 文件上传: 图片上传示例
- [ ] 定时任务: 简单的定时任务
- [x] 代码生成: 根据表生成model, 根据表生成增删改查后端代码和前台页面
- [x] JWT认证: 支持JWT认证
#### 项目结构
```
Tornado Admin
├─common # 公共类
│ ├─ __init__.py # 项目启动时, 初始化数据库连接
│ ├─ ApsChedulerHelper.py # 定时任务类
│ ├─ DbHelper.py # 数据库连接公共类
│ └─ HttpHelper.py # Http请求公共类
├─config # 配置文件
│ └─ __init__.py
├─docker_data
├─docs # 文档
├─handler # 控制器层
│ ├─ __init__.py
│ ├─ IndexHandler.py # 前台首页
│ ├─ HomeHandler.py # 后台首页
│ ├─ AuthHandler.py # 前台登录相关
│ ├─ BaseHandler.py # 公共类
│ ├─ DeptHandler.py # 部门管理
│ ├─ DictHandler.py # 字典管理
│ ├─ FileHandler.py # 文件管理
│ ├─ LogHandler.py # 日志管理
│ ├─ MonitorHandler.py # 监控管理
│ ├─ PowerHandler.py # 权限管理
│ ├─ RoleHandler.py # 角色管理
│ ├─ TaskHandler.py # 任务管理
│ └─ UserHandler.py # 用户管理
├─models
├─static # 静态文件
├─templates # 模板
├─app.py # 主入口
├─docker-compose.yml
├─Dockerfile
├─README.md
├─requirements.txt
└─start.sh
```
#### 本地开发环境搭建
```bash
基础环境: python>=3.6 mysql>=5.6
示例: 使用conda 创建虚拟环境
# conda create -n <自定义名称> python=3.6
conda create -n asset36 python=3.6
# 下 载
git clone https://gitee.com/meadhu/asset-management.git
# 安 装
pip install -r requirements.txt -i https://pypi.douban.com/simple
# 修改数据库连接信息 【不需要提前创建数据库, 在第一次运行时会自动创建并导入初始化数据】
config/__init__.py
# 执行命令启动项目 【项目第一次启动, 会自动创建数据库, 并导入初始化数据】
python run.py
# 访问 默认后台账号密码 admin admin
http://127.0.0.1:3001/
# docker 启动 grafana
docker run -d --name=asset_grafana --user=root -p 3000:3000 -v $PWD/docker_data/grafana:/var/lib/grafana grafana/grafana
# grafana 访问地址
http://127.0.0.1:3000/
```
#### 线上docker部署
```shell
# 下 载
git clone https://gitee.com/meadhu/asset-management.git
# 进入项目目录
cd asset-management
# docker compose 构建项目镜像
docker-compose build
# docker-compose 运行项目
docker-compose up -d
# 访问
http://<线上IP>:3001/
```
#### 命令行工具
```bash
# 通过表名, 反向生成Model
python app.py gen_model <表名>
# 通过表名,生成CRUD页面和功能(前提是已经生成了Model)
python app.py gen_crud <表名>
# 在 /app.py 中, 添加路由
# 通过model生成sql表
python app.py gen_table <表名,表名...>
# 通过model删除sql表
python app.py del_table <表名,表名...>
# 表字段更新先删除在创建
```
#### 代码生成示例
```shell
# 1. 创建表
DROP TABLE IF EXISTS `admin_project`;
CREATE TABLE `admin_project` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '项目名称',
`code` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '项目标识',
`remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '备注',
`details` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '详情',
`sort` int(11) NULL DEFAULT NULL COMMENT '排序',
`create_at` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_at` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`enable` int(11) NULL DEFAULT NULL COMMENT '是否启用',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = DYNAMIC;
INSERT INTO `admin_project` VALUES (1, '示例项目一', 'proj_code_1', '这是示例项目一的备注信息', '这是示例项目一的详情信息', 10, NULL, NULL, 1);
INSERT INTO `admin_project` VALUES (2, '示例项目2', 'proj_code_2', '这是示例项目2的备注信息', '这是示例项目2的详情信息', 20, NULL, NULL, 1);
INSERT INTO `admin_project` VALUES (3, '示例项目3', 'proj_code_3', '这是示例项目3的备注信息', '这是示例项目3的详情信息', 30, NULL, NULL, 0);
SELECT * from `admin_project` ORDER BY id DESC LIMIT 10;
```
```shell
# 2. 生成 model
python app.py gen_model admin_project
```
```shell
# 3. 生成 后端功能和前端页面
python app.py gen_crud admin_project
```
```shell
# 4. 修改 app.py 中,路由配置
```
```shell
# 5. 启动项目,访问 http://localhost:3001/admin/project/main
```
#### 预览项目
```shell
# 分页代码 https://github.com/wizeline/sqlalchemy-pagination
from sqlalchemy_pagination import paginate
page = paginate(session.query(User), 1, 25)
# The pagination objects has the following attributes
#items: The items of the current page base on the query
#total: Total number of items
#pages: Total number of pages
#has_next: Boolean indication wether there are more pages to fetch
#has_previous: Boolean indicating wether there are previous pages
#next_page: Next page number or None if the current page is the last one
#previous_page: Previous page number or None if the current page is the last one
```
#### JWT认证 使用
```shell
# 添加 verify_token 参数值为 True, 示例在 handler/UserHandler.py def data()
@authorize("admin:user:main", log=False, verify_token=True)
# 请求示例
curl --location --request GET 'http://localhost:3001/admin/user/data?page=1&limit=10' \
--header 'Authorization: Bearer eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjUzNjU2NjI2fQ.h2WryWa_6jcdEtUtRZM1kEUDNTUif8b5pcb_ZBMgGQ4'
```
### SQLAlchemy
```
emp = session.query(Employee).filter_by(EMP_ID='1001').first()
emp = session.query(Employee).filter(Employee.EMP_ID == '1001').first()
emps = session.query(Employee).filter(Employee.EMP_ID.like('%9')).all()
emps = session.query(Employee).filter(Employee.MARITAL_STAT == None).all()
emps = session.query(Employee).filter(Employee.EDUCATION.in_(['Bachelor', 'Master'])).all()
emps = session.query(Employee).filter(Employee.GENDER=='Female', Employee.EDUCATION=='Bachelor').all()
emps = session.query(Employee).filter(and_(Employee.GENDER=='Female', Employee.EDUCATION=='Bachelor')).all()
emps = session.query(Employee).filter(or_(Employee.MARITAL_STAT=='Single', Employee.NR_OF_CHILDREN==0)).all()
from sqlalchemy import and_, or_
User.query.order_by(and_(User.popularity.desc(), User.date_created.desc())).all()
cvg_items = session.query(Ww).order_by(and_(Ww.site, Ww.pn)).all()
def test_create_emp(self):
emp = Employee(
EMP_ID = "9002",
FIRST_NAME= "Lauren",
LAST_NAME = "Daigle",
GENDER = "Female",
AGE = 20,
EMAIL = "unknown",
PHONE_NR = "unknown",
EDUCATION = "Bachelor",
MARITAL_STAT = "Single",
NR_OF_CHILDREN = 0
)
session.add(emp)
session.commit()
def test_modify(self):
emp = session.query(Employee).filter_by(EMP_ID='9002').first()
emp.AGE = '21'
session.commit()
def test_delete(self):
emp = session.query(Employee).filter_by(EMP_ID='9002').first()
session.delete(emp)
session.commit()
# 表间关系
https://blog.csdn.net/stone0823/article/details/112344065
class User(Base, ModelExt):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
fullname = Column(String)
nickname = Column(String)
addresses = relationship("Address", back_populates="user")
class Address(Base, ModelExt):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
email_address = Column(String, nullable=False)
#user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE", onupdate="CASCADE"))
user_id = Column(Integer, ForeignKey("users.id"))
user = relationship("User", back_populates="addresses")
# 多表关联查询
def test_query_via_join(self):
result = session.query(User, Address).join(Address).all()
for item in result:
print(item[0].id, item[0].fullname, item[1].email_address)
def test_query_via_relation(self):
result = session.query(User).all()
for item in result:
addresses = item.addresses
for addr in addresses:
print(item.id, item.fullname, addr.email_address)
def test_query_many_to_one(self):
result = session.query(Address).all()
for addr in result:
print(addr.id, addr.email_address, addr.user.fullname)
def test_get_address_manually(self):
"""
不管数据库是否建立关系,sqlalchemy是否建立关系
都可以用下面手工的方式查询和获取
:return:
"""
user = session.query(User).filter_by(id=2).first()
addresses = session.query(Address).filter(Address.user_id == user.id).all()
print(addresses)
```
```shell
UML 查看
mac安装graphviz的3种方法
https://zhuanlan.zhihu.com/p/417012153
SQLAlchemy创建数据表
https://www.sunxiaoning.com/language/1036.html
```
```shell
docker run -d --name container_name \ //-d表示容器后台运行 --name指定容器名字
-p 7474:7474 -p 7687:7687 \ //映射容器的端口号到宿主机的端口号
-v /home/neo4j/data:/data \ //把容器内的数据目录挂载到宿主机的对应目录下
-v /home/neo4j/logs:/logs \ //挂载日志目录
-v /home/neo4j/conf:/var/lib/neo4j/conf //挂载配置目录
-v /home/neo4j/import:/var/lib/neo4j/import \ //挂载数据导入目录
--env NEO4J_AUTH=neo4j/password \ //设定数据库的名字的访问密码
neo4j //指定使用的镜像
docker run -d --name neo4j -p 7474:7474 -p 7687:7687 -v $PWD/docker_data/neo4j/data:/data -v $PWD/docker_data/neo4j/logs:/logs -v $PWD/docker_data/neo4j/conf:/var/lib/neo4j/conf -v $PWD/docker_data/neo4j/import:/var/lib/neo4j/import --env NEO4J_AUTH=neo4j/123456 neo4j:latest
```