Compare commits

...

2 Commits

Author SHA1 Message Date
zhongchangyuyu
1cf46e5633 资产优化 2025-04-25 19:53:53 +08:00
zhongchangyuyu
4d0ce8cdb6 资产管理 2025-04-25 10:02:24 +08:00
190 changed files with 12462 additions and 29334 deletions

View File

@ -1,160 +0,0 @@
# 4 数据库对象命名规则
## 4.1 表
### 4.1.1 表名规范
- 数据表名称以 TB_为前缀命名规则为“TB_模块名_表名”。
- 模块名由特征含义的单词或缩写组成,避免使用数据库关键词。
- 命名一律为大写字母。
- 如果用来存储历史资料,命名以 HISTORY 结尾。
- 如果用来存储日志资料,命名以 LOG 结尾。
- 如果用来存储类别资料,命名以 TYPE 结尾。
### 4.1.2 表分区
- 表分区命名带有 P 字段命名规则为“TB_模块名_表名_分区名”。
- 分区名必须有特定含义的单词或字串。
- 子分区命名父分区名_SP_区域缩写根据实际情况进行组合。
### 4.1.3 字段名
- 字段名全部采用英文单词如有多个单词则单词之间需用“_”隔开。
### 4.1.4 主键
- 主键名称以 PK_为前缀命名规则为“PK_表名(或缩写)_XXXX”。
- XXXX 以数字或者英文字母表示,应明确表达出代表含义。
- 主键定义需要在数据库设计文档中体现出来。
### 4.1.5 外键名
- 表外键名称以 FK_为前缀命名规则为“FK_表名(或缩写)_主表名(或缩写)_XXXX”。
- XXXX 以数字或者英文字母表示,应明确表达出代表含义。
- 外键定义需要在数据字典中体现出来。
## 4.2 索引
### 4.2.1 普通索引
- 普通索引以 IDX_为前缀命名规则为“IDX_表名_字段名(或缩写)”。
- 若索引由多个字段组成则命名为“IDX_表名_字段名(或缩写)1_字段名(或缩写)2_......”。
- 若索引长度超限则命名规则为“IDX_表名_序号”。
- 每个索引名称与其对应的字段名需要在数据字典中明确体现。
### 4.2.2 主键索引
- 主键索引应与主键约束名相同,或 IDX_主键约束名。
### 4.2.3 唯一索引
- 唯一索引以 IDX_UNI_为前缀名称规则为“IDX_UNI_表名_数字”。
### 4.2.4 外键索引
- 外键索引应与外键约束名相同,或以 IDX_外键约束名。
### 4.2.5 函数索引
- 函数索引以 IDX_FUNC_为前缀名称规则为“IDX_FUNC_表名_数字”。
## 4.3 视图
- 视图名称以 V_为前缀命名规则为“V_模块名_视图名”规则与表的命名类似。
## 4.4 实体化视图
- 实体化视图名称以 MV_为前缀命名规则为“MV_模块名_实体化视图名”规则与表的命名类似。
## 4.5 存储过程
- 存储过程名称以 SP_为前缀命名规则为“SP_过程名称(缩写)”,名称能表达实际含义的英文单词或缩写,多个单词需用下划线分割各个组成部分。
## 4.6 触发器
- 触发器名称以 TR_为前缀命名规则为“TR_表名_触发器名”。
## 4.7 函数
- 函数名称以 FUN_为前缀命名规则为“FUN_功能名称”按业务操作命名存储过程。
## 4.8 序列
- 序列名称以 SEQ_为前缀命名规则为“SEQ_表名_数字”。
# 5 数据库应用设计
## 5.1 数据库用户
### 5.1.1 用户权限控制原则
- 坚持最小化权限分配原则。
- 每个业务用户不得授予 DBA 角色。
- 取消系统权限时,必须征询开发者的意见。
### 5.1.2 用户及其权限规范
- 根据数据库管理、数据维护、开发、功能等方面将数据库用户分为数据库管理员DBA、数据库属主DATA OWNER、交易用户Transaction、监控用户Monitor、其他用户Other
## 5.2 数据库 schema 设计规则
- 应用设计表,不能包含于 public schema 中。
- 创建与数据库属主用户同名 schema所有者为数据库属主用户应用设计表全部包含于此 schema 中。
- 对于其它用户对数据库属主用户同名 schema 只有 USAGE 权限。
## 5.3 数据库分库设计
- 应用系统的业务功能和数据处理流程匹配,数据结构设计层次清晰,结合总体应用逻辑架构设计进行各子系统数据库的分库设计,最终获得数据库与应用的架构清晰、性能优化的综合要求。
- 应用系统在设计时,应根据应用特点、应用场景、单库的处理能力、单库的数据容量,确定数据库分库的数量。
- 分库规则应语义明确清晰,如按客户号、签约协议号、商家代码或业务代码等。根据指定的分库键,将数据表进行拆分,通过应用或在数据访问中间件上配置规则,实现数据路由。
- 数据库分库设计遵循如下原则:
- 需分库的数据,原则上不能有冗余。
- 分库的算法不能存放在所分库的任何其中一个库中。
- 通过应用服务或数据库访问中间件支持分库访问。
## 5.4 数据库表设计规则
- 遵循应用设计规范,保证数据的完整性与正确性。
- 开发时应遵循数据模型设计要求。
- 使用统一的命名规则,数据库对象能表达出明确的含义。
- 开发时应考虑程序的优化设计。
- 表设计时不建议创建外键约束。
- 除配置信息外,业务表建议预留 3-5 个冗余字段及时间戳字段。
- 根据业务场景选择表字段个数,宽表字段个数建议不超过 200窄表建议不超过 50。
## 5.5 数据库表分区设计
### 5.5.1 表分区的目标
- 提高访问表的性能。
- 增强表的管理和维护性。
### 5.5.2 表分区基本原则
- 依据数据模型的大小进行分区设计。
- 依据应用处理功能需求进行分区设计。
- 依据提高并行处理性能进行分区。
- 依据数据访问特性进行分区设计。
- 依据数据维护可行性、方便性需求进行分区设计。
### 5.5.3 分区表的使用
- 建议使用 PostgreSQL 自带的分区表功能。
## 5.6 数据库长连接
- 建议从应用层面解决数据库长连接耗内存的问题:间隔一段时间或处理了一定笔数的交易后主动断开数据库长连接重连;使用数据库连接池的,可在 datasource_releaseconnection()扩展下功能,定时或达到一定计数后断链重连。
## 5.7 游标的使用
- 由于游标的效率较低,如果在代码中可以通过不同的条件分支实现的逻辑,尽量不要使用游标。
- 动态游标通常都可以转化为若干个静态游标,除非必要,尽量使用静态游标代替动态游标。
- 应采用如下顺序使用游标DECLARE 声明游标、OPEN 打开游标、操作游标、关闭游标,使用 FETCH、MOVE、UPDATE 或 DELETE 语句操作游标。
- 如果使用 UPDATE WHERE CURRENT OF 或 DELETE WHERE CURRENT OF 语句更新或删除行时,游标在定义时应定义为更新游标。更新游标应结合 SAVEPOINT 语句提供更新失败后事务回滚点。
## 5.8 查询事务
- 在 PostgreSQL所有的 SQL 操作都在事务中,包括 SELECT 语句,在 SELECT 操作后如果长时间没有事务提交或事务回滚就存在查询事务查询事务过多将占有数据库服务器的资源PostgreSQL 数据库监控软件会监控到这一现象从而引发告警。建议应用开发时,考虑查询事务,及时将事务提交或回滚。
## 5.9 批量处理、批转联处理
- 批量处理、批转联处理须避免大事务,批转联须进行并发度控制。
- 单次任务处理原则上不应超过 5000 笔。
- 单次任务调用或数据库单事务提交的明细数量,原则上不应超过 5000 笔。
## 5.10 索引设计规范
- 在 PostgreSQL 中,索引是提高查询性能的重要工具,但过多的索引会增加磁盘空间的需求,并且在数据写入时也会增加索引维护的开销。因此,应该根据实际需要合理使用索引。只有在查询频繁且对性能有显著提升的情况下才考虑创建索引。在使用过程中索引也需要定期维护和更新,当表数据发生频繁变化时,可以定期更新索引,也可以使用 VACUUM 命令进行表碎片整理,以减少索引的碎片并提高性能。对不再使用的索引可以定期检查并删除以提高查询性能。
### 5.10.1 索引主要两种形式
- 主键和唯一约束对于主键和唯一约束PostgreSQL 会自动创建一个唯一索引以确保数据的唯一性。
- 多列索引PostgreSQL 支持基于多个字段的索引,即多列索引(复合索引),从而提高复杂查询的性能。默认情况下,一个多列索引最多可以使用 32 个字段。
### 5.10.2 索引设计原则
- 列名一致性:多表中的相同列应保证列名一致,数据类型也需保持一致。
- 字段大小限制btree 索引字段不建议超过 2000 字节。如果有超过 2000 字节的字段需要建索引,建议使用函数索引(例如哈希值索引),或者使用分词索引。
- 外键索引:如果使用的 PostgreSQL 版本没有自动建立外键的索引,则需要手工为 foreign key 建立索引,否则可能影响 references 列的更新或删除性能。原则上尽量少使用或不使用外键索引。
- 避免冗余索引:不要重复创建功能相同的索引,例如已经存在的联合索引(a, b, c)就涵盖了(a)、(a, b)的功能,可降低存储和维护成本。
- 索引覆盖与更新频率:设计单个索引尽可能覆盖更多的 SQL 语句,同时对于更新频繁的表要控制索引的数量,以避免过度的维护开销,对于频繁更新的列,原则上不建议在该列建立索引,因为频繁的更新操作可能会导致索引维护成本增加甚至是索引失效,从而降低索引的效率。
- 索引数量及列的空值问题:单张表的索引数量尽量控制在 5 个以内,且确保索引列不含 NULL 值,因为 NULL 值很难用于优化查询。
- 区分度与字段顺序:选择区分度高的列建立索引,通常将用作查询条件、连接操作或排序的列作为创建索引的候选列。在组合索引中将区分度最高的字段放在前面,以提高效率。部分索引和在线索引:根据业务场景需要可建立包含满足特定条件的部分索引设定用于优化特定条件下的查询性能,对于生产环境在不锁定写入的情况下构建索引,可以采用在线创建索引的方式构建。
## 5.11 数据库和表的统计信息定期收集
- PostgreSQL 从低版本采用 pg_upgrade 方式升级到高版本后,应收集数据库统计信息。
- 数据量变化频繁的表,建议定期进行表的统计信息收集,建议增加应用系统的日终任务进行表的统计信息收集。
## 5.12 慎用存储过程
- 由于应用系统使用存储过程存在调试排错困难的问题,业务逻辑不建议使用存储过程实现,应通过应用程序逻辑实现。
## 5.13 频繁更新表
- 对于频繁更新的表,建议建表时指定表的 fillfactor=85,每页预留 15%的空间给 HOT 更新使用。
## 5.14 宽表查询
- 对于宽表,宜将前台列表查询所涉及字段外其它字段分开设计到另一张表。注意,另一张表只有在前台列表中选择单个记录时,再关联。

View File

@ -1,3 +0,0 @@
# eden-basic
后端逻辑

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,478 +0,0 @@
# JAVA 语言编码规范
## 4.1 命名规范
### 规则1.1(强制)
标识符由字母、数字、下划线和美元符($)组成,不能包含@、%和空格等特殊字符不能以下划线、数字或美元符号开始或结束不能是Java关键字区分大小写。
### 规则1.2(强制)
各种命名应使用正确的英文拼写和语法,避免拼音、混合拼写、不规范缩写、中文命名。
### 规则1.3(强制)
包名统一小写点分隔符间一个自然语义单词词意从大到小前缀顶级域名项目包名以com.psbc.开头。
### 规则1.4(强制)
类名使用UpperCamelCase命名方式简洁且富于描述。
### 规则1.5(建议)
抽象类命名建议Abstract开头异常类Exception结尾测试类Test结尾设计模式体现于类名。
### 规则1.6(强制)
方法名使用lowerCamelCase命名方式。
### 规则1.7(强制)
常量命名全大写,单词间下划线隔开,语义完整清楚。
### 规则1.8(建议)
布尔类型实例变量命名不使用is前缀。
### 规则1.9(强制)
参数名、实例变量、局部变量使用lowerCamelCase命名方式。
### 规则1.10(强制)
类型与中括号紧挨表示数组。
### 规则1.11(强制)
避免子父类成员变量、不同代码块局部变量相同命名。
### 规则1.12(建议)
自定义编程元素命名尽量完整单词组合。
### 规则1.13(建议)
常量、变量命名表示类型名词放词尾。
### 规则1.14(建议)
接口类方法、属性不加修饰符号简洁并加Javadoc注释接口少定义变量。
### 规则1.15(强制)
Service和DAO类基于SOA理念暴露服务是接口内部实现类用Impl后缀。
### 规则1.16(建议)
形容词接口名用形容词able结尾。
### 规则1.17(建议)
枚举类名带Enum后缀成员名称全大写单词间下划线隔开。
### 规则1.18(建议)
a) Service/DAO层方法命名
1获取单个对象用get前缀
2获取多个对象用list前缀复数结尾
3获取统计值用count前缀
4插入用save/insert前缀
5删除用remove/delete前缀
6修改用update前缀。
b) 领域模型命名:
1领域对象以xxxDO/DTO/VO命名
2避免以POJO结尾实体对象或值对象命名。
## 4.2 格式规范
### 规则2.1(强制)
源文件编码UTF-8Unix换行符。
### 规则2.2(建议)
除注释外方法签名等总行数不超过80行。
### 规则2.3(强制)
类文件组成部分顺序:文件头注释、包引入、类声明、常量、变量、构造函数、方法。
### 规则2.4(建议)
多个构造函数或同名方法应顺序在一起。
### 规则2.5(建议)
方法按功能而非作用域或访问权限分组。
### 规则2.6(强制)
引用类时禁止用*引入包全部类避免无用、重复引用不import java.lang和同包类。
### 规则2.7(强制)
Java关键字正确顺序public、protected、private、abstract、static、final、transient、volatile、synchronized、native。
### 规则2.8(建议)
避免多余关键字,接口方法属性不加修饰符号。
### 规则2.9(强制)
每行至多一条语句。
### 规则2.10(强制)
大括号使用约定:
a左大括号前不换行
b左大括号后换行
c右大括号前换行
d右大括号后有else等代码不换行终止后必须换行
e大括号内为空简洁写成{},不换行。
### 规则2.11(建议)
缩进采用4个空格多行变量声明赋值不强制等号对齐tab设为4空格宽度。
### 规则2.12(建议)
单行字符数不超过120换行原则
a第二行相对第一行缩进4空格第三行起不再缩进
b运算符与下文换行
c点符号与下文换行
d方法调用参数多需换行时在逗号后换行
e括号前不换行。
### 规则2.13(建议)
用空行分隔逻辑相关代码段,提高可读性,不插入多个空行。
## 4.3 注释规范
### 规则3.1(建议)
类、属性、方法注释使用Javadoc格式。
### 规则3.2(强制)
公共API代码必须编写JavaDoc注释类、属性、方法、参数、返回值、异常全部注释。
## 4.4 常量变量
### 规则4.1(建议)
避免使用魔法数,应定义为常量。
### 规则4.2(强制)
长整型常量后使用大写字母“L”。
### 规则4.3(建议)
浮点数类型数值后缀统一为大写D或F。
### 规则4.4(建议)
按功能或模块归类常量。
### 规则4.5(建议)
常量复用层次分为五层:跨应用、应用内、子工程内、包内、类内。
### 规则4.6(建议)
变量值在固定范围变化时用enum类型。
### 规则4.7(强制)
每行只声明一个变量。
### 规则4.8(建议)
局部变量声明时初始化。
### 规则4.9(强制)
避免一个语句中给多个变量赋相同值,不用内嵌赋值运算符。
### 规则4.10(强制)
避免局部变量覆盖上一级变量。
### 规则4.11(强制)
数组类型变量声明采用数据类型[]的方式。
## 4.5 控制语句
### 规则5.1(强制)
switch括号内为String且为外部参数时先进行null判断。
### 规则5.2(强制)
每个switch语句包含default分支且default在最后。
### 规则5.3(强制)
if/else/for/while/do语句中必须使用大括号。
### 规则5.4(建议)
避免不可达的else if和else分支。
### 规则5.5(强制)
条件表达式尽量简化。
### 规则5.6(建议)
返回表达式尽量简化。
### 规则5.7(建议)
避免在循环中重复执行相同内容。
### 规则5.8(建议)
慎用非短路逻辑运算符。
### 规则5.9(建议)
含多种运算符的表达式中使用圆括号。
### 规则5.10(建议)
三元运算符前的表达式需加圆括号。
### 规则5.11(建议)
复杂条件判断结果赋值给布尔变量。
## 4.6 面向对象
### 规则6.1(强制)
禁止用类的实例对象引用访问静态变量或方法。
### 规则6.2(强制)
覆写方法必须加@Override注解
### 规则6.3(强制)
覆写equals必须覆写hashCode。
### 规则6.4(建议)
过时接口或方法加@Deprecated注解
### 规则6.5(建议)
避免使用过时类或方法。
### 规则6.6(建议)
慎用可变参数编程。
### 规则6.7(建议)
用常量或确定有值的对象调用equals。
### 规则6.8(建议)
参数个数相同的方法重载时,避免用自动转换类型,建议用不同方法名。
### 规则6.9(建议)
使用final关键字的情况
a不允许继承的类
b不允许修改引用的域对象
c不允许重写的方法
d不允许重新赋值的局部变量
e避免重复使用变量。
### 规则6.10(建议)
慎用Object的clone方法。
### 规则6.11(强制)
类成员变量访问控制从严。
### 规则6.12(建议)
类方法访问控制从严。
### 规则6.13(强制)
构造方法中避免处理业务逻辑初始化逻辑放init方法。
### 规则6.14(强制)
RPC方法返回值和参数用包装类型。
### 规则6.15(建议)
局部变量用基本类型。
### 规则6.16(建议)
POJO类不设定属性默认值。
### 规则6.17(强制)
POJO类必须写toString方法。
### 规则6.18(强制)
POJO类禁止同时存在isXxx和getXxx方法。
### 规则6.19(建议)
setter中参数名与成员变量名一致this.成员名=参数名。getter/setter中不加业务逻辑。
### 规则6.20(强制)
序列化类新增属性时不修改serialVersionUID不兼容升级时修改。
## 4.7 数值计算
### 规则7.1(强制)
比较字符串用equals不用==或!=。
### 规则7.2(强制)
避免自动类型转换导致溢出或精度丢失。
### 规则7.3(建议)
避免高精度转低精度溢出。
### 规则7.4(强制)
禁止用BigDecimal(double)构造用String构造或valueOf。
### 规则7.5(强制)
精确计算用BigDecimal不用浮点数。
### 规则7.6(强制)
与NaN比较用isNaN(),不用==或!=。
### 规则7.7(强制)
自增自减运算符使用要产生实际影响。
### 规则7.8(强制)
BigDecimal比较用compareTo不用equals。
### 规则7.9(强制)
SimpleDateFormat非线程安全不用在并发环境。JDK8及以上用DateTimeFormatter。
### 规则7.10(强制)
日期格式化Pattern统一规范。
### 规则7.11(建议)
三目运算符注意类型对齐可能引发空指针。
## 4.8 集合规范
### 规则8.1(建议)
慎用ArrayList.subList。
### 规则8.2(建议)
慎用Arrays.asList。
### 规则8.3(强制)
集合转数组用toArray(T[] array)传入长度为0的空数组。
### 规则8.4(强制)
禁止集合中添加自身。
### 规则8.5(强制)
foreach循环中不做remove/add操作用Iterator。
### 规则8.6(建议)
参照常用Map集合K/V存储null情况。
### 规则8.7(建议)
合理利用集合的有序性和稳定性。
### 规则8.8(强制)
集合明确泛型。
### 规则8.9(建议)
判断集合元素是否为空用isEmpty不用size()==0。
### 规则8.10(强制)
toMap()方法必须用带mergeFunction的。
### 规则8.11(强制)
toMap()方法中value为NULL时处理。
### 规则8.12(强制)
不可对keySet()/values()/entrySet()返回集合做增删。
### 规则8.13(强制)
排序时Comparator要满足三个条件。
## 4.9 线程安全
### 规则9.1(建议)
单例获取保证线程安全,推荐饿汉模式。
### 规则9.2(建议)
并发访问的单例禁止有状态成员变量。
### 规则9.3(建议)
创建线程或线程池指定有意义名称。
### 规则9.4(建议)
多个资源加锁保持一致顺序。
### 规则9.5(强制)
CountDownLatch使用确保countDown被执行。
### 规则9.6(强制)
锁在finally中释放。
### 规则9.7(强制)
同步代码中不用public属性。
### 规则9.8(强制)
占用锁时不调用Thread.sleep。
### 规则9.9(建议)
并发场景用线程安全集合。
### 规则9.10(强制)
线程资源通过线程池提供,不显式创建。
### 规则9.11(建议)
线程池用ThreadPoolExecutor不用Executors。
### 规则9.12(建议)
高并发避免用“等于”判断,用区间判断。
## 4.10 异常处理
### 规则10.1(强制)
可通过预检查避免的RuntimeException不捕获。
### 规则10.2(建议)
不用异常做流程控制。
### 规则10.3(建议)
finally中不使用return。
### 规则10.4(强制)
非稳定代码catch区分异常类型。
### 规则10.5(强制)
捕获异常要处理,不抛弃,最外层业务使用者处理异常。
### 规则10.6(强制)
try在事务代码中catch后手动回滚事务。
### 规则10.7(强制)
finally中关闭资源JDK7+用try-with-resources。
### 规则10.8(建议)
捕获与抛出异常类型匹配。
### 规则10.9(建议)
捕获异常区分Exception和Error。
## 4.11 日志规范
### 规则11.1(强制)
应用用日志框架SLF4JAPI不用直接用Log4j、Logback API。
### 规则11.2(强制)
禁止用System.out.print等方法用标准日志框架。
### 规则11.3(建议)
日志用占位符,不用“+”拼接。
### 规则11.4(建议)
日志级别分为FATAL、ERROR、WARN、INFO、DEBUG各级别使用建议。
### 规则11.5(建议)
info/debug日志进行开关判断。
### 规则11.6(强制)
日志中用户敏感信息脱敏。
### 规则11.7(建议)
日志打印参数中避免复杂表达式。
## 4.12 性能相关
### 规则13.1(建议)
同步调用优先用无锁数据结构,锁代码块尽可能小。
### 规则13.2(建议)
避免频繁创建可复用资源。
### 规则13.3(建议)
避免创建不必要的对象。
### 规则13.4(建议)
避免变量重复计算。
### 规则13.5(建议)
try/catch放在循环外层。
### 规则13.6(建议)
池化重量级资源。
### 规则13.7(建议)
遍历Map用entrySetJDK8+用Map.foreach。
### 规则13.8(建议)
用Set去重不用List的contains。
### 规则13.9(建议)
慎用递归,必须时保证出口可执行。
### 规则13.10(建议)
用观察者模式替换无限轮询。
### 规则13.11(建议)
缓存不常变化数据。
### 规则13.12(建议)
字符串拼接用StringBuilder或StringBuffer。
### 规则13.13(建议)
数组拷贝用System.arraycopy。
### 规则13.14(建议)
合理使用单例。
### 规则13.15(建议)
避免随意用静态变量。
### 规则13.16(建议)
未发生线程安全时用HashMap、ArrayList。
### 规则13.17(建议)
避免用二维数组。
### 规则13.18(建议)
合理使用线性表和链表。
### 规则13.19(建议)
避免非常大的内存分配。
### 规则13.20(建议)
正则表达式编译后复用。

View File

@ -1,178 +0,0 @@
## 4.1 SQL 编写
- **规则 1建议**:在编写 SQL 语句时,每行建议不要超过 80 字符,基于列对齐原则,应采用下一行缩进。
- **规则 2建议**:使用两个空格表示缩进,同层次的语句应保持纵向对齐。
- **规则 3建议**:建议使用预编译 SQLPreparedStatement预编译语句既可以提高性能又可以有效防止 SQL 注入。
## 4.2 查询语句规范
- **规则 4建议**:尽量避免使用 COUNT(列名)来替代 COUNT(*)COUNT(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。COUNT(*)会统计值为 NULL 的行,而 COUNT(列名)不会统计此列为 NULL 值的行。
- **规则 5强制**:禁止使用 SELECT * 操作,所有查询操作必须明确指定列名,避免查询过多无用的列。
- **规则 6强制**:禁止使用不带 WHERE 条件的 DELETE、UPDATE 语句操作全表,且要使用索引列操作,避免锁表。对于需要客户端传参来创建 SQL 的场景,必须要对参数进行非空判断,确保 SQL 含有 WHERE 条件。
- **规则 7强制**:对于同时使用包括 AND 和 OR 的复合查询语句,必须增加必要的括号,以保证查询优先级的准确性。
- **规则 8建议**:使用 IN 查询代替 OR。如使用 IN 查询,需要控制 IN 后边的集合元素数量,不宜过多,需控制在 1000 个之内。
- **规则 9建议**:建议以合理的方式使用分页查询,使用 LIMIT、OFFSET 查询时,页数越大,需要扫描的数据量越大,查询就越慢。
- **规则 10建议**:分库分表场景下,尽量避免进行聚合查询,建议 SQL 语句均加上分片键。若
有聚合查询的需求,可单独通过聚合库实现。
## 4.3 索引规范
- **规则 11强制**:禁止隐式类型转换,对于字段是字符串类型的,查询必须加引号。
- **规则 12强制**:禁止索引列使用函数运算或者使用表达式,否则会导致索引失效。
- **规则 13强制**:索引列使用 LIKE 模糊查询时,禁止使用%前导查询。
- **规则 14强制**:联合索引查询要遵守最左匹配原则。
- **规则 15建议**:尽量避免对索引列使用负向查询,例如 NOT IN、!=、NOT LIKE、<>,否则可能导致索引失效。
- **规则 16建议**:合理使用覆盖索引,可减少 IO 次数,有效避免回表查询引起的性能问题。
- **规则 17建议**:尽量避免全表扫描,需在生产环境执行的 SQL 原则上都应被索引,如 UPDATE、DELETE 的 WHERE 条件列、ORDER BY、GROUP BY、DISTINCT 字段、多表 JOIN 字段。
## 4.4 联结表规范
- **规则 18强制**JOIN 联查时必须使用索引列联查,且索引列的类型要保持一致。
- **规则 19建议**:在进行 JOIN 联查时,联查的表数量尽量控制在 3 张表以内,过多的表联查会严重影响性能。
- **规则 20建议**:在进行多表查询时,要用 JOIN 代替子查询,子查询的性能相比于 JOIN 联查较差。
- **规则 21建议**JOIN 联查时,如果两个表的编码不一致,也有可能导致索引失效,因此要尽量确保联查表的编码方式相同。
## 4.5 分组与排序
- **规则 22建议**:对查询结果顺序无要求时,不要使用 ORDER BY 进行排序,在需要排序的场景下尽量避免使用非索引列进行 ORDER BY 排序。
- **规则 23建议**:避免冗余的排序,使用 GROUP BY 时,默认会进行排序,如果不需要排序,可以使用 ORDER BY NULL。
- **规则 24建议**:避免使用 DISTINCT可以使用 GROUP BY 代替,如果使用 DISTINCT 获取多列名称时,会对多列进行重复行去重,而不是单独对某一列去重。
- **规则 25建议**:由于 UNION 默认会对结果集进行排序、去重,因此在性能上要比 UNION ALL 差一些。如果结果集没有重复行或者不需要进行重复行合并,建议使用 UNION ALL。
## 4.6 事务处理
- **规则 26建议**:避免大事务(执行时间较长的事务),如耗时的操作、一次性操作的数据过多,数据库单事务提交的明细数量原则上不应超过 5000 笔。大事务可能会导致数据库出现大量的阻塞和锁等待的现象,同时在并发较高的场景下,会造成客户端连接池耗尽、主从延迟等问题。建议将大事务拆分成小事务,分多次操作。
- **规则 27强制**:禁止使用 INSERT INTO B SELECT * FROM A 语句,该语句当执行异常时会出现数据不一致的问题,同时可能会对原表 A 加表锁。
- **规则 28建议**:事务操作,注意获取资源的顺序,避免出现死锁的情况。
## 4.7 其他
- **规则 29建议**:获取大量数据时,建议分批次获取数据,每次数据量应小于 500 条,结果集应小于 1M。
- **规则 30建议**:大批量更新数据时,索引维护成本很高,因此大批量数据更新建议拆分出小粒度,分多次更新。
- **规则 31强制**:当某一列的值全是 NULL 时count(col)的返回结果为 0但 sum(col)的返回结果为 NULL因此使用 sum()时要注意空指针异常问题。
- **规则 32强制**:禁止使用外键与级联更新,可通过应用程序来约束。级联更新是强阻塞,存在数据库更新风暴的风险,使用数据库外键可能出现死锁,同时影响数据库的插入速度。
- **规则 33建议**SQL 中避免出现 random()、sysdate()等不确定结果的函数。
- **规则 34强制**:必须使用 IS NULL 来判断是否是 NULL 值。
- **规则 35建议**:谨慎使用存储过程,存储过程难以调试和扩展,不具有移植性。
# 6 SQL 语句编写规则
## 6.1 查询语句索引使用原则(建议)
- 查询列、排序列尽量使用索引列并与其次序保持一致。
- 应避免在 WHERE 子句中对索引列使用 IS NULLIS NOT NULL。
- 应避免在 WHERE 子句中对索引列使用 LIKE %xxx%、LIKE %xxx可以使用 LIKE xxx%’。
- 对于复合索引WHERE 子句中包含索引的第一列查询效率最高。
- 表结构中字段定义的数据类型与应用程序中的定义保持一致,避免隐式/显式转换情况发生。
- WHERE 子句中表字段尽量不参加计算。
- 建议使用||连接字符串,不使用 CONCAT 函数CONCAT 函数不走索引。
- WHERE 子句索引列上尽量不用 NOT、OR、!=、<>。
## 6.2 查询语句排序原则(建议)
- 应尽量减少 ORDER BY 排序操作。
- 如果一定要排序,应尽量建立在索引列上,且 NULLS FIRST 或 NULLS LAST 与索引 NULLS FIRST 或 NULLS LAST 一致。
- 如果业务规则允许结果集不需要唯一确定或者各子查询的结果集没有重复,应使用 UNION ALL 替代 UNION。
- ORDER BY 明确指定列名。
## 6.3 使用 case when 减少数据库访问次数(建议)
- 合并一条语句,减少对数据库的多次访问。
## 6.4 使用 EXIST 代替 IN 语句(建议)
- 使用 EXIST 代替 IN 可能会提高性能,但并非所有情况都适用。具体要依据测试结果而定。
## 6.5 使用 NOT EXIST 或外连接代替 NOT IN 语句(建议)
- 使用 NOT EXIST 或外连接代替 NOT IN 可能会提高性能,但并非所有情况都适用。具体要依据测试结果而定。
- 注意 NOT IN 与 NOT EXISTS 不一定相等,需注意对空值的处理。
## 6.6 使用表连接替代 EXIST 子句(建议)
- 一般情况下,使用表连接替代 EXIST 子句可提高性能。
## 6.7 使用 EXIST 子句替代 DISTINCT 子句。(建议)
- 一般情况下,使用 EXIST 子句替代 DISTINCT 子句可提高性能。
## 6.8 慎用 with 子查询(建议)
- WITH语句Common Table Expressions CTEs 通用表达式,定义只在查询中存在临时表,占用一定的资源。
## 6.9 日期格式明确化 (建议)
- 日期格式应明确化。
## 6.10 列表查询(建议)
- 应当控制前台列表查询输出项个数,不要输出太多字段,需要查看更多字段时,从前台列表中选择单个记录,再显示详细信息,列表查询输出项所有字段所涉及表,建议控制在 3 个表以内,除 SELECT 字段列表内所涉及表外,其它表采用动态 SQL 方式。
## 6.11 增加查询过滤条件(建议)
- 建议添加时间、机构维度的 WHERE 过滤条件,例如:流水类数据、消息提醒等,提高查询性能。
- 在不改变查询结果的前提下,增加查询条件中的索引字段。没有索引字段的建议选择性大的字段放前面。
## 6.12 避免用*返回查询结果(强制)
- 任何地方都不要使用 SELECT * FROM t ,用具体的字段列表代表*,不要返回用不到的任何字段,另外表结构发生变化也容易出现问题。
## 6.13 避免使用 delete 全表(强制)
- 不要使用 DELETE 全表,性能很差,请使用 TRUNCATE 代替。
## 6.14 null 值的处理(强制)
- 注意 NULL 值的处理,对于数值计算,使用 COALESCE(value [, ...])函数为 NULL 值提供默认值 0。
## 6.15 表别名和列别名的命名(强制)
- 表别名,列别名命名时,尽量按照原表名和列名的省略缩写形式,保持 SQL 的可读性。
## 6.16 插入列明确指定(强制)
- 插入列时应明确指定列名。
## 6.17 查询事务(强制)
- 对于基于查询结果判断后续操作的,应在同一事务内,且查询使用 FOR UPDATE 进行查询加锁。
- 在查询事务中,应避免写复杂 SQL可将复杂 SQL 拆分成多个小 SQL 来完成对应查询。
## 6.18 批量插入(建议)
- 批量插入可以显著减少与数据库的通信开销提高数据库性能并降低事务处理的复杂性INSERT INTO ... VALUES 语句可以一次插入多行数据。采用批量插入 SQL 语句时,单次建议不超过 1000 条。
## 6.19 多表联合查询(建议)
- 多表查询建议使用 JOIN 关键字进行多表关联查询,而不是使用子查询,要根据需求选择 JOIN 方式,并确保所有连接操作都有明确的连接条件,连接条件中的列有适当的索引,尽量控制关联的表数量在 3 个以内,同时建议将记录数较少的表放在前面,可以减少中间结果集的大小,从而提高查询效率。
- 对于多表关联查询时返回大量数据的情况,应通过定义合适的查询条件尽量控制返回结果集的大小,只获取需要的数据。
## 6.20 分页查询(建议)
- 应该尽量避免向客户端返回大数据量,若因业务需要获取大量数据时,建议不要一次性全量获取,而是分批次获取数据。每次获取的数据量应小于 500 条,结果集应小于 1M。
- 建议使用合理的分页方式以提高分页效率,分页时尽量避免直接使用 LIMIT OFFSET否则可能数据越靠后查询越慢可以考虑使用游标分页或基于主键的分页或者加上一些 WHERE 限制条件进行分页查询。
## 6.21 COMMENT 格式规范
### 6.21.1 表 COMMENT建议
- 数据库表应采用 Comment 进行注释。在注释中应明确表的中文名称。
- 语法COMMENT ON TABLE table_name IS string_literal;
- string_literal 格式:表中文名
- string_literal 为必填项。
### 6.21.2 字段 COMMENT建议
- 数据库表的字段应采用Comment进行注释。在注释中应明确字段的中文名称、数据标准、数据质量等信息。
- 语法COMMENT ON COLUMN table_name.column_name ISstring_literal;
- string_literal 格式:名称:字段中文名。标准:数据项编号|不贯标原因。质量:数据质量规则编号|无。
- string_literal 为必填项,包括字段名称、数据标准、数据质量三个必填部分,分别以“名称:”、“标准:”、“质量:”开头,以“。”结尾。其中标准部分,若已贯标,请填写引用的企业级数据字典数据项编号;若未贯标,请填写不贯标原因。质量部分,若匹配了企业级数据质量规则,请填写数据质量规则编号,多条规则间采用半角“,”分隔;若未匹配,请填写“无”。

View File

@ -1,153 +0,0 @@
# 工程生成规范
### 角色定位
你是专业软件工程师结合Claude-3.7特性完成Vue前端和Java后端开发。
### 输入信息
- 《Sql表结构.sql》含系统现有表结构确保代码与之兼容。
- 《sql初始化脚本.sql》大模型依《需求规范文档.md》创建的新表。
- 《SQL语言编码规则.md》此文档是SQL编码的核心准则在编写任何SQL语句时必须严格遵守其中的所有规则特别是“强制性规则”具有最高优先级。
- 《Java编码规范.md》此文档是Java编码的核心准则在编写Java代码时必须严格遵守其中的所有规则特别是“强制性规则”具有最高优先级。
- 《需求规范文档.md》数据建模和工程生成的核心依据详细描述了系统的功能需求、业务规则、操作权限等信息。在进行数据建模和工程生成时应仔细研读此文档确保生成的代码符合文档中的要求。。
## 技术栈
- Java 8类库丰富性能稳定Lambda和Stream API提高开发效率。
- Spring Boot 2.7.18简化Spring开发提供自动配置和嵌入式服务器。
- MyBatis 3.5.9持久层框架支持定制SQL和高级映射。
- Vue 2.6:轻量级前端框架,响应式数据绑定和组件化开发。
- PostgreSQL 14.7:开源关系型数据库,支持复杂查询和高并发。
- Redis 7.0.15:键值对数据库,用于缓存和会话管理。
- Druid 1.2.8数据库连接池提供监控和防御SQL注入功能。
- ShardingSphere 5.4.1:分布式数据库中间件,支持数据分片和读写分离。
## 架构规范
### 分层架构
分层架构的设计理念是将不同功能模块分离,提高代码的可维护性和可扩展性。以下是各层的详细说明:
1. **实体层**
- 继承数据建模结果中的字段定义:确保实体类与数据库表结构一致,方便数据的映射和操作。
- 包含JSR303校验注解@NotBlank/@Pattern):对实体类的字段进行数据校验,提高数据的准确性和完整性。
- 审计字段:@CreateBy/@LastModifiedDate:记录数据的创建和修改信息,方便数据的追溯和管理。
2. **Mapper层**负责数据库操作的接口层将Java方法与SQL语句进行映射。
- XML文件需包含<sql id="Base_Column_List">定义基础列名提高SQL语句的复用性。
- 使用注解对mapper方法入参校验确保传入参数的合法性避免SQL注入等安全问题。
- 分页查询使用PageHelper插件简化分页查询的实现提高开发效率。
- 多园区数据隔离通过@TenantId实现:实现不同园区数据的隔离,提高数据的安全性。
- 多表关联需使用@Results/@ResultMap:处理多表关联查询的结果映射,提高数据的准确性。
- 复杂查询需使用@SelectProvider:灵活处理复杂查询,提高查询的性能。
- 多表关联批量更新需使用@UpdateProvider:提高多表关联批量更新的效率。
- 多表关联批量删除需使用@DeleteProvider:提高多表关联批量删除的效率。
- 外键字段需使用@TableField(exist=false)标记:避免在实体类中映射外键字段,减少数据冗余。
3. **Service层**处理业务逻辑的核心层调用Mapper层的方法完成具体业务操作。
- 业务方法需@Transactional:确保业务操作的原子性,保证数据的一致性。
- 使用策略模式实现业务扩展点:提高代码的可扩展性,方便业务的扩展和维护。
- 异常处理统一在@ServiceAdvice中处理集中处理API层的异常提高代码的可维护性。
4. **Controller层**负责接收客户端请求调用Service层的方法处理请求并返回响应结果。
- 统一返回Result<T>包装类:规范接口返回数据的格式,提高接口的可读性和可维护性。
- API路径遵循/v1/模块名/功能名遵循RESTful API设计原则提高接口的规范性和可维护性。
- 使用@Validated进行参数校验:确保传入参数的合法性,避免因参数错误导致的业务异常。
- 分页查询使用Pageable接口方便实现分页查询功能提高用户体验。
5. **Api层**对外提供服务接口将业务逻辑封装成API供外部系统调用。
- 统一返回Result<T>包装类
- API路径遵循/v1/模块名/功能名
- 使用@Validated进行参数校验
### Vue架构
Vue架构采用组件化开发的思想提高代码的可复用性和可维护性。以下是组件规范和接口调用的详细说明
1. **组件规范**
- 使用Composition API写法提高代码的逻辑复用性和可维护性方便组件的组合和拆分。
- 状态管理采用Pinia轻量级的状态管理库具有响应式数据和模块化设计的特点方便管理组件间的状态。
- 全局组件存放在/src/components/common将全局组件统一管理提高组件的复用性。
2. **接口调用**负责与后端API进行数据交互获取和更新数据。
- axios实例配置请求拦截器在请求发送前进行统一处理如添加请求头、处理请求参数等。
- API模块按功能域划分将不同功能的API接口进行分组管理提高代码的可维护性。
- 错误处理统一在响应拦截器:集中处理接口调用的错误,提高用户体验。
## 设计要求
### UI/UX
- 全屏响应式,适配不同设备。
- 支持亮色和夜间模式用CSS变量和JavaScript实现切换。
- 现代化、简洁界面,用清晰字体和图标。
- 色彩丰富且和谐,可用搭配工具选色。
- 流畅交互动画,如按钮点击动画。
- 按钮等地方添加图标,确保与文字一致。
- 参考苹果官网设计美学。
### 后端
- 遵循RESTful仅用POST方法。
- 采用合理的软件设计模式(如策略模式、工厂模式等)保证模块功能可扩展性和可维护性。
- Controller和Mapper层实现错误处理和输入验证确保接口的健壮性。
- 用线程池处理异步操作,提高系统的性能和响应速度。
- 关键步骤添加详细注释,提高代码的可读性和可维护性。
- 确保Java实体类字段和数据库表结构的含义一致。
- 注意代码注释和规范。
- 确保API接口正确安全。
- 注意异常处理和日志记录:在代码中添加异常处理和日志记录逻辑,确保系统的稳定性和可维护性。
- 代码质量可读性高遵循SOLID原则性能达标可扩展性强可测试性好可部署性好可复用性高。
• 可读性使用有意义的命名保持方法长度≤50行
• 可维护性遵循SOLID原则圈复杂度≤5
• 性能查询响应时间≤500msJVM内存占用≤2GB
• 可扩展性:使用策略模式实现核心业务扩展点
• 可测试性单元测试覆盖率≥90%,包含边界条件测试
• 可部署性:确保代码可以在不同的环境中正常部署和运行。
• 可复用性:编写可复用的代码,提高代码的开发效率和可维护性。
### 总结思考要求
1. **规范违背情况**在建模过程中若发现有违背《SQL语言编码规则.md》、《Java编码规范.md》的规则或要求的地方请详细指出并说明原因和提供具体的改进建议。
2. **需求实现情况统计**:明确指出实现了《需求规范文档.md》哪些功能还有哪些功能没有实现。
3. **设计原则与特点**:详细阐述设计遵循的原则和设计特点,同时深入分析结果存在的不足,并提出切实可行的改进措施。例如,分析系统的架构设计是否符合分层架构的原则,是否存在性能瓶颈等问题,并提出相应的改进建议。
## 执行流程
1. 在03工程目录执行`mvn archetype:generate`创建工程。
2. 用`npm create vue@latest`初始化前端工程。
3. 暂不生成用户权限管理代码。
4. 开发顺序Entity -> Mapper -> Service -> Controller -> Api -> Vue页面。
5. 输出Api层接口文档生成UT单元测试用例。
6. 启动前后端服务,验证接口功能,确认是否满足需求文档的要求。
7. 自动执行“auto - run”模式成功则继续失败则尝试不同方法多次失败暂停。执行中记录日志。
## 环境配置
```yaml
spring:
datasource:
url: jdbc:postgresql://1.14.121.39:5432/edendb
username: edenuser
password: edenpswd
redis:
host: 1.14.121.39
password: edenpswd
```
## 代码生成模板
```java
// 实体类示例
@Getter
@Setter
public class User {
@NotBlank(message="用户名不能为空")
private String username;
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
}
// Mapper示例
@SelectProvider(type = UserSqlProvider.class, method = "queryByCondition")
Page<User> selectByCondition(@Param("condition") UserQuery condition);
// Service示例
@Transactional
public PageInfo<User> pagingQuery(UserQuery query) {
PageHelper.startPage(query.getPageNum(), query.getPageSize());
return new PageInfo<>(userMapper.selectByCondition(query));
}
// Controller示例
@RestController
@RequestMapping("/v1/user")
public class UserController {
@PostMapping("/create")
public Result<Long> createUser(@Valid @RequestBody UserCreateDTO dto) {
return Result.success(userService.create(dto));
}
}
```

View File

@ -1,56 +0,0 @@
# 1.功能描述
# 2.业务规则
# 3.操作权限及授权
# 4.功能场景描述
## 4.1界面描述(输入和输出)
<font color='red'>备注:使用表格或者前端页面进行描述</font>
## 4.2业务流程
<font color='red'>**备注:**把图片格式的流程图转换成Mermaid语法描述</font>
```mermaid
graph TD
A[开始] --> B[发送取文件通知消息]
B --> C[接收响应信息]
C --> D[结束]
```
## 4.3处理过程
<font color='red'>备注:处理过程基于流程图来描述。</font>
## 4.4异常处理
# 5.外部接口
| 外系统名称 | 接口名称 | 说明 |
| ---------- | -------- | ---- |
| Comm-mid | 文件下载 | |
| 文件上传 | | |
# 6.特殊事项
| 测试要点 | 要点说明 |
| ----------------- | ------------------------------------------------------------ |
| 文件上传 | 文件成功上传,请求系统收到正确的消息通知 |
| 文件下载 | 文件下载成功,请求系统收到正确文件下载内容 |
| 文件下载失败 | 返回comm-mid的响应结果 |
| 文件上传失败 | 返回comm-mid的响应结果 |
| 文件上传-文件监控 | 查询日志监控是否推送成功或者失败,根据流水号在监控查询是否存在 |

Binary file not shown.

View File

@ -1,3 +0,0 @@
# eden-basic
后端逻辑

2
asset-management/.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,2 @@
# Default ignored files
/workspace.xml

19
asset-management/.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="asset-management" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="asset-management" options="-parameters" />
</option>
</component>
</project>

7
asset-management/.idea/encodings.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
</component>
</project>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="rdc-releases" />
<option name="name" value="rdc-releases" />
<option name="url" value="http://mirrors.cloud.tencent.com/nexus/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="edas-oss-central" />
<option name="name" value="taobao mirror central" />
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="rdc-snapshots" />
<option name="name" value="rdc-snapshots" />
<option name="url" value="http://mirrors.cloud.tencent.com/nexus/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="public" />
<option name="name" value="public" />
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
</component>
</project>

View File

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: jakarta.activation:jakarta.activation-api:1.2.2">
<CLASSES>
<root url="jar://$USER_HOME$/.m2/repository/jakarta/activation/jakarta.activation-api/1.2.2/jakarta.activation-api-1.2.2.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$USER_HOME$/.m2/repository/jakarta/activation/jakarta.activation-api/1.2.2/jakarta.activation-api-1.2.2-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$USER_HOME$/.m2/repository/jakarta/activation/jakarta.activation-api/1.2.2/jakarta.activation-api-1.2.2-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3">
<CLASSES>
<root url="jar://$USER_HOME$/.m2/repository/jakarta/xml/bind/jakarta.xml.bind-api/2.3.3/jakarta.xml.bind-api-2.3.3.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$USER_HOME$/.m2/repository/jakarta/xml/bind/jakarta.xml.bind-api/2.3.3/jakarta.xml.bind-api-2.3.3-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$USER_HOME$/.m2/repository/jakarta/xml/bind/jakarta.xml.bind-api/2.3.3/jakarta.xml.bind-api-2.3.3-sources.jar!/" />
</SOURCES>
</library>
</component>

14
asset-management/.idea/misc.xml generated Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/classes" />
</component>
</project>

8
asset-management/.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/asset-management.iml" filepath="$PROJECT_DIR$/asset-management.iml" />
</modules>
</component>
</project>

124
asset-management/.idea/uiDesigner.xml generated Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

7
asset-management/.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="web" name="Web">
<configuration>
<webroots />
</configuration>
</facet>
<facet type="Spring" name="Spring">
<configuration />
</facet>
</component>
</module>

128
asset-management/pom.xml Normal file
View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.eden</groupId>
<artifactId>asset-management</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mybatis-plus.version>3.4.3.4</mybatis-plus.version>
<pagehelper.version>1.4.1</pagehelper.version>
<postgresql.version>42.2.23</postgresql.version>
<lombok.version>1.18.20</lombok.version>
<validation.version>2.0.1.Final</validation.version>
<aspectj.version>1.9.7</aspectj.version>
<poi.version>5.0.0</poi.version>
</properties>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- PageHelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.version}</version>
</dependency>
<!-- PostgreSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<!-- Apache POI for Excel -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<!-- Validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${validation.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,19 @@
package com.psbc.cpsp.eden;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* 资产管理系统启动类
*
* @author eden
*/
@SpringBootApplication
@EnableTransactionManagement
@org.mybatis.spring.annotation.MapperScan("com.psbc.cpsp.eden.mapper")
public class AssetManagementApplication {
public static void main(String[] args) {
SpringApplication.run(AssetManagementApplication.class, args);
}
}

View File

@ -0,0 +1,19 @@
package com.psbc.cpsp.eden.common.context;
import lombok.Data;
/**
* 上下文用户信息
*/
@Data
public class ContextUser {
/**
* 用户ID
*/
private String userId;
/**
* 租户ID
*/
private String tenantId;
}

View File

@ -0,0 +1,29 @@
package com.psbc.cpsp.eden.common.context;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* 租户上下文工具类
*/
public class TenantContext {
private static final String TENANT_ID_HEADER = "X-Tenant-ID";
/**
* 从请求头中获取租户ID
*
* @return 租户ID如果获取不到则返回默认值
*/
public static String getCurrentTenantId() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes == null) {
return "1";
}
HttpServletRequest request = attributes.getRequest();
String tenantId = request.getHeader(TENANT_ID_HEADER);
return tenantId != null && !tenantId.isEmpty() ? tenantId : "1";
}
}

View File

@ -0,0 +1,63 @@
package com.psbc.cpsp.eden.common.context;
/**
* 用户上下文
*
* 用于存储当前用户的信息
*/
public class UserContext {
private static final ThreadLocal<String> userIdHolder = new ThreadLocal<>();
private static final ThreadLocal<String> tenantIdHolder = new ThreadLocal<>();
/**
* 设置用户ID
*
* @param userId 用户ID
*/
public static void setUserId(String userId) {
userIdHolder.set(userId);
}
/**
* 获取用户ID
*
* @return 用户ID
*/
public static String getUserId() {
String userId = userIdHolder.get();
if (userId == null || userId.isEmpty()) {
return "system";
}
return userId;
}
/**
* 设置租户ID
*
* @param tenantId 租户ID
*/
public static void setTenantId(String tenantId) {
tenantIdHolder.set(tenantId);
}
/**
* 获取租户ID
*
* @return 租户ID
*/
public static String getTenantId() {
String tenantId = tenantIdHolder.get();
if (tenantId == null || tenantId.isEmpty()) {
return "1";
}
return tenantId;
}
/**
* 清除当前线程的用户上下文
*/
public static void clear() {
userIdHolder.remove();
tenantIdHolder.remove();
}
}

View File

@ -0,0 +1,112 @@
package com.psbc.cpsp.eden.common.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.psbc.cpsp.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.math.BigDecimal;
import java.util.Date;
/**
* 资产实体类
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("tb_asset")
public class Asset extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 资产ID */
@TableId(type = IdType.AUTO)
private Long id;
/** 资产编码 */
@NotBlank(message = "资产编码不能为空")
@Size(max = 100, message = "资产编码长度不能超过100个字符")
private String assetCode;
/** 资产名称 */
@NotBlank(message = "资产名称不能为空")
@Size(max = 200, message = "资产名称长度不能超过200个字符")
private String assetName;
/** 资产分类ID */
@NotNull(message = "资产分类不能为空")
private Long classificationId;
/** 管理员ID */
private String adminUserId;
/** 所属公司ID */
@NotNull(message = "所属公司不能为空")
private Long companyId;
/** 所在位置ID */
@NotNull(message = "所在位置不能为空")
private Long locationId;
/** 购置方式 采购/租赁 */
@NotBlank(message = "购置方式不能为空")
@Size(max = 20, message = "购置方式长度不能超过20个字符")
private String purchaseType;
/** 品牌 */
@NotBlank(message = "品牌不能为空")
@Size(max = 100, message = "品牌长度不能超过100个字符")
private String brand;
/** 型号 */
@Size(max = 100, message = "型号长度不能超过100个字符")
private String model;
/** 设备序列号 */
@Size(max = 100, message = "设备序列号长度不能超过100个字符")
private String serialNumber;
/** 购置金额 */
private BigDecimal purchaseAmount;
/** 预计使用期限(月) */
private Integer expectedUsefulLife;
/** 备注 */
private String remark;
/** 购置时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date purchaseDate;
/** 入库时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date storageDate;
/** 资产照片URL */
@Size(max = 500, message = "资产照片URL长度不能超过500个字符")
private String assetPhoto;
/** 保养到期时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date maintenanceDueDate;
/** 保养说明 */
private String maintenanceDescription;
/** 预计折旧期限(月) */
private Integer expectedDepreciationPeriod;
/** 资产状态 空闲/在用/借用/派发中/退库中/借出中/归还中/维修中 */
private String assetStatus;
/** 保养是否到期筛选 1-已到期 0-未到期 */
private String maintenanceExpired;
/** 是否删除 0-未删除 1-已删除 */
@TableLogic
@TableField(fill = FieldFill.INSERT)
private String delFlag;
}

View File

@ -0,0 +1,54 @@
package com.psbc.cpsp.eden.common.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.psbc.cpsp.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
/**
* 资产分类实体类
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("tb_asset_classification")
public class AssetClassification extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 分类ID */
@TableId(type = IdType.AUTO)
private Long id;
/** 分类编码 */
@NotBlank(message = "分类编码不能为空")
@Size(max = 50, message = "分类编码长度不能超过50个字符")
private String classificationCode;
/** 分类名称 */
@NotBlank(message = "分类名称不能为空")
@Size(max = 100, message = "分类名称长度不能超过100个字符")
private String classificationName;
/** 上级分类ID */
private Long parentId;
/** 状态 1-启用 0-禁用 */
@NotBlank(message = "状态不能为空")
@Size(max = 1, message = "状态长度不能超过1个字符")
private String status;
/** 备注 */
private String remark;
/** 是否删除 0-未删除 1-已删除 */
@TableLogic
@TableField(fill = FieldFill.INSERT)
private String delFlag;
}

View File

@ -0,0 +1,61 @@
package com.psbc.cpsp.eden.common.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.psbc.cpsp.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import javax.validation.constraints.Pattern;
/**
* 资产标签实体类
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("tb_asset_label")
public class AssetLabel extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 标签ID */
@TableId(type = IdType.AUTO)
private Long id;
/** 模板类型 1-模板1 2-模板2 3-模板3 4-模板4数值代表最多可选择的标签数量 */
@NotBlank(message = "模板类型不能为空")
@Size(max = 1, message = "模板类型长度为1个字符")
@Pattern(regexp = "[1-4]", message = "模板类型必须是1-4之间的数字")
private String templateType;
/**
* 标签集合(以逗号分隔的数字):
* 1-资产名称,2-资产分类,3-资产编码,4-资产位置,5-品牌,6-型号,7-设备序列号,8-管理员,9-保养到期时间,10-保养说明,11-使用部门
*/
private String labelItems;
/** 纸张类型 1-标签专用纸 2-A4(2列标签) 3-A4(3列标签) */
// @NotBlank(message = "纸张类型不能为空")
@Size(max = 1, message = "纸张类型长度为1个字符")
@Pattern(regexp = "[1-3]", message = "纸张类型必须是1-3之间的数字")
private String paperType;
/** 标签宽度(mm),仅当纸张类型为标签专用纸时有效 */
@Size(max = 8, message = "标签宽度不能超过8个字符")
// @Pattern(regexp = "^[1-9]\\d*$", message = "标签宽度必须是正整数")
private String labelWidth;
/** 标签高度(mm),仅当纸张类型为标签专用纸时有效 */
@Size(max = 8, message = "标签高度不能超过8个字符")
// @Pattern(regexp = "^[1-9]\\d*$", message = "标签高度必须是正整数")
private String labelHeight;
/** 是否删除 0-未删除 1-已删除 */
@TableLogic
@TableField(fill = FieldFill.INSERT)
private String delFlag;
}

View File

@ -0,0 +1,54 @@
package com.psbc.cpsp.eden.common.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.psbc.cpsp.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
/**
* 资产位置实体类
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("tb_asset_location")
public class AssetLocation extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 位置ID */
@TableId(type = IdType.AUTO)
private Long id;
/** 位置编码 */
@NotBlank(message = "位置编码不能为空")
@Size(max = 50, message = "位置编码长度不能超过50个字符")
private String locationCode;
/** 位置名称 */
@NotBlank(message = "位置名称不能为空")
@Size(max = 100, message = "位置名称长度不能超过100个字符")
private String locationName;
/** 上级位置ID */
private Long parentId;
/** 状态 1-启用 0-禁用 */
@NotBlank(message = "状态不能为空")
@Size(max = 1, message = "状态长度不能超过1个字符")
private String status;
/** 备注 */
private String remark;
/** 是否删除 0-未删除 1-已删除 */
@TableLogic
@TableField(fill = FieldFill.INSERT)
private String delFlag;
}

View File

@ -0,0 +1,40 @@
package com.psbc.cpsp.eden.common.interceptor;
import com.psbc.cpsp.eden.common.context.UserContext;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 租户拦截器
*/
public class TenantInterceptor implements HandlerInterceptor {
// 固定租户ID
private static final String FIXED_TENANT_ID = "1";
// 默认用户ID
private static final String DEFAULT_USER_ID = "admin";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 获取用户ID如果没有则使用默认值
String userId = request.getHeader("X-User-ID");
if (userId == null || userId.isEmpty()) {
userId = DEFAULT_USER_ID;
}
// 设置到ThreadLocal中租户ID统一使用固定值"1"
UserContext.setTenantId(FIXED_TENANT_ID);
UserContext.setUserId(userId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
// 清理ThreadLocal
UserContext.clear();
}
}

View File

@ -0,0 +1,74 @@
package com.psbc.cpsp.eden.common.request;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 资产查询请求对象
*/
@Data
public class AssetQueryRequest {
/** 资产编码 */
private String assetCode;
/** 资产名称 */
private String assetName;
/** 资产分类ID */
private Long classificationId;
/** 管理员ID */
private String adminUserId;
/** 所属公司ID */
private Long companyId;
/** 所在位置ID */
private Long locationId;
/** 购置方式 */
private String purchaseType;
/** 品牌 */
private String brand;
/** 型号 */
private String model;
/** 设备序列号 */
private String serialNumber;
/** 购置金额范围-最小值 */
private BigDecimal purchaseAmountMin;
/** 购置金额范围-最大值 */
private BigDecimal purchaseAmountMax;
/** 购置时间范围-开始时间 */
private Date purchaseDateStart;
/** 购置时间范围-结束时间 */
private Date purchaseDateEnd;
/** 入库时间范围-开始时间 */
private Date storageDateStart;
/** 入库时间范围-结束时间 */
private Date storageDateEnd;
/** 保养到期时间范围-开始时间 */
private Date maintenanceDueDateStart;
/** 保养到期时间范围-结束时间 */
private Date maintenanceDueDateEnd;
/** 资产状态 */
private String assetStatus;
/** 页码 */
private Integer pageNum = 1;
/** 每页记录数 */
private Integer pageSize = 10;
}

View File

@ -0,0 +1,76 @@
package com.psbc.cpsp.eden.common.response;
import lombok.Data;
/**
* 统一响应结果类
*
* @author eden
*/
@Data
public class AssetResult<T> {
/**
* 响应码
*/
private String code;
/**
* 响应消息
*/
private String message;
/**
* 响应数据
*/
private T data;
public AssetResult(String code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
/**
* 返回成功消息
*
* @param data 数据对象
* @return 成功消息
*/
public static <T> AssetResult<T> success(T data) {
return new AssetResult<>("0000000000000000", "操作成功", data);
}
/**
* 返回成功消息
*
* @return 成功消息
*/
public static <T> AssetResult<T> success() {
return new AssetResult<>("0000000000000000", "操作成功", null);
}
/**
* 返回错误消息
*
* @param message 错误消息
* @return 错误消息
*/
public static <T> AssetResult<T> error(String message) {
return new AssetResult<>("999999", message, null);
}
/**
* 返回错误消息
*
* @param code 错误码
* @param message 错误消息
* @return 错误消息
*/
public static <T> AssetResult<T> error(String code, String message) {
return new AssetResult<>(code, message, null);
}
public boolean isSuccess() {
return "0000000000000000".equals(code);
}
}

View File

@ -0,0 +1,73 @@
package com.psbc.cpsp.eden.common.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* 管理员用户缓存工具类
* 用于存储管理员ID和姓名的映射关系
*/
@Slf4j
@Component
public class AdminUserCache {
/**
* 管理员ID和姓名的映射关系
* key: 管理员ID, value: 管理员姓名
*/
private static final Map<String, String> ADMIN_USERS = new HashMap<>();
/**
* 初始化管理员数据
*/
@PostConstruct
public void init() {
log.info("初始化管理员数据...");
// 添加管理员数据
ADMIN_USERS.put("1", "张三");
ADMIN_USERS.put("2", "李四");
ADMIN_USERS.put("3", "王五");
ADMIN_USERS.put("4", "赵六");
ADMIN_USERS.put("7", "钱七");
log.info("管理员数据初始化完成,共 {} 条记录", ADMIN_USERS.size());
}
/**
* 获取管理员姓名
*
* @param adminUserId 管理员ID
* @return 管理员姓名如果不存在则返回未知管理员
*/
public static String getAdminUserName(String adminUserId) {
if (adminUserId == null || adminUserId.isEmpty()) {
return "未知管理员";
}
return ADMIN_USERS.getOrDefault(adminUserId, "未知管理员(" + adminUserId + ")");
}
/**
* 获取所有管理员数据
*
* @return 管理员ID和姓名的映射关系
*/
public static Map<String, String> getAllAdminUsers() {
return Collections.unmodifiableMap(ADMIN_USERS);
}
/**
* 判断管理员ID是否存在
*
* @param adminUserId 管理员ID
* @return 是否存在
*/
public static boolean exists(String adminUserId) {
return adminUserId != null && ADMIN_USERS.containsKey(adminUserId);
}
}

View File

@ -0,0 +1,185 @@
package com.psbc.cpsp.eden.common.utils;
import com.psbc.cpsp.eden.common.entity.Asset;
import com.psbc.cpsp.eden.common.vo.AssetImportVO;
import com.psbc.cpsp.eden.service.asset.IAssetClassificationService;
import com.psbc.cpsp.eden.service.asset.IAssetLocationService;
import com.psbc.cpsp.eden.enums.PurchaseTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 资产导入监听器
*/
@Slf4j
public class AssetImportListener {
private HttpServletRequest request;
private final IAssetClassificationService assetClassificationService;
private final IAssetLocationService assetLocationService;
public AssetImportListener(IAssetClassificationService assetClassificationService,
IAssetLocationService assetLocationService) {
this.assetClassificationService = assetClassificationService;
this.assetLocationService = assetLocationService;
this.request = null;
}
public AssetImportListener(IAssetClassificationService assetClassificationService,
IAssetLocationService assetLocationService,
HttpServletRequest HTTPrequest) {
this.assetClassificationService = assetClassificationService;
this.assetLocationService = assetLocationService;
this.request = HTTPrequest;
}
/**
* 验证导入数据并转换为Asset实体
*
* @param importVOList 导入数据列表
* @return 转换后的Asset对象列表包含验证结果
*/
public List<Asset> validateAndConvert(List<AssetImportVO> importVOList) {
List<Asset> assetList = new ArrayList<>();
for (AssetImportVO vo : importVOList) {
Asset asset = new Asset();
asset.setDelFlag("0");
asset.setCreateTime(new Date());
asset.setCreateUserId("admin"); // 使用默认的系统管理员
StringBuilder errorMsg = new StringBuilder();
// 验证和转换资产分类
if (!StringUtils.hasText(vo.getClassificationName())) {
errorMsg.append("资产分类不能为空; ");
} else {
try {
// 获取资产分类ID
Long classificationId = assetClassificationService
.selectIdByName(vo.getClassificationName());
if (classificationId == null) {
errorMsg.append("资产分类不存在; ");
} else {
asset.setClassificationId(classificationId);
}
} catch (Exception e) {
errorMsg.append("查询资产分类失败; ");
log.error("查询资产分类异常", e);
}
}
// 验证和转换资产名称
if (!StringUtils.hasText(vo.getAssetName())) {
errorMsg.append("资产名称不能为空; ");
} else {
asset.setAssetName(vo.getAssetName());
}
// 验证和转换品牌
if (!StringUtils.hasText(vo.getBrand())) {
errorMsg.append("品牌不能为空; ");
} else {
asset.setBrand(vo.getBrand());
}
// 转换型号可选
asset.setModel(vo.getModel());
// 转换设备序列号可选
asset.setSerialNumber(vo.getSerialNumber());
// 转换管理员
asset.setAdminUserId(vo.getAdminUserName());
// 验证和转换所属公司
if (!StringUtils.hasText(vo.getCompanyName())) {
errorMsg.append("所属公司不能为空; ");
} else {
// todo 公司名称转换为公司id
asset.setCompanyId(null);
}
// 转换购置金额可选
asset.setPurchaseAmount(vo.getPurchaseAmount());
// 验证和转换所在位置
if (!StringUtils.hasText(vo.getLocationName())) {
errorMsg.append("所在位置不能为空; ");
} else {
try {
// 获取资产位置ID
Long locationId = assetLocationService.selectIdByName(vo.getLocationName());
if (locationId == null) {
errorMsg.append("所在位置不存在; ");
} else {
asset.setLocationId(locationId);
}
} catch (Exception e) {
errorMsg.append("查询资产位置失败; ");
log.error("查询资产位置异常", e);
}
}
// 转换购置时间可选
asset.setPurchaseDate(vo.getPurchaseDate());
// 验证和转换购置方式
if (!StringUtils.hasText(vo.getPurchaseType())) {
errorMsg.append("购置方式不能为空; ");
} else {
// 检查购置方式是否为枚举值如果是文本名称则转换为代码
String purchaseTypeCode = vo.getPurchaseType();
if ("采购".equals(purchaseTypeCode)) {
purchaseTypeCode = PurchaseTypeEnum.BUY.getCode();
} else if ("租赁".equals(purchaseTypeCode)) {
purchaseTypeCode = PurchaseTypeEnum.RENT.getCode();
} else if (!PurchaseTypeEnum.isValid(purchaseTypeCode)) {
errorMsg.append("购置方式无效,应为采购或租赁; ");
purchaseTypeCode = null;
}
if (purchaseTypeCode != null) {
asset.setPurchaseType(purchaseTypeCode);
}
}
// 转换入库时间可选
asset.setStorageDate(vo.getStorageDate());
// 转换预计使用期限可选
asset.setExpectedUsefulLife(vo.getExpectedUsefulLife());
// 转换备注可选
asset.setRemark(vo.getRemark());
// 转换保养到期时间可选
asset.setMaintenanceDueDate(vo.getMaintenanceDueDate());
// 转换保养说明可选
asset.setMaintenanceDescription(vo.getMaintenanceDescription());
// 转换预计折旧期限可选
asset.setExpectedDepreciationPeriod(vo.getExpectedDepreciationPeriod());
// 设置默认资产状态为空闲
asset.setAssetStatus("空闲");
// 记录导入结果
vo.setImportResult(errorMsg.length() > 0 ? errorMsg.toString() : "导入成功");
// 只有验证通过的才加入列表
if (errorMsg.length() == 0) {
assetList.add(asset);
}
}
return assetList;
}
}

View File

@ -0,0 +1,73 @@
package com.psbc.cpsp.eden.common.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* 公司数据缓存工具类
* 用于存储公司ID和公司名称的映射关系
*/
@Slf4j
@Component
public class CompanyCache {
/**
* 公司ID和名称的映射关系
* key: 公司ID, value: 公司名称
*/
private static final Map<String, String> COMPANIES = new HashMap<>();
/**
* 初始化公司数据
*/
@PostConstruct
public void init() {
log.info("初始化公司数据...");
// 添加公司数据
COMPANIES.put("1", "总公司");
COMPANIES.put("2", "北京分公司");
COMPANIES.put("3", "上海分公司");
COMPANIES.put("4", "广州分公司");
COMPANIES.put("5", "深圳分公司");
log.info("公司数据初始化完成,共 {} 条记录", COMPANIES.size());
}
/**
* 获取公司名称
*
* @param companyId 公司ID
* @return 公司名称如果不存在则返回未知公司
*/
public static String getCompanyName(Long companyId) {
if (companyId == null) {
return "未知公司";
}
return COMPANIES.getOrDefault(companyId, "未知公司(" + companyId + ")");
}
/**
* 获取所有公司数据
*
* @return 公司ID和名称的映射关系
*/
public static Map<String, String> getAllCompanies() {
return Collections.unmodifiableMap(COMPANIES);
}
/**
* 判断公司ID是否存在
*
* @param companyId 公司ID
* @return 是否存在
*/
public static boolean exists(Long companyId) {
return companyId != null && COMPANIES.containsKey(companyId);
}
}

View File

@ -0,0 +1,613 @@
package com.psbc.cpsp.eden.common.utils;
import com.psbc.cpsp.eden.common.entity.Asset;
import com.psbc.cpsp.eden.common.vo.AssetExportVO;
import com.psbc.cpsp.eden.common.vo.AssetImportVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import com.psbc.cpsp.eden.enums.PurchaseTypeEnum;
/**
* Excel处理工具类
*/
@Slf4j
public class ExcelUtils {
private static final String EXCEL_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final SimpleDateFormat SDF = new SimpleDateFormat(DATE_FORMAT);
/**
* 导入资产Excel
*
* @param file Excel文件
* @return 资产导入VO列表
*/
public static List<AssetImportVO> importAssetExcel(MultipartFile file) {
List<AssetImportVO> list = new ArrayList<>();
try (InputStream is = file.getInputStream();
Workbook workbook = WorkbookFactory.create(is)) {
Sheet sheet = workbook.getSheetAt(0);
// 跳过表头(前3行)第一条记录从第4行开始
int startRow = 3;
int lastRowNum = sheet.getLastRowNum();
for (int i = startRow; i <= lastRowNum; i++) {
Row row = sheet.getRow(i);
if (row == null) {
continue;
}
// 跳过空行
if (isRowEmpty(row)) {
continue;
}
AssetImportVO vo = new AssetImportVO();
// 根据资产导入模板顺序读取单元格数据
vo.setClassificationName(getCellStringValue(row.getCell(0)));
vo.setAssetName(getCellStringValue(row.getCell(1)));
vo.setBrand(getCellStringValue(row.getCell(2)));
vo.setModel(getCellStringValue(row.getCell(3)));
vo.setSerialNumber(getCellStringValue(row.getCell(4)));
vo.setAdminUserName(getCellStringValue(row.getCell(5)));
vo.setCompanyName(getCellStringValue(row.getCell(6)));
vo.setPurchaseAmount(getCellNumericValueAsBigDecimal(row.getCell(7)));
vo.setLocationName(getCellStringValue(row.getCell(8)));
vo.setPurchaseDate(getCellDateValue(row.getCell(9)));
vo.setPurchaseType(getCellStringValue(row.getCell(10)));
vo.setStorageDate(getCellDateValue(row.getCell(11)));
vo.setExpectedUsefulLife(getCellNumericValueAsInteger(row.getCell(12)));
vo.setRemark(getCellStringValue(row.getCell(13)));
vo.setMaintenanceDueDate(getCellDateValue(row.getCell(14)));
vo.setMaintenanceDescription(getCellStringValue(row.getCell(15)));
vo.setExpectedDepreciationPeriod(getCellNumericValueAsInteger(row.getCell(16)));
// 第17列是导入结果说明不属于AssetImportVO对象忽略
list.add(vo);
}
} catch (Exception e) {
log.error("导入Excel异常", e);
}
return list;
}
/**
* 使用AssetImportListener导入Excel
*
* @param file Excel文件
* @param clazz 目标类
* @param listener 导入监听器
* @return 导入数据列表
*/
public static <T> List<T> importExcel(MultipartFile file, Class<T> clazz, AssetImportListener listener) {
if (file == null || clazz == null) {
return new ArrayList<>();
}
if (clazz == AssetImportVO.class) {
List<AssetImportVO> importList = importAssetExcel(file);
// 这里实际不需要使用listener参数listener主要用于其它处理场景
return (List<T>) importList;
}
return new ArrayList<>();
}
/**
* 将导入VO转换为实体对象
*
* @param importList 导入VO列表
* @return 实体对象列表
*/
public static List<Asset> convertImportData(List<AssetImportVO> importList) {
if (importList == null || importList.isEmpty()) {
return new ArrayList<>();
}
List<Asset> assetList = new ArrayList<>();
for (AssetImportVO vo : importList) {
Asset asset = new Asset();
// 设置基本信息
asset.setAssetName(vo.getAssetName());
asset.setBrand(vo.getBrand());
asset.setModel(vo.getModel());
asset.setSerialNumber(vo.getSerialNumber());
asset.setAdminUserId(vo.getAdminUserName());
// todo 公司名称转换为公司id
asset.setCompanyId(null);
asset.setPurchaseAmount(vo.getPurchaseAmount());
asset.setPurchaseDate(vo.getPurchaseDate());
asset.setPurchaseType(vo.getPurchaseType());
asset.setStorageDate(vo.getStorageDate());
asset.setExpectedUsefulLife(vo.getExpectedUsefulLife());
asset.setRemark(vo.getRemark());
asset.setMaintenanceDueDate(vo.getMaintenanceDueDate());
asset.setMaintenanceDescription(vo.getMaintenanceDescription());
asset.setExpectedDepreciationPeriod(vo.getExpectedDepreciationPeriod());
// 设置默认状态
asset.setAssetStatus("空闲");
asset.setDelFlag("0");
asset.setCreateTime(new Date());
assetList.add(asset);
}
return assetList;
}
/**
* 导出资产Excel
*
* @param response HTTP响应
* @param dataList 资产导出数据列表
* @param fileName 导出文件名
*/
public static void exportAssetExcel(HttpServletResponse response, List<AssetExportVO> dataList, String fileName) {
try (Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet("资产清单");
// 创建表头样式
CellStyle headerStyle = workbook.createCellStyle();
headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
headerStyle.setBorderBottom(BorderStyle.THIN);
headerStyle.setBorderLeft(BorderStyle.THIN);
headerStyle.setBorderRight(BorderStyle.THIN);
headerStyle.setBorderTop(BorderStyle.THIN);
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerStyle.setFont(headerFont);
// 创建表头
Row headerRow = sheet.createRow(0);
String[] headers = {
"资产编码", "资产分类", "资产名称", "资产状态", "品牌", "型号", "设备序列号",
"管理员", "所在位置", "购置时间", "购置方式", "购置金额(含税)", "入库时间",
"预计使用期限(月)", "备注", "资产照片链接", "保养到期时间", "保养说明", "预计折旧期限(月)"
};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
// 根据内容设置列宽
sheet.setColumnWidth(i, 256 * 20);
}
// 创建数据行
CellStyle dataStyle = workbook.createCellStyle();
dataStyle.setBorderBottom(BorderStyle.THIN);
dataStyle.setBorderLeft(BorderStyle.THIN);
dataStyle.setBorderRight(BorderStyle.THIN);
dataStyle.setBorderTop(BorderStyle.THIN);
// 日期样式
CellStyle dateStyle = workbook.createCellStyle();
dateStyle.cloneStyleFrom(dataStyle);
CreationHelper creationHelper = workbook.getCreationHelper();
dateStyle.setDataFormat(creationHelper.createDataFormat().getFormat(DATE_FORMAT));
int rowNum = 1;
for (AssetExportVO vo : dataList) {
Row row = sheet.createRow(rowNum++);
// 资产编码
createCell(row, 0, vo.getAssetCode(), dataStyle);
// 资产分类
createCell(row, 1, vo.getClassificationName(), dataStyle);
// 资产名称
createCell(row, 2, vo.getAssetName(), dataStyle);
// 资产状态
createCell(row, 3, vo.getAssetStatus(), dataStyle);
// 品牌
createCell(row, 4, vo.getBrand(), dataStyle);
// 型号
createCell(row, 5, vo.getModel(), dataStyle);
// 设备序列号
createCell(row, 6, vo.getSerialNumber(), dataStyle);
// 管理员
createCell(row, 7, vo.getAdminUserName(), dataStyle);
// 所在位置
createCell(row, 8, vo.getLocationName(), dataStyle);
// 购置时间
createDateCell(row, 9, vo.getPurchaseDate(), dateStyle);
// 购置方式 - 转换编码为名称
String purchaseTypeName = PurchaseTypeEnum.getNameByCode(vo.getPurchaseType());
createCell(row, 10, purchaseTypeName, dataStyle);
// 购置金额(含税)
createCell(row, 11, vo.getPurchaseAmount(), dataStyle);
// 入库时间
createDateCell(row, 12, vo.getStorageDate(), dateStyle);
// 预计使用期限()
createCell(row, 13, vo.getExpectedUsefulLife(), dataStyle);
// 备注
createCell(row, 14, vo.getRemark(), dataStyle);
// 资产照片链接
createCell(row, 15, vo.getAssetPhoto(), dataStyle);
// 保养到期时间
createDateCell(row, 16, vo.getMaintenanceDueDate(), dateStyle);
// 保养说明
createCell(row, 17, vo.getMaintenanceDescription(), dataStyle);
// 预计折旧期限()
createCell(row, 18, vo.getExpectedDepreciationPeriod(), dataStyle);
}
// 设置响应头
response.setContentType(EXCEL_CONTENT_TYPE);
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment;filename="
+ URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
// 写入响应
workbook.write(response.getOutputStream());
} catch (Exception e) {
log.error("导出Excel异常", e);
}
}
/**
* 通用导出Excel方法
*
* @param list 数据列表
* @param sheetName sheet名称
* @param clazz 对象类型
* @param fileName 文件名
* @param response HTTP响应
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, String fileName,
HttpServletResponse response) {
if (list == null || list.isEmpty() || clazz == null) {
return;
}
if (clazz == AssetExportVO.class) {
exportAssetExcel(response, (List<AssetExportVO>) list, fileName);
return;
}
// 默认导出处理
try (Workbook workbook = new XSSFWorkbook()) {
// 创建sheet
Sheet sheet = workbook.createSheet(sheetName);
// 设置响应头
response.setContentType(EXCEL_CONTENT_TYPE);
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment;filename="
+ URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
// 写入响应
workbook.write(response.getOutputStream());
} catch (Exception e) {
log.error("导出Excel异常", e);
}
}
/**
* 通用导出Excel方法 - 不带文件名版本
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
String fileName = sheetName + "_" + new SimpleDateFormat("yyyyMMdd").format(new Date());
exportExcel(list, sheetName, clazz, fileName, response);
}
/**
* 创建Excel导入模板
*/
public static List<AssetImportVO> createExcelTemplate() {
// 创建空的模板数据列表
List<AssetImportVO> templateList = new ArrayList<>();
// 添加一条示例数据
AssetImportVO example = new AssetImportVO();
example.setClassificationName("办公设备");
example.setAssetName("办公电脑");
example.setBrand("联想");
example.setModel("ThinkPad X1");
example.setSerialNumber("SN12345678");
example.setAdminUserName("张三");
example.setCompanyName("总公司");
example.setPurchaseAmount(new BigDecimal("5999.00"));
example.setLocationName("总部办公室");
example.setPurchaseType("采购");
example.setRemark("这是一条示例数据");
templateList.add(example);
return templateList;
}
/**
* 获取单元格字符串值
*/
private static String getCellStringValue(Cell cell) {
if (cell == null) {
return "";
}
String value = "";
switch (cell.getCellType()) {
case STRING:
value = cell.getStringCellValue();
break;
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
value = SDF.format(cell.getDateCellValue());
} else {
value = String.valueOf((long) cell.getNumericCellValue());
}
break;
case BOOLEAN:
value = String.valueOf(cell.getBooleanCellValue());
break;
case FORMULA:
try {
value = cell.getStringCellValue();
} catch (Exception e) {
value = String.valueOf(cell.getNumericCellValue());
}
break;
default:
break;
}
return value;
}
/**
* 获取单元格日期值
*/
private static Date getCellDateValue(Cell cell) {
if (cell == null) {
return null;
}
Date date = null;
switch (cell.getCellType()) {
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
date = cell.getDateCellValue();
}
break;
case STRING:
String dateStr = cell.getStringCellValue();
if (dateStr != null && !dateStr.isEmpty()) {
try {
date = SDF.parse(dateStr);
} catch (ParseException e) {
log.error("日期格式解析异常: {}", dateStr, e);
}
}
break;
default:
break;
}
return date;
}
/**
* 获取单元格数值为BigDecimal
*/
private static BigDecimal getCellNumericValueAsBigDecimal(Cell cell) {
if (cell == null) {
return null;
}
BigDecimal value = null;
switch (cell.getCellType()) {
case NUMERIC:
value = new BigDecimal(cell.getNumericCellValue());
break;
case STRING:
String numStr = cell.getStringCellValue();
if (numStr != null && !numStr.isEmpty()) {
try {
value = new BigDecimal(numStr);
} catch (NumberFormatException e) {
log.error("数值格式解析异常: {}", numStr, e);
}
}
break;
default:
break;
}
return value;
}
/**
* 获取单元格数值为Integer
*/
private static Integer getCellNumericValueAsInteger(Cell cell) {
if (cell == null) {
return null;
}
Integer value = null;
switch (cell.getCellType()) {
case NUMERIC:
value = (int) cell.getNumericCellValue();
break;
case STRING:
String numStr = cell.getStringCellValue();
if (numStr != null && !numStr.isEmpty()) {
try {
value = Integer.valueOf(numStr);
} catch (NumberFormatException e) {
log.error("整数格式解析异常: {}", numStr, e);
}
}
break;
default:
break;
}
return value;
}
/**
* 创建单元格并设置值
*/
private static void createCell(Row row, int columnIndex, Object value, CellStyle style) {
Cell cell = row.createCell(columnIndex);
if (value instanceof String) {
cell.setCellValue((String) value);
} else if (value instanceof Integer) {
cell.setCellValue((Integer) value);
} else if (value instanceof Long) {
cell.setCellValue((Long) value);
} else if (value instanceof Double) {
cell.setCellValue((Double) value);
} else if (value instanceof BigDecimal) {
cell.setCellValue(((BigDecimal) value).doubleValue());
} else if (value instanceof Boolean) {
cell.setCellValue((Boolean) value);
} else if (value != null) {
cell.setCellValue(value.toString());
}
cell.setCellStyle(style);
}
/**
* 创建日期单元格
*/
private static void createDateCell(Row row, int columnIndex, Date date, CellStyle style) {
Cell cell = row.createCell(columnIndex);
if (date != null) {
cell.setCellValue(date);
}
cell.setCellStyle(style);
}
/**
* 判断行是否为空
*/
private static boolean isRowEmpty(Row row) {
if (row == null) {
return true;
}
for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
Cell cell = row.getCell(i);
if (cell != null && cell.getCellType() != CellType.BLANK) {
return false;
}
}
return true;
}
/**
* 创建导入模板 (旧方法)
*/
public static void createImportTemplate(HttpServletResponse response) throws IOException {
try (Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet("资产导入模板");
// 设置列宽
for (int i = 0; i < 18; i++) {
sheet.setColumnWidth(i, 256 * 20);
}
// 创建标题行样式
CellStyle titleStyle = workbook.createCellStyle();
titleStyle.setAlignment(HorizontalAlignment.CENTER);
Font titleFont = workbook.createFont();
titleFont.setBold(true);
titleFont.setFontHeightInPoints((short) 14);
titleStyle.setFont(titleFont);
// 创建标题行
Row titleRow = sheet.createRow(0);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellValue("资产导入模板");
titleCell.setCellStyle(titleStyle);
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 17));
// 创建说明行样式
CellStyle descriptionStyle = workbook.createCellStyle();
descriptionStyle.setAlignment(HorizontalAlignment.LEFT);
Font descriptionFont = workbook.createFont();
descriptionFont.setFontHeightInPoints((short) 11);
descriptionStyle.setFont(descriptionFont);
// 创建说明行
Row descriptionRow = sheet.createRow(1);
Cell descriptionCell = descriptionRow.createCell(0);
descriptionCell.setCellValue("导入说明: 带*号的为必填项请按照模板格式填写日期格式为yyyy-MM-dd。");
descriptionCell.setCellStyle(descriptionStyle);
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 17));
// 创建表头样式
CellStyle headerStyle = workbook.createCellStyle();
headerStyle.setAlignment(HorizontalAlignment.CENTER);
headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
headerStyle.setBorderBottom(BorderStyle.THIN);
headerStyle.setBorderLeft(BorderStyle.THIN);
headerStyle.setBorderRight(BorderStyle.THIN);
headerStyle.setBorderTop(BorderStyle.THIN);
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerStyle.setFont(headerFont);
// 创建表头
Row headerRow = sheet.createRow(2);
String[] headers = {
"*资产分类", "*资产名称", "*品牌", "型号", "设备序列号",
"管理员", "*所属公司", "购置金额(含税)", "*所在位置",
"购置时间", "*购置方式", "入库时间", "预计使用期限(月)",
"备注", "保养到期日期", "保养说明", "预计折旧期限(月)", "导入结果说明"
};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
cell.setCellStyle(headerStyle);
}
// 设置数据验证 - 购置方式下拉列表
DataValidationHelper dvHelper = sheet.getDataValidationHelper();
// 购置方式列(下标10)有效值采购租赁
CellRangeAddressList purchaseTypeRange = new CellRangeAddressList(3, 1000, 10, 10);
DataValidationConstraint purchaseTypeConstraint = dvHelper.createExplicitListConstraint(
new String[] { "采购", "租赁" });
DataValidation purchaseTypeValidation = dvHelper.createValidation(purchaseTypeConstraint,
purchaseTypeRange);
purchaseTypeValidation.setShowErrorBox(true);
sheet.addValidationData(purchaseTypeValidation);
// 设置响应头
response.setContentType(EXCEL_CONTENT_TYPE);
response.setCharacterEncoding("UTF-8");
String fileName = URLEncoder.encode("资产导入模板.xlsx", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
// 写入响应
workbook.write(response.getOutputStream());
}
}
}

View File

@ -0,0 +1,81 @@
package com.psbc.cpsp.eden.common.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 资产导出VO类
*/
@Data
public class AssetExportVO {
/** 资产编码 */
private String assetCode;
/** 资产分类 */
private String classificationName;
/** 资产名称 */
private String assetName;
/** 资产状态 */
private String assetStatus;
/** 品牌 */
private String brand;
/** 型号 */
private String model;
/** 设备序列号 */
private String serialNumber;
/** 管理员 */
private String adminUserName;
/** 所在位置 */
private String locationName;
/** 所属公司 */
private String companyName;
/** 所属公司ID */
private Long companyId;
/** 管理员ID */
private String adminUserId;
/** 购置时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date purchaseDate;
/** 购置方式 */
private String purchaseType;
/** 购置金额(含税) */
private BigDecimal purchaseAmount;
/** 入库时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date storageDate;
/** 预计使用期限(月) */
private Integer expectedUsefulLife;
/** 备注 */
private String remark;
/** 资产照片链接 */
private String assetPhoto;
/** 保养到期时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date maintenanceDueDate;
/** 保养说明 */
private String maintenanceDescription;
/** 预计折旧期限(月) */
private Integer expectedDepreciationPeriod;
}

View File

@ -0,0 +1,70 @@
package com.psbc.cpsp.eden.common.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 资产导入VO类
*/
@Data
public class AssetImportVO {
/** 资产分类名称 */
private String classificationName;
/** 资产名称 */
private String assetName;
/** 品牌 */
private String brand;
/** 型号 */
private String model;
/** 设备序列号 */
private String serialNumber;
/** 管理员 */
private String adminUserName;
/** 所属公司 */
private String companyName;
/** 购置金额(含税) */
private BigDecimal purchaseAmount;
/** 所在位置 */
private String locationName;
/** 购置时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date purchaseDate;
/** 购置方式 */
private String purchaseType;
/** 入库时间 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date storageDate;
/** 预计使用期限(月) */
private Integer expectedUsefulLife;
/** 备注 */
private String remark;
/** 保养到期日期 */
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date maintenanceDueDate;
/** 保养说明 */
private String maintenanceDescription;
/** 预计折旧期限(月) */
private Integer expectedDepreciationPeriod;
/** 导入结果说明 */
private String importResult;
}

View File

@ -0,0 +1,89 @@
package com.psbc.cpsp.eden.common.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 资产视图对象
*/
@Data
public class AssetVO {
/** 资产ID */
private String id;
/** 资产编码 */
private String assetCode;
/** 资产名称 */
private String assetName;
/** 资产分类ID */
private Long classificationId;
/** 资产分类名称 */
private String classificationName;
/** 管理员ID */
private String adminUserId;
/** 管理员名称 */
private String adminUserName;
/** 所属公司ID */
private Long companyId;
/** 所属公司名称 */
private String companyName;
/** 所在位置ID */
private Long locationId;
/** 所在位置名称 */
private String locationName;
/** 购置方式 */
private String purchaseType;
/** 品牌 */
private String brand;
/** 型号 */
private String model;
/** 设备序列号 */
private String serialNumber;
/** 购置金额 */
private BigDecimal purchaseAmount;
/** 预计使用期限(月) */
private Integer expectedUsefulLife;
/** 备注 */
private String remark;
/** 购置时间 */
private Date purchaseDate;
/** 入库时间 */
private Date storageDate;
/** 资产照片URL */
private String assetPhoto;
/** 保养到期时间 */
private Date maintenanceDueDate;
/** 保养说明 */
private String maintenanceDescription;
/** 预计折旧期限(月) */
private Integer expectedDepreciationPeriod;
/** 资产状态 */
private String assetStatus;
/** 资产状态描述 */
private String assetStatusDesc;
}

View File

@ -0,0 +1,28 @@
package com.psbc.cpsp.eden.common.vo;
import lombok.Data;
import java.util.List;
/**
* 树节点
*/
@Data
public class TreeNode {
/** 节点id */
private Long id;
/** 节点标签 */
private String label;
/** 父节点id */
private Long parentId;
/** 是否有子节点 */
private Boolean hasChildren;
/** 节点编码 */
private String code;
/** 子节点列表 */
private List<TreeNode> children;
}

View File

@ -0,0 +1,33 @@
package com.psbc.cpsp.eden.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* 跨域配置
*/
@Configuration
public class CorsConfig {
/**
* 允许跨域调用的过滤器
*/
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// 允许所有域名进行跨域调用
config.addAllowedOrigin("*");
// 允许跨越发送cookie
config.setAllowCredentials(false);
// 放行全部原始头信息
config.addAllowedHeader("*");
// 允许所有请求方法跨域调用
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}

View File

@ -0,0 +1,49 @@
package com.psbc.cpsp.eden.config;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 数据源配置类
* 添加数据库连接池监控
*/
@Slf4j
@Configuration
public class DataSourceConfig {
@Autowired
private DataSourceProperties properties;
/**
* 数据源配置
*/
@Bean
@Primary
public DataSource dataSource() {
HikariDataSource dataSource = properties.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
// 使用线程池定期打印连接池状态
ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);
scheduledExecutorService.scheduleAtFixedRate(() -> {
log.info("HikariCP连接池状态 - 活动连接数: {}, 空闲连接数: {}, 总连接数: {}, 等待连接数: {}",
dataSource.getHikariPoolMXBean().getActiveConnections(),
dataSource.getHikariPoolMXBean().getIdleConnections(),
dataSource.getHikariPoolMXBean().getTotalConnections(),
dataSource.getHikariPoolMXBean().getThreadsAwaitingConnection());
}, 60, 60, TimeUnit.SECONDS);
return dataSource;
}
}

View File

@ -0,0 +1,15 @@
package com.psbc.cpsp.eden.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* MyBatis配置类
* 解决MyBatis-Plus与PageHelper集成问题
*/
@Configuration
@EnableTransactionManagement
public class MyBatisConfig {
// 此配置类为空用于标记MyBatis配置
// 实际的配置内容已在MybatisPlusConfig中实现
}

View File

@ -0,0 +1,31 @@
package com.psbc.cpsp.eden.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MyBatis-Plus配置类
*/
@Configuration
public class MybatisPlusConfig {
/**
* 配置MyBatis-Plus插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
// 防止全表更新与删除
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
}

View File

@ -0,0 +1,55 @@
package com.psbc.cpsp.eden.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.psbc.cpsp.eden.enums.DelFlagEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* MyBatis-Plus自动填充处理器
*/
@Slf4j
@Component
public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
// 固定的租户ID和用户ID
private static final String FIXED_TENANT_ID = "1";
private static final String FIXED_USER_ID = "admin";
@Override
public void insertFill(MetaObject metaObject) {
log.debug("自动填充Insert字段");
// 创建时间
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
// 创建人
this.strictInsertFill(metaObject, "createUserId", String.class, FIXED_USER_ID);
// 是否删除
this.strictInsertFill(metaObject, "delFlag", String.class, DelFlagEnum.NORMAL.getCode());
// 租户ID - 直接使用固定值
this.strictInsertFill(metaObject, "tenantId", String.class, FIXED_TENANT_ID);
// 最后修改时间
this.strictInsertFill(metaObject, "lastModTime", Date.class, new Date());
// 最后修改人
this.strictInsertFill(metaObject, "lastModUserId", String.class, FIXED_USER_ID);
}
@Override
public void updateFill(MetaObject metaObject) {
log.debug("自动填充Update字段");
// 最后修改时间
this.strictUpdateFill(metaObject, "lastModTime", Date.class, new Date());
// 最后修改人 - 直接使用固定值
this.strictUpdateFill(metaObject, "lastModUserId", String.class, FIXED_USER_ID);
}
}

View File

@ -0,0 +1,38 @@
package com.psbc.cpsp.eden.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
import javax.sql.DataSource;
/**
* 事务管理配置
* 设置事务超时防止长事务占用连接
*/
@Configuration
@EnableTransactionManagement
public class TransactionConfig implements TransactionManagementConfigurer {
private final DataSource dataSource;
public TransactionConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public PlatformTransactionManager txManager() {
DataSourceTransactionManager txManager = new DataSourceTransactionManager(dataSource);
// 设置默认事务超时时间为30秒
txManager.setDefaultTimeout(30);
return txManager;
}
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return txManager();
}
}

View File

@ -0,0 +1,16 @@
package com.psbc.cpsp.eden.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Web配置类
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 移除租户拦截器改为直接在代码中写入租户ID和用户ID
}
}

View File

@ -0,0 +1,37 @@
package com.psbc.cpsp.eden.constants;
/**
* 资产管理常量类
*/
public class AssetConstants {
/** 购置方式-采购 */
public static final String PURCHASE_TYPE_BUY = "1";
/** 购置方式-租赁 */
public static final String PURCHASE_TYPE_RENT = "2";
/** 购置方式名称-采购 */
public static final String PURCHASE_TYPE_BUY_NAME = "采购";
/** 购置方式名称-租赁 */
public static final String PURCHASE_TYPE_RENT_NAME = "租赁";
/** 默认页码 */
public static final int DEFAULT_PAGE_NUM = 1;
/** 默认每页记录数 */
public static final int DEFAULT_PAGE_SIZE = 10;
/** 最大每页记录数 */
public static final int MAX_PAGE_SIZE = 100;
/** 资产编码前缀 */
public static final String ASSET_CODE_PREFIX = "ZC";
/** 资产编码日期格式 */
public static final String ASSET_CODE_DATE_FORMAT = "yyyyMMdd";
/** 资产编码序号长度 */
public static final int ASSET_CODE_SEQUENCE_LENGTH = 4;
/** 资产照片存储路径 */
public static final String ASSET_PHOTO_PATH = "/asset/photo/";
/** 允许的资产照片文件类型 */
public static final String[] ALLOWED_PHOTO_TYPES = { ".jpg", ".jpeg", ".png" };
/** 资产照片最大大小(字节) */
public static final long MAX_PHOTO_SIZE = 5 * 1024 * 1024;
}

View File

@ -0,0 +1,253 @@
package com.psbc.cpsp.eden.controller.asset;
import com.psbc.cpsp.entity.CpspReponse;
import com.psbc.cpsp.eden.common.entity.AssetClassification;
import com.psbc.cpsp.eden.enums.StatusEnum;
import com.psbc.cpsp.eden.common.vo.TreeNode;
import com.psbc.cpsp.eden.service.asset.IAssetClassificationService;
import com.github.pagehelper.PageHelper;
import com.psbc.cpsp.entity.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;
/**
* 资产分类Controller
*
* @author eden
*/
@Slf4j
@Validated
@RestController
@RequestMapping("/asset/classifications")
public class AssetClassificationController {
@Autowired
private IAssetClassificationService assetClassificationService;
/**
* 查询资产分类列表
*/
@GetMapping("/list")
public CpspReponse<PageInfo<AssetClassification>> list(AssetClassification classification,
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize) {
log.info("查询资产分类列表, 参数: {}, pageNum: {}, pageSize: {}", classification, pageNum, pageSize);
PageHelper.startPage(pageNum, pageSize);
List<AssetClassification> list = assetClassificationService.selectList(classification);
// 使用GitHub的PageInfo获取分页信息
com.github.pagehelper.PageInfo<AssetClassification> githubPageInfo = new com.github.pagehelper.PageInfo<>(list);
// 转换为自定义的PageInfo
PageInfo<AssetClassification> pageInfo = new PageInfo<>(
githubPageInfo.getPageSize(),
githubPageInfo.getPageNum(),
githubPageInfo.getTotal(),
githubPageInfo.getList());
return CpspReponse.ok(pageInfo);
}
/**
* 查询资产分类树结构
*/
@GetMapping("/tree")
public CpspReponse<List<TreeNode>> tree(AssetClassification classification) {
log.info("查询资产分类树结构, 参数: {}", classification);
try {
List<TreeNode> tree = assetClassificationService.selectTree(classification);
log.info("查询资产分类树结构成功, 返回节点数: {}", tree != null ? tree.size() : 0);
// 检查树结构中的根节点是否有children属性
if (tree != null && !tree.isEmpty()) {
for (TreeNode node : tree) {
log.debug("根节点: {} - {} 子节点数: {}",
node.getId(), node.getLabel(),
node.getChildren() != null ? node.getChildren().size() : 0);
}
}
return CpspReponse.ok(tree);
} catch (Exception e) {
log.error("查询资产分类树结构出错", e);
return CpspReponse.error("999999", "查询资产分类树结构出错: " + e.getMessage());
}
}
/**
* 根据父ID查询子节点列表
*/
@GetMapping("/children/{parentId}")
public CpspReponse<PageInfo<AssetClassification>> children(@NotNull @PathVariable Long parentId,
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize) {
log.info("查询子节点列表, parentId: {}, pageNum: {}, pageSize: {}",
parentId, pageNum, pageSize);
PageHelper.startPage(pageNum, pageSize);
List<AssetClassification> list = assetClassificationService.selectChildrenByParentId(parentId);
// 使用GitHub的PageInfo获取分页信息
com.github.pagehelper.PageInfo<AssetClassification> githubPageInfo = new com.github.pagehelper.PageInfo<>(list);
// 转换为自定义的PageInfo
PageInfo<AssetClassification> pageInfo = new PageInfo<>(
githubPageInfo.getPageSize(),
githubPageInfo.getPageNum(),
githubPageInfo.getTotal(),
githubPageInfo.getList());
return CpspReponse.ok(pageInfo);
}
/**
* 获取资产分类详细信息
*/
@GetMapping("/{id}")
public CpspReponse<AssetClassification> getInfo(@NotNull @PathVariable Long id) {
log.info("获取资产分类详情, id: {}", id);
AssetClassification classification = assetClassificationService.selectById(id);
if (classification == null) {
log.warn("获取资产分类详情失败,未找到资产分类, id: {}", id);
return CpspReponse.error("999999", "未找到资产分类");
}
return CpspReponse.ok(classification);
}
/**
* 新增资产分类
*/
@PostMapping("/create")
public CpspReponse<AssetClassification> create(@Validated @RequestBody AssetClassification classification) {
log.info("新增资产分类, 参数: {}", classification);
if (classification.getCreateUserId() == null || classification.getCreateUserId().isEmpty()) {
classification.setCreateUserId("system");
}
// 校验状态值是否有效
if (!StatusEnum.isValid(classification.getStatus())) {
return CpspReponse.error("999999", "无效的状态值");
}
// 校验编码是否唯一
if (assetClassificationService.checkCodeUnique(classification)) {
return CpspReponse.error("999999", "资产分类编码已存在");
}
if (assetClassificationService.checkClassificationNameExists(classification)) {
return CpspReponse.error("999999", "资产分类名称已存在");
}
// 设置创建时间
classification.setCreateTime(new Date());
return CpspReponse.ok(assetClassificationService.insert(classification));
}
/**
* 修改资产分类
*/
@PostMapping("/update/{id}")
public CpspReponse<AssetClassification> update(@NotNull @PathVariable Long id,
@Validated @RequestBody AssetClassification classification) {
log.info("修改资产分类, id: {}, 参数: {}", id, classification);
classification.setId(id);
// 设置必要的默认值
if (classification.getLastModUserId() == null || classification.getLastModUserId().isEmpty()) {
classification.setLastModUserId("system");
}
// 校验状态值是否有效
if (classification.getStatus() != null && !StatusEnum.isValid(classification.getStatus())) {
return CpspReponse.error("999999", "无效的状态值");
}
// 校验编码是否唯一
if (assetClassificationService.checkCodeUnique(classification)) {
return CpspReponse.error("999999", "资产分类编码已存在");
}
if (assetClassificationService.checkClassificationNameExists(classification)) {
return CpspReponse.error("999999", "资产分类名称已存在");
}
// 设置修改时间
classification.setLastModTime(new Date());
return CpspReponse.ok(assetClassificationService.update(classification));
}
/**
* 禁用资产分类
*/
@PostMapping("/{id}/status/disable")
public CpspReponse<Void> disable(@NotNull @PathVariable Long id) {
log.info("禁用资产分类, id: {}", id);
try {
// 使用专用的状态更新方法
String lastModUserId ="system";
boolean success = assetClassificationService.updateStatus(id, StatusEnum.DISABLED.getCode(), lastModUserId);
if (success) {
return CpspReponse.ok();
} else {
return CpspReponse.error("999999", "禁用资产分类失败");
}
} catch (Exception e) {
log.error("禁用资产分类异常", e);
return CpspReponse.error("999999", "禁用资产分类失败");
}
}
/**
* 启用资产分类
*/
@PostMapping("/{id}/status/enable")
public CpspReponse<Void> enable(@NotNull @PathVariable Long id) {
log.info("启用资产分类, id: {}, lastModUserId: {}", id);
try {
// 使用专用的状态更新方法
String lastModUserId ="system";
boolean success = assetClassificationService.updateStatus(id, StatusEnum.ENABLED.getCode(), lastModUserId);
if (success) {
return CpspReponse.ok();
} else {
return CpspReponse.error("999999", "启用资产分类失败");
}
} catch (Exception e) {
log.error("启用资产分类异常", e);
return CpspReponse.error("999999", "启用资产分类失败");
}
}
/**
* 删除资产分类
*/
@PostMapping("/delete/{id}")
public CpspReponse<Void> delete(@NotNull @PathVariable Long id) {
log.info("删除资产分类, id: {}", id);
try {
// 直接执行删除无需先查询
String lastModUserId ="system";
assetClassificationService.deleteById(id, lastModUserId);
return CpspReponse.ok();
} catch (Exception e) {
log.error("删除资产分类异常", e);
return CpspReponse.error("999999", "删除资产分类失败");
}
}
/**
* 批量删除资产分类
*/
@PostMapping("/deleteBatch")
public CpspReponse<Void> deleteBatch(@NotEmpty @RequestBody List<Long> ids) {
log.info("批量删除资产分类, ids: {}", ids);
String lastModUserId = "system";
assetClassificationService.deleteBatchIds(ids, lastModUserId);
return CpspReponse.ok();
}
}

View File

@ -0,0 +1,366 @@
package com.psbc.cpsp.eden.controller.asset;
import com.github.pagehelper.PageHelper;
import com.psbc.cpsp.entity.PageInfo;
import com.psbc.cpsp.eden.common.entity.Asset;
import com.psbc.cpsp.eden.common.entity.AssetLabel;
import com.psbc.cpsp.entity.CpspReponse;
import com.psbc.cpsp.eden.common.utils.AssetImportListener;
import com.psbc.cpsp.eden.common.utils.ExcelUtils;
import com.psbc.cpsp.eden.common.vo.AssetExportVO;
import com.psbc.cpsp.eden.common.vo.AssetImportVO;
import com.psbc.cpsp.eden.service.asset.IAssetClassificationService;
import com.psbc.cpsp.eden.service.asset.IAssetLabelService;
import com.psbc.cpsp.eden.service.asset.IAssetLocationService;
import com.psbc.cpsp.eden.service.asset.IAssetService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* 资产Controller
*
* @author eden
*/
@Slf4j
@Validated
@RestController
@RequestMapping("/asset")
public class AssetController {
@Autowired
@Qualifier("basicAssetService")
private IAssetService assetService;
@Autowired
private IAssetClassificationService assetClassificationService;
@Autowired
private IAssetLocationService assetLocationService;
@Autowired
private HttpServletRequest request;
@Autowired
private IAssetLabelService assetLabelService;
/**
* 查询资产列表
*/
@GetMapping("/list")
public CpspReponse<PageInfo<Asset>> list(Asset asset,
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize,
@RequestParam(required = false) String adminUserId) {
log.info("查询资产列表, 参数: {}, pageNum: {}, pageSize: {}", asset, pageNum, pageSize);
// 设置管理员ID查询条件
if (adminUserId != null && !adminUserId.isEmpty()) {
asset.setAdminUserId(adminUserId);
log.info("设置管理员ID查询条件: {}", adminUserId);
}
PageHelper.startPage(pageNum, pageSize);
List<Asset> list = assetService.selectList(asset);
// 使用GitHub的PageInfo获取分页信息
com.github.pagehelper.PageInfo<Asset> githubPageInfo = new com.github.pagehelper.PageInfo<>(list);
// 转换为自定义的PageInfo
PageInfo<Asset> pageInfo = new PageInfo<>(
githubPageInfo.getPageSize(),
githubPageInfo.getPageNum(),
githubPageInfo.getTotal(),
githubPageInfo.getList());
return CpspReponse.ok(pageInfo);
}
/**
* 获取资产详细信息
*/
@GetMapping("/{assetCode}")
public CpspReponse<Asset> getInfo(@NotBlank @PathVariable String assetCode) {
log.info("获取资产详细信息, assetCode: {}", assetCode);
// 根据资产编码查询资产
Asset asset = assetService.selectByCode(assetCode);
if (asset == null) {
return CpspReponse.error("999999", "未找到资产");
}
return CpspReponse.ok(asset);
}
/**
* 新增资产
*/
@PostMapping("/create")
public CpspReponse<Asset> create(@Validated @RequestBody Asset asset) {
log.info("新增资产, 参数: {}", asset);
// 设置默认值
if (asset.getCreateUserId() == null || asset.getCreateUserId().isEmpty()) {
asset.setCreateUserId("1");
}
// 校验编码是否唯一
log.info("开始校验资产编码唯一性: {}", asset.getAssetCode());
boolean isUnique = assetService.checkCodeUnique(asset);
if (!isUnique) {
log.warn("资产编码已存在: {}", asset.getAssetCode());
return CpspReponse.error("999999", "资产编码已存在");
}
log.info("资产编码唯一性校验通过");
// 设置创建时间
asset.setCreateTime(new Date());
try {
return CpspReponse.ok(assetService.insert(asset));
} catch (RuntimeException e) {
log.error("新增资产失败", e);
return CpspReponse.error("999999", e.getMessage());
}
}
/**
* 修改资产
*/
@PostMapping("/update/{assetCode}")
public CpspReponse<Asset> update(@NotBlank @PathVariable String assetCode,
@Validated @RequestBody Asset asset) {
log.info("修改资产, assetCode: {}, 参数: {}", assetCode, asset);
// 根据资产编码获取原资产信息
Asset existingAsset = assetService.selectByCode(assetCode);
if (existingAsset == null) {
return CpspReponse.error("999999", "未找到资产");
}
// 设置资产ID
asset.setId(existingAsset.getId());
// 设置资产编码
asset.setAssetCode(assetCode);
// 设置必要的默认值
if (asset.getLastModUserId() == null || asset.getLastModUserId().isEmpty()) {
asset.setLastModUserId("1");
}
// 设置修改时间
asset.setLastModTime(new Date());
return CpspReponse.ok(assetService.update(asset));
}
/**
* 更新资产状态
*/
@PostMapping("/{id}/status/{status}")
public CpspReponse<Void> updateStatus(@NotNull @PathVariable Long id,
@NotBlank @PathVariable String status) {
log.info("更新资产状态, id: {}, status: {}", id, status);
try {
// 使用专用的状态更新方法
String lastModUserId = "system";
boolean success = assetService.updateStatus(id, status, lastModUserId);
if (success) {
return CpspReponse.ok();
} else {
return CpspReponse.error("999999", "更新资产状态失败");
}
} catch (Exception e) {
log.error("更新资产状态异常", e);
return CpspReponse.error("999999", "更新资产状态失败");
}
}
/**
* 删除资产
*/
@PostMapping("/delete/{assetCode}")
public CpspReponse<Void> delete(@NotBlank @PathVariable String assetCode) {
log.info("删除资产, assetCode: {}", assetCode);
// 根据资产编码查询资产
Asset asset = assetService.selectByCode(assetCode);
if (asset == null) {
return CpspReponse.error("999999", "未找到资产");
}
// 删除资产
assetService.deleteById(asset.getId(), getCurrentUserId());
return CpspReponse.ok();
}
/**
* 批量删除资产
*/
@PostMapping("/batch/delete")
public CpspReponse<Void> deleteBatch(@NotEmpty @RequestBody List<Long> ids) {
log.info("批量删除资产, ids: {}", ids);
assetService.deleteBatchIds(ids, getCurrentUserId());
return CpspReponse.ok();
}
/**
* 校验资产编码唯一性
*/
@GetMapping("/check/code")
public CpspReponse<Boolean> checkCode(Asset asset) {
log.info("校验资产编码唯一性, asset: {}", asset);
return CpspReponse.ok(assetService.checkCodeUnique(asset));
}
/**
* 导入资产数据
*/
@PostMapping("/import")
public CpspReponse<String> importAsset(@RequestPart("file") MultipartFile file,
@RequestParam(defaultValue = "false") boolean updateSupport) {
log.info("导入资产数据, 文件名: {}, 大小: {}, 是否更新已存在数据: {}",
file.getOriginalFilename(), file.getSize(), updateSupport);
try {
// 使用EasyExcel导入数据
List<AssetImportVO> importList = ExcelUtils.importExcel(file, AssetImportVO.class,
new AssetImportListener(assetClassificationService, assetLocationService, request));
log.info("导入数据解析完成,共 {} 条记录", importList.size());
// 将导入VO转换为实体并保存
String operName = getUsername();
String message = assetService.importAsset(ExcelUtils.convertImportData(importList), updateSupport,
operName);
return CpspReponse.ok(message);
} catch (Exception e) {
log.error("导入资产数据异常", e);
return CpspReponse.error("999999", "导入资产数据失败:" + e.getMessage());
}
}
/**
* 导出资产数据
*/
@GetMapping("/export")
public void exportAsset(Asset asset, HttpServletResponse response,
@RequestParam(required = false) String maintenanceExpired,
@RequestParam(required = false) String adminUserId) {
log.info("导出资产数据, 参数: {}, maintenanceExpired: {}", asset, maintenanceExpired);
// 设置维护到期筛选条件
if (maintenanceExpired != null && !maintenanceExpired.isEmpty()) {
asset.setMaintenanceExpired(maintenanceExpired);
log.info("设置维护到期筛选条件: {}", maintenanceExpired);
}
// 设置管理员ID查询条件
if (adminUserId != null && !adminUserId.isEmpty()) {
asset.setAdminUserId(adminUserId);
log.info("设置管理员ID查询条件: {}", adminUserId);
}
// 查询导出数据
List<AssetExportVO> list = assetService.exportAsset(asset);
log.info("查询到符合条件的资产数据 {} 条", list.size());
// 设置导出文件名
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String fileName = "资产清单" + sdf.format(new Date());
// 获取每个资产的公司名称和管理员名称
for (AssetExportVO vo : list) {
Asset assetTemp = new Asset();
assetTemp.setCompanyId(vo.getCompanyId());
String companyName = assetService.getAssetCompanyName(assetTemp);
vo.setCompanyName(companyName);
assetTemp.setAdminUserId(vo.getAdminUserId());
String adminName = assetService.getAssetAdminName(assetTemp);
vo.setAdminUserName(adminName);
}
// 导出Excel
ExcelUtils.exportExcel(list, "资产清单", AssetExportVO.class, fileName, response);
}
/**
* 获取用户名
*/
private String getUsername() {
// 从请求头或Session中获取用户信息
String username = request.getHeader("X-Username");
if (username == null || username.isEmpty()) {
username = "admin"; // 默认用户名
}
return username;
}
/**
* 获取当前用户ID
*/
private String getCurrentUserId() {
// 返回固定的用户ID "admin"
log.debug("使用固定用户ID: admin");
return "admin";
}
/**
* 获取标签信息
*/
@GetMapping("/label")
public CpspReponse<AssetLabel> getLabel() {
log.info("获取标签信息");
AssetLabel label = assetLabelService.selectByAll();
if (label == null) {
// 如果未找到标签创建默认标签
label = new AssetLabel();
label.setTemplateType("1");
label.setLabelItems("1,2,3");
label.setPaperType("1");
label.setCreateUserId(getCurrentUserId());
label.setCreateTime(new Date());
label.setDelFlag("0");
assetLabelService.insert(label);
log.info("创建默认标签: {}", label);
}
return CpspReponse.ok(label);
}
/**
* 下载资产导入模板
*/
@GetMapping("/template/download")
public void downloadTemplate(HttpServletResponse response) {
log.info("下载资产导入模板");
try {
// 设置响应头
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String fileName = "资产导入模板.xlsx";
fileName = java.net.URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
// 填充模板数据
List<AssetImportVO> list = ExcelUtils.createExcelTemplate();
// 导出模板
ExcelUtils.exportExcel(list, "资产数据", AssetImportVO.class, response);
} catch (IOException e) {
log.error("下载模板失败", e);
}
}
}

View File

@ -0,0 +1,109 @@
package com.psbc.cpsp.eden.controller.asset;
import com.psbc.cpsp.entity.CpspReponse;
import com.psbc.cpsp.eden.common.entity.AssetLabel;
import com.psbc.cpsp.eden.service.asset.IAssetLabelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
/**
* 资产标签Controller
*
* @author eden
*/
@Slf4j
@Validated
@RestController
@RequestMapping("/asset/label")
public class AssetLabelController {
@Autowired
private IAssetLabelService assetLabelService;
/**
* 查询资产标签列表
* 每个租户只能有一条记录
*/
@GetMapping("/list")
public CpspReponse<AssetLabel> list() {
log.info("查询资产标签列表");
// 根据租户ID查询标签
AssetLabel label = assetLabelService.selectByAll();
if (label == null) {
log.info("当前租户未配置资产标签,请前往配置");
}
return CpspReponse.ok(label);
}
/**
* 新增资产标签
* 每个租户只能有一条记录
*/
@PostMapping("/create")
public CpspReponse<AssetLabel> create(@Validated @RequestBody AssetLabel label) {
log.info("新增资产标签, 参数: {}", label);
// 验证标签数据
try {
assetLabelService.validateAssetLabel(label);
} catch (Exception e) {
return CpspReponse.error("999999", e.getMessage());
}
// 检查租户是否已有标签
AssetLabel existingLabel = assetLabelService.selectByAll();
if (existingLabel != null) {
// 如果租户已存在标签则更新标签信息
label.setId(existingLabel.getId());
label.setLastModUserId("system");
label.setLastModTime(new Date());
return CpspReponse.ok(assetLabelService.update(label));
}
// 设置默认值
if (label.getCreateUserId() == null || label.getCreateUserId().isEmpty()) {
label.setCreateUserId("system");
}
// 设置创建时间
label.setCreateTime(new Date());
return CpspReponse.ok(assetLabelService.insert(label));
}
/**
* 修改资产标签
* 根据当前租户ID修改标签
*/
@PostMapping("/update")
public CpspReponse<AssetLabel> update(@Validated @RequestBody AssetLabel label) {
log.info("修改资产标签, 参数: {}", label);
// 根据租户ID查询现有标签
AssetLabel existingLabel = assetLabelService.selectByAll();
if (existingLabel == null) {
return CpspReponse.error("999999", "当前租户未配置资产标签");
}
// 验证标签数据
try {
assetLabelService.validateAssetLabel(label);
} catch (Exception e) {
return CpspReponse.error("999999", e.getMessage());
}
// 设置ID和租户ID
label.setId(existingLabel.getId());
// 设置修改人和修改时间
label.setLastModUserId("system");
label.setLastModTime(new Date());
return CpspReponse.ok(assetLabelService.update(label));
}
}

View File

@ -0,0 +1,243 @@
package com.psbc.cpsp.eden.controller.asset;
import com.psbc.cpsp.entity.CpspReponse;
import com.psbc.cpsp.eden.common.entity.AssetLocation;
import com.psbc.cpsp.eden.enums.StatusEnum;
import com.psbc.cpsp.eden.common.vo.TreeNode;
import com.psbc.cpsp.eden.service.asset.IAssetLocationService;
import com.github.pagehelper.PageHelper;
import com.psbc.cpsp.entity.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;
/**
* 资产位置Controller
*
* @author eden
*/
@Slf4j
@Validated
@RestController
@RequestMapping("/asset/locations")
public class AssetLocationController {
@Autowired
private IAssetLocationService assetLocationService;
/**
* 查询资产位置列表
*/
@GetMapping("/list")
public CpspReponse<PageInfo<AssetLocation>> list(AssetLocation location,
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize) {
log.info("查询资产位置列表, 参数: {}, pageNum: {}, pageSize: {}", location, pageNum, pageSize);
PageHelper.startPage(pageNum, pageSize);
List<AssetLocation> list = assetLocationService.selectList(location);
// 使用GitHub的PageInfo获取分页信息
com.github.pagehelper.PageInfo<AssetLocation> githubPageInfo = new com.github.pagehelper.PageInfo<>(list);
// 转换为自定义的PageInfo
PageInfo<AssetLocation> pageInfo = new PageInfo<>(
githubPageInfo.getPageSize(),
githubPageInfo.getPageNum(),
githubPageInfo.getTotal(),
githubPageInfo.getList());
return CpspReponse.ok(pageInfo);
}
/**
* 查询资产位置树结构
*/
@GetMapping("/tree")
public CpspReponse<List<TreeNode>> tree(AssetLocation location) {
log.info("查询资产位置树结构, 参数: {}", location);
return CpspReponse.ok(assetLocationService.selectTree(location));
}
/**
* 根据父ID查询子节点列表
*/
@GetMapping("/children/{parentId}")
public CpspReponse<PageInfo<AssetLocation>> children(@NotNull @PathVariable Long parentId,
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize) {
log.info("查询子节点列表, parentId: {}, pageNum: {}, pageSize: {}",
parentId, pageNum, pageSize);
PageHelper.startPage(pageNum, pageSize);
List<AssetLocation> list = assetLocationService.selectChildrenByParentId(parentId);
// 使用GitHub的PageInfo获取分页信息
com.github.pagehelper.PageInfo<AssetLocation> githubPageInfo = new com.github.pagehelper.PageInfo<>(list);
// 转换为自定义的PageInfo
PageInfo<AssetLocation> pageInfo = new PageInfo<>(
githubPageInfo.getPageSize(),
githubPageInfo.getPageNum(),
githubPageInfo.getTotal(),
githubPageInfo.getList());
return CpspReponse.ok(pageInfo);
}
/**
* 获取资产位置详细信息
*/
@GetMapping("/{id}")
public CpspReponse<AssetLocation> getInfo(@NotNull @PathVariable Long id) {
log.info("获取资产位置详情, id: {}", id);
AssetLocation location = assetLocationService.selectById(id);
if (location == null) {
log.warn("获取资产位置详情失败,未找到资产位置, id: {}", id);
return CpspReponse.error("999999", "未找到资产位置");
}
return CpspReponse.ok(location);
}
/**
* 新增资产位置
*/
@PostMapping("/create")
public CpspReponse<AssetLocation> create(@Validated @RequestBody AssetLocation location) {
log.info("新增资产位置, 参数: {}", location);
// 设置必要的默认值
if (location.getStatus() == null || location.getStatus().isEmpty()) {
location.setStatus(StatusEnum.ENABLED.getCode());
}
if (location.getCreateUserId() == null || location.getCreateUserId().isEmpty()) {
location.setCreateUserId("system");
}
// 校验状态值是否有效
if (!StatusEnum.isValid(location.getStatus())) {
return CpspReponse.error("999999", "无效的状态值");
}
// 校验编码是否唯一
if (assetLocationService.checkCodeUnique(location)) {
return CpspReponse.error("999999", "资产位置编码已存在");
}
// 校验名称是否唯一
if (assetLocationService.checkNameUnique(location)) {
return CpspReponse.error("999999", "资产位置名称已存在");
}
// 设置创建时间
location.setCreateTime(new Date());
return CpspReponse.ok(assetLocationService.insert(location));
}
/**
* 修改资产位置
*/
@PostMapping("/update/{id}")
public CpspReponse<AssetLocation> update(@NotNull @PathVariable Long id,
@Validated @RequestBody AssetLocation location) {
log.info("修改资产位置, id: {}, 参数: {}", id, location);
location.setId(Long.valueOf(id));
// 设置必要的默认值
if (location.getLastModUserId() == null || location.getLastModUserId().isEmpty()) {
location.setLastModUserId("system");
}
// 校验状态值是否有效
if (location.getStatus() != null && !StatusEnum.isValid(location.getStatus())) {
return CpspReponse.error("999999", "无效的状态值");
}
// 校验编码是否唯一
if (assetLocationService.checkCodeUnique(location)) {
return CpspReponse.error("999999", "资产位置编码已存在");
}
// 校验名称是否唯一
if (assetLocationService.checkNameUnique(location)) {
return CpspReponse.error("999999", "资产位置名称已存在");
}
// 设置修改时间
location.setLastModTime(new Date());
return CpspReponse.ok(assetLocationService.update(location));
}
/**
* 禁用资产位置
*/
@PostMapping("/{id}/status/disable")
public CpspReponse<Void> disable(@NotNull @PathVariable Long id) {
log.info("禁用资产位置, id: {}", id);
try {
// 使用专用的状态更新方法
String lastModUserId = "system";
boolean success = assetLocationService.updateStatus(id, StatusEnum.DISABLED.getCode(), lastModUserId);
if (success) {
return CpspReponse.ok();
} else {
return CpspReponse.error("999999", "禁用资产位置失败");
}
} catch (Exception e) {
log.error("禁用资产位置异常", e);
return CpspReponse.error("999999", "禁用资产位置失败");
}
}
/**
* 启用资产位置
*/
@PostMapping("/{id}/status/enable")
public CpspReponse<Void> enable(@NotNull @PathVariable Long id) {
log.info("启用资产位置, id: {}", id);
try {
// 使用专用的状态更新方法
String lastModUserId = "system";
boolean success = assetLocationService.updateStatus(id, StatusEnum.ENABLED.getCode(), lastModUserId);
if (success) {
return CpspReponse.ok();
} else {
return CpspReponse.error("999999", "启用资产位置失败");
}
} catch (Exception e) {
log.error("启用资产位置异常", e);
return CpspReponse.error("999999", "启用资产位置失败");
}
}
/**
* 删除资产位置
*/
@PostMapping("/delete/{id}")
public CpspReponse<Void> delete(@NotNull @PathVariable Long id) {
log.info("删除资产位置, id: {}", id);
String lastModUserId = "system";
assetLocationService.deleteById(id, lastModUserId);
return CpspReponse.ok();
}
/**
* 批量删除资产位置
*/
@PostMapping("/batch/delete")
public CpspReponse<Void> deleteBatch(@NotEmpty @RequestBody List<Long> ids) {
log.info("批量删除资产位置, ids: {}", ids);
String lastModUserId = "system";
assetLocationService.deleteBatchIds(ids, lastModUserId);
return CpspReponse.ok();
}
/**
* 检查编码唯一性
*/
@GetMapping("/check/code")
public CpspReponse<Boolean> checkCode(AssetLocation location) {
log.info("检查编码唯一性, 参数: {}", location);
return CpspReponse.ok(assetLocationService.checkCodeUnique(location));
}
}

View File

@ -0,0 +1,35 @@
package com.psbc.cpsp.eden.enums;
import lombok.Getter;
/**
* 资产状态枚举
*/
@Getter
public enum AssetStatusEnum {
IDLE("0", "空闲"),
IN_USE("1", "在用"),
BORROWED("2", "借用"),
DISPATCHING("3", "派发中"),
RETURNING("4", "退库中"),
LENDING("5", "借出中"),
RETURNING_BORROWED("6", "归还中"),
MAINTAINING("7", "维修中");
private final String code;
private final String desc;
AssetStatusEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public static boolean isValid(String code) {
for (AssetStatusEnum status : values()) {
if (status.getCode().equals(code)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,28 @@
package com.psbc.cpsp.eden.enums;
import lombok.Getter;
/**
* 删除标志枚举
*/
@Getter
public enum DelFlagEnum {
NORMAL("0", "正常"),
DELETED("1", "已删除");
private final String code;
private final String desc;
DelFlagEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public static boolean isNormal(String code) {
return NORMAL.getCode().equals(code);
}
public static boolean isDeleted(String code) {
return DELETED.getCode().equals(code);
}
}

View File

@ -0,0 +1,38 @@
package com.psbc.cpsp.eden.enums;
import lombok.Getter;
/**
* 标签项目枚举
*/
@Getter
public enum LabelItemEnum {
ASSET_NAME("1", "资产名称"),
ASSET_CLASSIFICATION("2", "资产分类"),
ASSET_CODE("3", "资产编码"),
ASSET_LOCATION("4", "资产位置"),
BRAND("5", "品牌"),
MODEL("6", "型号"),
SERIAL_NUMBER("7", "设备序列号"),
ADMIN("8", "管理员"),
MAINTENANCE_DUE_DATE("9", "保养到期时间"),
MAINTENANCE_DESCRIPTION("10", "保养说明"),
DEPARTMENT("11", "使用部门");
private final String code;
private final String name;
LabelItemEnum(String code, String name) {
this.code = code;
this.name = name;
}
public static LabelItemEnum getByCode(String code) {
for (LabelItemEnum item : values()) {
if (item.getCode().equals(code)) {
return item;
}
}
return null;
}
}

View File

@ -0,0 +1,33 @@
package com.psbc.cpsp.eden.enums;
import lombok.Getter;
/**
* 标签模板类型枚举
*/
@Getter
public enum LabelTemplateTypeEnum {
TEMPLATE_1("1", "模板1", 1),
TEMPLATE_2("2", "模板2", 2),
TEMPLATE_3("3", "模板3", 3),
TEMPLATE_4("4", "模板4", 4);
private final String code;
private final String name;
private final int maxItems;
LabelTemplateTypeEnum(String code, String name, int maxItems) {
this.code = code;
this.name = name;
this.maxItems = maxItems;
}
public static LabelTemplateTypeEnum getByCode(String code) {
for (LabelTemplateTypeEnum type : values()) {
if (type.getCode().equals(code)) {
return type;
}
}
return null;
}
}

View File

@ -0,0 +1,34 @@
package com.psbc.cpsp.eden.enums;
import lombok.Getter;
/**
* 纸张类型枚举
*/
@Getter
public enum PaperTypeEnum {
SPECIAL_PAPER("1", "标签专用纸"),
A4_TWO_COLUMN("2", "A4(2列标签)"),
A4_THREE_COLUMN("3", "A4(3列标签)");
private final String code;
private final String name;
PaperTypeEnum(String code, String name) {
this.code = code;
this.name = name;
}
public static PaperTypeEnum getByCode(String code) {
for (PaperTypeEnum type : values()) {
if (type.getCode().equals(code)) {
return type;
}
}
return null;
}
public boolean isSpecialPaper() {
return this == SPECIAL_PAPER;
}
}

View File

@ -0,0 +1,78 @@
package com.psbc.cpsp.eden.enums;
import com.psbc.cpsp.eden.constants.AssetConstants;
/**
* 购置方式枚举
*/
public enum PurchaseTypeEnum {
/**
* 采购
*/
BUY(AssetConstants.PURCHASE_TYPE_BUY, AssetConstants.PURCHASE_TYPE_BUY_NAME),
/**
* 租赁
*/
RENT(AssetConstants.PURCHASE_TYPE_RENT, AssetConstants.PURCHASE_TYPE_RENT_NAME);
private final String code;
private final String name;
PurchaseTypeEnum(String code, String name) {
this.code = code;
this.name = name;
}
/**
* 获取编码
*/
public String getCode() {
return code;
}
/**
* 获取名称
*/
public String getName() {
return name;
}
/**
* 根据编码获取枚举对象
*
* @param code 编码
* @return 枚举对象
*/
public static PurchaseTypeEnum getByCode(String code) {
for (PurchaseTypeEnum type : values()) {
if (type.getCode().equals(code)) {
return type;
}
}
return null;
}
/**
* 获取显示名称
*
* @param code 编码
* @return 显示名称
*/
public static String getNameByCode(String code) {
PurchaseTypeEnum type = getByCode(code);
return type != null ? type.getName() : code;
}
/**
* 校验编码是否有效
*
* @param code 编码
* @return 是否有效
*/
public static boolean isValid(String code) {
return getByCode(code) != null;
}
}

View File

@ -0,0 +1,29 @@
package com.psbc.cpsp.eden.enums;
import lombok.Getter;
/**
* 状态枚举
*/
@Getter
public enum StatusEnum {
DISABLED("0", "禁用"),
ENABLED("1", "启用");
private final String code;
private final String desc;
StatusEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public static boolean isValid(String code) {
for (StatusEnum status : values()) {
if (status.getCode().equals(code)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,118 @@
package com.psbc.cpsp.eden.mapper.asset;
import com.psbc.cpsp.eden.common.entity.AssetClassification;
import com.psbc.cpsp.eden.common.vo.TreeNode;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Date;
/**
* 资产分类Mapper接口
*/
@Mapper
public interface AssetClassificationMapper {
/**
* 新增资产分类
*
* @param classification 资产分类信息
* @return 影响行数
*/
int insert(AssetClassification classification);
/**
* 修改资产分类
*
* @param classification 资产分类信息
* @return 影响行数
*/
int update(AssetClassification classification);
/**
* 更新资产分类状态
*
* @param id 资产分类ID
* @param status 状态
* @param lastModUserId 最后修改人ID
* @param lastModTime 最后修改时间
* @return 影响行数
*/
int updateStatus(@Param("id") Long id, @Param("status") String status,
@Param("lastModUserId") String lastModUserId,
@Param("lastModTime") Date lastModTime);
/**
* 根据ID查询资产分类详情
*
* @param id 资产分类ID
* @return 资产分类信息
*/
AssetClassification selectById(@Param("id") Long id);
/**
* 查询资产分类列表
*
* @param classification 资产分类信息
* @return 资产分类集合
*/
List<AssetClassification> selectList(AssetClassification classification);
/**
* 逻辑删除资产分类
*
* @param id 资产分类ID
* @param lastModUserId 最后修改人ID
* @return 影响行数
*/
int deleteById(@Param("id") Long id, @Param("lastModUserId") String lastModUserId);
/**
* 批量逻辑删除资产分类
*
* @param ids 资产分类ID集合
* @param lastModUserId 最后修改人ID
* @return 影响行数
*/
int deleteBatchIds(@Param("ids") List<Long> ids, @Param("lastModUserId") String lastModUserId);
/**
* 查询资产分类树结构
*
* @param classification 资产分类信息
* @return 树节点集合
*/
List<TreeNode> selectTree(AssetClassification classification);
/**
* 根据父ID查询所有子节点包含子节点的子节点
*
* @param parentId 父节点ID
* @return 子节点集合
*/
List<AssetClassification> selectChildrenByParentId(@Param("parentId") Long parentId);
/**
* 校验分类编码是否唯一
*
* @param classificationCode 分类编码
* @param id 分类ID
* @return 结果
*/
int checkCodeUnique(@Param("classificationCode") String classificationCode, @Param("id") Long id);
/**
* 根据分类名称查询分类ID
*
* @param classificationName 分类名称
* @return 分类ID
*/
Long selectIdByName(@Param("classificationName") String classificationName);
/**
* 校验分类名称是否存在
*
* @param classificationName 分类名称
* @return 结果
*/
int checkNameExists(@Param("classificationName") String classificationName, @Param("id") Long id);
}

View File

@ -0,0 +1,62 @@
package com.psbc.cpsp.eden.mapper.asset;
import com.psbc.cpsp.eden.common.entity.AssetLabel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 资产标签Mapper接口
*/
@Mapper
public interface AssetLabelMapper {
/**
* 新增资产标签
*
* @param label 资产标签信息
* @return 影响行数
*/
int insert(AssetLabel label);
/**
* 修改资产标签
*
* @param label 资产标签信息
* @return 影响行数
*/
int update(AssetLabel label);
/**
* 根据ID查询资产标签详情
*
* @param id 资产标签ID
* @return 资产标签信息
*/
AssetLabel selectById(@Param("id") Long id);
/**
* 根据条件查询资产标签列表
*
* @param label 查询条件
* @return 资产标签列表
*/
List<AssetLabel> selectList(AssetLabel label);
/**
* 逻辑删除资产标签
*
* @param id 资产标签ID
* @param lastModUserId 最后修改人ID
* @return 影响行数
*/
int deleteById(@Param("id") Long id, @Param("lastModUserId") String lastModUserId);
/**
* 批量逻辑删除资产标签
*
* @param ids 资产标签ID集合
* @param lastModUserId 最后修改人ID
* @return 影响行数
*/
int deleteBatchIds(@Param("ids") List<Long> ids, @Param("lastModUserId") String lastModUserId);
}

View File

@ -0,0 +1,121 @@
package com.psbc.cpsp.eden.mapper.asset;
import com.psbc.cpsp.eden.common.entity.AssetLocation;
import com.psbc.cpsp.eden.common.vo.TreeNode;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Date;
/**
* 资产位置Mapper接口
*/
@Mapper
public interface AssetLocationMapper {
/**
* 新增资产位置
*
* @param location 资产位置信息
* @return 影响行数
*/
int insert(AssetLocation location);
/**
* 修改资产位置
*
* @param location 资产位置信息
* @return 影响行数
*/
int update(AssetLocation location);
/**
* 更新资产位置状态
*
* @param id 资产位置ID
* @param status 状态
* @param lastModUserId 最后修改人ID
* @param lastModTime 最后修改时间
* @return 影响行数
*/
int updateStatus(@Param("id") Long id, @Param("status") String status,
@Param("lastModUserId") String lastModUserId,
@Param("lastModTime") Date lastModTime);
/**
* 根据ID查询资产位置详情
*
* @param id 资产位置ID
* @return 资产位置信息
*/
AssetLocation selectById(@Param("id") Long id);
/**
* 查询资产位置列表
*
* @param location 资产位置信息
* @return 资产位置集合
*/
List<AssetLocation> selectList(AssetLocation location);
/**
* 逻辑删除资产位置
*
* @param id 资产位置ID
* @param lastModUserId 最后修改人ID
* @return 影响行数
*/
int deleteById(@Param("id") Long id, @Param("lastModUserId") String lastModUserId);
/**
* 批量逻辑删除资产位置
*
* @param ids 资产位置ID集合
* @param lastModUserId 最后修改人ID
* @return 影响行数
*/
int deleteBatchIds(@Param("ids") List<Long> ids, @Param("lastModUserId") String lastModUserId);
/**
* 查询资产位置树结构
*
* @param location 资产位置信息
* @return 树节点集合
*/
List<TreeNode> selectTree(AssetLocation location);
/**
* 根据父ID查询所有子节点包含子节点的子节点
*
* @param parentId 父节点ID
* @return 子节点集合
*/
List<AssetLocation> selectChildrenByParentId(@Param("parentId") Long parentId);
/**
* 校验位置编码是否唯一
*
* @param locationCode 位置编码
* @param id 位置ID
* @return 结果
*/
int checkCodeUnique(@Param("locationCode") String locationCode, @Param("id") Long id);
/**
* 校验位置名称是否唯一
*
* @param locationName 位置名称
* @param id 位置ID
* @return 结果
*/
int checkNameUnique(@Param("locationName") String locationName, @Param("id") Long id);
/**
* 根据位置名称查询位置ID
*
* @param locationName 位置名称
* @return 位置ID
*/
Long selectIdByName(@Param("locationName") String locationName);
}

View File

@ -0,0 +1,101 @@
package com.psbc.cpsp.eden.mapper.asset;
import com.psbc.cpsp.eden.common.entity.Asset;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Date;
/**
* 资产Mapper接口
*/
@Mapper
public interface AssetMapper {
/**
* 新增资产
*
* @param asset 资产信息
* @return 影响行数
*/
int insert(Asset asset);
/**
* 修改资产
*
* @param asset 资产信息
* @return 影响行数
*/
int update(Asset asset);
/**
* 更新资产状态
*
* @param id 资产ID
* @param status 状态
* @param lastModUserId 最后修改人ID
* @param lastModTime 最后修改时间
* @return 影响行数
*/
int updateStatus(@Param("id") Long id, @Param("status") String status,
@Param("lastModUserId") String lastModUserId,
@Param("lastModTime") Date lastModTime);
/**
* 根据ID查询资产详情
*
* @param id 资产ID
* @return 资产信息
*/
Asset selectById(@Param("id") Long id);
/**
* 查询资产列表
*
* @param asset 资产信息
* @return 资产集合
*/
List<Asset> selectList(Asset asset);
/**
* 逻辑删除资产
*
* @param id 资产ID
* @param lastModUserId 最后修改人ID
* @return 影响行数
*/
int deleteById(@Param("id") Long id, @Param("lastModUserId") String lastModUserId);
/**
* 批量逻辑删除资产
*
* @param ids 资产ID集合
* @param lastModUserId 最后修改人ID
* @return 影响行数
*/
int deleteBatchIds(@Param("ids") List<Long> ids, @Param("lastModUserId") String lastModUserId);
/**
* 校验资产编码是否唯一
*
* @param assetCode 资产编码
* @param id 资产ID
* @return 结果
*/
int checkCodeUnique(@Param("assetCode") String assetCode, @Param("id") Long id);
/**
* 根据资产编码查询资产信息
*
* @param assetCode 资产编码
* @return 资产信息
*/
Asset selectByCode(@Param("assetCode") String assetCode);
/**
* 获取指定前缀的最大序列号
*
* @param prefix 前缀
* @return 最大序列号
*/
int getMaxSequenceByPrefix(@Param("prefix") String prefix);
}

View File

@ -0,0 +1,107 @@
package com.psbc.cpsp.eden.service.asset;
import com.psbc.cpsp.eden.common.entity.AssetClassification;
import com.psbc.cpsp.eden.common.vo.TreeNode;
import java.util.List;
/**
* 资产分类Service接口
*/
public interface IAssetClassificationService {
/**
* 查询资产分类列表
*
* @param classification 资产分类信息
* @return 资产分类集合
*/
List<AssetClassification> selectList(AssetClassification classification);
/**
* 查询资产分类树结构
*
* @param classification 资产分类信息
* @return 树节点集合
*/
List<TreeNode> selectTree(AssetClassification classification);
/**
* 根据父ID查询所有子节点
*
* @param parentId 父节点ID
* @return 子节点集合
*/
List<AssetClassification> selectChildrenByParentId(Long parentId);
/**
* 根据ID查询资产分类
*
* @param id 资产分类ID
* @return 资产分类信息
*/
AssetClassification selectById(Long id);
/**
* 新增资产分类
*
* @param classification 资产分类信息
* @return 资产分类信息
*/
AssetClassification insert(AssetClassification classification);
/**
* 修改资产分类
*
* @param classification 资产分类信息
* @return 资产分类信息
*/
AssetClassification update(AssetClassification classification);
/**
* 更新资产分类状态
*
* @param id 资产分类ID
* @param status 状态
* @param lastModUserId 最后修改人ID
* @return 是否成功
*/
boolean updateStatus(Long id, String status, String lastModUserId);
/**
* 删除资产分类
*
* @param id 资产分类ID
* @param lastModUserId 最后修改人ID
*/
void deleteById(Long id, String lastModUserId);
/**
* 批量删除资产分类
*
* @param ids 需要删除的数据ID集合
* @param lastModUserId 最后修改人ID
*/
void deleteBatchIds(List<Long> ids, String lastModUserId);
/**
* 校验资产分类编码是否唯一
*
* @param classification 资产分类信息
* @return 结果
*/
boolean checkCodeUnique(AssetClassification classification);
/**
* 根据分类名称查询分类ID
*
* @param classificationName 分类名称
* @return 分类ID
*/
Long selectIdByName(String classificationName);
/**
* 检查分类名称是否存在
*
* @return 是否存在
*/
boolean checkClassificationNameExists(AssetClassification assetClassification);
}

View File

@ -0,0 +1,73 @@
package com.psbc.cpsp.eden.service.asset;
import com.psbc.cpsp.eden.common.entity.AssetLabel;
import com.psbc.cpsp.eden.common.response.AssetResult;
import java.util.List;
/**
* 资产标签Service接口
*/
public interface IAssetLabelService {
/**
* 查询资产标签列表
*
* @param label 资产标签信息
* @return 资产标签集合
*/
List<AssetLabel> selectList(AssetLabel label);
/**
* 根据ID查询资产标签
*
* @param id 资产标签ID
* @return 资产标签信息
*/
AssetLabel selectById(Long id);
/**
* 新增资产标签
*
* @param label 资产标签信息
* @return 资产标签信息
*/
AssetLabel insert(AssetLabel label);
/**
* 修改资产标签
*
* @param label 资产标签信息
* @return 资产标签信息
*/
AssetLabel update(AssetLabel label);
/**
* 删除资产标签
*
* @param id 资产标签ID
* @param lastModUserId 最后修改人ID
*/
void deleteById(Long id, String lastModUserId);
/**
* 批量删除资产标签
*
* @param ids 需要删除的数据ID集合
* @param lastModUserId 最后修改人ID
*/
void deleteBatchIds(List<Long> ids, String lastModUserId);
/**
* 验证资产标签数据
*
* @param label 资产标签对象
* @return 验证结果
*/
AssetResult<AssetLabel> validateAssetLabel(AssetLabel label);
/**
* 根据租户ID查询资产标签
*
* @return 资产标签对象
*/
AssetLabel selectByAll();
}

View File

@ -0,0 +1,108 @@
package com.psbc.cpsp.eden.service.asset;
import com.psbc.cpsp.eden.common.entity.AssetLocation;
import com.psbc.cpsp.eden.common.vo.TreeNode;
import java.util.List;
/**
* 资产位置Service接口
*/
public interface IAssetLocationService {
/**
* 查询资产位置列表
*
* @param location 资产位置信息
* @return 资产位置集合
*/
List<AssetLocation> selectList(AssetLocation location);
/**
* 查询资产位置树结构
*
* @param location 资产位置信息
* @return 树节点集合
*/
List<TreeNode> selectTree(AssetLocation location);
/**
* 根据父ID查询所有子节点
*
* @param parentId 父节点ID
* @return 子节点集合
*/
List<AssetLocation> selectChildrenByParentId(Long parentId);
/**
* 根据ID查询资产位置
*
* @param id 资产位置ID
* @return 资产位置信息
*/
AssetLocation selectById(Long id);
/**
* 新增资产位置
*
* @param location 资产位置信息
* @return 资产位置信息
*/
AssetLocation insert(AssetLocation location);
/**
* 修改资产位置
*
* @param location 资产位置信息
* @return 资产位置信息
*/
AssetLocation update(AssetLocation location);
/**
* 更新资产位置状态
*
* @param id 资产位置ID
* @param status 状态
* @param lastModUserId 最后修改人ID
* @return 是否成功
*/
boolean updateStatus(Long id, String status, String lastModUserId);
/**
* 删除资产位置
*
* @param id 资产位置ID
* @param lastModUserId 最后修改人ID
*/
void deleteById(Long id, String lastModUserId);
/**
* 批量删除资产位置
*
* @param ids 需要删除的数据ID集合
* @param lastModUserId 最后修改人ID
*/
void deleteBatchIds(List<Long> ids, String lastModUserId);
/**
* 校验资产位置编码是否唯一
*
* @param location 资产位置信息
* @return 结果
*/
boolean checkCodeUnique(AssetLocation location);
/**
* 校验资产名称是否唯一
*
* @param location 资产名称信息
* @return 结果
*/
boolean checkNameUnique(AssetLocation location);
/**
* 根据位置名称查询位置ID
*
* @param locationName 位置名称
* @return 位置ID
*/
Long selectIdByName(String locationName);
}

View File

@ -0,0 +1,43 @@
package com.psbc.cpsp.eden.service.asset;
import com.psbc.cpsp.eden.common.request.AssetQueryRequest;
import com.psbc.cpsp.eden.common.vo.AssetVO;
import com.github.pagehelper.PageInfo;
/**
* 资产管理服务接口
*/
public interface IAssetManagerService {
/**
* 查询资产列表
*
* @param request 查询条件
* @return 资产列表
*/
PageInfo<AssetVO> queryAssetList(AssetQueryRequest request);
/**
* 获取资产详情
*
* @param id 资产ID
* @return 资产详情
*/
AssetVO getAssetDetail(String id);
/**
* 更新资产状态
*
* @param id 资产ID
* @param status 状态
* @param userId 操作人ID
* @return 是否成功
*/
boolean updateAssetStatus(String id, String status, String userId);
/**
* 生成资产编码
*
* @return 资产编码
*/
String generateAssetCode();
}

View File

@ -0,0 +1,118 @@
package com.psbc.cpsp.eden.service.asset;
import com.psbc.cpsp.eden.common.entity.Asset;
import com.psbc.cpsp.eden.common.vo.AssetExportVO;
import java.util.List;
/**
* 资产Service接口
*/
public interface IAssetService {
/**
* 查询资产列表
*
* @param asset 资产信息
* @return 资产集合
*/
List<Asset> selectList(Asset asset);
/**
* 根据ID查询资产
*
* @param id 资产ID
* @return 资产信息
*/
Asset selectById(Long id);
/**
* 新增资产
*
* @param asset 资产信息
* @return 资产信息
*/
Asset insert(Asset asset);
/**
* 修改资产
*
* @param asset 资产信息
* @return 资产信息
*/
Asset update(Asset asset);
/**
* 更新资产状态
*
* @param id 资产ID
* @param status 状态
* @param lastModUserId 最后修改人ID
* @return 是否成功
*/
boolean updateStatus(Long id, String status, String lastModUserId);
/**
* 删除资产
*
* @param id 资产ID
* @param lastModUserId 最后修改人ID
*/
void deleteById(Long id, String lastModUserId);
/**
* 批量删除资产
*
* @param ids 需要删除的数据ID集合
* @param lastModUserId 最后修改人ID
*/
void deleteBatchIds(List<Long> ids, String lastModUserId);
/**
* 校验资产编码是否唯一
*
* @param asset 资产信息
* @return 结果
*/
boolean checkCodeUnique(Asset asset);
/**
* 导入资产数据
*
* @param assetList 资产数据列表
* @param updateSupport 是否更新支持如果已存在是否更新
* @param operName 操作人
* @return 结果消息
*/
String importAsset(List<Asset> assetList, boolean updateSupport, String operName);
/**
* 导出资产数据
*
* @param asset 查询条件
* @return 资产导出数据列表
*/
List<AssetExportVO> exportAsset(Asset asset);
/**
* 根据资产编码查询资产
*
* @param assetCode 资产编码
* @return 资产信息
*/
Asset selectByCode(String assetCode);
/**
* 获取资产管理员名称
*
* @param asset 资产信息
* @return 管理员名称
*/
String getAssetAdminName(Asset asset);
/**
* 获取资产所属公司名称
*
* @param asset 资产信息
* @return 公司名称
*/
String getAssetCompanyName(Asset asset);
}

View File

@ -0,0 +1,240 @@
package com.psbc.cpsp.eden.service.asset.impl;
import com.psbc.cpsp.eden.common.entity.AssetClassification;
import com.psbc.cpsp.eden.enums.DelFlagEnum;
import com.psbc.cpsp.eden.common.vo.TreeNode;
import com.psbc.cpsp.eden.mapper.asset.AssetClassificationMapper;
import com.psbc.cpsp.eden.service.asset.IAssetClassificationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
/**
* 资产分类Service实现类
*/
@Slf4j
@Service
public class AssetClassificationServiceImpl implements IAssetClassificationService {
private final AssetClassificationMapper assetClassificationMapper;
public AssetClassificationServiceImpl(AssetClassificationMapper assetClassificationMapper) {
this.assetClassificationMapper = assetClassificationMapper;
}
@Override
public List<AssetClassification> selectList(AssetClassification classification) {
return assetClassificationMapper.selectList(classification);
}
@Override
public List<TreeNode> selectTree(AssetClassification classification) {
if (classification == null) {
classification = new AssetClassification();
}
// 获取所有节点
List<TreeNode> allNodes = assetClassificationMapper.selectTree(classification);
if (allNodes == null || allNodes.isEmpty()) {
return allNodes;
}
// 构建树结构
List<TreeNode> rootNodes = buildTree(allNodes);
return rootNodes;
}
/**
* 构建树结构
*
* @param allNodes 所有节点
* @return 树根节点列表
*/
private List<TreeNode> buildTree(List<TreeNode> allNodes) {
// 使用Map存储所有节点以便快速查找
Map<Long, TreeNode> nodeMap = new HashMap<>();
List<TreeNode> rootNodes = new ArrayList<>();
log.info("开始构建树结构,总节点数: {}", allNodes.size());
// 第一次遍历将所有节点放入Map
for (TreeNode node : allNodes) {
nodeMap.put(node.getId(), node);
log.debug("节点ID: {}, 父节点ID: {}, 标签: {}", node.getId(), node.getParentId(), node.getLabel());
}
// 第二次遍历构建树结构
for (TreeNode node : allNodes) {
Long parentId = node.getParentId();
if (parentId == null) {
// 如果没有父节点则为根节点
log.debug("添加根节点: {}, 标签: {}", node.getId(), node.getLabel());
rootNodes.add(node);
} else {
// 如果有父节点将当前节点添加到父节点的children列表中
TreeNode parentNode = nodeMap.get(parentId);
if (parentNode != null) {
if (parentNode.getChildren() == null) {
parentNode.setChildren(new ArrayList<>());
}
log.debug("添加子节点: {}, 标签: {} 到父节点: {}", node.getId(), node.getLabel(), parentId);
parentNode.getChildren().add(node);
} else {
// 如果找不到父节点作为根节点处理
log.warn("找不到父节点ID为: {} 的节点将节点ID: {}, 标签: {} 作为根节点处理", parentId, node.getId(), node.getLabel());
rootNodes.add(node);
}
}
}
log.info("树结构构建完成,根节点数: {}", rootNodes.size());
return rootNodes;
}
@Override
public List<AssetClassification> selectChildrenByParentId(Long parentId) {
return assetClassificationMapper.selectChildrenByParentId(parentId);
}
@Override
public AssetClassification selectById(Long id) {
log.info("根据ID查询资产分类, id: {}", id);
try {
AssetClassification result = assetClassificationMapper.selectById(id);
if (result == null) {
log.warn("未找到对应的资产分类, id: {}", id);
}
return result;
} catch (Exception e) {
log.error("查询资产分类异常", e);
return null;
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public AssetClassification insert(AssetClassification classification) {
if (classification.getCreateTime() == null) {
classification.setCreateTime(new Date());
}
if (classification.getDelFlag() == null) {
classification.setDelFlag(DelFlagEnum.NORMAL.getCode());
}
int rows = assetClassificationMapper.insert(classification);
if (rows > 0) {
log.info("新增资产分类成功, id: {}", classification.getId());
} else {
log.error("新增资产分类失败");
}
return classification;
}
@Override
@Transactional(rollbackFor = Exception.class)
public AssetClassification update(AssetClassification classification) {
classification.setLastModTime(new Date());
int rows = assetClassificationMapper.update(classification);
if (rows > 0) {
log.info("更新资产分类成功, id: {}", classification.getId());
} else {
log.error("更新资产分类失败, id: {}", classification.getId());
}
return classification;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteById(Long id, String lastModUserId) {
try {
// todo 校验是否被使用注意
int rows = assetClassificationMapper.deleteById(id, lastModUserId);
if (rows > 0) {
log.info("删除资产分类成功, id: {}", id);
} else {
log.error("删除资产分类失败, id: {}", id);
}
} catch (Exception e) {
log.error("删除资产分类异常, id: {}", id, e);
throw e;
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteBatchIds(List<Long> ids, String lastModUserId) {
if (ids == null || ids.isEmpty()) {
return;
}
try {
log.info("批量删除资产分类,数量: {}", ids.size());
int rows = assetClassificationMapper.deleteBatchIds(ids, lastModUserId);
if (rows > 0) {
log.info("批量删除资产分类成功, 删除数量: {}", rows);
} else {
log.error("批量删除资产分类失败");
}
} catch (Exception e) {
log.error("批量删除资产分类异常", e);
throw e;
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateStatus(Long id, String status, String lastModUserId) {
log.info("更新资产分类状态, id: {}, status: {}, lastModUserId: {}", id, status, lastModUserId);
try {
Date lastModTime = new Date();
int rows = assetClassificationMapper.updateStatus(id, status, lastModUserId, lastModTime);
if (rows > 0) {
log.info("更新资产分类状态成功");
return true;
} else {
log.error("更新资产分类状态失败");
return false;
}
} catch (Exception e) {
log.error("更新资产分类状态异常", e);
return false;
}
}
@Override
public boolean checkCodeUnique(AssetClassification classification) {
int count = assetClassificationMapper.checkCodeUnique(
classification.getClassificationCode(),
classification.getId());
return count > 0;
}
/**
* 根据分类名称查询分类ID
*
* @param classificationName 分类名称
* @return 分类ID
*/
@Override
public Long selectIdByName(String classificationName) {
return assetClassificationMapper.selectIdByName(classificationName);
}
/**
* 检查分类名称是否存在
*
* @return 是否存在
*/
@Override
public boolean checkClassificationNameExists(AssetClassification classification) {
return assetClassificationMapper.checkNameExists(classification.getClassificationName(),classification.getId()) > 0;
}
}

View File

@ -0,0 +1,193 @@
package com.psbc.cpsp.eden.service.asset.impl;
import com.psbc.cpsp.eden.common.entity.AssetLabel;
import com.psbc.cpsp.eden.enums.LabelTemplateTypeEnum;
import com.psbc.cpsp.eden.enums.LabelItemEnum;
import com.psbc.cpsp.eden.enums.PaperTypeEnum;
import com.psbc.cpsp.eden.mapper.asset.AssetLabelMapper;
import com.psbc.cpsp.eden.service.asset.IAssetLabelService;
import com.psbc.cpsp.entity.CpspReponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.List;
import com.psbc.cpsp.eden.common.response.AssetResult;
/**
* 资产标签Service实现类
*/
@Service
public class AssetLabelServiceImpl implements IAssetLabelService {
@Autowired
private AssetLabelMapper assetLabelMapper;
/**
* 查询资产标签列表
*
* @param label 资产标签信息
* @return 资产标签集合
*/
@Override
public List<AssetLabel> selectList(AssetLabel label) {
return assetLabelMapper.selectList(label);
}
/**
* 根据ID查询资产标签
*
* @param id 资产标签ID
* @return 资产标签信息
*/
@Override
public AssetLabel selectById(Long id) {
return assetLabelMapper.selectById(id);
}
/**
* 新增资产标签
*
* @param label 资产标签信息
* @return 资产标签信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public AssetLabel insert(AssetLabel label) {
AssetResult<AssetLabel> validationAssetResult = validateAssetLabel(label);
if (!validationAssetResult.isSuccess()) {
throw new IllegalArgumentException(validationAssetResult.getMessage());
}
// 设置默认值
if (label.getDelFlag() == null || label.getDelFlag().isEmpty()) {
label.setDelFlag("0");
}
// 执行插入操作
assetLabelMapper.insert(label);
return label;
}
/**
* 修改资产标签
*
* @param label 资产标签信息
* @return 资产标签信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public AssetLabel update(AssetLabel label) {
AssetResult<AssetLabel> validationAssetResult = validateAssetLabel(label);
if (!validationAssetResult.isSuccess()) {
throw new IllegalArgumentException(validationAssetResult.getMessage());
}
// 执行更新操作
assetLabelMapper.update(label);
return assetLabelMapper.selectById(label.getId());
}
/**
* 删除资产标签
*
* @param id 资产标签ID
* @param lastModUserId 最后修改人ID
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteById(Long id, String lastModUserId) {
assetLabelMapper.deleteById(id, lastModUserId);
}
/**
* 批量删除资产标签
*
* @param ids 需要删除的数据ID集合
* @param lastModUserId 最后修改人ID
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteBatchIds(List<Long> ids, String lastModUserId) {
assetLabelMapper.deleteBatchIds(ids, lastModUserId);
}
/**
* 验证资产标签数据
*/
@Override
public AssetResult<AssetLabel> validateAssetLabel(AssetLabel label) {
// 验证模板类型
if (!Arrays.asList("1", "2", "3", "4").contains(label.getTemplateType())) {
return AssetResult.error("模板类型无效");
}
// 验证标签集合
if (StringUtils.isEmpty(label.getLabelItems())) {
return AssetResult.error("标签集合不能为空");
}
// 验证标签数量
String[] items = label.getLabelItems().split(",");
int maxLabels = Integer.parseInt(label.getTemplateType());
if (items.length > maxLabels) {
return AssetResult.error("选择的标签数量超过模板限制");
}
// 验证标签项
for (String item : items) {
if (!Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11").contains(item)) {
return AssetResult.error("标签项无效");
}
}
// 验证纸张类型
/*
* if (!Arrays.asList("1", "2", "3").contains(label.getPaperType())) {
* return Result.error("纸张类型无效");
* }
*
* // 验证标签尺寸
* if ("1".equals(label.getPaperType())) {
* if (StringUtils.isEmpty(label.getLabelWidth()) ||
* StringUtils.isEmpty(label.getLabelHeight())) {
* return Result.error("标签专用纸必须指定宽度和高度");
* }
* try {
* int width = Integer.parseInt(label.getLabelWidth());
* int height = Integer.parseInt(label.getLabelHeight());
* if (width < 20 || width > 100 || height < 20 || height > 100) {
* return Result.error("标签尺寸必须在20-100mm之间");
* }
* } catch (NumberFormatException e) {
* return Result.error("标签尺寸必须是有效的数字");
* }
* }
*/
return AssetResult.success(label);
}
private boolean isValidTemplateType(String templateType) {
LabelTemplateTypeEnum template = LabelTemplateTypeEnum.getByCode(templateType);
return template != null;
}
private boolean isValidLabelItem(String item) {
LabelItemEnum labelItem = LabelItemEnum.getByCode(item);
return labelItem != null;
}
private boolean isValidPaperType(String paperType) {
PaperTypeEnum paper = PaperTypeEnum.getByCode(paperType);
return paper != null;
}
@Override
public AssetLabel selectByAll() {
AssetLabel label = new AssetLabel();
List<AssetLabel> list = assetLabelMapper.selectList(label);
return list.isEmpty() ? null : list.get(0);
}
}

View File

@ -0,0 +1,250 @@
package com.psbc.cpsp.eden.service.asset.impl;
import com.psbc.cpsp.eden.common.entity.AssetLocation;
import com.psbc.cpsp.eden.enums.DelFlagEnum;
import com.psbc.cpsp.eden.common.vo.TreeNode;
import com.psbc.cpsp.eden.mapper.asset.AssetLocationMapper;
import com.psbc.cpsp.eden.service.asset.IAssetLocationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
/**
* 资产位置Service实现类
*/
@Slf4j
@Service
public class AssetLocationServiceImpl implements IAssetLocationService {
@Autowired
private AssetLocationMapper assetLocationMapper;
@Override
public List<AssetLocation> selectList(AssetLocation location) {
// 设置租户ID
if (location == null) {
location = new AssetLocation();
}
return assetLocationMapper.selectList(location);
}
@Override
public List<TreeNode> selectTree(AssetLocation location) {
// 设置租户ID
if (location == null) {
location = new AssetLocation();
}
// 获取所有节点
List<TreeNode> allNodes = assetLocationMapper.selectTree(location);
if (allNodes == null || allNodes.isEmpty()) {
return allNodes;
}
// 构建树结构
List<TreeNode> rootNodes = buildTree(allNodes);
return rootNodes;
}
/**
* 构建树结构
*
* @param allNodes 所有节点
* @return 树根节点列表
*/
private List<TreeNode> buildTree(List<TreeNode> allNodes) {
// 使用Map存储所有节点以便快速查找
Map<Long, TreeNode> nodeMap = new HashMap<>();
List<TreeNode> rootNodes = new ArrayList<>();
// 第一次遍历将所有节点放入Map
for (TreeNode node : allNodes) {
nodeMap.put(node.getId(), node);
}
// 第二次遍历构建树结构
for (TreeNode node : allNodes) {
Long parentId = node.getParentId();
if (parentId == null) {
// 如果没有父节点则为根节点
rootNodes.add(node);
} else {
// 如果有父节点将当前节点添加到父节点的children列表中
TreeNode parentNode = nodeMap.get(parentId);
if (parentNode != null) {
if (parentNode.getChildren() == null) {
parentNode.setChildren(new ArrayList<>());
}
parentNode.getChildren().add(node);
} else {
// 如果找不到父节点作为根节点处理
rootNodes.add(node);
}
}
}
return rootNodes;
}
@Override
public List<AssetLocation> selectChildrenByParentId(Long parentId) {
return assetLocationMapper.selectChildrenByParentId(parentId);
}
@Override
public AssetLocation selectById(Long id) {
log.info("根据ID查询资产位置, id: {}", id);
try {
AssetLocation result = assetLocationMapper.selectById(id);
if (result == null) {
log.warn("未找到对应的资产位置, id: {}", id);
}
return result;
} catch (Exception e) {
log.error("查询资产位置异常", e);
return null;
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public AssetLocation insert(AssetLocation location) {
if (location.getCreateTime() == null) {
location.setCreateTime(new Date());
}
if (location.getDelFlag() == null) {
location.setDelFlag(DelFlagEnum.NORMAL.getCode());
}
try {
int rows = assetLocationMapper.insert(location);
if (rows > 0) {
log.info("新增资产位置成功, id: {}", location.getId());
} else {
log.error("新增资产位置失败");
}
} catch (Exception e) {
log.error("新增资产位置异常", e);
throw e;
}
return location;
}
@Override
@Transactional(rollbackFor = Exception.class)
public AssetLocation update(AssetLocation location) {
location.setLastModTime(new Date());
try {
int rows = assetLocationMapper.update(location);
if (rows > 0) {
log.info("更新资产位置成功, id: {}", location.getId());
} else {
log.error("更新资产位置失败, id: {}", location.getId());
}
} catch (Exception e) {
log.error("更新资产位置异常", e);
throw e;
}
return location;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteById(Long id, String lastModUserId) {
try {
// todo
int rows = assetLocationMapper.deleteById(id, lastModUserId);
if (rows > 0) {
log.info("删除资产位置成功, id: {}", id);
} else {
log.error("删除资产位置失败, id: {}", id);
}
} catch (Exception e) {
log.error("删除资产位置异常, id: {}", id, e);
throw e;
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteBatchIds(List<Long> ids, String lastModUserId) {
if (ids == null || ids.isEmpty()) {
return;
}
try {
log.info("批量删除资产位置,数量: {}", ids.size());
// todo
int rows = assetLocationMapper.deleteBatchIds(ids, lastModUserId);
if (rows > 0) {
log.info("批量删除资产位置成功, 删除数量: {}", rows);
} else {
log.error("批量删除资产位置失败");
}
} catch (Exception e) {
log.error("批量删除资产位置异常", e);
throw e;
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateStatus(Long id, String status, String lastModUserId) {
log.info("更新资产位置状态, id: {}, status: {}, lastModUserId: {}", id, status, lastModUserId);
try {
Date lastModTime = new Date();
int rows = assetLocationMapper.updateStatus(id, status, lastModUserId, lastModTime);
if (rows > 0) {
log.info("更新资产位置状态成功, id: {}", id);
return true;
} else {
log.error("更新资产位置状态失败, id: {}", id);
return false;
}
} catch (Exception e) {
log.error("更新资产位置状态异常, id: {}", id, e);
throw e;
}
}
@Override
public boolean checkCodeUnique(AssetLocation location) {
int count = assetLocationMapper.checkCodeUnique(
location.getLocationCode(),
location.getId());
return count > 0;
}
@Override
public boolean checkNameUnique(AssetLocation location) {
int count = assetLocationMapper.checkNameUnique(
location.getLocationName(),
location.getId());
return count > 0;
}
/**
* 根据位置名称查询位置ID
*
* @param locationName 位置名称
* @return 位置ID
*/
@Override
public Long selectIdByName(String locationName) {
return assetLocationMapper.selectIdByName(locationName);
}
}

View File

@ -0,0 +1,57 @@
package com.psbc.cpsp.eden.service.asset.impl;
import com.psbc.cpsp.eden.constants.AssetConstants;
import com.psbc.cpsp.eden.enums.AssetStatusEnum;
import com.psbc.cpsp.eden.common.request.AssetQueryRequest;
import com.psbc.cpsp.eden.common.vo.AssetVO;
import com.psbc.cpsp.eden.service.asset.IAssetManagerService;
import com.psbc.cpsp.entity.BizException;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 资产管理服务实现类
*/
@Service("managementAssetService")
public class AssetManagerServiceImpl implements IAssetManagerService {
@Override
public PageInfo<AssetVO> queryAssetList(AssetQueryRequest request) {
// 设置分页
PageHelper.startPage(request.getPageNum(), request.getPageSize());
// TODO: 实现查询逻辑
return new PageInfo<>();
}
@Override
public AssetVO getAssetDetail(String id) {
// TODO: 实现查询逻辑
return null;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateAssetStatus(String id, String status, String userId) {
// 校验状态值是否有效
if (!AssetStatusEnum.isValid(status)) {
throw new BizException("400", "无效的状态值");
}
// TODO: 实现状态更新逻辑
return true;
}
@Override
public String generateAssetCode() {
// 生成资产编码前缀 + 日期 + 4位序号
SimpleDateFormat sdf = new SimpleDateFormat(AssetConstants.ASSET_CODE_DATE_FORMAT);
String dateStr = sdf.format(new Date());
// TODO: 获取当天序号并加1
String sequence = String.format("%04d", 1);
return AssetConstants.ASSET_CODE_PREFIX + dateStr + sequence;
}
}

View File

@ -0,0 +1,395 @@
package com.psbc.cpsp.eden.service.asset.impl;
import com.psbc.cpsp.eden.common.entity.Asset;
import com.psbc.cpsp.eden.common.entity.AssetClassification;
import com.psbc.cpsp.eden.common.entity.AssetLocation;
import com.psbc.cpsp.eden.common.utils.AdminUserCache;
import com.psbc.cpsp.eden.common.utils.CompanyCache;
import com.psbc.cpsp.eden.common.vo.AssetExportVO;
import com.psbc.cpsp.eden.mapper.asset.AssetClassificationMapper;
import com.psbc.cpsp.eden.mapper.asset.AssetLocationMapper;
import com.psbc.cpsp.eden.mapper.asset.AssetMapper;
import com.psbc.cpsp.eden.service.asset.IAssetService;
import com.psbc.cpsp.entity.BizException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 资产Service实现类
*/
@Slf4j
@Service("basicAssetService")
public class AssetServiceImpl implements IAssetService {
@Autowired
private AssetMapper assetMapper;
@Autowired
private AssetClassificationMapper assetClassificationMapper;
@Autowired
private AssetLocationMapper assetLocationMapper;
/**
* 查询资产列表
*
* @param asset 资产信息
* @return 资产集合
*/
@Override
public List<Asset> selectList(Asset asset) {
return assetMapper.selectList(asset);
}
/**
* 根据ID查询资产
*
* @param id 资产ID
* @return 资产信息
*/
@Override
public Asset selectById(Long id) {
return assetMapper.selectById(id);
}
/**
* 新增资产
*
* @param asset 资产信息
* @return 资产信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Asset insert(Asset asset) {
// 设置默认值
if (asset.getDelFlag() == null || asset.getDelFlag().isEmpty()) {
asset.setDelFlag("0");
}
// 再次检查资产编码是否已存在防止并发问题
if (asset.getAssetCode() != null && !asset.getAssetCode().isEmpty()) {
log.info("Service层再次校验资产编码唯一性 - 编码: {}",
asset.getAssetCode());
// 直接使用SQL查询检查编码是否存在
Asset existingAsset = assetMapper.selectByCode(asset.getAssetCode());
if (existingAsset != null) {
log.error("资产编码 [{}] 已存在于数据库中!", asset.getAssetCode());
throw new BizException("400", "资产编码 [" + asset.getAssetCode() + "] 已存在,请更换一个唯一的编码");
}
int count = assetMapper.checkCodeUnique(asset.getAssetCode(), null);
log.info("Service层再次校验资产编码唯一性 - 结果: {}", count == 0 ? "唯一" : "已存在");
if (count > 0) {
log.error("资产编码 [{}] 已存在!", asset.getAssetCode());
throw new BizException("400", "资产编码 [" + asset.getAssetCode() + "] 已存在,请更换一个唯一的编码");
}
// 新增默认空闲状态
asset.setAssetStatus("空闲");
}
try {
// 执行插入操作
log.info("开始执行资产插入操作,资产编码: {}", asset.getAssetCode());
int rows = assetMapper.insert(asset);
log.info("资产插入执行结果: {} 行受影响", rows);
return asset;
} catch (Exception e) {
log.error("资产插入操作异常: {}", e.getMessage(), e);
throw new BizException("500", "资产创建失败: " + e.getMessage());
}
}
/**
* 修改资产
*
* @param asset 资产信息
* @return 资产信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Asset update(Asset asset) {
// 执行更新操作
assetMapper.update(asset);
return assetMapper.selectById(asset.getId());
}
/**
* 更新资产状态
*
* @param id 资产ID
* @param status 状态
* @param lastModUserId 最后修改人ID
* @return 是否成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateStatus(Long id, String status, String lastModUserId) {
Date lastModTime = new Date();
return assetMapper.updateStatus(id, status, lastModUserId, lastModTime) > 0;
}
/**
* 删除资产
*
* @param id 资产ID
* @param lastModUserId 最后修改人ID
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteById(Long id, String lastModUserId) {
assetMapper.deleteById(id, lastModUserId);
}
/**
* 批量删除资产
*
* @param ids 需要删除的数据ID集合
* @param lastModUserId 最后修改人ID
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteBatchIds(List<Long> ids, String lastModUserId) {
assetMapper.deleteBatchIds(ids, lastModUserId);
}
/**
* 校验资产编码是否唯一
*
* @param asset 资产信息
* @return 结果
*/
@Override
public boolean checkCodeUnique(Asset asset) {
log.info("校验资产编码唯一性 - 编码: {}, ID: {}",
asset.getAssetCode(), asset.getId());
int count = assetMapper.checkCodeUnique(asset.getAssetCode(),asset.getId());
log.info("校验资产编码唯一性 - 结果: {}", count == 0 ? "唯一" : "已存在");
return count == 0;
}
/**
* 导入资产数据
*
* @param assetList 资产数据列表
* @param updateSupport 是否更新支持如果已存在是否更新
* @param operName 操作人
* @return 结果消息
*/
@Override
@Transactional(rollbackFor = Exception.class, noRollbackFor = { BizException.class })
public String importAsset(List<Asset> assetList, boolean updateSupport, String operName) {
if (assetList == null || assetList.isEmpty()) {
return "导入资产数据不能为空!";
}
log.info("开始导入资产数据,数量: {}, 是否支持更新: {}, 操作人: {}", assetList.size(), updateSupport, operName);
// 结果计数器
int successNum = 0;
int failureNum = 0;
StringBuilder successMsg = new StringBuilder();
StringBuilder failureMsg = new StringBuilder();
for (Asset asset : assetList) {
try {
// 设置默认值
if (asset.getCreateUserId() == null || asset.getCreateUserId().isEmpty()) {
asset.setCreateUserId(operName);
}
asset.setCreateTime(new Date());
asset.setDelFlag("0");
// 校验资产编码是否唯一
Asset existAsset = null;
if (asset.getAssetCode() != null && !asset.getAssetCode().isEmpty()) {
// 如果提供了资产编码检查是否存在
int count = assetMapper.checkCodeUnique(asset.getAssetCode(), null);
if (count > 0) {
if (!updateSupport) {
// 如果不允许更新则记为失败
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、资产 ").append(asset.getAssetName())
.append(" 导入失败:资产编码 [").append(asset.getAssetCode()).append("] 已存在");
continue;
}
// 如果允许更新则获取现有资产
existAsset = assetMapper.selectByCode(asset.getAssetCode());
}
}
if (existAsset != null && updateSupport) {
// 更新已有资产
asset.setId(existAsset.getId());
asset.setLastModUserId(operName);
asset.setLastModTime(new Date());
assetMapper.update(asset);
successNum++;
successMsg.append("<br/>").append(successNum).append("、资产 ").append(asset.getAssetName())
.append(" 更新成功");
} else if (existAsset == null) {
// 新增资产
// 如果未提供资产编码则自动生成
if (asset.getAssetCode() == null || asset.getAssetCode().isEmpty()) {
asset.setAssetCode(generateAssetCode());
}
assetMapper.insert(asset);
successNum++;
successMsg.append("<br/>").append(successNum).append("、资产 ").append(asset.getAssetName())
.append(" 导入成功");
} else {
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、资产 ").append(asset.getAssetName())
.append(" 已存在");
}
} catch (Exception e) {
failureNum++;
String message = "<br/>" + failureNum + "、资产 " + asset.getAssetName() + " 导入失败:";
failureMsg.append(message).append(e.getMessage());
log.error("资产导入失败: 资产名称={}, 资产编码={}, 异常信息={}",
asset.getAssetName(), asset.getAssetCode(), e.getMessage(), e);
}
}
if (failureNum > 0) {
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
return failureMsg.toString();
} else {
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
return successMsg.toString();
}
}
/**
* 导出资产数据
*
* @param asset 查询条件
* @return 资产导出数据列表
*/
@Override
public List<AssetExportVO> exportAsset(Asset asset) {
log.info("开始导出资产数据,查询条件: {}", asset);
// 获取资产列表
List<Asset> assetList = assetMapper.selectList(asset);
log.info("查询到 {} 条资产记录", assetList.size());
List<AssetExportVO> exportList = new ArrayList<>();
// 遍历资产列表转换为导出对象
for (Asset item : assetList) {
AssetExportVO exportVO = new AssetExportVO();
BeanUtils.copyProperties(item, exportVO);
// 获取资产分类信息
if (item.getClassificationId() != null) {
log.info("查询资产分类信息, classificationId: {}", item.getClassificationId());
AssetClassification classification = assetClassificationMapper.selectById(item.getClassificationId());
if (classification != null) {
exportVO.setClassificationName(classification.getClassificationName());
log.info("找到资产分类: {}", classification.getClassificationName());
} else {
log.warn("未找到资产分类, classificationId: {}", item.getClassificationId());
}
}
// 获取资产位置信息
if (item.getLocationId() != null) {
log.info("查询资产位置信息, locationId: {}", item.getLocationId());
AssetLocation location = assetLocationMapper.selectById(item.getLocationId());
if (location != null) {
exportVO.setLocationName(location.getLocationName());
log.info("找到资产位置: {}", location.getLocationName());
} else {
log.warn("未找到资产位置, locationId: {}", item.getLocationId());
}
}
// 获取管理员信息
if (item.getAdminUserId() != null) {
String adminUserName = AdminUserCache.getAdminUserName(item.getAdminUserId());
exportVO.setAdminUserName(adminUserName);
}
// 获取公司信息
if (item.getCompanyId() != null) {
String companyName = CompanyCache.getCompanyName(item.getCompanyId());
exportVO.setCompanyName(companyName);
}
exportList.add(exportVO);
}
log.info("资产数据导出转换完成,共 {} 条记录", exportList.size());
return exportList;
}
/**
* 生成资产编码
* 规则ZC + 年月日 + 4位序列号
*/
private String generateAssetCode() {
// 获取当前日期
Date now = new Date();
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyyMMdd");
String dateStr = sdf.format(now);
// 获取当天最大序列号
String prefix = "ZC" + dateStr;
int maxSequence = assetMapper.getMaxSequenceByPrefix(prefix);
// 生成新的序列号
int newSequence = maxSequence + 1;
String sequenceStr = String.format("%04d", newSequence);
return prefix + sequenceStr;
}
@Override
public Asset selectByCode(String assetCode) {
log.info("根据资产编码查询资产, assetCode: {}", assetCode);
Asset asset = new Asset();
asset.setAssetCode(assetCode);
asset.setDelFlag("0");
List<Asset> list = assetMapper.selectList(asset);
return list != null && !list.isEmpty() ? list.get(0) : null;
}
/**
* 获取资产管理员名称
*
* @param asset 资产信息
* @return 管理员名称
*/
@Override
public String getAssetAdminName(Asset asset) {
if (asset == null || asset.getAdminUserId() == null) {
return "未知管理员";
}
return AdminUserCache.getAdminUserName(asset.getAdminUserId());
}
/**
* 获取资产所属公司名称
*
* @param asset 资产信息
* @return 公司名称
*/
@Override
public String getAssetCompanyName(Asset asset) {
if (asset == null || asset.getCompanyId() == null) {
return "未知公司";
}
return CompanyCache.getCompanyName(asset.getCompanyId());
}
}

View File

@ -0,0 +1,2 @@
# Default ignored files
/workspace.xml

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/entity.iml" filepath="$PROJECT_DIR$/.idea/entity.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../../../../../../../../../.." vcs="Git" />
</component>
</project>

View File

@ -9,6 +9,7 @@ import lombok.NoArgsConstructor;
/**
* 智慧园区统一响应实体类
*
* @param <T>
*/
@Data
@ -30,16 +31,16 @@ public class CpspReponse<T> {
this.message = respCodeEnum.getDesc();
}
public boolean isSuccess() {
return BaseRespCodeEnum.SUCCESS.getCode().equals(this.getCode());
}
public static <T> CpspReponse<T> ok(T t) {
return new CpspReponse<>(BaseRespCodeEnum.SUCCESS, t);
}
public static <T> CpspReponse<T> ok(BaseRespCodeInterFace respCodeEnum,T t) {
public static <T> CpspReponse<T> ok(BaseRespCodeInterFace respCodeEnum, T t) {
return new CpspReponse<>(respCodeEnum, t);
}
@ -51,9 +52,47 @@ public class CpspReponse<T> {
return CpspReponse.ok(PageInfo.toPageInfo(pageData));
}
/**
* 返回错误响应带数据
*
* @param code 错误码
* @param message 错误信息
* @param data 数据
* @return 响应对象
*/
public static <T> CpspReponse<T> error(String code, String message, T data) {
return new CpspReponse<>(code, message, data);
}
/**
* 返回错误响应不带数据
*
* @param code 错误码
* @param message 错误信息
* @return 响应对象
*/
public static <T> CpspReponse<T> error(String code, String message) {
return new CpspReponse<>(code, message, null);
}
/**
* 返回错误响应使用错误码枚举
*
* @param respCodeEnum 错误码枚举
* @return 响应对象
*/
public static <T> CpspReponse<T> error(BaseRespCodeInterFace respCodeEnum) {
return new CpspReponse<>(respCodeEnum);
}
/**
* 返回错误响应使用错误码枚举和数据
*
* @param respCodeEnum 错误码枚举
* @param data 数据
* @return 响应对象
*/
public static <T> CpspReponse<T> error(BaseRespCodeInterFace respCodeEnum, T data) {
return new CpspReponse<>(respCodeEnum, data);
}
}

View File

@ -7,7 +7,6 @@ import static com.psbc.cpsp.entity.constant.ResponseConstant.BUSINESS_ERROR;
import static com.psbc.cpsp.entity.constant.ResponseConstant.BUSINESS_LOGIC;
/**
* 业务类错误-Y
* 说明业务/人为错误处理人为业务人员/用户 例举密码 权限 风控等

View File

@ -3,7 +3,8 @@ package com.psbc.cpsp.entity.enums;
import java.text.MessageFormat;
import static com.psbc.cpsp.entity.config.ResponsePrefixConfig.preFix;
import static com.psbc.cpsp.entity.constant.ResponseConstant.*;
import static com.psbc.cpsp.entity.constant.ResponseConstant.NETWORK_COMMUNICATION;
import static com.psbc.cpsp.entity.constant.ResponseConstant.THIRD_PARTY_ERROR;
/**

View File

@ -0,0 +1,176 @@
# 资产管理API接口文档
## 1. 资产标签管理
### 1.1 查询资产标签列表
- **接口说明**:查询资产标签列表
- **请求方式**GET
- **请求路径**/asset/label/list
- **请求参数**
```json
{
"templateType": "1", // 模板类型 1-模板1 2-模板2 3-模板3 4-模板4
"paperType": "1", // 纸张类型 1-标签专用纸 2-A4(2列标签) 3-A4(3列标签)
"delFlag": "0" // 是否删除 0-未删除 1-已删除
}
```
- **响应结果**
```json
{
"code": 200,
"message": "success",
"data": [
{
"id": "1",
"templateType": "1",
"labelItems": "1,2,3",
"paperType": "1",
"labelWidth": "100",
"labelHeight": "50",
"createUserId": "admin",
"createTime": "2024-04-07 10:00:00",
"delFlag": "0",
"lastModUserId": "admin",
"lastModTime": "2024-04-07 10:00:00",
"tenantId": "tenant1"
}
]
}
```
### 1.2 新增资产标签
- **接口说明**:新增资产标签
- **请求方式**POST
- **请求路径**/asset/label
- **请求参数**
```json
{
"templateType": "1", // 模板类型 1-模板1 2-模板2 3-模板3 4-模板4
"labelItems": "1,2,3", // 标签集合,以逗号分隔的数字
"paperType": "1", // 纸张类型 1-标签专用纸 2-A4(2列标签) 3-A4(3列标签)
"labelWidth": "100", // 标签宽度(mm),仅当纸张类型为标签专用纸时有效
"labelHeight": "50" // 标签高度(mm),仅当纸张类型为标签专用纸时有效
}
```
- **响应结果**
```json
{
"code": 200,
"message": "success",
"data": {
"id": "1",
"templateType": "1",
"labelItems": "1,2,3",
"paperType": "1",
"labelWidth": "100",
"labelHeight": "50",
"createUserId": "admin",
"createTime": "2024-04-07 10:00:00",
"delFlag": "0",
"lastModUserId": "admin",
"lastModTime": "2024-04-07 10:00:00",
"tenantId": "tenant1"
}
}
```
### 1.3 修改资产标签
- **接口说明**:修改资产标签
- **请求方式**PUT
- **请求路径**/asset/label
- **请求参数**
```json
{
"id": "1",
"templateType": "1", // 模板类型 1-模板1 2-模板2 3-模板3 4-模板4
"labelItems": "1,2,3", // 标签集合,以逗号分隔的数字
"paperType": "1", // 纸张类型 1-标签专用纸 2-A4(2列标签) 3-A4(3列标签)
"labelWidth": "100", // 标签宽度(mm),仅当纸张类型为标签专用纸时有效
"labelHeight": "50" // 标签高度(mm),仅当纸张类型为标签专用纸时有效
}
```
- **响应结果**
```json
{
"code": 200,
"message": "success",
"data": {
"id": "1",
"templateType": "1",
"labelItems": "1,2,3",
"paperType": "1",
"labelWidth": "100",
"labelHeight": "50",
"createUserId": "admin",
"createTime": "2024-04-07 10:00:00",
"delFlag": "0",
"lastModUserId": "admin",
"lastModTime": "2024-04-07 10:00:00",
"tenantId": "tenant1"
}
}
```
### 1.4 删除资产标签
- **接口说明**:删除资产标签
- **请求方式**DELETE
- **请求路径**/asset/label/{id}
- **请求参数**
- id资产标签ID
- **响应结果**
```json
{
"code": 200,
"message": "success"
}
```
### 1.5 批量删除资产标签
- **接口说明**:批量删除资产标签
- **请求方式**DELETE
- **请求路径**/asset/label/batch
- **请求参数**
```json
{
"ids": ["1", "2", "3"] // 资产标签ID列表
}
```
- **响应结果**
```json
{
"code": 200,
"message": "success"
}
```
## 2. 错误码说明
| 错误码 | 说明 |
|--------|------|
| 200 | 成功 |
| 400 | 请求参数错误 |
| 401 | 未授权 |
| 403 | 禁止访问 |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |
## 3. 注意事项
1. 所有接口都需要在请求头中携带token进行身份验证
2. 请求参数中的时间格式统一为yyyy-MM-dd HH:mm:ss
3. 分页查询接口的页码从1开始
4. 所有接口返回的数据都包含在data字段中
5. 删除操作是逻辑删除将del_flag设置为1
6. 资产标签的宽度和高度仅在纸张类型为标签专用纸时有效
7. 标签集合中的数字对应关系:
- 1-资产名称
- 2-资产分类
- 3-资产编码
- 4-资产位置
- 5-品牌
- 6-型号
- 7-设备序列号
- 8-管理员
- 9-保养到期时间
- 10-保养说明
- 11-使用部门

View File

@ -0,0 +1,184 @@
# 标签管理API接口文档
## 1. 接口说明
本文档描述了标签管理相关的API接口包括标签的查询、新增、修改、删除等操作。所有接口都需要进行身份验证请求头中需要携带token。
## 2. 接口列表
### 2.1 查询标签列表
- **接口说明**:查询标签列表,支持分页和条件查询
- **请求方式**GET
- **请求路径**/api/v1/labels
- **请求参数**
```json
{
"pageNum": 1, // 页码从1开始
"pageSize": 10, // 每页记录数
"templateType": "1", // 模板类型,可选
"paperType": "1", // 纸张类型,可选
"delFlag": "0" // 是否删除,可选
}
```
- **响应结果**
```json
{
"code": 200,
"message": "success",
"data": {
"total": 100,
"list": [
{
"id": "1",
"templateType": "1",
"labelItems": "1,2,3",
"paperType": "1",
"labelWidth": "100",
"labelHeight": "50",
"createUserId": "admin",
"createTime": "2024-04-07 10:00:00",
"delFlag": "0",
"lastModUserId": "admin",
"lastModTime": "2024-04-07 10:00:00",
"tenantId": "tenant1"
}
]
}
}
```
### 2.2 新增标签
- **接口说明**:新增标签
- **请求方式**POST
- **请求路径**/api/v1/labels
- **请求参数**
```json
{
"templateType": "1", // 模板类型 1-模板1 2-模板2 3-模板3 4-模板4
"labelItems": "1,2,3", // 标签集合,以逗号分隔的数字
"paperType": "1", // 纸张类型 1-标签专用纸 2-A4(2列标签) 3-A4(3列标签)
"labelWidth": "100", // 标签宽度(mm),仅当纸张类型为标签专用纸时有效
"labelHeight": "50" // 标签高度(mm),仅当纸张类型为标签专用纸时有效
}
```
- **响应结果**
```json
{
"code": 200,
"message": "success",
"data": {
"id": "1",
"templateType": "1",
"labelItems": "1,2,3",
"paperType": "1",
"labelWidth": "100",
"labelHeight": "50",
"createUserId": "admin",
"createTime": "2024-04-07 10:00:00",
"delFlag": "0",
"lastModUserId": "admin",
"lastModTime": "2024-04-07 10:00:00",
"tenantId": "tenant1"
}
}
```
### 2.3 修改标签
- **接口说明**:修改标签
- **请求方式**PUT
- **请求路径**/api/v1/labels/{id}
- **请求参数**
```json
{
"templateType": "1", // 模板类型 1-模板1 2-模板2 3-模板3 4-模板4
"labelItems": "1,2,3", // 标签集合,以逗号分隔的数字
"paperType": "1", // 纸张类型 1-标签专用纸 2-A4(2列标签) 3-A4(3列标签)
"labelWidth": "100", // 标签宽度(mm),仅当纸张类型为标签专用纸时有效
"labelHeight": "50" // 标签高度(mm),仅当纸张类型为标签专用纸时有效
}
```
- **响应结果**
```json
{
"code": 200,
"message": "success",
"data": {
"id": "1",
"templateType": "1",
"labelItems": "1,2,3",
"paperType": "1",
"labelWidth": "100",
"labelHeight": "50",
"createUserId": "admin",
"createTime": "2024-04-07 10:00:00",
"delFlag": "0",
"lastModUserId": "admin",
"lastModTime": "2024-04-07 10:00:00",
"tenantId": "tenant1"
}
}
```
### 2.4 删除标签
- **接口说明**:删除标签
- **请求方式**DELETE
- **请求路径**/api/v1/labels/{id}
- **请求参数**
- id标签ID
- **响应结果**
```json
{
"code": 200,
"message": "success"
}
```
### 2.5 批量删除标签
- **接口说明**:批量删除标签
- **请求方式**DELETE
- **请求路径**/api/v1/labels/batch
- **请求参数**
```json
{
"ids": ["1", "2", "3"] // 标签ID列表
}
```
- **响应结果**
```json
{
"code": 200,
"message": "success"
}
```
## 3. 错误码说明
| 错误码 | 说明 |
|--------|------|
| 200 | 成功 |
| 400 | 请求参数错误 |
| 401 | 未授权 |
| 403 | 禁止访问 |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |
## 4. 注意事项
1. 所有接口都需要在请求头中携带token进行身份验证
2. 请求参数中的时间格式统一为yyyy-MM-dd HH:mm:ss
3. 分页查询接口的页码从1开始
4. 所有接口返回的数据都包含在data字段中
5. 删除操作是逻辑删除将del_flag设置为1
6. 标签的宽度和高度仅在纸张类型为标签专用纸时有效
7. 标签集合中的数字对应关系:
- 1-资产名称
- 2-资产分类
- 3-资产编码
- 4-资产位置
- 5-品牌
- 6-型号
- 7-设备序列号
- 8-管理员
- 9-保养到期时间
- 10-保养说明
- 11-使用部门

View File

@ -0,0 +1,82 @@
server:
port: 8082
servlet:
context-path: /api
spring:
application:
name: asset-management
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://1.14.121.39:5432/edendb?currentSchema=public&charSet=UTF-8
username: edenuser
password: edenpswd
# HikariCP连接池配置
hikari:
# 连接池最大连接数
maximum-pool-size: 10
# 最小空闲连接数
minimum-idle: 5
# 连接最大空闲时间(毫秒)
idle-timeout: 30000
# 连接最大生存时间(毫秒)
max-lifetime: 1800000
# 连接超时时间(毫秒)
connection-timeout: 30000
# 测试连接有效性的SQL
connection-test-query: SELECT 1
# 连接泄漏检测,可帮助识别未关闭的连接
leak-detection-threshold: 60000
# 文件上传配置
servlet:
multipart:
enabled: true
max-file-size: 10MB
max-request-size: 20MB
file-size-threshold: 2KB
redis:
host: 1.14.121.39
port: 6379
password: edenpswd
database: 0
# MyBatis-Plus配置
mybatis-plus:
# XML映射文件路径
mapper-locations: classpath:mapper/**/*.xml
# 实体类包路径
type-aliases-package: com.psbc.cpsp.eden.common.entity
configuration:
# 下划线转驼峰命名
map-underscore-to-camel-case: true
# 打印SQL日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 主键类型设置为自增
id-type: auto
# 表名使用下划线风格
table-underline: true
# 逻辑删除配置
logic-delete-field: del_flag
logic-delete-value: 1
logic-not-delete-value: 0
# PageHelper分页插件配置
pagehelper:
# 数据库方言
helper-dialect: postgresql
# 分页合理化参数
reasonable: true
# 支持通过Mapper接口参数来传递分页参数
support-methods-arguments: true
# 自动识别count查询
params: count=countSql
# 日志配置
logging:
level:
com.eden.asset.mapper: debug
com.baomidou.mybatisplus: debug
com.baomidou.mybatisplus.core.executor: debug
org.apache.ibatis: debug

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- SQL日志配置 - 使用MyBatis-Plus原生日志 -->
<logger name="com.psbc.cpsp.eden.asset.mapper" level="DEBUG"/>
<logger name="com.baomidou.mybatisplus.core.executor" level="DEBUG"/>
<logger name="com.baomidou.mybatisplus.core.executor.BaseExecutor" level="DEBUG"/>
<!-- 设置打印SQL语句所在的包 -->
<logger name="org.apache.ibatis" level="DEBUG"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<!-- 根日志级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

View File

@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.psbc.cpsp.eden.mapper.asset.AssetClassificationMapper">
<resultMap id="BaseResultMap" type="com.psbc.cpsp.eden.common.entity.AssetClassification">
<id column="id" property="id"/>
<result column="classification_code" property="classificationCode"/>
<result column="classification_name" property="classificationName"/>
<result column="parent_id" property="parentId"/>
<result column="status" property="status"/>
<result column="remark" property="remark"/>
<result column="create_user_id" property="createUserId"/>
<result column="create_time" property="createTime"/>
<result column="del_flag" property="delFlag"/>
<result column="last_mod_user_id" property="lastModUserId"/>
<result column="last_mod_time" property="lastModTime"/>
<result column="tenant_id" property="tenantId"/>
</resultMap>
<resultMap id="TreeResultMap" type="com.psbc.cpsp.eden.common.vo.TreeNode">
<id column="id" property="id"/>
<result column="classification_name" property="label"/>
<result column="parent_id" property="parentId"/>
<result column="has_children" property="hasChildren"/>
<result column="classification_code" property="code"/>
</resultMap>
<sql id="Base_Column_List">
"id", "classification_code", "classification_name", "parent_id", "status", "remark",
"create_user_id", "create_time", "del_flag", "last_mod_user_id", "last_mod_time", "tenant_id"
</sql>
<!-- 新增资产分类 -->
<insert id="insert" parameterType="com.psbc.cpsp.eden.common.entity.AssetClassification" useGeneratedKeys="true" keyProperty="id">
INSERT INTO "tb_asset_classification" (
"classification_code", "classification_name", "parent_id", "status", "remark",
"create_user_id", "create_time", "del_flag", "last_mod_user_id", "last_mod_time"
) VALUES (
#{classificationCode}, #{classificationName}, #{parentId}, #{status}, #{remark},
#{createUserId}, #{createTime}, #{delFlag}, #{lastModUserId}, #{lastModTime}
)
</insert>
<!-- 修改资产分类 -->
<update id="update" parameterType="com.psbc.cpsp.eden.common.entity.AssetClassification">
UPDATE "tb_asset_classification"
<set>
"classification_code" = #{classificationCode},
"classification_name" = #{classificationName},
"parent_id" = #{parentId},
"status" = #{status},
"remark" = #{remark},
"last_mod_user_id" = #{lastModUserId},
"last_mod_time" = #{lastModTime}
</set>
WHERE "id" = #{id} AND "del_flag" = '0'
</update>
<!-- 更新资产分类状态 -->
<update id="updateStatus">
UPDATE "tb_asset_classification"
SET "status" = #{status},
"last_mod_user_id" = #{lastModUserId},
"last_mod_time" = #{lastModTime}
WHERE "id" = #{id} AND "del_flag" = '0'
</update>
<!-- 根据ID查询资产分类详情 -->
<select id="selectById" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM "tb_asset_classification"
WHERE "id" = #{id} AND "del_flag" = '0'
</select>
<!-- 查询资产分类列表 -->
<select id="selectList" parameterType="com.psbc.cpsp.eden.common.entity.AssetClassification" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM "tb_asset_classification"
WHERE "del_flag" = '0'
<if test="classificationCode != null and classificationCode != ''">
AND "classification_code" LIKE CONCAT('%', #{classificationCode}, '%')
</if>
<if test="classificationName != null and classificationName != ''">
AND "classification_name" LIKE CONCAT('%', #{classificationName}, '%')
</if>
<if test="parentId != null">
AND "parent_id" = #{parentId}
</if>
<if test="status != null and status != ''">
AND "status" = #{status}
</if>
ORDER BY "classification_code"
</select>
<!-- 逻辑删除资产分类 -->
<update id="deleteById">
UPDATE "tb_asset_classification"
SET "del_flag" = '1',
"last_mod_user_id" = #{lastModUserId},
"last_mod_time" = NOW()
WHERE "id" = #{id} AND "del_flag" = '0'
</update>
<!-- 批量逻辑删除资产分类 -->
<update id="deleteBatchIds">
UPDATE "tb_asset_classification"
SET "del_flag" = '1',
"last_mod_user_id" = #{lastModUserId},
"last_mod_time" = NOW()
WHERE "id" IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
AND "del_flag" = '0'
</update>
<!-- 检查分类编码是否唯一 -->
<select id="checkCodeUnique" resultType="int">
SELECT COUNT(1)
FROM "tb_asset_classification"
WHERE "classification_code" = #{classificationCode}
AND "del_flag" = '0'
<if test="id != null">
AND "id" !=#{id}
</if>
</select>
<!-- 检查分类名称是否存在 -->
<select id="checkNameExists" resultType="java.lang.Integer">
SELECT COUNT(1)
FROM "tb_asset_classification"
WHERE "classification_name" = #{classificationName}
AND "del_flag" = '0'
<if test="id != null ">
AND "id" !=#{id}
</if>
</select>
<!-- 查询资产分类树结构 -->
<select id="selectTree" resultMap="TreeResultMap">
SELECT
a."id",
a."classification_name",
a."parent_id",
a."classification_code",
CASE WHEN EXISTS (
SELECT 1 FROM "tb_asset_classification" b
WHERE b."parent_id" = a."id"
AND b."del_flag" = '0'
) THEN TRUE ELSE FALSE END AS "has_children"
FROM "tb_asset_classification" a
WHERE a."del_flag" = '0'
<if test="classificationName != null and classificationName != ''">
AND a."classification_name" LIKE CONCAT('%' ,#{classificationName}, '%')
</if>
<if test="status != null and status != ''">
AND a."status" = #{status}
</if>
ORDER BY a."parent_id" NULLS FIRST, a."classification_code"
</select>
<!-- 根据父ID查询所有子节点 -->
<select id="selectChildrenByParentId" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM "tb_asset_classification"
WHERE "del_flag" = '0'
<if test="parentId != null">
AND "parent_id" = #{parentId}
</if>
ORDER BY "classification_code"
</select>
<!-- 根据分类名称查询分类ID -->
<select id="selectIdByName" resultType="java.lang.Long">
SELECT "id"
FROM "tb_asset_classification"
WHERE "classification_name" = #{classificationName}
AND "del_flag" = '0'
LIMIT 1
</select>
</mapper>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.psbc.cpsp.eden.mapper.asset.AssetLabelMapper">
<resultMap id="BaseResultMap" type="com.psbc.cpsp.eden.common.entity.AssetLabel">
<id column="id" property="id"/>
<result column="template_type" property="templateType"/>
<result column="label_items" property="labelItems"/>
<result column="paper_type" property="paperType"/>
<result column="label_width" property="labelWidth"/>
<result column="label_height" property="labelHeight"/>
<result column="create_user_id" property="createUserId"/>
<result column="create_time" property="createTime"/>
<result column="del_flag" property="delFlag"/>
<result column="last_mod_user_id" property="lastModUserId"/>
<result column="last_mod_time" property="lastModTime"/>
<result column="tenant_id" property="tenantId"/>
</resultMap>
<sql id="Base_Column_List">
"id", "template_type", "label_items", "paper_type", "label_width", "label_height",
"create_user_id", "create_time", "del_flag", "last_mod_user_id", "last_mod_time", "tenant_id"
</sql>
<!-- 新增资产标签 -->
<insert id="insert" parameterType="com.psbc.cpsp.eden.common.entity.AssetLabel" useGeneratedKeys="true" keyProperty="id">
INSERT INTO "tb_asset_label" (
"template_type", "label_items", "paper_type", "label_width", "label_height",
"create_user_id", "create_time", "del_flag", "last_mod_user_id", "last_mod_time"
) VALUES (
#{templateType}, #{labelItems}, #{paperType}, #{labelWidth}, #{labelHeight},
#{createUserId}, #{createTime}, #{delFlag}, #{lastModUserId}, #{lastModTime}
)
</insert>
<!-- 修改资产标签 -->
<update id="update" parameterType="com.psbc.cpsp.eden.common.entity.AssetLabel">
UPDATE "tb_asset_label"
<set>
"template_type" = #{templateType},
"label_items" = #{labelItems},
"paper_type" = #{paperType},
"label_width" = #{labelWidth},
"label_height" = #{labelHeight},
"last_mod_user_id" = #{lastModUserId},
"last_mod_time" = #{lastModTime}
</set>
WHERE "id" = #{id} AND "del_flag" = '0'
</update>
<!-- 根据ID查询资产标签详情 -->
<select id="selectById" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM "tb_asset_label"
WHERE "id" = #{id} AND "del_flag" = '0'
</select>
<!-- 查询资产标签列表 -->
<select id="selectList" parameterType="com.psbc.cpsp.eden.common.entity.AssetLabel" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM "tb_asset_label"
WHERE "del_flag" = '0'
<if test="templateType != null and templateType != ''">
AND "template_type" = #{templateType}
</if>
<if test="paperType != null and paperType != ''">
AND "paper_type" = #{paperType}
</if>
ORDER BY "id"
</select>
<!-- 逻辑删除资产标签 -->
<update id="deleteById">
UPDATE "tb_asset_label"
SET "del_flag" = '1',
"last_mod_user_id" = #{lastModUserId},
"last_mod_time" = NOW()
WHERE "id" = #{id} AND "del_flag" = '0'
</update>
<!-- 批量逻辑删除资产标签 -->
<update id="deleteBatchIds">
UPDATE "tb_asset_label"
SET "del_flag" = '1',
"last_mod_user_id" = #{lastModUserId},
"last_mod_time" = NOW()
WHERE "id" IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
AND "del_flag" = '0'
</update>
<!-- 根据租户ID查询资产标签 -->
<select id="selectByTenantId" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM "tb_asset_label"
WHERE "del_flag" = '0'
LIMIT 1
</select>
</mapper>

View File

@ -0,0 +1,187 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.psbc.cpsp.eden.mapper.asset.AssetLocationMapper">
<resultMap id="BaseResultMap" type="com.psbc.cpsp.eden.common.entity.AssetLocation">
<id column="id" property="id"/>
<result column="location_code" property="locationCode"/>
<result column="location_name" property="locationName"/>
<result column="parent_id" property="parentId"/>
<result column="status" property="status"/>
<result column="remark" property="remark"/>
<result column="create_user_id" property="createUserId"/>
<result column="create_time" property="createTime"/>
<result column="del_flag" property="delFlag"/>
<result column="last_mod_user_id" property="lastModUserId"/>
<result column="last_mod_time" property="lastModTime"/>
<result column="tenant_id" property="tenantId"/>
</resultMap>
<resultMap id="TreeResultMap" type="com.psbc.cpsp.eden.common.vo.TreeNode">
<id column="id" property="id"/>
<result column="location_name" property="label"/>
<result column="parent_id" property="parentId"/>
<result column="has_children" property="hasChildren"/>
<result column="location_code" property="code"/>
</resultMap>
<sql id="Base_Column_List">
"id", "location_code", "location_name", "parent_id", "status", "remark",
"create_user_id", "create_time", "del_flag", "last_mod_user_id", "last_mod_time", "tenant_id"
</sql>
<!-- 新增资产位置 -->
<insert id="insert" parameterType="com.psbc.cpsp.eden.common.entity.AssetLocation" useGeneratedKeys="true" keyProperty="id">
INSERT INTO "tb_asset_location" (
"location_code", "location_name", "parent_id", "status", "remark",
"create_user_id", "create_time", "del_flag", "last_mod_user_id", "last_mod_time"
) VALUES (
#{locationCode}, #{locationName}, #{parentId}, #{status}, #{remark},
#{createUserId}, #{createTime}, #{delFlag}, #{lastModUserId}, #{lastModTime}
)
</insert>
<!-- 修改资产位置 -->
<update id="update" parameterType="com.psbc.cpsp.eden.common.entity.AssetLocation">
UPDATE "tb_asset_location"
<set>
"location_code" = #{locationCode},
"location_name" = #{locationName},
"parent_id" = #{parentId},
"status" = #{status},
"remark" = #{remark},
"last_mod_user_id" = #{lastModUserId},
"last_mod_time" = #{lastModTime}
</set>
WHERE "id" = #{id} AND "del_flag" = '0'
</update>
<!-- 更新资产位置状态 -->
<update id="updateStatus">
UPDATE "tb_asset_location"
SET "status" = #{status},
"last_mod_user_id" = #{lastModUserId},
"last_mod_time" = #{lastModTime}
WHERE "id" = #{id} AND "del_flag" = '0'
</update>
<!-- 根据ID查询资产位置详情 -->
<select id="selectById" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM "tb_asset_location"
WHERE "id" = #{id} AND "del_flag" = '0'
</select>
<!-- 查询资产位置列表 -->
<select id="selectList" parameterType="com.psbc.cpsp.eden.common.entity.AssetLocation" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM "tb_asset_location"
WHERE "del_flag" = '0'
<if test="locationCode != null and locationCode != ''">
AND "location_code" LIKE CONCAT('%', #{locationCode}, '%')
</if>
<if test="locationName != null and locationName != ''">
AND "location_name" LIKE CONCAT('%', #{locationName}, '%')
</if>
<if test="parentId != null">
AND "parent_id" = #{parentId}
</if>
<if test="status != null and status != ''">
AND "status" = #{status}
</if>
ORDER BY "location_code"
</select>
<!-- 逻辑删除资产位置 -->
<update id="deleteById">
UPDATE "tb_asset_location"
SET "del_flag" = '1',
"last_mod_user_id" = #{lastModUserId},
"last_mod_time" = NOW()
WHERE "id" = #{id} AND "del_flag" = '0'
</update>
<!-- 批量逻辑删除资产位置 -->
<update id="deleteBatchIds">
UPDATE "tb_asset_location"
SET "del_flag" = '1',
"last_mod_user_id" = #{lastModUserId},
"last_mod_time" = NOW()
WHERE "id" IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
AND "del_flag" = '0'
</update>
<!-- 检查位置编码是否唯一 -->
<select id="checkCodeUnique" resultType="int">
SELECT COUNT(1)
FROM "tb_asset_location"
WHERE "location_code" = #{locationCode}
AND "del_flag" = '0'
<if test="id != null">
AND "id" != #{id}
</if>
</select>
<select id="checkNameUnique" resultType="int">
SELECT COUNT(1)
FROM "tb_asset_location"
WHERE "location_name" = #{locationName}
AND "del_flag" = '0'
<if test="id != null">
AND "id" != #{id}
</if>
</select>
<!-- 查询资产位置树结构 -->
<select id="selectTree" resultMap="TreeResultMap">
SELECT
a."id",
a."location_name",
a."parent_id",
a."location_code",
CASE WHEN EXISTS (
SELECT 1 FROM "tb_asset_location" b
WHERE b."parent_id" =a."id"
AND b."del_flag" = '0'
) THEN TRUE ELSE FALSE END AS "has_children"
FROM "tb_asset_location" a
WHERE a."del_flag" = '0'
<if test="locationName != null and locationName != ''">
AND a."location_name" LIKE CONCAT('%', #{locationName}, '%')
</if>
<if test="status != null and status != ''">
AND a."status" = #{status}
</if>
ORDER BY a."parent_id" NULLS FIRST, a."location_code"
</select>
<!-- 根据父ID查询所有子节点 -->
<select id="selectChildrenByParentId" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM "tb_asset_location"
WHERE "del_flag" = '0'
<if test="parentId != null">
AND "parent_id" = #{parentId}
</if>
ORDER BY "location_code"
</select>
<!-- 根据位置名称查询位置ID -->
<select id="selectIdByName" resultType="java.lang.Long">
SELECT "id"
FROM "tb_asset_location"
WHERE "location_name" = #{locationName}
AND "del_flag" = '0'
</select>
<!-- 检查位置名称是否存在 -->
<select id="checkLocationNameExists" resultType="boolean">
SELECT COUNT(1) > 0
FROM "tb_asset_location"
WHERE "location_name" = #{locationName}
AND "del_flag" = '0'
</select>
</mapper>

Some files were not shown because too many files have changed in this diff Show More