# 资产管理系统 **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 ```