commit 267eba1eca7379e9cb2e19f80b012ca52deb48a0 Author: Fdily <2949549024@qq.com> Date: Thu Apr 23 16:58:11 2026 +0800 Initial commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..60c1da2 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..846dcd6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### JRebel ### +rebel.xml + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml + +**/.vscode/ +**/__pycache__/ +**/uv.lock \ No newline at end of file diff --git a/bin/clean.bat b/bin/clean.bat new file mode 100644 index 0000000..578e77d --- /dev/null +++ b/bin/clean.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [信息] 清理工程target生成路径。 +echo. + +%~d0 +cd %~dp0 + +cd .. +call mvn clean + +pause \ No newline at end of file diff --git a/bin/package.bat b/bin/package.bat new file mode 100644 index 0000000..735b65e --- /dev/null +++ b/bin/package.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [信息] 打包Web工程,生成war/jar包文件。 +echo. + +%~d0 +cd %~dp0 + +cd .. +call mvn clean package -Dmaven.test.skip=true + +pause \ No newline at end of file diff --git a/bin/run.bat b/bin/run.bat new file mode 100644 index 0000000..ebeb8f8 --- /dev/null +++ b/bin/run.bat @@ -0,0 +1,14 @@ +@echo off +echo. +echo [信息] 使用Jar命令运行Web工程。 +echo. + +cd %~dp0 +cd ../ruoyi-admin/target + +set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m + +java -jar %JAVA_OPTS% ruoyi-admin.jar --server.port=8081 + +cd bin +pause \ No newline at end of file diff --git a/doc/业务数据表整理.md b/doc/业务数据表整理.md new file mode 100644 index 0000000..14e7dad --- /dev/null +++ b/doc/业务数据表整理.md @@ -0,0 +1,177 @@ +区分动态数据表和静态数据表,动态数据表是网站运行过程中用户产生的数据记录,重置网站时,可以清理掉。 +静态数据表是网站功能需要的基础数据,不能清理掉,清理掉会让网站不能运行。 + +# 系统表 +gen_table +gen_table_column +qrtz_blob_triggers +qrtz_calendars +qrtz_cron_triggers +qrtz_fired_triggers +qrtz_job_details +qrtz_locks +qrtz_paused_trigger_grps +qrtz_scheduler_state +qrtz_simple_triggers +qrtz_simprop_triggers +qrtz_triggers +sys_config +sys_dept +sys_dict_data +sys_dict_type +sys_job +sys_job_log +sys_logininfor +sys_menu +sys_notice +sys_oper_log +sys_post +sys_role +sys_role_dept +sys_role_menu +sys_user +sys_user_post +sys_user_role + + +# 开箱游戏 +tt_box 开箱游戏共用的箱子数据, boxType字段用来区分是哪种开箱游戏的 +tt_box_ornaments 箱子中的饰品数据,主要是开出概率。 +tt_box_records 动态数据表,开箱记录。箱子id-饰品id-开出人-拥有人 +tt_box_third_explosive_user 开箱三级爆率用户,拥有特殊的开箱奖池 +tt_box_type 开箱 +tt_ornament 饰品元信息,所有开箱游戏共享相同的饰品信息,如名字,价格等 +tt_ornaments_level +tt_comp_record 箱子的补偿奖池记录? + +# 百变竞技 +tt_robot_fight_group +tt_robot_fight_group_box +tt_fight 动态数据表 +tt_fight_ranking_reward 对战排行榜奖励 +tt_fight_result 动态数据表 +tt_fight_round 动态数据表 +tt_fight_user 动态数据表 + +# 指数盲盒 +tt_exponent +tt_exponent_user +tt_exponent_user_box + +# roll房 +tt_roll +tt_roll_jackpot +tt_roll_jackpot_ornaments +tt_roll_user +tt_roll_user_prize +tt_time_roll +tt_time_roll_user + +# 极速永痕 +game_sugar_record 动态数据表 +game_sugar_spin +game_sugar_spin_free +game_sugar_spin_super +game_sugar_step_info +game_sugar_step_info_free +game_sugar_step_info_super +game_sugar_user 动态数据表 +game_sugar_win 动态数据表 + +# 疯狂转盘 +game_wheel_round 动态数据表 +game_wheel_round_bet 动态数据表 +game_wheel_user 动态数据表 + +# 充值支付系统 包含CDK +tt_order 动态数据表。用户购买的充值订单 +tt_recharge_card 充值卡密 +tt_recharge_config +tt_recharge_prod +tt_recharge_ranking_reward 对战排行榜奖励 +tt_recharge_record +tianxin_order 无用 + +# 提货与转增 +tt_box_transfer_record 动态数据表。 转增记录 + +# 发货记录 +tt_delivery_record 动态数据表 + +# 公告 ApiAnnouncementController +tt_announcement +tt_announcement_read + +# 签到记录 +tt_attendance_record + +# 首页大屏广告banner +tt_advertisement + +# 网站设置 -- 更偏向于网页功能 +tt_banner 和tt_advertisement功能一模一样 +tt_content 文章 +tt_content_type 文章 + +# 推广 +tt_commission_record 动态数据表,推广佣金记录 无用 +tt_promotion_level 动态数据表 +tt_promotion_record 动态数据表 +tt_promotion_update 动态数据表 + +# 后台操作向用户消息 +tt_message +tt_message_send + +# 系统异步向用户推送结果通知 +tt_notice 前端的消息通知是这个 + +## 福利红包 +与CDK相比福利红包不记录到充值流水。 + +tt_red_pack 动态数据表,红包模板 +tt_red_packet 动态数据表,红包数据 +tt_red_packet_record 动态数据表,红包领取记录 + +# 任务中心 +tt_task_center +tt_task_center_user + +# 黄金装备 +tt_upgrade_fail_ornaments +tt_upgrade_ornaments +tt_upgrade_record + +# 用户系统 +tt_user 动态数据表。删除数据需谨慎 +tt_user_avatar 用户头像表 +tt_user_blend_ercash 动态数据表。流水日志 + +tt_user_amount_records 淘汰。被tt_user_blend_ercash替换了 +tt_user_credits_records 淘汰。被tt_user_blend_ercash替换了 + +# vip系统 +tt_vip_level 用户VIP等级配置 + +# 福利系统 +tt_welfare +tt_welfare_monthly_recharges +tt_welfare_record +tt_welfare_vip_level + +# 奖金 +tt_bonus 福利元信息 +tt_bonus_receive_record 福利领取记录 + +# 首充赠送 +tt_first_recharge + +# 无用 +tt_replacement_record 汰换记录 +tt_ornament_yy yy市场的饰品信息 +tt_ornament_zbt zbt市场的饰品信息 +tt_box_open_chance 开箱机会,可以干掉 +tt_user_box_open_chance +tt_user_label +tt_game_mould 未知 +tt_game_mould_box 未知 \ No newline at end of file diff --git a/doc/修复图片地址错误.md b/doc/修复图片地址错误.md new file mode 100644 index 0000000..e69de29 diff --git a/doc/开发标准/多环境配置文件.md b/doc/开发标准/多环境配置文件.md new file mode 100644 index 0000000..2422e2e --- /dev/null +++ b/doc/开发标准/多环境配置文件.md @@ -0,0 +1,12 @@ +application.yml 是项目的通用配置文件 +application-{profile}.yml 是项目不同环境的配置文件,如果与application.yml配置内容重复,则以application-{profile}.yml配置为准 + +当前有三个环境的配置文件: +- application-dev.yml 开发环境配置。本地测试用 +- application-test.yml 测试环境配置。服务器上测试环境用 +- application-pro.yml 生产环境配置 + + +application.yml中默认启用的是application-dev.yml。可以通过环境变量进行修改。 + +在IDEA中,编辑启动配置,添加环境变量`SPRING_PROFILES_ACTIVE=test`, 即可启用application-test.yml配置。以此类推 diff --git a/doc/开发标准/安全.md b/doc/开发标准/安全.md new file mode 100644 index 0000000..3fe199a --- /dev/null +++ b/doc/开发标准/安全.md @@ -0,0 +1,3 @@ +任何被用户使用的接口,都是用POST方法,并使用加密传输。 + +这里的用户包括游戏用户,也包括主播用户。 \ No newline at end of file diff --git a/doc/开发标准/时间.md b/doc/开发标准/时间.md new file mode 100644 index 0000000..50b947d --- /dev/null +++ b/doc/开发标准/时间.md @@ -0,0 +1,70 @@ +JDK与时间相关的包和API: + +# 包 + +包 | 引入 | 描述 +-------|------|------------- +java.time | 1.8+ | 推荐使用 +java.util | 旧版 | 不推荐 +java.text | 旧版 | 日期字符串格式化相关,不推荐使用 +java.sql | 1.1 | 数据库相关,业务不推荐使用 +java.time.chrono| 1.8 | 可选,能不用就不用。支持非 ISO 历法系统 + +# java.time包中的类 +| 类/接口 | 用途 | +| ------------------- | --------------------- | +| `LocalDate` | 日期(年-月-日) | +| `LocalTime` | 时间(时:分:秒.纳秒) | +| `LocalDateTime` | 日期+时间 | +| `ZonedDateTime` | 带时区的日期时间 | +| `Instant` | 时间戳(从1970-01-01的秒/毫秒) | +| `Duration` | 时间间隔(基于秒和纳秒) | +| `Period` | 日期间隔(基于年月日) | +| `DateTimeFormatter` | 日期时间格式化 | +LocalDate,LocalTime,LocalDateTime都是指表示一个时间数字,没有时区概念。比如LocalTime表示8:20,并不能表示出具体的时间,需要结合时区才能知道是哪里的8:20。 + + +java.time.format +DateTimeFormatter 及其相关类 +用于格式化与解析日期时间 +java.time.temporal + 时间访问和调整的底层接口 + 包含 Temporal, TemporalField, TemporalUnit 等 + +java.time.zone + 时区相关类 + ZoneId, ZoneOffset, ZoneRules 等 + +# java.util包中的类 +| 类 | 说明 | +| ---------- |-------------------| +| `Date` | 传统日期类(已不推荐),不带时区 | +| `Calendar` | 日历计算(已不推荐),默认系统时区 | +| `TimeZone` | 时区信息 | + +# java.text包中的类 +SimpleDateFormat - 旧版日期格式化(线程不安全) + +# java.sql +这些也都没有时区概念,只表示时间值 +| 类 | 用途 | +| ----------- | ------- | +| `Date` | SQL 日期 | +| `Time` | SQL 时间 | +| `Timestamp` | SQL 时间戳 | + +# 最佳实践 + +关于时区: +1. 系统内部应该只用java.time下的LocalDate,LocalTime,LocalDateTime这些不带时区的概念。 +2. 只有与外部交互时,才应该转成带时区的概念。 比如把前端请求的日期转成LocalDateTime,返回给前端的时间应该转成前端对应时区的时间。或者请求外部依赖时,需要传递时间,应该确定好时区的概念。 +3. 定时任务的时间设置应该带上时区,否则将会按照系统所在时区执行,切换服务器时,时区会变,定时任务的执行时间也会变。 +作为中国开发者,应该都用东八区时间。`@Scheduled(cron = "2 15 0 * * ?", zone = "Asia/Shanghai")` +4. 服务器的时区都设置成东八区时间 + +查看时区:`timedatectl` +列出可支持的时区:`timedatectl list-timezones | grep Shanghai` +设置时区:`sudo timedatectl set-timezone Asia/Shanghai` + +关于存储时间的格式: +1. 系统中只允许使用LocalDateTime,或者Long时间戳存储时间。优先用LocalDateTime,既拥有时间戳的所有信息,也有丰富的API操作时间。 \ No newline at end of file diff --git a/doc/开发标准/标准.md b/doc/开发标准/标准.md new file mode 100644 index 0000000..d5e33ad --- /dev/null +++ b/doc/开发标准/标准.md @@ -0,0 +1,19 @@ +# controller 请求参数 +使用一个自定义的Request类来接收参数。 + +# controller 返回的类型 +现状:目前返回的类型有R,AjaxResult,PageDataInfo等 +标准:统一返回R<具体类型>类型。 + +# mapper +mapper继承Mybatis-Plus的BaseMapper。 +```java +@Mapper +public interface AdminGameSugarUserMapper extends BaseMapper { +} +``` + +单表查询时,使用mybatis-plus的Lambda查询方式。 + +要执行复杂的查询逻辑时,用Lambda查询不好实现,或者很复杂时,可以使用@Select注解,在mapper类中写sql实现。 + diff --git a/doc/开箱玩法/Roll房/Roll房.md b/doc/开箱玩法/Roll房/Roll房.md new file mode 100644 index 0000000..8585065 --- /dev/null +++ b/doc/开箱玩法/Roll房/Roll房.md @@ -0,0 +1,3 @@ +1.查询roll房的基本信息,是否存在,是否开奖,是否有参与者 +2.查看是否有系统指定用户获取饰品,调用rollSurplusOrnaments()方法,传入全部参与用户和饰品,将该饰品指定给需要指定的用户,扣除该饰品并返回剩下的饰品,并且将指定用户过滤出 +3.获取没有指定获取饰品的用户,再将这些没指定的用户打乱顺序,再去遍历有库存饰品,按顺序给随机用户,每人一件,保存结果,更新roll房状态 \ No newline at end of file diff --git a/doc/开箱玩法/极速永恒/极速永恒.md b/doc/开箱玩法/极速永恒/极速永恒.md new file mode 100644 index 0000000..0a5b7df --- /dev/null +++ b/doc/开箱玩法/极速永恒/极速永恒.md @@ -0,0 +1,8 @@ +1.先效验传入的下注金额是否正确,再去给每个用户申请一把锁,根据用户id去redis中读取游戏缓存数据,缓存为空,扣费并且更新用户累计的投注,在去更新用户的总投注额,再去查找今天0点的统计记录,不存在则创建,否则将今日统计表中下注金额累计,再将redis中的全局奖励池累加 +再去初始化缓存数据,再去通过selectGame()方法去挑选一局游戏 + 1.1selectGame()方法,首先去查询用户的历史数据(历史投钱数,游戏多少次),查询不到就去初始化一条全新数据,再去redis中读取游戏的配置信息,在去读取配置表(有rtp=0.8),再从redis中拿到全局的奖励池(所有玩家总投注,所有玩家总获奖),再将StrategyContext对象组装 + 再去执行三个策略,分别是:普通旋转,免费旋转,超级旋转,三个旋转只是查询条件不同,最后封装GameSpin对象返回 +2.处理免费旋转,再去更新缓存,再去检测并且去累加S出现的次数,再去累加倍数到累计赢取额,然后写回缓存保存更新 +3.判断游戏是否结束,去计算最终的奖励,总得分(score) × 单注(bet) = 本局实际奖励金额,若计算结果≤0,强制设为0.01保底,再将赢取的金额加到玩家账号 +4.本局游戏结束之后,将该用户的redis缓存为空,去更新用户累计的输赢金额,再去更新统计表和Redis的全局奖励池 +5.构建完整数据并且返回 \ No newline at end of file diff --git a/doc/开箱玩法/疯狂转盘/疯狂转盘.md b/doc/开箱玩法/疯狂转盘/疯狂转盘.md new file mode 100644 index 0000000..156411a --- /dev/null +++ b/doc/开箱玩法/疯狂转盘/疯狂转盘.md @@ -0,0 +1,10 @@ +1.开奖算法:决定转盘停在哪个格子 + 1.1 无人下注的话,过滤特殊效果S,从剩下的格子里面随机选择一个 + 1.2 有人下注的话,需要去计算每个格子的纯利润,需要去遍历每个格子符号,去获取每个格子的位置,将特殊符号S过滤,去计算需要赔付多少钱payoutIf()方法,计算出赔付金额, + 通过本局的总注额减去赔付金额就能计算出净利润,再将格子下标和净利润加入列表 + 1.2.1payoutIf()方法,根据下注的符号,去计算需要赔付多少钱,赔付金额是 = 总下注额*赔率 + 1.3 判断是否触发特殊效果S,生成一个0到1的随机数,如果小于特殊效果S的概率,则触发特殊效果S,然后再去随机选择一种特殊效果,记录特殊效果名 + 1.3.1 如果是系统赢,随机找一个非s的下标,将下标和:S0组装表示为系统赢,直接返回 + 1.3.2 免费旋转,默认是1次,随机找一个非s的下标,将下标和:S1组装表示为免费旋转1次,可以旋转3次 + 1.4 普通开奖,计算出净利润大于0的格子的集合,再从盈利的格子随机选一个,如果没有净利润大于0的格子就去选亏得最少的格子,将结果加入 + diff --git a/doc/开箱玩法/百变竞技/百变竞技.md b/doc/开箱玩法/百变竞技/百变竞技.md new file mode 100644 index 0000000..97e363b --- /dev/null +++ b/doc/开箱玩法/百变竞技/百变竞技.md @@ -0,0 +1,10 @@ +1.创建对战房间,先效验创建房间参数,0是欧皇,1是非酋,几人对战,配置宝箱的信息以及费用,回合数是1-15次,验证余额是否足够,初始化座位, +再去构建对战对象,再去扣减房主的报名费,房主自动入第一个座,再去保存对战人员的信息,创建房间成功通过异步WebSocket广播通知大厅有新房间 +2.玩家加入对战房间,先获取锁,防止并发加入,再去效验对战信息joinFightCheck()方法,方法首先查询未对战信息, +再去查询是否报名,再去检查余额是否足够,再去寻找空位入座,检查是否重复加入,更新对战座位信息,扣除加入人员报名费,保存参与记录,异步WebSocket广播用户加入房间 +3.fightBegin开始游戏方法,获取锁防止重复开始,检查游戏是否开始,执行多回合的抽奖newComputerFight()方法,再去计算胜负computerWinner()方法,该方法包含对战模式,座位信息,抽奖结果, +去分配奖品,如果是多个赢家,失败者的奖品价值平分,赢家的奖品归自己,失败者的奖品累计,去平分失败者的总价值,如果是单个玩家或全员平局,单个赢家,将失败者奖品分给赢家,全员平局的话就轮流分配, +最后再去批量保存开箱记录,更新对战状态为进行中,在异步WebSocket广播,构建开箱结果,广播给房间用户和大厅 + 3.1.newComputerFight()方法,获取宝箱配置,获取所有玩家的id,设置回合计数器,外层遍历每一个宝箱,内层记录宝箱开启的次数,执行一回合的抽奖roundLottery()方法,将对局结果addAll全部加入宝箱记录集合中,回合数累加,再去更新宝箱历史开箱统计,在补充对战的id + 3.1.1.roundLottery()方法,遍历当前回合所有的玩家,再去使用经典开箱方法singleLottery(),获取饰品详情,再去构建开箱的记录 + 3.2 computerWinner()方法,有两个模式,欧皇模式(总价值最高者胜)和非酋模式(总价值最低者胜),欧皇模式去累加该玩家获得的所有的饰品价值,比较是否为最高或者平局,非酋模式是否为最低或者平局 diff --git a/doc/开箱玩法/经典开箱/经典开箱饰品分配.md b/doc/开箱玩法/经典开箱/经典开箱饰品分配.md new file mode 100644 index 0000000..86d54ff --- /dev/null +++ b/doc/开箱玩法/经典开箱/经典开箱饰品分配.md @@ -0,0 +1,12 @@ +1.根据用户类型确定使用哪个奖池,普通奖池,补偿奖池,三级爆率奖池, +在再根据宝箱id和用户类型获取锁,防止同一个宝箱同一个用户类型的并发抽奖,持续10秒,重试三次 +2.获取三个奖池对象,判断三个奖池是否为空,如果为空,就初始化创建空奖池 +3.查询用户是否拥有三级爆率,如果不为空,说明有三级爆率 +4.根据是否有三级爆率权限,选择使用哪个奖池,checkPool() 方法首先查看当前prizePool对象是否有数据, +是直接返回,否则去redis中查看是否有数据,有则将数据加入prizePool对象中, +否则去数据库中查询,并加入prizePool对象中,有三级爆率就使用thirdExplosivePrizePool,否则使用prizePool +5.补偿机制,先查询用户的补偿记录是否有值,如果是补偿箱并且没有补偿记录,创建空的补偿记录,如果有补偿记录,查看是否到达补偿阈值, +如果是补偿箱并且补偿值>=阈值,将切换到补偿奖池,并将补偿值清楚,将补偿记录删除 +6.执行抽奖的算法doLottery()方法,箱子里面包含饰品及其数量,获取饰品总数量,并且随机五次并记录最后一次的值, +然后循环每个饰品的位置,如果这个位置的值和记录的最后一次的值相同,拿到当前饰品的id, +如果循环多次没有拿到,就取饰品的第一个(按序给),库存减一 \ No newline at end of file diff --git a/doc/数据库变更.md b/doc/数据库变更.md new file mode 100644 index 0000000..325aae6 --- /dev/null +++ b/doc/数据库变更.md @@ -0,0 +1,6 @@ +添加机器人爆率 + +ALTER TABLE `tt_box_ornaments` ADD COLUMN `robot_odds` int DEFAULT '0' COMMENT '机器人数量' AFTER `anchor_odds`; + +ALTER TABLE game_sugar_record +ADD COLUMN multiplier decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '倍率'; \ No newline at end of file diff --git a/doc/数据库备份.md b/doc/数据库备份.md new file mode 100644 index 0000000..a6e13dd --- /dev/null +++ b/doc/数据库备份.md @@ -0,0 +1,6 @@ +mysqldump -ufire -piTSJSPPZM3LSGAPC fire --no-tablespaces game_sugar_spin > game_sugar_spin.sql + +mysqldump -ufire -piTSJSPPZM3LSGAPC fire --no-tablespaces game_sugar_step_info > game_sugar_step_info.sql + + +mysql -u fire -piTSJSPPZM3LSGAPC fire < game_sugar_spin.sql \ No newline at end of file diff --git a/doc/新手环境搭建文档/img.png b/doc/新手环境搭建文档/img.png new file mode 100644 index 0000000..19d33e7 Binary files /dev/null and b/doc/新手环境搭建文档/img.png differ diff --git a/doc/新手环境搭建文档/环境搭建文档.md b/doc/新手环境搭建文档/环境搭建文档.md new file mode 100644 index 0000000..b61f0f5 --- /dev/null +++ b/doc/新手环境搭建文档/环境搭建文档.md @@ -0,0 +1,29 @@ +1.本地安装jdk21 + 配置jdk环境变量,idea官网下载版本2024.3.3 破解版 ——》 https://docs.qq.com/doc/DWkdRQVZRV0RoZHdh +2.本地安装maven3.6.1 + 配置settings.xml文件 + 1). 复制标签,粘贴到注释的外面(55行)左右 + D:\software\apache-maven-3.6.1\local_repo //自己的maven安装路径 + 2). 打开settings.xml文件,定位到160行左右 + 2). 在标签下为其添加子标签,内容如下: + + alimaven + aliyun maven + http://maven.aliyun.com/nexus/content/groups/public/ + central + + 3).配置Maven环境变量,MVN -V 查看是否配置成功 +3.安装docker for windows + 1).配置教程 https://developer.aliyun.com/article/1604556 + 2).国内镜像加速 ![img.png](img.png) 图片所示 加速器链接 "https://docker.1ms.run" +4.下载git 拉取代码 + 1).创建文件夹存放项目,git init初始化,使用git clone http://39.107.124.211:13000/cocos/ruoyi.git 拉取代码 + 2).账号密码是 cocos02 cocos02@163.com + 3).在ruoyi项目docker目录下cmd,使用命令docker compose up -d启动mysql,redis,rabbitmq + 4).导入sql文件 +5.安装vs code + 1).安装node.js 版本 node v22.22.0 npm版本 10.9.4 安装教程——》https://blog.csdn.net/Natsuago/article/details/145567734 + 2).npm run dev + + + diff --git a/doc/清理数据.md b/doc/清理数据.md new file mode 100644 index 0000000..2ad01a3 --- /dev/null +++ b/doc/清理数据.md @@ -0,0 +1,57 @@ +"game_sugar_record", +"game_sugar_user", +"game_wheel_round_bet", +"game_wheel_user", +"tt_announcement_read", +"tt_attendance_record", +"tt_bonus_receive_record", +"tt_box_records", +"tt_commission_record", +"tt_delivery_record", +"tt_exponent_user", +"tt_exponent_user_box", +"tt_fight", +"tt_fight_user", +"tt_notice", +"tt_order", +"tt_promotion_record", +"tt_recharge_record", +"tt_red_packet", +"tt_red_packet_record", +"tt_roll", +"tt_roll_user", +"tt_task_center_user", +"tt_time_roll_user", +"tt_upgrade_record", +"tt_user_blend_ercash", +"tt_welfare_record", +"tt_recharge_record", +"game_sugar_win", +"game_wheel_round", +"tt_recharge_card", + + + + + +```python +tables = [ + "tt_box", + "tt_box_open_chance", + "tt_box_ornaments", + "tt_box_records", + "tt_ornament", + "tt_upgrade_fail_ornaments", + "tt_upgrade_ornaments", + "tt_upgrade_record", + "tt_roll", + "tt_roll_jackpot", + "tt_roll_jackpot_ornaments", + "tt_roll_user_prize" +] + +for table in tables: + print(f"""CREATE TABLE {table}_new LIKE {table}; +RENAME TABLE {table} TO {table}_old, {table}_new TO {table};""") + +``` \ No newline at end of file diff --git a/doc/网站迁移.md b/doc/网站迁移.md new file mode 100644 index 0000000..eb131cc --- /dev/null +++ b/doc/网站迁移.md @@ -0,0 +1,23 @@ +服务器时区设置sudo timedatectl set-timezone Asia/Shanghai +数据库时区设置: +查看时区:SHOW GLOBAL VARIABLES LIKE '%time_zone%'; +SET GLOBAL time_zone = '+08:00'; + +同步的文件资源: +图片 +数据库文件 + + +安装依赖: +docker/nginx/mysql/redis/git +jdk21/maven3.6.3 + +需要启动的服务 +docker rabbitmq coder-doc +ruoyi +nginx admin zhubo + + + +需要加白的服务: +支付/发货 \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..61823f1 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,9 @@ +# 基于官方 MySQL 8.0.24 镜像 +FROM mysql:8.0.24 + +# 设置环境变量,防止警告 +ENV MYSQL_ROOT_PASSWORD=123456 + +# 将本地的 C9_fire.sql 文件复制到容器内的 /docker-entrypoint-initdb.d/ 目录下 +# MySQL 官方镜像会自动检测并执行该目录下的 .sql 文件 +# COPY C9_fire.sql /docker-entrypoint-initdb.d/C9_fire.sql \ No newline at end of file diff --git a/docker/docker-cn/Dockerfile b/docker/docker-cn/Dockerfile new file mode 100644 index 0000000..72eb34d --- /dev/null +++ b/docker/docker-cn/Dockerfile @@ -0,0 +1,9 @@ +# 基于官方 MySQL 8.0.24 镜像 +FROM m.daocloud.io/docker.io/mysql:8.0.24 + +# 设置环境变量,防止警告 +ENV MYSQL_ROOT_PASSWORD=123456 + +# 将本地的 C9_fire.sql 文件复制到容器内的 /docker-entrypoint-initdb.d/ 目录下 +# MySQL 官方镜像会自动检测并执行该目录下的 .sql 文件 +# COPY C9_fire.sql /docker-entrypoint-initdb.d/C9_fire.sql \ No newline at end of file diff --git a/docker/docker-cn/docker-compose.yml b/docker/docker-cn/docker-compose.yml new file mode 100644 index 0000000..457e1c7 --- /dev/null +++ b/docker/docker-cn/docker-compose.yml @@ -0,0 +1,62 @@ +services: + # MySQL 8.0.24 服务 + mysql: + build: + context: . + dockerfile: Dockerfile + container_name: mysql_db + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: 123456 # 设置 root 密码 + MYSQL_DATABASE: fire # 可选:自动创建名为 c9_fire 的数据库 + MYSQL_USER: fire # 可选:创建新用户 + MYSQL_PASSWORD: iTSJSPPZM3LSGAPC # 可选:新用户密码 + ports: + - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql # 挂载数据卷以持久化数据 + networks: + - app_network + + # Redis 服务 + redis: + image: m.daocloud.io/docker.io/redis:latest + container_name: redis_cache + restart: unless-stopped + command: redis-server --requirepass FireSkins@99999 + ports: + - "6379:6379" + networks: + - app_network + + # RabbitMQ 3.9 服务 + rabbitmq: + image: m.daocloud.io/docker.io/rabbitmq:3.9-management + container_name: rabbitmq_server + restart: unless-stopped + environment: + RABBITMQ_DEFAULT_USER: skins # 设置管理界面用户名 + RABBITMQ_DEFAULT_PASS: mk # 设置管理界面密码 + ports: + - "5672:5672" # 应用连接端口 + - "15672:15672" # 管理界面 Web 端口 + command: > + bash -c "rabbitmq-server & + sleep 20 && + rabbitmqctl add_vhost /skins && + rabbitmqctl set_permissions -p /skins skins '.*' '.*' '.*' && + rabbitmqadmin --vhost=/skins --username=skins --password=mk declare queue name=notice_queue durable=true && + rabbitmqadmin --vhost=/skins --username=skins --password=mk declare queue name=dlk_queue durable=true && + rabbitmqadmin --vhost=/skins --username=skins --password=mk declare queue name=delivery_queue durable=true && + wait" + networks: + - app_network + +# 定义数据卷 +volumes: + mysql_data: + +# 定义网络 +networks: + app_network: + driver: bridge diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..52d817e --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,63 @@ +services: + # MySQL 8.0.24 服务 + mysql: + build: + context: . + dockerfile: Dockerfile + container_name: mysql_db + restart: unless-stopped + environment: + TZ: Asia/Shanghai + MYSQL_ROOT_PASSWORD: 123456 # 设置 root 密码 + MYSQL_DATABASE: fire # 可选:自动创建名为 c9_fire 的数据库 + MYSQL_USER: fire # 可选:创建新用户 + MYSQL_PASSWORD: iTSJSPPZM3LSGAPC # 可选:新用户密码 + ports: + - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql # 挂载数据卷以持久化数据 + networks: + - app_network + + # Redis 服务 + redis: + image: redis:latest + container_name: redis_cache + restart: unless-stopped + command: redis-server --requirepass FireSkins@99999 + ports: + - "6379:6379" + networks: + - app_network + + # RabbitMQ 3.9 服务 + rabbitmq: + image: rabbitmq:3.9-management + container_name: rabbitmq_server + restart: unless-stopped + environment: + RABBITMQ_DEFAULT_USER: skins # 设置管理界面用户名 + RABBITMQ_DEFAULT_PASS: mk # 设置管理界面密码 + ports: + - "5672:5672" # 应用连接端口 + - "15672:15672" # 管理界面 Web 端口 + command: > + bash -c "rabbitmq-server & + sleep 20 && + rabbitmqctl add_vhost /skins && + rabbitmqctl set_permissions -p /skins skins '.*' '.*' '.*' && + rabbitmqadmin --vhost=/skins --username=skins --password=mk declare queue name=notice_queue durable=true && + rabbitmqadmin --vhost=/skins --username=skins --password=mk declare queue name=dlk_queue durable=true && + rabbitmqadmin --vhost=/skins --username=skins --password=mk declare queue name=delivery_queue durable=true && + wait" + networks: + - app_network + +# 定义数据卷 +volumes: + mysql_data: + +# 定义网络 +networks: + app_network: + driver: bridge diff --git a/docker/readme.md b/docker/readme.md new file mode 100644 index 0000000..c7913da --- /dev/null +++ b/docker/readme.md @@ -0,0 +1,9 @@ +重新创建某个容器 +docker compose up -d --build --force-recreate rabbitmq + +docker compose up -d +docker compose stop +docker compose rm + +# 大陆环境 +进入docker-cn目录,执行上面的命令 \ No newline at end of file diff --git a/fire.sql b/fire.sql new file mode 100644 index 0000000..429c1ae --- /dev/null +++ b/fire.sql @@ -0,0 +1,4162 @@ +-- phpMyAdmin SQL Dump +-- version 5.2.3 +-- https://www.phpmyadmin.net/ +-- +-- 主机: localhost +-- 生成日期: 2026-03-07 20:25:43 +-- 服务器版本: 8.0.24 +-- PHP 版本: 8.2.28 + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +START TRANSACTION; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; + +-- +-- 数据库: `fire` +-- + +-- -------------------------------------------------------- + +-- +-- 表的结构 `game_sugar_spin` +-- + +CREATE TABLE `game_sugar_spin` ( + `gid` bigint NOT NULL COMMENT 'spinfree_spinspin', + `score` decimal(10,2) NOT NULL DEFAULT '0.00', + `count` int NOT NULL DEFAULT '0' COMMENT 'step', + `feature` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'normal' COMMENT 'normal:spin free: super_free:', + `extra_free` int NOT NULL DEFAULT '0' COMMENT 'spinspin', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='spin'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `game_sugar_spin_free` +-- + +CREATE TABLE `game_sugar_spin_free` ( + `gid` bigint NOT NULL COMMENT 'spinfree_spinspin', + `score` decimal(10,2) NOT NULL DEFAULT '0.00', + `count` int NOT NULL DEFAULT '0' COMMENT 'step', + `feature` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'normal' COMMENT 'normal:spin free: super_free:', + `extra_free` int NOT NULL DEFAULT '0' COMMENT 'spinspin', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='spin'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `game_sugar_spin_super` +-- + +CREATE TABLE `game_sugar_spin_super` ( + `gid` bigint NOT NULL COMMENT 'spinfree_spinspin', + `score` decimal(10,2) NOT NULL DEFAULT '0.00', + `count` int NOT NULL DEFAULT '0' COMMENT 'step', + `feature` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'normal' COMMENT 'normal:spin free: super_free:', + `extra_free` int NOT NULL DEFAULT '0' COMMENT 'spinspin', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='spin'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `game_sugar_step_info` +-- + +CREATE TABLE `game_sugar_step_info` ( + `id` bigint NOT NULL COMMENT 'id', + `gid` bigint NOT NULL COMMENT 'spin id', + `free_spin_id` int NOT NULL COMMENT 'free spin id', + `aes` int NOT NULL COMMENT 'stepspinstep', + `multipler` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', + `grid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', + `symbol_links` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT 'JSON', + `score` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT 'step * ', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `game_sugar_step_info_free` +-- + +CREATE TABLE `game_sugar_step_info_free` ( + `id` bigint NOT NULL COMMENT 'id', + `gid` bigint NOT NULL COMMENT 'spin id', + `free_spin_id` int NOT NULL COMMENT 'free spin id', + `aes` int NOT NULL COMMENT 'stepspinstep', + `multipler` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', + `grid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', + `symbol_links` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT 'JSON', + `score` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT 'step * ', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `game_sugar_step_info_super` +-- + +CREATE TABLE `game_sugar_step_info_super` ( + `id` bigint NOT NULL COMMENT 'id', + `gid` bigint NOT NULL COMMENT 'spin id', + `free_spin_id` int NOT NULL COMMENT 'free spin id', + `aes` int NOT NULL COMMENT 'stepspinstep', + `multipler` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', + `grid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', + `symbol_links` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT 'JSON', + `score` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT 'step * ', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `game_sugar_user` +-- + +CREATE TABLE `game_sugar_user` ( + `id` bigint NOT NULL COMMENT 'id', + `user_id` bigint NOT NULL COMMENT 'user表的id', + `total_bet` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户总下注额', + `total_win` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户总赢得金额', + `count` int NOT NULL DEFAULT '0' COMMENT '总spin次数', + `free_count` int NOT NULL DEFAULT '0' COMMENT '总购买free spin次数', + `super_free_count` int NOT NULL DEFAULT '0' COMMENT '总购买super_free spin次数', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='糖果游戏用户表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `game_sugar_win` +-- + +CREATE TABLE `game_sugar_win` ( + `id` bigint NOT NULL COMMENT 'id', + `date` datetime NOT NULL COMMENT '日期', + `init_bet` decimal(10,2) NOT NULL COMMENT '系统初始下注额', + `total_bet` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户总下注额', + `total_win` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户总赢得金额', + `count` int NOT NULL DEFAULT '0' COMMENT '总spin次数', + `free_count` int NOT NULL DEFAULT '0' COMMENT '总购买free spin次数', + `super_free_count` int NOT NULL DEFAULT '0' COMMENT '总购买super_free spin次数', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='糖果游戏每日统计表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `game_wheel_round` +-- + +CREATE TABLE `game_wheel_round` ( + `id` bigint NOT NULL COMMENT '唯一自增id', + `date` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '所属日期', + `round_id` bigint NOT NULL COMMENT 'round id', + `start_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '开始时间', + `end_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '结束时间', + `phase_times` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '每个阶段的时间点', + `total_bet` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '本期总下注数', + `total_win` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '本期玩家总赢数', + `symbols` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '转盘上的符号', + `result` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '开出的奖的集合', + `is_manual` int NOT NULL DEFAULT '0' COMMENT '是否后台开奖', + `status` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '状态', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转盘每期表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `game_wheel_round_bet` +-- + +CREATE TABLE `game_wheel_round_bet` ( + `id` bigint NOT NULL COMMENT '唯一自增id', + `round_id` bigint NOT NULL COMMENT 'round id', + `user_id` bigint NOT NULL DEFAULT '0' COMMENT '用户id', + `bet` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '下注数', + `bet_symbol` varchar(4) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '压注目标', + `win` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '赔付结果', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转盘下注表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `game_wheel_user` +-- + +CREATE TABLE `game_wheel_user` ( + `id` bigint NOT NULL COMMENT 'id', + `user_id` bigint NOT NULL COMMENT 'user表的id', + `total_bet` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户总下注额', + `total_win` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户总赢得金额', + `count` int NOT NULL DEFAULT '0' COMMENT '总下注次数', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转盘用户表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `gen_table` +-- + +CREATE TABLE `gen_table` ( + `table_id` bigint NOT NULL, + `table_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `table_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `sub_table_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `sub_table_fk_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `class_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `tpl_category` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'crud', + `package_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `module_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `business_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `function_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `function_author` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `gen_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `gen_path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '/', + `options` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `update_time` datetime DEFAULT NULL, + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `gen_table_column` +-- + +CREATE TABLE `gen_table_column` ( + `column_id` bigint NOT NULL, + `table_id` bigint DEFAULT NULL, + `column_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `column_comment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `column_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `java_type` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `java_field` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `is_pk` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `is_increment` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `is_required` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `is_insert` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `is_edit` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `is_list` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `is_query` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `query_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'EQ', + `html_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `dict_type` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `sort` int DEFAULT NULL, + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `qrtz_blob_triggers` +-- + +CREATE TABLE `qrtz_blob_triggers` ( + `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度名称', + `trigger_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_triggers表trigger_name的外键', + `trigger_group` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_triggers表trigger_group的外键', + `blob_data` blob COMMENT '存放持久化Trigger对象' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Blob类型的触发器表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `qrtz_calendars` +-- + +CREATE TABLE `qrtz_calendars` ( + `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度名称', + `calendar_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '日历名称', + `calendar` blob NOT NULL COMMENT '存放持久化calendar对象' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='日历信息表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `qrtz_cron_triggers` +-- + +CREATE TABLE `qrtz_cron_triggers` ( + `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度名称', + `trigger_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_triggers表trigger_name的外键', + `trigger_group` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_triggers表trigger_group的外键', + `cron_expression` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'cron表达式', + `time_zone_id` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '时区' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Cron类型的触发器表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `qrtz_fired_triggers` +-- + +CREATE TABLE `qrtz_fired_triggers` ( + `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度名称', + `entry_id` varchar(95) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度器实例id', + `trigger_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_triggers表trigger_name的外键', + `trigger_group` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_triggers表trigger_group的外键', + `instance_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度器实例名', + `fired_time` bigint NOT NULL COMMENT '触发的时间', + `sched_time` bigint NOT NULL COMMENT '定时器制定的时间', + `priority` int NOT NULL COMMENT '优先级', + `state` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '状态', + `job_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '任务名称', + `job_group` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '任务组名', + `is_nonconcurrent` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否并发', + `requests_recovery` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否接受恢复执行' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='已触发的触发器表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `qrtz_job_details` +-- + +CREATE TABLE `qrtz_job_details` ( + `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度名称', + `job_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务名称', + `job_group` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务组名', + `description` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '相关介绍', + `job_class_name` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '执行任务类名称', + `is_durable` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '是否持久化', + `is_nonconcurrent` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '是否并发', + `is_update_data` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '是否更新数据', + `requests_recovery` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '是否接受恢复执行', + `job_data` blob COMMENT '存放持久化job对象' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='任务详细信息表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `qrtz_locks` +-- + +CREATE TABLE `qrtz_locks` ( + `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度名称', + `lock_name` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '悲观锁名称' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='存储的悲观锁信息表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `qrtz_paused_trigger_grps` +-- + +CREATE TABLE `qrtz_paused_trigger_grps` ( + `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度名称', + `trigger_group` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_triggers表trigger_group的外键' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='暂停的触发器表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `qrtz_scheduler_state` +-- + +CREATE TABLE `qrtz_scheduler_state` ( + `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度名称', + `instance_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '实例名称', + `last_checkin_time` bigint NOT NULL COMMENT '上次检查时间', + `checkin_interval` bigint NOT NULL COMMENT '检查间隔时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='调度器状态表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `qrtz_simple_triggers` +-- + +CREATE TABLE `qrtz_simple_triggers` ( + `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度名称', + `trigger_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_triggers表trigger_name的外键', + `trigger_group` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_triggers表trigger_group的外键', + `repeat_count` bigint NOT NULL COMMENT '重复的次数统计', + `repeat_interval` bigint NOT NULL COMMENT '重复的间隔时间', + `times_triggered` bigint NOT NULL COMMENT '已经触发的次数' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='简单触发器的信息表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `qrtz_simprop_triggers` +-- + +CREATE TABLE `qrtz_simprop_triggers` ( + `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度名称', + `trigger_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_triggers表trigger_name的外键', + `trigger_group` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_triggers表trigger_group的外键', + `str_prop_1` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'String类型的trigger的第一个参数', + `str_prop_2` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'String类型的trigger的第二个参数', + `str_prop_3` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'String类型的trigger的第三个参数', + `int_prop_1` int DEFAULT NULL COMMENT 'int类型的trigger的第一个参数', + `int_prop_2` int DEFAULT NULL COMMENT 'int类型的trigger的第二个参数', + `long_prop_1` bigint DEFAULT NULL COMMENT 'long类型的trigger的第一个参数', + `long_prop_2` bigint DEFAULT NULL COMMENT 'long类型的trigger的第二个参数', + `dec_prop_1` decimal(13,4) DEFAULT NULL COMMENT 'decimal类型的trigger的第一个参数', + `dec_prop_2` decimal(13,4) DEFAULT NULL COMMENT 'decimal类型的trigger的第二个参数', + `bool_prop_1` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Boolean类型的trigger的第一个参数', + `bool_prop_2` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'Boolean类型的trigger的第二个参数' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='同步机制的行锁表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `qrtz_triggers` +-- + +CREATE TABLE `qrtz_triggers` ( + `sched_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调度名称', + `trigger_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '触发器的名字', + `trigger_group` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '触发器所属组的名字', + `job_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_job_details表job_name的外键', + `job_group` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'qrtz_job_details表job_group的外键', + `description` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '相关介绍', + `next_fire_time` bigint DEFAULT NULL COMMENT '上一次触发时间(毫秒)', + `prev_fire_time` bigint DEFAULT NULL COMMENT '下一次触发时间(默认为-1表示不触发)', + `priority` int DEFAULT NULL COMMENT '优先级', + `trigger_state` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '触发器状态', + `trigger_type` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '触发器的类型', + `start_time` bigint NOT NULL COMMENT '开始时间', + `end_time` bigint DEFAULT NULL COMMENT '结束时间', + `calendar_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '日程表名称', + `misfire_instr` smallint DEFAULT NULL COMMENT '补偿执行的策略', + `job_data` blob COMMENT '存放持久化job对象' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='触发器详细信息表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_config` +-- + +CREATE TABLE `sys_config` ( + `config_id` int NOT NULL COMMENT '参数主键', + `config_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '参数名称', + `config_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '参数键名', + `config_value` varchar(1024) COLLATE utf8mb4_general_ci DEFAULT NULL, + `config_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'N' COMMENT '系统内置(Y是 N否)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='参数配置表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_dept` +-- + +CREATE TABLE `sys_dept` ( + `dept_id` bigint NOT NULL COMMENT '部门id', + `parent_id` bigint DEFAULT '0' COMMENT '父部门id', + `ancestors` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '祖级列表', + `dept_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '部门名称', + `order_num` int DEFAULT '0' COMMENT '显示顺序', + `leader` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '负责人', + `phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '联系电话', + `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '邮箱', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '部门状态(0正常 1停用)', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='部门表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_dict_data` +-- + +CREATE TABLE `sys_dict_data` ( + `dict_code` bigint NOT NULL COMMENT '字典编码', + `dict_sort` int DEFAULT '0' COMMENT '字典排序', + `dict_label` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '字典标签', + `dict_value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '字典键值', + `dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '字典类型', + `css_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '样式属性(其他样式扩展)', + `list_class` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '表格回显样式', + `is_default` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'N' COMMENT '是否默认(Y是 N否)', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '状态(0正常 1停用)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='字典数据表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_dict_type` +-- + +CREATE TABLE `sys_dict_type` ( + `dict_id` bigint NOT NULL COMMENT '字典主键', + `dict_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '字典名称', + `dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '字典类型', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '状态(0正常 1停用)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='字典类型表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_job` +-- + +CREATE TABLE `sys_job` ( + `job_id` bigint NOT NULL COMMENT '任务ID', + `job_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '任务名称', + `job_group` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'DEFAULT' COMMENT '任务组名', + `invoke_target` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调用目标字符串', + `cron_expression` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'cron执行表达式', + `misfire_policy` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '3' COMMENT '计划执行错误策略(1立即执行 2执行一次 3放弃执行)', + `concurrent` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '是否并发执行(0允许 1禁止)', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '状态(0正常 1暂停)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '备注信息' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='定时任务调度表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_job_log` +-- + +CREATE TABLE `sys_job_log` ( + `job_log_id` bigint NOT NULL COMMENT '任务日志ID', + `job_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务名称', + `job_group` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务组名', + `invoke_target` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调用目标字符串', + `job_message` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '日志信息', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '执行状态(0正常 1失败)', + `exception_info` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '异常信息', + `create_time` datetime DEFAULT NULL COMMENT '创建时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='定时任务调度日志表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_logininfor` +-- + +CREATE TABLE `sys_logininfor` ( + `info_id` bigint NOT NULL COMMENT '访问ID', + `user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户账号', + `ipaddr` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '登录IP地址', + `login_location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '登录地点', + `browser` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '浏览器类型', + `os` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '操作系统', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '登录状态(0成功 1失败)', + `msg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '提示消息', + `login_time` datetime DEFAULT NULL COMMENT '访问时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统访问记录' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_menu` +-- + +CREATE TABLE `sys_menu` ( + `menu_id` bigint NOT NULL COMMENT '菜单ID', + `menu_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '菜单名称', + `parent_id` bigint DEFAULT '0' COMMENT '父菜单ID', + `order_num` int DEFAULT '0' COMMENT '显示顺序', + `path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '路由地址', + `component` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '组件路径', + `query` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '路由参数', + `is_frame` int DEFAULT '1' COMMENT '外链', + `is_cache` int DEFAULT '0' COMMENT '缓存', + `menu_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '类型', + `visible` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '标识', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '状态', + `perms` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '权限标识', + `icon` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '#' COMMENT '菜单图标', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '备注' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='菜单权限表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_notice` +-- + +CREATE TABLE `sys_notice` ( + `notice_id` int NOT NULL COMMENT '公告ID', + `notice_title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公告标题', + `notice_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '公告类型(1通知 2公告)', + `notice_content` longblob COMMENT '公告内容', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '公告状态(0正常 1关闭)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='通知公告表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_oper_log` +-- + +CREATE TABLE `sys_oper_log` ( + `oper_id` bigint NOT NULL COMMENT '日志主键', + `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '模块标题', + `business_type` int DEFAULT '0' COMMENT '业务类型(0其它 1新增 2修改 3删除)', + `method` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '方法名称', + `request_method` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '请求方式', + `operator_type` int DEFAULT '0' COMMENT '操作类别(0其它 1后台用户 2手机端用户)', + `oper_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '操作人员', + `dept_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '部门名称', + `oper_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '请求URL', + `oper_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '主机地址', + `oper_location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '操作地点', + `oper_param` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '请求参数', + `json_result` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '返回参数', + `status` int DEFAULT '0' COMMENT '操作状态(0正常 1异常)', + `error_msg` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '错误消息', + `oper_time` datetime DEFAULT NULL COMMENT '操作时间', + `cost_time` bigint DEFAULT '0' COMMENT '消耗时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='操作日志记录' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_post` +-- + +CREATE TABLE `sys_post` ( + `post_id` bigint NOT NULL COMMENT '岗位ID', + `post_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '岗位编码', + `post_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '岗位名称', + `post_sort` int NOT NULL COMMENT '显示顺序', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '状态(0正常 1停用)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='岗位信息表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_role` +-- + +CREATE TABLE `sys_role` ( + `role_id` bigint NOT NULL COMMENT '角色ID', + `role_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色名称', + `role_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色权限字符串', + `role_sort` int NOT NULL COMMENT '显示顺序', + `data_scope` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', + `menu_check_strictly` tinyint(1) DEFAULT '1' COMMENT '菜单树选择项是否关联显示', + `dept_check_strictly` tinyint(1) DEFAULT '1' COMMENT '部门树选择项是否关联显示', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色状态(0正常 1停用)', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='角色信息表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_role_dept` +-- + +CREATE TABLE `sys_role_dept` ( + `role_id` bigint NOT NULL COMMENT '角色ID', + `dept_id` bigint NOT NULL COMMENT '部门ID' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='角色和部门关联表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_role_menu` +-- + +CREATE TABLE `sys_role_menu` ( + `role_id` bigint NOT NULL COMMENT '角色ID', + `menu_id` bigint NOT NULL COMMENT '菜单ID' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='角色和菜单关联表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_user` +-- + +CREATE TABLE `sys_user` ( + `user_id` bigint NOT NULL COMMENT '用户ID', + `dept_id` bigint DEFAULT NULL COMMENT '部门ID', + `user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户账号', + `nick_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户昵称', + `user_type` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '00' COMMENT '用户类型(00系统用户)', + `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户邮箱', + `phonenumber` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '手机号码', + `sex` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)', + `avatar` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '头像地址', + `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '密码', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '帐号状态(0正常 1停用)', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', + `login_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '最后登录IP', + `login_date` datetime DEFAULT NULL COMMENT '最后登录时间', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户信息表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_user_post` +-- + +CREATE TABLE `sys_user_post` ( + `user_id` bigint NOT NULL COMMENT '用户ID', + `post_id` bigint NOT NULL COMMENT '岗位ID' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户与岗位关联表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `sys_user_role` +-- + +CREATE TABLE `sys_user_role` ( + `user_id` bigint NOT NULL COMMENT '用户ID', + `role_id` bigint NOT NULL COMMENT '角色ID' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户和角色关联表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tianxin_order` +-- + +CREATE TABLE `tianxin_order` ( + `order_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '系统订单号(唯一)', + `pay_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '支付方式:1支付宝', + `goods_id` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '商户商品ID', + `goods_price` decimal(10,2) DEFAULT NULL COMMENT '商户商品价格', + `goods_num` int DEFAULT NULL COMMENT '商户商品数量', + `total_amount` decimal(10,2) DEFAULT NULL COMMENT '付款总价', + `user_ip` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '用户IP', + `pay_status` int DEFAULT NULL COMMENT '支付状态: 0待支付,1已支付,2,取消支付', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '状态更新时间', + `user_id` int DEFAULT NULL COMMENT '订单发起用户ID', + `user_name` varchar(25) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '订单发起用户名', + `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注', + `call_back_order_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '回传单号', + `call_back_msg` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '回传信息', + `call_back_status` varchar(25) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '回传状态 成功,其他失败', + `sign` text CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT 'sign', + `subject` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '商品名' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_advertisement` +-- + +CREATE TABLE `tt_advertisement` ( + `id` int NOT NULL, + `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `picture` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `jump_link` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `sort` int DEFAULT '0', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_announcement` +-- + +CREATE TABLE `tt_announcement` ( + `announcement_id` int NOT NULL COMMENT '公告ID', + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '标题', + `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '内容', + `create_time` datetime DEFAULT NULL COMMENT '创建时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='公告表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + + +-- +-- 表的结构 `tt_announcement_read` +-- + +CREATE TABLE `tt_announcement_read` ( + `announcement_id` int NOT NULL COMMENT '公告ID', + `user_id` int NOT NULL COMMENT '用户ID' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='已读公告表' ROW_FORMAT=DYNAMIC; + + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_attendance_record` +-- + +CREATE TABLE `tt_attendance_record` ( + `id` int NOT NULL, + `user_id` int DEFAULT NULL COMMENT '用户id', + `create_time` datetime DEFAULT NULL, + `coin` int DEFAULT NULL COMMENT '获得金币数', + `update_time` datetime DEFAULT NULL, + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_banner` +-- + +CREATE TABLE `tt_banner` ( + `id` int NOT NULL, + `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `picture` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `jump_link` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `sort` int DEFAULT '0', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_bonus` +-- + +CREATE TABLE `tt_bonus` ( + `id` int NOT NULL, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `description` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `cover_picture` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '2', + `condition_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '3', + `recharge_threshold` decimal(10,2) UNSIGNED DEFAULT NULL, + `award_section` json DEFAULT NULL, + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1', + `sort_by` int DEFAULT '0', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_bonus_receive_record` +-- + +CREATE TABLE `tt_bonus_receive_record` ( + `id` int NOT NULL, + `type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `bonus_id` int DEFAULT NULL, + `vip_level_id` int DEFAULT NULL, + `promotion_level_id` int DEFAULT NULL, + `user_id` int DEFAULT NULL, + `award_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `award_id` int DEFAULT '0', + `award_price` decimal(10,2) UNSIGNED DEFAULT NULL, + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `receive_time` datetime DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + + -------------------------------------------------------- + +-- +-- 表的结构 `tt_box` +-- + +CREATE TABLE `tt_box` ( + `box_id` int NOT NULL, + `box_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `box_type_id` int DEFAULT '1', + `price` decimal(10,2) DEFAULT NULL, + `box_img01` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `box_img02` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `sort` int DEFAULT '0', + `is_fight` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `open_num` bigint DEFAULT '0', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `update_time` datetime DEFAULT NULL, + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `is_home` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `box_kind` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `box_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '宝箱类型', + `comp_amount` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '补偿值' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_box_open_chance` +-- + +CREATE TABLE `tt_box_open_chance` ( + `ornament_id` int NOT NULL COMMENT '道具ID', + `box_id` int DEFAULT NULL COMMENT '宝箱ID', + `chance_num` int DEFAULT NULL COMMENT '开启次数' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='开箱机会' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_box_ornaments` +-- + +CREATE TABLE `tt_box_ornaments` ( + `id` int NOT NULL, + `box_id` int DEFAULT NULL, + `ornament_id` int DEFAULT NULL, + `level` int DEFAULT '1', + `odds` int DEFAULT '1' COMMENT '显示数量', + `real_odds` int DEFAULT '0' COMMENT '真实数量', + `anchor_odds` int DEFAULT '0' COMMENT '主播数量', + `robot_odds` int DEFAULT '0' COMMENT '机器人数量', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `update_time` datetime DEFAULT NULL, + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `market_hash_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '市场唯一hash名称', + `ornaments_zbt_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '扎比特平台id', + `ornaments_yy_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'yyyouping平台id', + `comp_odds` int DEFAULT '0' COMMENT '补偿概率', + `real_comp_odds` int DEFAULT '0' COMMENT '真实补偿概率', + `third_explosive_odds` int DEFAULT '0' COMMENT '三级爆率' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_box_records` +-- + +CREATE TABLE `tt_box_records` ( + `id` bigint NOT NULL, + `user_id` int DEFAULT NULL COMMENT '开箱者ID', + `box_id` int DEFAULT NULL, + `box_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `box_price` decimal(10,2) DEFAULT NULL, + `ornament_id` int DEFAULT NULL, + `ornaments_price` decimal(10,2) DEFAULT NULL, + `ornaments_level_id` int DEFAULT '1', + `status` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `source` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `fight_id` int DEFAULT NULL, + `roll_id` int DEFAULT NULL, + `holder_user_id` int DEFAULT NULL COMMENT '最终持有者ID', + `is_show` int DEFAULT '1' COMMENT '是否显示0不显示、1显示 *弃用字段', + `image_url` varchar(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `fight_round_number` int DEFAULT NULL COMMENT '对战模式的回合数', + `ornament_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `market_hash_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '市场唯一hash名称', + `ornaments_zbt_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '扎比特平台id', + `ornaments_yy_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'yyyouping平台id', + `ornament_level_img` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '等级图片' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='所有开箱记录' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_box_third_explosive_user` +-- + +CREATE TABLE `tt_box_third_explosive_user` ( + `id` int NOT NULL, + `box_id` int DEFAULT NULL, + `user_id` int DEFAULT NULL, + `create_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_box_type` +-- + +CREATE TABLE `tt_box_type` ( + `id` int NOT NULL, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `sort` int DEFAULT '0', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `is_fight_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_commission_record` +-- + +CREATE TABLE `tt_commission_record` ( + `id` int NOT NULL COMMENT 'ID', + `user_id` int DEFAULT NULL COMMENT '推广人员ID', + `commission` decimal(15,2) DEFAULT NULL COMMENT '佣金', + `summary_time` datetime DEFAULT NULL COMMENT '汇总时间', + `claim_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '领取状态(0未领取 1已领取)', + `claim_time` datetime DEFAULT NULL COMMENT '领取时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='佣金记录表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_commission_record_0812` +-- + +CREATE TABLE `tt_commission_record_0812` ( + `id` int NOT NULL COMMENT 'ID', + `user_id` int DEFAULT NULL COMMENT '推广人员ID', + `commission` decimal(15,2) DEFAULT NULL COMMENT '佣金', + `summary_time` datetime DEFAULT NULL COMMENT '汇总时间', + `claim_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '领取状态(0未领取 1已领取)', + `claim_time` datetime DEFAULT NULL COMMENT '领取时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='佣金记录表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_comp_record` +-- + +CREATE TABLE `tt_comp_record` ( + `user_id` int NOT NULL COMMENT '用户ID', + `box_id` int NOT NULL COMMENT '宝箱ID', + `comp_amount` int DEFAULT NULL COMMENT '补偿值' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='补偿值记录表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_content` +-- + +CREATE TABLE `tt_content` ( + `id` int NOT NULL, + `type_id` int DEFAULT NULL, + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, + `link` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `page_view` int DEFAULT '0', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_content_type` +-- + +CREATE TABLE `tt_content_type` ( + `id` int NOT NULL, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `alias` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_delivery_record` +-- + +CREATE TABLE `tt_delivery_record` ( + `id` int NOT NULL COMMENT 'ID', + `user_id` int DEFAULT NULL COMMENT '用户ID', + `box_records_id` int DEFAULT NULL COMMENT '用户背包记录ID', + `ornament_id` int DEFAULT NULL COMMENT '饰品ID', + `ornaments_price` decimal(10,2) DEFAULT NULL COMMENT '饰品价格', + `out_trade_no` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '网站订单号', + `delivery` int DEFAULT NULL COMMENT '网站发货模式(1人工发货 2自动发货 3主播号提取)', + `buy_price` decimal(10,2) DEFAULT NULL COMMENT '实际购买价格', + `thirdparty_delivery` int DEFAULT NULL COMMENT '第三方发货模式', + `order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '第三方平台订单号', + `status` int DEFAULT '1' COMMENT '发货订单状态(1待发货 3待收货 10订单完成 11订单取消)', + `message` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `update_time` datetime DEFAULT NULL, + `market_hash_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '市场唯一hash名称' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='申请提货记录' ROW_FORMAT=DYNAMIC; + + + +-- +-- 表的结构 `tt_exponent` +-- + +CREATE TABLE `tt_exponent` ( + `id` int NOT NULL, + `exponent` int DEFAULT NULL COMMENT '指数结果', + `points` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `forward` int DEFAULT NULL, + `number` int DEFAULT NULL COMMENT '当天第几期', + `create_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_exponent_user` +-- + +CREATE TABLE `tt_exponent_user` ( + `id` int NOT NULL, + `exponent_id` int DEFAULT NULL, + `box_id` int DEFAULT NULL, + `user_id` int DEFAULT NULL, + `price` decimal(10,2) DEFAULT NULL, + `forward` int DEFAULT NULL COMMENT '1上升 2下降', + `status` int DEFAULT NULL COMMENT '1成功 2失败', + `box_record_id` int DEFAULT NULL COMMENT '成功的话开出来的盒子记录ID', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_exponent_user_box` +-- + +CREATE TABLE `tt_exponent_user_box` ( + `id` int NOT NULL, + `box_id` int DEFAULT NULL, + `user_id` int DEFAULT NULL, + `number` int DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_fight` +-- + +CREATE TABLE `tt_fight` ( + `id` int NOT NULL, + `user_id` int DEFAULT NULL COMMENT '房主', + `model` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '模式(0欧皇 1非酋)', + `player_num` int DEFAULT NULL COMMENT '房间人数', + `box_data` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `box_price_total` decimal(10,2) DEFAULT NULL COMMENT '宝箱总价', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '状态(0等待中 1进行中 2已结束)', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL, + `seats` json DEFAULT NULL COMMENT '座位', + `begin_time` datetime DEFAULT NULL COMMENT '开始时间', + `round_number` int DEFAULT NULL COMMENT '回合数', + `end_time` datetime DEFAULT NULL COMMENT '结束时间', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `winner_ids` json DEFAULT NULL COMMENT '赢家id', + `box_sequence` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '箱子顺序', + `fight_type` int DEFAULT '0' COMMENT '对战方式0不合资1合资', + `fight_result` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '合资对战信息' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='对战表,相当于一个房间' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_fight_ranking_reward` +-- + +CREATE TABLE `tt_fight_ranking_reward` ( + `id` int NOT NULL COMMENT 'ID', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '排名名称', + `reward` decimal(10,2) DEFAULT NULL COMMENT '奖励金额' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='对战奖励金额表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_fight_result` +-- + +CREATE TABLE `tt_fight_result` ( + `id` int NOT NULL, + `fight_id` int DEFAULT NULL, + `fight_result` json DEFAULT NULL, + `create_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='废弃表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_fight_round` +-- + +CREATE TABLE `tt_fight_round` ( + `id` int NOT NULL, + `current_round` int DEFAULT NULL, + `total_round` int DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='对战回合 废弃表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_fight_user` +-- + +CREATE TABLE `tt_fight_user` ( + `id` int NOT NULL, + `fight_id` int DEFAULT NULL, + `user_id` int DEFAULT NULL, + `join_price` decimal(10,2) DEFAULT NULL, + `join_seat_num` int DEFAULT NULL, + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '玩家状态(0空位 1入座 2就绪)', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_first_recharge` +-- + +CREATE TABLE `tt_first_recharge` ( + `id` int NOT NULL COMMENT 'ID', + `min_amount` decimal(10,2) DEFAULT NULL COMMENT '充值金额下限', + `ratio` decimal(10,2) DEFAULT NULL COMMENT '赠送比例', + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '规则描述', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='首充赠送表' ROW_FORMAT=DYNAMIC; + + + +-- +-- 表的结构 `tt_game_mould` +-- + +CREATE TABLE `tt_game_mould` ( + `id` int NOT NULL, + `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_game_mould_box` +-- + +CREATE TABLE `tt_game_mould_box` ( + `id` int NOT NULL, + `box_id` int DEFAULT NULL, + `mould_id` int DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_message` +-- + +CREATE TABLE `tt_message` ( + `id` int NOT NULL, + `message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_message_send` +-- + +CREATE TABLE `tt_message_send` ( + `id` int NOT NULL, + `rec_id` int DEFAULT NULL, + `message_id` int DEFAULT NULL, + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `send_time` datetime DEFAULT NULL, + `reading_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_notice` +-- + +CREATE TABLE `tt_notice` ( + `notice_id` int NOT NULL COMMENT '通知ID', + `user_id` int NOT NULL COMMENT '用户ID', + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '标题', + `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '内容', + `read` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '阅读状态(0未读 1已读)', + `create_time` datetime DEFAULT NULL COMMENT '创建时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='通知表' ROW_FORMAT=DYNAMIC; + + + +-- +-- 表的结构 `tt_order` +-- + +CREATE TABLE `tt_order` ( + `id` int NOT NULL, + `user_id` int DEFAULT NULL, + `third_party` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '平台', + `type` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '0卡密 1支付宝 2微信 。。。', + `goods_id` int DEFAULT NULL, + `goods_price` decimal(10,2) DEFAULT NULL, + `goods_num` int DEFAULT '1', + `total_amount` decimal(10,2) DEFAULT NULL, + `order_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `sign` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `status` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `out_trade_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `pay_url` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `bd_vid` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + + +-- +-- 表的结构 `tt_ornament` +-- + +CREATE TABLE `tt_ornament` ( + `id` bigint NOT NULL COMMENT 'hash转码得到 加索引', + `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `use_price` decimal(10,2) DEFAULT NULL, + `image_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `market_hash_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '市场唯一hash', + `item_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '弃用 删除', + `price` decimal(10,2) DEFAULT NULL, + `quantity` int DEFAULT '0', + `short_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `type` varchar(70) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `type_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `quality` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1', + `quality_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `quality_color` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `rarity` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `rarity_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `rarity_color` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `exterior` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `exterior_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `is_putaway` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '是否商城上架(0上架 1下架)', + `is_proprietary_property` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '是否本网站自定义道具(0是 1否)', + `zbt_id` bigint DEFAULT NULL COMMENT 'zbt平台id', + `yyyouping_id` bigint DEFAULT NULL COMMENT 'yy平台id', + `type_hash_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '类型Hash', + `exterior_hash_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '外观hash', + `quality_hash_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '品质哈希', + `rarity_hash_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '稀有度哈希' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='zbt平台所有商品模板' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_ornaments_level` +-- + +CREATE TABLE `tt_ornaments_level` ( + `id` int NOT NULL, + `level` varchar(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `level_img` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_ornament_yy` +-- + +CREATE TABLE `tt_ornament_yy` ( + `id` int NOT NULL, + `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `hash_name` varchar(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `type_id` int DEFAULT NULL, + `type_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `type_hash_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `weapon_id` int DEFAULT NULL, + `weapon_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `weapon_hash_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_ornament_zbt` +-- + +CREATE TABLE `tt_ornament_zbt` ( + `id` bigint NOT NULL COMMENT 'hash转码得到', + `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `use_price` decimal(15,2) DEFAULT NULL, + `image_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `market_hash_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '市场唯一hash', + `item_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '弃用 删除', + `price` decimal(15,2) DEFAULT NULL, + `quantity` int DEFAULT '0', + `short_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `type` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `type_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `quality` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1', + `quality_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `quality_color` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `rarity` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `rarity_name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `rarity_color` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `exterior` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `exterior_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `is_putaway` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '不知道有啥用', + `is_proprietary_property` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '不知道有啥用', + `zbt_id` bigint DEFAULT NULL COMMENT 'zbt平台id', + `yyyouping_id` bigint DEFAULT NULL COMMENT 'yy平台id' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='zbt平台所有商品模板' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_promotion_level` +-- + +CREATE TABLE `tt_promotion_level` ( + `id` int NOT NULL, + `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `recharge_threshold` decimal(10,2) UNSIGNED DEFAULT NULL COMMENT '推广等级达标金额', + `commissions` decimal(10,2) UNSIGNED DEFAULT NULL COMMENT '推广返佣比例', + `added_bonus` decimal(10,2) UNSIGNED DEFAULT NULL COMMENT '等级达标奖励红包金额', + `recharge_gift_ratio` decimal(10,2) DEFAULT NULL COMMENT '充值赠送比例', + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_promotion_record` +-- + +CREATE TABLE `tt_promotion_record` ( + `id` int NOT NULL COMMENT 'ID', + `user_id` int DEFAULT NULL COMMENT '用户ID', + `subordinate_user_id` int DEFAULT NULL COMMENT '下级用户ID', + `recharge_price` decimal(10,2) DEFAULT NULL, + `rebate` decimal(10,2) DEFAULT NULL, + `recharge_record_id` int DEFAULT NULL, + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_promotion_update` +-- + +CREATE TABLE `tt_promotion_update` ( + `id` int NOT NULL, + `employee_id` int DEFAULT NULL, + `boss_id` int DEFAULT NULL, + `create_time` timestamp NULL DEFAULT NULL, + `update_time` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_recharge_card` +-- + +CREATE TABLE `tt_recharge_card` ( + `id` int NOT NULL, + `recharge_list_id` int DEFAULT NULL, + `price` decimal(10,2) UNSIGNED DEFAULT NULL, + `password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `use_user_id` int DEFAULT NULL, + `use_time` datetime DEFAULT NULL, + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `update_time` datetime DEFAULT NULL, + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_recharge_config` +-- + +CREATE TABLE `tt_recharge_config` ( + `id` int NOT NULL, + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `price` decimal(10,2) DEFAULT NULL, + `product_a` decimal(10,2) DEFAULT NULL COMMENT '产品:金币', + `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `picture` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='充值配置表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_recharge_prod` +-- + +CREATE TABLE `tt_recharge_prod` ( + `id` int NOT NULL, + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `price` decimal(10,2) DEFAULT NULL, + `card_link` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `product_a` decimal(10,2) DEFAULT NULL COMMENT '产品:金币', + `product_c` decimal(10,2) DEFAULT NULL COMMENT '产品:积分', + `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `picture` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `transaction_link` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='充值商品列表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_recharge_ranking_reward` +-- + +CREATE TABLE `tt_recharge_ranking_reward` ( + `id` int NOT NULL COMMENT 'ID', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '排名名称', + `reward` decimal(10,2) DEFAULT NULL COMMENT '奖励金额' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='对战奖励金额表' ROW_FORMAT=DYNAMIC; + +-- +-- 表的结构 `tt_recharge_record` +-- + +CREATE TABLE `tt_recharge_record` ( + `id` int UNSIGNED NOT NULL, + `user_id` int UNSIGNED DEFAULT NULL, + `parent_id` int UNSIGNED DEFAULT '0', + `arrival_amount` decimal(10,2) UNSIGNED DEFAULT NULL, + `amount_actually_paid` decimal(10,2) UNSIGNED DEFAULT NULL, + `finally_price` decimal(10,2) UNSIGNED DEFAULT NULL, + `order_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', + `out_trade_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `channel_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='充值记录 弃用表' ROW_FORMAT=DYNAMIC; + + + +-- +-- 表的结构 `tt_red_pack` +-- + +CREATE TABLE `tt_red_pack` ( + `id` int NOT NULL COMMENT '主键', + `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '标题', + `pack_type` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '红包类型 1.充值红包 2.普通红包', + `recharge_limit` decimal(10,2) DEFAULT NULL COMMENT '充值金额', + `pack_count` int DEFAULT NULL COMMENT '总数量 0,无限制', + `valid_count` int DEFAULT NULL COMMENT '剩余数量', + `value_min` decimal(10,2) DEFAULT NULL COMMENT '红包最小值', + `value_max` decimal(10,2) DEFAULT NULL COMMENT '红包最大值', + `begin_time` datetime DEFAULT NULL COMMENT '开始时间', + `end_time` datetime DEFAULT NULL COMMENT '结束时间', + `status` int DEFAULT NULL COMMENT '状态0 已结束 1 进行中', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '修改时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_red_packet` +-- + +CREATE TABLE `tt_red_packet` ( + `id` int NOT NULL, + `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `description` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `num` int DEFAULT '1', + `amount` json DEFAULT NULL, + `user_id` int DEFAULT NULL, + `password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `opening_time` datetime DEFAULT NULL, + `validity` datetime DEFAULT NULL, + `status` int DEFAULT '0', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `update_time` datetime DEFAULT NULL, + `use_status` int DEFAULT NULL, + `del_flag` int DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- +---------------------------------------- + +-- +-- 表的结构 `tt_red_packet_record` +-- + +CREATE TABLE `tt_red_packet_record` ( + `id` int NOT NULL, + `red_packet_id` int DEFAULT NULL, + `user_id` int DEFAULT NULL, + `receive_password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `receive_amount` decimal(10,2) DEFAULT NULL, + `receive_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_replacement_record` +-- + +CREATE TABLE `tt_replacement_record` ( + `id` int NOT NULL COMMENT 'id', + `uid` int DEFAULT NULL COMMENT '用户id', + `uname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '用户名称', + `oids` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '饰品ids', + `award_oid` int DEFAULT NULL COMMENT '合成饰品的id', + `award_oname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '合成饰品的名称', + `award_oprice` decimal(10,2) DEFAULT NULL COMMENT '合成饰品的价格', + `time` datetime(6) DEFAULT NULL COMMENT '合成日期', + `create_time` datetime(6) DEFAULT NULL COMMENT '创建日期', + `update_time` datetime(6) DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间', + `award_oimg` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '饰品图片' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_robot_fight_group` +-- + +CREATE TABLE `tt_robot_fight_group` ( + `group_id` int NOT NULL COMMENT '分组ID', + `group_name` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分组名称', + `seat_num` int DEFAULT NULL COMMENT '座位数量', + `model` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '对战模式(0欧皇 1非酋)' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='机器人对战分组表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_robot_fight_group_box` +-- + +CREATE TABLE `tt_robot_fight_group_box` ( + `group_id` int NOT NULL COMMENT '分组ID', + `box_id` int NOT NULL COMMENT '宝箱ID', + `box_num` int DEFAULT NULL COMMENT '宝箱数量' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='机器人对战分组宝箱表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_roll` +-- + +CREATE TABLE `tt_roll` ( + `id` int NOT NULL COMMENT 'ROLL房ID', + `jackpot_id` int DEFAULT NULL COMMENT '奖池ID', + `user_id` int DEFAULT '0' COMMENT '主播ID', + `roll_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT 'ROLL房类型(0官方 1主播)', + `roll_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'ROLL房名称', + `description` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '描述', + `end_time` datetime DEFAULT NULL COMMENT '结束时间', + `people_num` int DEFAULT '1' COMMENT '参加人数', + `roll_password` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'ROLL房密码', + `min_recharge` decimal(10,2) DEFAULT NULL COMMENT '最低充值金额', + `recharge_start_time` datetime DEFAULT NULL COMMENT '充值开始时间', + `sort_by` int DEFAULT '0' COMMENT '排序', + `roll_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT 'ROLL房状态(0未开奖 1已开奖)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建人', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新人', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除状态' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + + +-- +-- 表的结构 `tt_roll_jackpot` +-- + +CREATE TABLE `tt_roll_jackpot` ( + `jackpot_id` int NOT NULL COMMENT '奖池ID', + `jackpot_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '奖池名称', + `description` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '描述', + `total_price` decimal(10,2) DEFAULT NULL COMMENT '奖池总价', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建人', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新人', + `update_time` datetime DEFAULT NULL COMMENT '更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ROLL房奖池表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_roll_jackpot_ornaments` +-- + +CREATE TABLE `tt_roll_jackpot_ornaments` ( + `id` int NOT NULL COMMENT 'ID', + `jackpot_id` int DEFAULT NULL COMMENT '奖池ID', + `ornaments_id` int DEFAULT NULL COMMENT '饰品ID', + `ornament_level_id` int DEFAULT '1' COMMENT '饰品等级ID', + `ornaments_num` int DEFAULT '1' COMMENT '饰品数量', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `price` decimal(10,2) DEFAULT NULL COMMENT '价格', + `img_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '图片路径', + `ornament_name` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '饰品名称' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ROLL房奖池奖品表' ROW_FORMAT=DYNAMIC; + + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_roll_user` +-- + +CREATE TABLE `tt_roll_user` ( + `id` int NOT NULL COMMENT 'ID', + `roll_id` int DEFAULT NULL COMMENT 'ROLL房ID', + `user_id` int DEFAULT NULL COMMENT '用户ID', + `jackpot_ornaments_id` int DEFAULT '0' COMMENT '奖池饰品ID', + `ornaments_id` int DEFAULT '0' COMMENT '饰品ID', + `box_record_id` bigint DEFAULT '0' COMMENT '开箱记录ID', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '状态', + `designated_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '指定人', + `join_time` datetime DEFAULT NULL COMMENT '加入时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `get_prize_way` int DEFAULT NULL COMMENT '获奖方式(0指定)', + `user_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户名', + `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '头像', + `nick_name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '昵称' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ROLL房用户关联表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_roll_user_prize` +-- + +CREATE TABLE `tt_roll_user_prize` ( + `id` int NOT NULL COMMENT 'ID', + `roll_user_id` int DEFAULT NULL COMMENT '用户ID', + `roll_jackpot_id` int DEFAULT NULL COMMENT '奖池ID', + `roll_jackpot_ornament_id` int DEFAULT NULL COMMENT '奖池物品ID', + `number` int DEFAULT NULL COMMENT '数量', + `ornament_id` bigint DEFAULT NULL COMMENT '物品ID', + `price` decimal(10,2) DEFAULT NULL COMMENT '价格', + `ornament_name` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '物品名称', + `img_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '物品图片', + `update_time` datetime DEFAULT NULL COMMENT '更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + + +-- +-- 表的结构 `tt_task_center` +-- + +CREATE TABLE `tt_task_center` ( + `task_id` int NOT NULL COMMENT '任务ID', + `task_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '任务名称', + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '任务描述', + `identify` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '任务标识', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '任务状态(0停用 1启用)', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='任务中心表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_task_center_user` +-- + +CREATE TABLE `tt_task_center_user` ( + `task_id` int NOT NULL COMMENT '任务ID', + `user_id` int NOT NULL COMMENT '用户ID', + `credit` decimal(15,2) DEFAULT NULL COMMENT '奖励积分', + `claimed` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否已领取(1已领取)' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + + + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_time_roll` +-- + +CREATE TABLE `tt_time_roll` ( + `id` int NOT NULL, + `jackpot_id` int DEFAULT NULL, + `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `description` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `recharge_condition` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1', + `min_recharge` decimal(10,2) DEFAULT NULL, + `sort_by` int DEFAULT '0', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1', + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `update_time` datetime DEFAULT NULL, + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `job_id` int DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + + +-- +-- 表的结构 `tt_time_roll_user` +-- + +CREATE TABLE `tt_time_roll_user` ( + `id` int NOT NULL, + `time_roll_id` int DEFAULT NULL, + `user_id` int DEFAULT NULL, + `jackpot_ornaments_id` int DEFAULT '0', + `ornaments_id` int DEFAULT '0', + `box_record_id` bigint DEFAULT '0', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `designated_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `join_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `end_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_upgrade_fail_ornaments` +-- + +CREATE TABLE `tt_upgrade_fail_ornaments` ( + `id` int NOT NULL, + `upgrade_id` int DEFAULT NULL COMMENT 'tt_upgrade_ornaments表id', + `ornament_id` int DEFAULT NULL COMMENT '失败奖励饰品id', + `ornament_level_id` int DEFAULT '1', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `ornament_price` decimal(10,2) DEFAULT NULL, + `ornament_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `ornament_number` int DEFAULT NULL COMMENT '数量' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + + +-- +-- 表的结构 `tt_upgrade_ornaments` +-- + +CREATE TABLE `tt_upgrade_ornaments` ( + `id` int NOT NULL, + `ornaments_id` int DEFAULT NULL, + `ornaments_level_id` int DEFAULT '1', + `luck_section` json DEFAULT NULL COMMENT '通用幸运区间', + `amount_required` decimal(10,2) DEFAULT NULL COMMENT '区间长度', + `amount_invested` decimal(10,2) DEFAULT NULL COMMENT '区间当前进度', + `anchor_luck_section` json DEFAULT NULL COMMENT '主播幸运区间', + `anchor_amount_required` decimal(10,2) DEFAULT NULL COMMENT '区间长度', + `anchor_amount_invested` decimal(10,2) DEFAULT NULL COMMENT '区间当前进度', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `ornament_price` decimal(10,2) DEFAULT NULL COMMENT '饰品原价', + `total_input` decimal(10,2) DEFAULT NULL COMMENT '当前总投入' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_upgrade_record` +-- + +CREATE TABLE `tt_upgrade_record` ( + `id` bigint NOT NULL, + `user_id` int DEFAULT NULL, + `amount_consumed` decimal(10,2) DEFAULT NULL COMMENT '本次消费', + `target_upgrade_id` int DEFAULT NULL COMMENT '目标升级物品id', + `target_ornament_id` int DEFAULT NULL COMMENT '目标饰品', + `target_ornament_price` decimal(10,2) DEFAULT NULL COMMENT '目标饰品价格', + `gain_upgrade_fail_id` int DEFAULT NULL COMMENT '*弃用字段', + `gain_ornament_list` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最终奖励饰品集合', + `gain_ornaments_price` decimal(10,2) DEFAULT NULL COMMENT '奖品总价', + `open_time` datetime DEFAULT NULL COMMENT '创建时间', + `user_type` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户类型', + `is_victory` bit(1) DEFAULT NULL COMMENT '成败 0失败 1成功', + `probability` int DEFAULT NULL COMMENT '概率', + `nick_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户昵称' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_user` +-- + +CREATE TABLE `tt_user` ( + `user_id` int NOT NULL, + `user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `nick_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `user_type` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '01' COMMENT '01主播 02普通玩家 03机器人', + `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `phone_number` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `avatar` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `account_amount` decimal(10,2) DEFAULT NULL COMMENT '金币', + `account_credits` decimal(10,2) DEFAULT NULL COMMENT '积分', + `invitation_code` varchar(6) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `parent_id` int DEFAULT NULL, + `vip_level` int DEFAULT '0', + `promotion_level` int DEFAULT '0', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `delivery_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `steam_id` bigint DEFAULT NULL, + `transaction_link` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'sream交易链接', + `real_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `id_num` varchar(18) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `certify_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `is_real_check` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '实名认证状态(0未实名 1已实名)', + `commission_rate` decimal(10,2) DEFAULT NULL COMMENT '佣金比例', + `login_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `login_date` datetime DEFAULT NULL, + `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `update_time` datetime DEFAULT NULL, + `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `delivery_address` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '收货地址', + `label_id` int DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_user_amount_records` +-- + +CREATE TABLE `tt_user_amount_records` ( + `id` int NOT NULL, + `user_id` int DEFAULT NULL, + `type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '1充值 2消费', + `source` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `amount` decimal(10,2) DEFAULT NULL, + `final_amount` decimal(10,2) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `remark` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `pw_child_id` int DEFAULT NULL COMMENT '推广福利 下级id', + `pw_child_name` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `pw_child_account` decimal(10,0) DEFAULT NULL, + `task_id` int DEFAULT NULL COMMENT '任务id' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_user_avatar` +-- + +CREATE TABLE `tt_user_avatar` ( + `id` int NOT NULL, + `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `is_default` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0', + `sort` int DEFAULT '0', + `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_user_blend_ercash` +-- + +CREATE TABLE `tt_user_blend_ercash` ( + `id` bigint NOT NULL, + `user_id` int DEFAULT NULL, + `amount` decimal(10,2) DEFAULT NULL COMMENT '金币', + `final_amount` decimal(19,2) DEFAULT NULL COMMENT '账户最终金币', + `credits` decimal(10,2) DEFAULT NULL COMMENT '积分', + `final_credits` decimal(10,2) DEFAULT NULL COMMENT '账户最终积分', + `total` decimal(10,2) DEFAULT NULL COMMENT '汇总', + `source` int DEFAULT NULL COMMENT '产生途径', + `type` int DEFAULT NULL COMMENT '0支出 1收入', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='综合消费日志' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + + +-- +-- 表的结构 `tt_user_box_open_chance` +-- + +CREATE TABLE `tt_user_box_open_chance` ( + `user_id` int NOT NULL COMMENT '用户ID', + `box_id` int NOT NULL COMMENT '宝箱ID', + `chance_num` int DEFAULT NULL COMMENT '开启次数' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_user_credits_records` +-- + +CREATE TABLE `tt_user_credits_records` ( + `id` int NOT NULL, + `user_id` int DEFAULT NULL, + `type` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `source` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `credits` decimal(10,2) DEFAULT NULL, + `final_credits` decimal(10,2) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `remark` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `pw_child_id` int DEFAULT NULL COMMENT '推广福利 下级id', + `pw_child_name` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `pw_child_account` decimal(10,0) DEFAULT NULL, + `task_id` int DEFAULT NULL COMMENT '任务id' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_user_credits_records_0812` +-- + +CREATE TABLE `tt_user_credits_records_0812` ( + `id` int NOT NULL, + `user_id` int DEFAULT NULL, + `type` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `source` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `credits` decimal(10,2) DEFAULT NULL, + `final_credits` decimal(10,2) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `remark` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `pw_child_id` int DEFAULT NULL COMMENT '推广福利 下级id', + `pw_child_name` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `pw_child_account` decimal(10,0) DEFAULT NULL, + `task_id` int DEFAULT NULL COMMENT '任务id' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_user_label` +-- + +CREATE TABLE `tt_user_label` ( + `id` int NOT NULL, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_vip_level` +-- + +CREATE TABLE `tt_vip_level` ( + `id` int NOT NULL, + `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `recharge_threshold` decimal(10,2) UNSIGNED DEFAULT NULL, + `commissions` decimal(10,2) UNSIGNED DEFAULT NULL, + `added_bonus` decimal(10,2) UNSIGNED DEFAULT NULL, + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '', + `create_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_welfare` +-- + +CREATE TABLE `tt_welfare` ( + `welfare_id` int NOT NULL COMMENT '福利ID', + `welfare_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '福利名称', + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '类型', + `vip_level` int DEFAULT NULL COMMENT 'VIP等级', + `box_id` int DEFAULT NULL COMMENT '箱子ID', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='福利表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_welfare_0812` +-- + +CREATE TABLE `tt_welfare_0812` ( + `welfare_id` int NOT NULL COMMENT '福利ID', + `welfare_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '福利名称', + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '类型', + `vip_level` int DEFAULT NULL COMMENT 'VIP等级', + `box_id` int DEFAULT NULL COMMENT '箱子ID', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='福利表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_welfare_monthly_recharges` +-- + +CREATE TABLE `tt_welfare_monthly_recharges` ( + `welfare_id` int NOT NULL COMMENT '福利ID', + `welfare_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '福利名称', + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '福利类型', + `vip_level` int DEFAULT NULL COMMENT 'VIP等级', + `amount` decimal(10,2) DEFAULT NULL COMMENT '充值金额', + `box_id` int DEFAULT NULL COMMENT '福利宝箱ID' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='每月充值福利表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_welfare_monthly_recharges_0812` +-- + +CREATE TABLE `tt_welfare_monthly_recharges_0812` ( + `welfare_id` int NOT NULL COMMENT '福利ID', + `welfare_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '福利名称', + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '福利类型', + `vip_level` int DEFAULT NULL COMMENT 'VIP等级', + `amount` decimal(10,2) DEFAULT NULL COMMENT '充值金额', + `box_id` int DEFAULT NULL COMMENT '福利宝箱ID' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='每月充值福利表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_welfare_record` +-- + +CREATE TABLE `tt_welfare_record` ( + `welfare_record_id` int NOT NULL COMMENT '福利领取记录ID', + `welfare_id` int DEFAULT NULL COMMENT '福利ID', + `user_id` int DEFAULT NULL COMMENT '用户ID', + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '福利类型', + `create_time` datetime DEFAULT NULL COMMENT '创建时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='福利领取记录表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_welfare_record_0812` +-- + +CREATE TABLE `tt_welfare_record_0812` ( + `welfare_record_id` int NOT NULL COMMENT '福利领取记录ID', + `welfare_id` int DEFAULT NULL COMMENT '福利ID', + `user_id` int DEFAULT NULL COMMENT '用户ID', + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '福利类型', + `create_time` datetime DEFAULT NULL COMMENT '创建时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='福利领取记录表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `tt_welfare_vip_level` +-- + +CREATE TABLE `tt_welfare_vip_level` ( + `welfare_id` int NOT NULL COMMENT '福利ID', + `welfare_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '福利名称', + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '类型', + `vip_level` int DEFAULT NULL COMMENT 'VIP等级', + `box_id` int DEFAULT NULL COMMENT '宝箱ID' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='福利表' ROW_FORMAT=DYNAMIC; + +-- -------------------------------------------------------- + +CREATE TABLE `tt_box_transfer_record` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', + `src_uid` bigint NOT NULL COMMENT '转增人uid', + `dst_uid` bigint NOT NULL COMMENT '授予人uid', + `box_record_id` bigint NOT NULL COMMENT '宝箱记录ID', + `ornament_id` bigint DEFAULT NULL COMMENT '宝箱中饰品ID', + `market_hash_name` varchar(255) DEFAULT NULL COMMENT '饰品市场哈希名称', + `ornaments_zbt_id` varchar(64) DEFAULT NULL COMMENT '饰品ZBT ID', + `ornaments_yy_id` varchar(64) DEFAULT NULL COMMENT '饰品YY ID', + `ornament_name` varchar(255) DEFAULT NULL COMMENT '饰品名称', + `ornaments_price` decimal(10,2) DEFAULT NULL COMMENT '饰品价格', + `image_url` varchar(500) DEFAULT NULL COMMENT '饰品图片URL', + `ornaments_level_id` int DEFAULT NULL COMMENT '饰品等级ID', + `ornament_level_img` varchar(500) DEFAULT NULL COMMENT '饰品等级图片', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_src_dst_boxid` (`src_uid`,`dst_uid`, `box_record_id`), + KEY `idx_src_uid` (`src_uid`), + KEY `idx_dst_uid` (`dst_uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='宝箱转增记录表'; + +-- +-- 表的结构 `tt_welfare_vip_level_0812` +-- + +CREATE TABLE `tt_welfare_vip_level_0812` ( + `welfare_id` int NOT NULL COMMENT '福利ID', + `welfare_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '福利名称', + `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '类型', + `vip_level` int DEFAULT NULL COMMENT 'VIP等级', + `box_id` int DEFAULT NULL COMMENT '宝箱ID' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='福利表' ROW_FORMAT=DYNAMIC; + +-- +-- 转储表的索引 +-- + +-- +-- 表的索引 `game_sugar_spin` +-- +ALTER TABLE `game_sugar_spin` + ADD PRIMARY KEY (`gid`), + ADD KEY `idx_feature` (`feature`), + ADD KEY `idx_score` (`score`); + +-- +-- 表的索引 `game_sugar_spin_free` +-- +ALTER TABLE `game_sugar_spin_free` + ADD PRIMARY KEY (`gid`), + ADD KEY `idx_feature` (`feature`), + ADD KEY `idx_score` (`score`); + +-- +-- 表的索引 `game_sugar_spin_super` +-- +ALTER TABLE `game_sugar_spin_super` + ADD PRIMARY KEY (`gid`), + ADD KEY `idx_feature` (`feature`), + ADD KEY `idx_score` (`score`); + +-- +-- 表的索引 `game_sugar_step_info` +-- +ALTER TABLE `game_sugar_step_info` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `uk_gid_aes` (`gid`,`aes`) COMMENT 'gid+aes: step', + ADD KEY `idx_gid` (`gid`); + +-- +-- 表的索引 `game_sugar_step_info_free` +-- +ALTER TABLE `game_sugar_step_info_free` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `uk_gid_aes` (`gid`,`aes`) COMMENT 'gid+aes: step', + ADD KEY `idx_gid` (`gid`); + +-- +-- 表的索引 `game_sugar_step_info_super` +-- +ALTER TABLE `game_sugar_step_info_super` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `uk_gid_aes` (`gid`,`aes`) COMMENT 'gid+aes: step', + ADD KEY `idx_gid` (`gid`); + +-- +-- 表的索引 `game_sugar_user` +-- +ALTER TABLE `game_sugar_user` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `idx_user_id` (`user_id`); + +-- +-- 表的索引 `game_sugar_win` +-- +ALTER TABLE `game_sugar_win` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `idx_date` (`date`); + +-- +-- 表的索引 `game_wheel_round` +-- +ALTER TABLE `game_wheel_round` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `uk_date_round_id` (`date`,`round_id`); + +-- +-- 表的索引 `game_wheel_round_bet` +-- +ALTER TABLE `game_wheel_round_bet` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `uk_round_user_symbol` (`round_id`,`user_id`,`bet_symbol`), + ADD KEY `idx_user_id` (`user_id`); + +-- +-- 表的索引 `game_wheel_user` +-- +ALTER TABLE `game_wheel_user` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `idx_user_id` (`user_id`); + +-- +-- 表的索引 `gen_table` +-- +ALTER TABLE `gen_table` + ADD PRIMARY KEY (`table_id`) USING BTREE; + +-- +-- 表的索引 `gen_table_column` +-- +ALTER TABLE `gen_table_column` + ADD PRIMARY KEY (`column_id`) USING BTREE; + +-- +-- 表的索引 `qrtz_blob_triggers` +-- +ALTER TABLE `qrtz_blob_triggers` + ADD PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE; + +-- +-- 表的索引 `qrtz_calendars` +-- +ALTER TABLE `qrtz_calendars` + ADD PRIMARY KEY (`sched_name`,`calendar_name`) USING BTREE; + +-- +-- 表的索引 `qrtz_cron_triggers` +-- +ALTER TABLE `qrtz_cron_triggers` + ADD PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE; + +-- +-- 表的索引 `qrtz_fired_triggers` +-- +ALTER TABLE `qrtz_fired_triggers` + ADD PRIMARY KEY (`sched_name`,`entry_id`) USING BTREE; + +-- +-- 表的索引 `qrtz_job_details` +-- +ALTER TABLE `qrtz_job_details` + ADD PRIMARY KEY (`sched_name`,`job_name`,`job_group`) USING BTREE; + +-- +-- 表的索引 `qrtz_locks` +-- +ALTER TABLE `qrtz_locks` + ADD PRIMARY KEY (`sched_name`,`lock_name`) USING BTREE; + +-- +-- 表的索引 `qrtz_paused_trigger_grps` +-- +ALTER TABLE `qrtz_paused_trigger_grps` + ADD PRIMARY KEY (`sched_name`,`trigger_group`) USING BTREE; + +-- +-- 表的索引 `qrtz_scheduler_state` +-- +ALTER TABLE `qrtz_scheduler_state` + ADD PRIMARY KEY (`sched_name`,`instance_name`) USING BTREE; + +-- +-- 表的索引 `qrtz_simple_triggers` +-- +ALTER TABLE `qrtz_simple_triggers` + ADD PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE; + +-- +-- 表的索引 `qrtz_simprop_triggers` +-- +ALTER TABLE `qrtz_simprop_triggers` + ADD PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE; + +-- +-- 表的索引 `qrtz_triggers` +-- +ALTER TABLE `qrtz_triggers` + ADD PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE, + ADD KEY `sched_name` (`sched_name`,`job_name`,`job_group`) USING BTREE; + +-- +-- 表的索引 `sys_config` +-- +ALTER TABLE `sys_config` + ADD PRIMARY KEY (`config_id`) USING BTREE; + +-- +-- 表的索引 `sys_dept` +-- +ALTER TABLE `sys_dept` + ADD PRIMARY KEY (`dept_id`) USING BTREE; + +-- +-- 表的索引 `sys_dict_data` +-- +ALTER TABLE `sys_dict_data` + ADD PRIMARY KEY (`dict_code`) USING BTREE; + +-- +-- 表的索引 `sys_dict_type` +-- +ALTER TABLE `sys_dict_type` + ADD PRIMARY KEY (`dict_id`) USING BTREE, + ADD UNIQUE KEY `dict_type` (`dict_type`) USING BTREE; + +-- +-- 表的索引 `sys_job` +-- +ALTER TABLE `sys_job` + ADD PRIMARY KEY (`job_id`,`job_name`,`job_group`) USING BTREE; + +-- +-- 表的索引 `sys_job_log` +-- +ALTER TABLE `sys_job_log` + ADD PRIMARY KEY (`job_log_id`) USING BTREE; + +-- +-- 表的索引 `sys_logininfor` +-- +ALTER TABLE `sys_logininfor` + ADD PRIMARY KEY (`info_id`) USING BTREE, + ADD KEY `idx_sys_logininfor_s` (`status`) USING BTREE, + ADD KEY `idx_sys_logininfor_lt` (`login_time`) USING BTREE; + +-- +-- 表的索引 `sys_menu` +-- +ALTER TABLE `sys_menu` + ADD PRIMARY KEY (`menu_id`) USING BTREE; + +-- +-- 表的索引 `sys_notice` +-- +ALTER TABLE `sys_notice` + ADD PRIMARY KEY (`notice_id`) USING BTREE; + +-- +-- 表的索引 `sys_oper_log` +-- +ALTER TABLE `sys_oper_log` + ADD PRIMARY KEY (`oper_id`) USING BTREE, + ADD KEY `idx_sys_oper_log_bt` (`business_type`) USING BTREE, + ADD KEY `idx_sys_oper_log_s` (`status`) USING BTREE, + ADD KEY `idx_sys_oper_log_ot` (`oper_time`) USING BTREE; + +-- +-- 表的索引 `sys_post` +-- +ALTER TABLE `sys_post` + ADD PRIMARY KEY (`post_id`) USING BTREE; + +-- +-- 表的索引 `sys_role` +-- +ALTER TABLE `sys_role` + ADD PRIMARY KEY (`role_id`) USING BTREE; + +-- +-- 表的索引 `sys_role_dept` +-- +ALTER TABLE `sys_role_dept` + ADD PRIMARY KEY (`role_id`,`dept_id`) USING BTREE; + +-- +-- 表的索引 `sys_role_menu` +-- +ALTER TABLE `sys_role_menu` + ADD PRIMARY KEY (`role_id`,`menu_id`) USING BTREE; + +-- +-- 表的索引 `sys_user` +-- +ALTER TABLE `sys_user` + ADD PRIMARY KEY (`user_id`) USING BTREE; + +-- +-- 表的索引 `sys_user_post` +-- +ALTER TABLE `sys_user_post` + ADD PRIMARY KEY (`user_id`,`post_id`) USING BTREE; + +-- +-- 表的索引 `sys_user_role` +-- +ALTER TABLE `sys_user_role` + ADD PRIMARY KEY (`user_id`,`role_id`) USING BTREE; + +-- +-- 表的索引 `tianxin_order` +-- +ALTER TABLE `tianxin_order` + ADD PRIMARY KEY (`order_id`) USING BTREE; + +-- +-- 表的索引 `tt_advertisement` +-- +ALTER TABLE `tt_advertisement` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_announcement` +-- +ALTER TABLE `tt_announcement` + ADD PRIMARY KEY (`announcement_id`) USING BTREE; + +-- +-- 表的索引 `tt_announcement_0812` +-- +ALTER TABLE `tt_announcement_0812` + ADD PRIMARY KEY (`announcement_id`) USING BTREE; + +-- +-- 表的索引 `tt_announcement_read` +-- +ALTER TABLE `tt_announcement_read` + ADD PRIMARY KEY (`announcement_id`,`user_id`) USING BTREE; + +-- +-- 表的索引 `tt_announcement_read_0812` +-- +ALTER TABLE `tt_announcement_read_0812` + ADD PRIMARY KEY (`announcement_id`,`user_id`) USING BTREE; + +-- +-- 表的索引 `tt_attendance_record` +-- +ALTER TABLE `tt_attendance_record` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_attendance_record_0812` +-- +ALTER TABLE `tt_attendance_record_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_banner` +-- +ALTER TABLE `tt_banner` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_bonus` +-- +ALTER TABLE `tt_bonus` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_bonus_0812` +-- +ALTER TABLE `tt_bonus_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_bonus_receive_record` +-- +ALTER TABLE `tt_bonus_receive_record` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_bonus_receive_record_0812` +-- +ALTER TABLE `tt_bonus_receive_record_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_box` +-- +ALTER TABLE `tt_box` + ADD PRIMARY KEY (`box_id`) USING BTREE; + +-- +-- 表的索引 `tt_box_open_chance` +-- +ALTER TABLE `tt_box_open_chance` + ADD PRIMARY KEY (`ornament_id`) USING BTREE; + +-- +-- 表的索引 `tt_box_open_chance_0812` +-- +ALTER TABLE `tt_box_open_chance_0812` + ADD PRIMARY KEY (`ornament_id`) USING BTREE; + +-- +-- 表的索引 `tt_box_ornaments` +-- +ALTER TABLE `tt_box_ornaments` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_box_records` +-- +ALTER TABLE `tt_box_records` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_box_records_0812` +-- +ALTER TABLE `tt_box_records_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_box_third_explosive_user` +-- +ALTER TABLE `tt_box_third_explosive_user` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_box_type` +-- +ALTER TABLE `tt_box_type` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_commission_record` +-- +ALTER TABLE `tt_commission_record` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_commission_record_0812` +-- +ALTER TABLE `tt_commission_record_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_comp_record` +-- +ALTER TABLE `tt_comp_record` + ADD PRIMARY KEY (`user_id`,`box_id`) USING BTREE; + +-- +-- 表的索引 `tt_comp_record_0812` +-- +ALTER TABLE `tt_comp_record_0812` + ADD PRIMARY KEY (`user_id`,`box_id`) USING BTREE; + +-- +-- 表的索引 `tt_content` +-- +ALTER TABLE `tt_content` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_content_type` +-- +ALTER TABLE `tt_content_type` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_delivery_record` +-- +ALTER TABLE `tt_delivery_record` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_delivery_record_0812` +-- +ALTER TABLE `tt_delivery_record_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_exponent` +-- +ALTER TABLE `tt_exponent` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_exponent_user` +-- +ALTER TABLE `tt_exponent_user` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_exponent_user_box` +-- +ALTER TABLE `tt_exponent_user_box` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_fight` +-- +ALTER TABLE `tt_fight` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_fight_0812` +-- +ALTER TABLE `tt_fight_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_fight_ranking_reward` +-- +ALTER TABLE `tt_fight_ranking_reward` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_fight_result` +-- +ALTER TABLE `tt_fight_result` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_fight_round` +-- +ALTER TABLE `tt_fight_round` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_fight_user` +-- +ALTER TABLE `tt_fight_user` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_fight_user_0812` +-- +ALTER TABLE `tt_fight_user_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_first_recharge` +-- +ALTER TABLE `tt_first_recharge` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_first_recharge_0812` +-- +ALTER TABLE `tt_first_recharge_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_game_mould` +-- +ALTER TABLE `tt_game_mould` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_game_mould_box` +-- +ALTER TABLE `tt_game_mould_box` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_message` +-- +ALTER TABLE `tt_message` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_message_send` +-- +ALTER TABLE `tt_message_send` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_notice` +-- +ALTER TABLE `tt_notice` + ADD PRIMARY KEY (`notice_id`) USING BTREE; + +-- +-- 表的索引 `tt_notice_0812` +-- +ALTER TABLE `tt_notice_0812` + ADD PRIMARY KEY (`notice_id`) USING BTREE; + +-- +-- 表的索引 `tt_order` +-- +ALTER TABLE `tt_order` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_order_0812` +-- +ALTER TABLE `tt_order_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_ornament` +-- +ALTER TABLE `tt_ornament` + ADD PRIMARY KEY (`market_hash_name`) USING BTREE, + ADD UNIQUE KEY `id` (`id`) USING BTREE; + +-- +-- 表的索引 `tt_ornaments_level` +-- +ALTER TABLE `tt_ornaments_level` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_ornament_yy` +-- +ALTER TABLE `tt_ornament_yy` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_ornament_zbt` +-- +ALTER TABLE `tt_ornament_zbt` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_promotion_level` +-- +ALTER TABLE `tt_promotion_level` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_promotion_record` +-- +ALTER TABLE `tt_promotion_record` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_promotion_update` +-- +ALTER TABLE `tt_promotion_update` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_recharge_card` +-- +ALTER TABLE `tt_recharge_card` + ADD PRIMARY KEY (`id`) USING BTREE, + ADD UNIQUE KEY `unique_password` (`password`) USING BTREE COMMENT '唯一约束(卡密)'; + +-- +-- 表的索引 `tt_recharge_card_0812` +-- +ALTER TABLE `tt_recharge_card_0812` + ADD PRIMARY KEY (`id`) USING BTREE, + ADD UNIQUE KEY `unique_password` (`password`) USING BTREE COMMENT '唯一约束(卡密)'; + +-- +-- 表的索引 `tt_recharge_config` +-- +ALTER TABLE `tt_recharge_config` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_recharge_prod` +-- +ALTER TABLE `tt_recharge_prod` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_recharge_ranking_reward` +-- +ALTER TABLE `tt_recharge_ranking_reward` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_recharge_ranking_reward_0812` +-- +ALTER TABLE `tt_recharge_ranking_reward_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_recharge_record` +-- +ALTER TABLE `tt_recharge_record` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_recharge_record_0812` +-- +ALTER TABLE `tt_recharge_record_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_red_pack` +-- +ALTER TABLE `tt_red_pack` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_red_packet` +-- +ALTER TABLE `tt_red_packet` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_red_packet_0812` +-- +ALTER TABLE `tt_red_packet_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_red_packet_record` +-- +ALTER TABLE `tt_red_packet_record` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_red_packet_record_0812` +-- +ALTER TABLE `tt_red_packet_record_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_red_pack_0812` +-- +ALTER TABLE `tt_red_pack_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_replacement_record` +-- +ALTER TABLE `tt_replacement_record` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_replacement_record_0812` +-- +ALTER TABLE `tt_replacement_record_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_robot_fight_group` +-- +ALTER TABLE `tt_robot_fight_group` + ADD PRIMARY KEY (`group_id`) USING BTREE; + +-- +-- 表的索引 `tt_robot_fight_group_0812` +-- +ALTER TABLE `tt_robot_fight_group_0812` + ADD PRIMARY KEY (`group_id`) USING BTREE; + +-- +-- 表的索引 `tt_robot_fight_group_box` +-- +ALTER TABLE `tt_robot_fight_group_box` + ADD PRIMARY KEY (`group_id`,`box_id`) USING BTREE; + +-- +-- 表的索引 `tt_robot_fight_group_box_0812` +-- +ALTER TABLE `tt_robot_fight_group_box_0812` + ADD PRIMARY KEY (`group_id`,`box_id`) USING BTREE; + +-- +-- 表的索引 `tt_roll` +-- +ALTER TABLE `tt_roll` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_roll_0812` +-- +ALTER TABLE `tt_roll_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_roll_jackpot` +-- +ALTER TABLE `tt_roll_jackpot` + ADD PRIMARY KEY (`jackpot_id`) USING BTREE; + +-- +-- 表的索引 `tt_roll_jackpot_0812` +-- +ALTER TABLE `tt_roll_jackpot_0812` + ADD PRIMARY KEY (`jackpot_id`) USING BTREE; + +-- +-- 表的索引 `tt_roll_jackpot_ornaments` +-- +ALTER TABLE `tt_roll_jackpot_ornaments` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_roll_jackpot_ornaments_0812` +-- +ALTER TABLE `tt_roll_jackpot_ornaments_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_roll_user` +-- +ALTER TABLE `tt_roll_user` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_roll_user_prize` +-- +ALTER TABLE `tt_roll_user_prize` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_roll_user_prize_0812` +-- +ALTER TABLE `tt_roll_user_prize_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_task_center` +-- +ALTER TABLE `tt_task_center` + ADD PRIMARY KEY (`task_id`) USING BTREE; + +-- +-- 表的索引 `tt_task_center_user` +-- +ALTER TABLE `tt_task_center_user` + ADD PRIMARY KEY (`user_id`,`task_id`) USING BTREE; + +-- +-- 表的索引 `tt_task_center_user_0812` +-- +ALTER TABLE `tt_task_center_user_0812` + ADD PRIMARY KEY (`user_id`,`task_id`) USING BTREE; + +-- +-- 表的索引 `tt_time_roll` +-- +ALTER TABLE `tt_time_roll` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_time_roll_0812` +-- +ALTER TABLE `tt_time_roll_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_time_roll_user` +-- +ALTER TABLE `tt_time_roll_user` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_upgrade_fail_ornaments` +-- +ALTER TABLE `tt_upgrade_fail_ornaments` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_upgrade_fail_ornaments_0812` +-- +ALTER TABLE `tt_upgrade_fail_ornaments_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_upgrade_ornaments` +-- +ALTER TABLE `tt_upgrade_ornaments` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_upgrade_ornaments_0812` +-- +ALTER TABLE `tt_upgrade_ornaments_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_upgrade_record` +-- +ALTER TABLE `tt_upgrade_record` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_upgrade_record_0812` +-- +ALTER TABLE `tt_upgrade_record_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_user` +-- +ALTER TABLE `tt_user` + ADD PRIMARY KEY (`user_id`) USING BTREE, + ADD UNIQUE KEY `unique_invitation_code` (`invitation_code`) USING BTREE COMMENT '唯一约束(邀请码)', + ADD UNIQUE KEY `unique_user_name` (`user_name`) USING BTREE COMMENT '唯一约束(用户名称)', + ADD UNIQUE KEY `unique_phone_number` (`phone_number`) USING BTREE COMMENT '唯一约束(用户手机号)'; + +-- +-- 表的索引 `tt_user_0812` +-- +ALTER TABLE `tt_user_0812` + ADD PRIMARY KEY (`user_id`) USING BTREE, + ADD UNIQUE KEY `unique_invitation_code` (`invitation_code`) USING BTREE COMMENT '唯一约束(邀请码)', + ADD UNIQUE KEY `unique_user_name` (`user_name`) USING BTREE COMMENT '唯一约束(用户名称)', + ADD UNIQUE KEY `unique_phone_number` (`phone_number`) USING BTREE COMMENT '唯一约束(用户手机号)'; + +-- +-- 表的索引 `tt_user_amount_records` +-- +ALTER TABLE `tt_user_amount_records` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_user_amount_records_0812` +-- +ALTER TABLE `tt_user_amount_records_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_user_avatar` +-- +ALTER TABLE `tt_user_avatar` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_user_blend_ercash` +-- +ALTER TABLE `tt_user_blend_ercash` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_user_blend_ercash_0812` +-- +ALTER TABLE `tt_user_blend_ercash_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_user_box_open_chance` +-- +ALTER TABLE `tt_user_box_open_chance` + ADD PRIMARY KEY (`user_id`,`box_id`) USING BTREE; + +-- +-- 表的索引 `tt_user_box_open_chance_0812` +-- +ALTER TABLE `tt_user_box_open_chance_0812` + ADD PRIMARY KEY (`user_id`,`box_id`) USING BTREE; + +-- +-- 表的索引 `tt_user_credits_records` +-- +ALTER TABLE `tt_user_credits_records` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_user_credits_records_0812` +-- +ALTER TABLE `tt_user_credits_records_0812` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_user_label` +-- +ALTER TABLE `tt_user_label` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_vip_level` +-- +ALTER TABLE `tt_vip_level` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- 表的索引 `tt_welfare` +-- +ALTER TABLE `tt_welfare` + ADD PRIMARY KEY (`welfare_id`) USING BTREE; + +-- +-- 表的索引 `tt_welfare_0812` +-- +ALTER TABLE `tt_welfare_0812` + ADD PRIMARY KEY (`welfare_id`) USING BTREE; + +-- +-- 表的索引 `tt_welfare_monthly_recharges` +-- +ALTER TABLE `tt_welfare_monthly_recharges` + ADD PRIMARY KEY (`welfare_id`) USING BTREE; + +-- +-- 表的索引 `tt_welfare_monthly_recharges_0812` +-- +ALTER TABLE `tt_welfare_monthly_recharges_0812` + ADD PRIMARY KEY (`welfare_id`) USING BTREE; + +-- +-- 表的索引 `tt_welfare_record` +-- +ALTER TABLE `tt_welfare_record` + ADD PRIMARY KEY (`welfare_record_id`) USING BTREE; + +-- +-- 表的索引 `tt_welfare_record_0812` +-- +ALTER TABLE `tt_welfare_record_0812` + ADD PRIMARY KEY (`welfare_record_id`) USING BTREE; + +-- +-- 表的索引 `tt_welfare_vip_level` +-- +ALTER TABLE `tt_welfare_vip_level` + ADD PRIMARY KEY (`welfare_id`) USING BTREE; + +-- +-- 表的索引 `tt_welfare_vip_level_0812` +-- +ALTER TABLE `tt_welfare_vip_level_0812` + ADD PRIMARY KEY (`welfare_id`) USING BTREE; + +-- +-- 在导出的表使用AUTO_INCREMENT +-- + +-- +-- 使用表AUTO_INCREMENT `game_sugar_spin` +-- +ALTER TABLE `game_sugar_spin` + MODIFY `gid` bigint NOT NULL AUTO_INCREMENT COMMENT 'spinfree_spinspin'; + +-- +-- 使用表AUTO_INCREMENT `game_sugar_spin_free` +-- +ALTER TABLE `game_sugar_spin_free` + MODIFY `gid` bigint NOT NULL AUTO_INCREMENT COMMENT 'spinfree_spinspin'; + +-- +-- 使用表AUTO_INCREMENT `game_sugar_spin_super` +-- +ALTER TABLE `game_sugar_spin_super` + MODIFY `gid` bigint NOT NULL AUTO_INCREMENT COMMENT 'spinfree_spinspin'; + +-- +-- 使用表AUTO_INCREMENT `game_sugar_step_info` +-- +ALTER TABLE `game_sugar_step_info` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id'; + +-- +-- 使用表AUTO_INCREMENT `game_sugar_step_info_free` +-- +ALTER TABLE `game_sugar_step_info_free` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id'; + +-- +-- 使用表AUTO_INCREMENT `game_sugar_step_info_super` +-- +ALTER TABLE `game_sugar_step_info_super` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id'; + +-- +-- 使用表AUTO_INCREMENT `game_sugar_user` +-- +ALTER TABLE `game_sugar_user` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id'; + +-- +-- 使用表AUTO_INCREMENT `game_sugar_win` +-- +ALTER TABLE `game_sugar_win` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id'; + +-- +-- 使用表AUTO_INCREMENT `game_wheel_round` +-- +ALTER TABLE `game_wheel_round` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT COMMENT '唯一自增id'; + +-- +-- 使用表AUTO_INCREMENT `game_wheel_round_bet` +-- +ALTER TABLE `game_wheel_round_bet` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT COMMENT '唯一自增id'; + +-- +-- 使用表AUTO_INCREMENT `game_wheel_user` +-- +ALTER TABLE `game_wheel_user` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id'; + +-- +-- 使用表AUTO_INCREMENT `gen_table` +-- +ALTER TABLE `gen_table` + MODIFY `table_id` bigint NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `gen_table_column` +-- +ALTER TABLE `gen_table_column` + MODIFY `column_id` bigint NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `sys_config` +-- +ALTER TABLE `sys_config` + MODIFY `config_id` int NOT NULL AUTO_INCREMENT COMMENT '参数主键'; + +-- +-- 使用表AUTO_INCREMENT `sys_dept` +-- +ALTER TABLE `sys_dept` + MODIFY `dept_id` bigint NOT NULL AUTO_INCREMENT COMMENT '部门id'; + +-- +-- 使用表AUTO_INCREMENT `sys_dict_data` +-- +ALTER TABLE `sys_dict_data` + MODIFY `dict_code` bigint NOT NULL AUTO_INCREMENT COMMENT '字典编码'; + +-- +-- 使用表AUTO_INCREMENT `sys_dict_type` +-- +ALTER TABLE `sys_dict_type` + MODIFY `dict_id` bigint NOT NULL AUTO_INCREMENT COMMENT '字典主键'; + +-- +-- 使用表AUTO_INCREMENT `sys_job` +-- +ALTER TABLE `sys_job` + MODIFY `job_id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务ID'; + +-- +-- 使用表AUTO_INCREMENT `sys_job_log` +-- +ALTER TABLE `sys_job_log` + MODIFY `job_log_id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务日志ID'; + +-- +-- 使用表AUTO_INCREMENT `sys_logininfor` +-- +ALTER TABLE `sys_logininfor` + MODIFY `info_id` bigint NOT NULL AUTO_INCREMENT COMMENT '访问ID'; + +-- +-- 使用表AUTO_INCREMENT `sys_menu` +-- +ALTER TABLE `sys_menu` + MODIFY `menu_id` bigint NOT NULL AUTO_INCREMENT COMMENT '菜单ID'; + +-- +-- 使用表AUTO_INCREMENT `sys_notice` +-- +ALTER TABLE `sys_notice` + MODIFY `notice_id` int NOT NULL AUTO_INCREMENT COMMENT '公告ID'; + +-- +-- 使用表AUTO_INCREMENT `sys_oper_log` +-- +ALTER TABLE `sys_oper_log` + MODIFY `oper_id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键'; + +-- +-- 使用表AUTO_INCREMENT `sys_post` +-- +ALTER TABLE `sys_post` + MODIFY `post_id` bigint NOT NULL AUTO_INCREMENT COMMENT '岗位ID'; + +-- +-- 使用表AUTO_INCREMENT `sys_role` +-- +ALTER TABLE `sys_role` + MODIFY `role_id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID'; + +-- +-- 使用表AUTO_INCREMENT `sys_user` +-- +ALTER TABLE `sys_user` + MODIFY `user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_advertisement` +-- +ALTER TABLE `tt_advertisement` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_announcement` +-- +ALTER TABLE `tt_announcement` + MODIFY `announcement_id` int NOT NULL AUTO_INCREMENT COMMENT '公告ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_announcement_0812` +-- +ALTER TABLE `tt_announcement_0812` + MODIFY `announcement_id` int NOT NULL AUTO_INCREMENT COMMENT '公告ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_attendance_record` +-- +ALTER TABLE `tt_attendance_record` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_attendance_record_0812` +-- +ALTER TABLE `tt_attendance_record_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_banner` +-- +ALTER TABLE `tt_banner` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_bonus` +-- +ALTER TABLE `tt_bonus` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_bonus_0812` +-- +ALTER TABLE `tt_bonus_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_bonus_receive_record` +-- +ALTER TABLE `tt_bonus_receive_record` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_bonus_receive_record_0812` +-- +ALTER TABLE `tt_bonus_receive_record_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_box` +-- +ALTER TABLE `tt_box` + MODIFY `box_id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_box_ornaments` +-- +ALTER TABLE `tt_box_ornaments` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_box_records` +-- +ALTER TABLE `tt_box_records` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_box_records_0812` +-- +ALTER TABLE `tt_box_records_0812` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_box_third_explosive_user` +-- +ALTER TABLE `tt_box_third_explosive_user` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_box_type` +-- +ALTER TABLE `tt_box_type` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_commission_record` +-- +ALTER TABLE `tt_commission_record` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_commission_record_0812` +-- +ALTER TABLE `tt_commission_record_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_content` +-- +ALTER TABLE `tt_content` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_content_type` +-- +ALTER TABLE `tt_content_type` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_delivery_record` +-- +ALTER TABLE `tt_delivery_record` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_delivery_record_0812` +-- +ALTER TABLE `tt_delivery_record_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_exponent` +-- +ALTER TABLE `tt_exponent` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_exponent_user` +-- +ALTER TABLE `tt_exponent_user` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_exponent_user_box` +-- +ALTER TABLE `tt_exponent_user_box` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_fight` +-- +ALTER TABLE `tt_fight` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_fight_0812` +-- +ALTER TABLE `tt_fight_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_fight_ranking_reward` +-- +ALTER TABLE `tt_fight_ranking_reward` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_fight_result` +-- +ALTER TABLE `tt_fight_result` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_fight_user` +-- +ALTER TABLE `tt_fight_user` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_fight_user_0812` +-- +ALTER TABLE `tt_fight_user_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_first_recharge` +-- +ALTER TABLE `tt_first_recharge` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_first_recharge_0812` +-- +ALTER TABLE `tt_first_recharge_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_game_mould` +-- +ALTER TABLE `tt_game_mould` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_game_mould_box` +-- +ALTER TABLE `tt_game_mould_box` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_message` +-- +ALTER TABLE `tt_message` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_message_send` +-- +ALTER TABLE `tt_message_send` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_notice` +-- +ALTER TABLE `tt_notice` + MODIFY `notice_id` int NOT NULL AUTO_INCREMENT COMMENT '通知ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_notice_0812` +-- +ALTER TABLE `tt_notice_0812` + MODIFY `notice_id` int NOT NULL AUTO_INCREMENT COMMENT '通知ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_order` +-- +ALTER TABLE `tt_order` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_order_0812` +-- +ALTER TABLE `tt_order_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_ornaments_level` +-- +ALTER TABLE `tt_ornaments_level` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_ornament_yy` +-- +ALTER TABLE `tt_ornament_yy` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_ornament_zbt` +-- +ALTER TABLE `tt_ornament_zbt` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'hash转码得到'; + +-- +-- 使用表AUTO_INCREMENT `tt_promotion_level` +-- +ALTER TABLE `tt_promotion_level` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_promotion_record` +-- +ALTER TABLE `tt_promotion_record` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_promotion_update` +-- +ALTER TABLE `tt_promotion_update` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_recharge_card` +-- +ALTER TABLE `tt_recharge_card` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_recharge_card_0812` +-- +ALTER TABLE `tt_recharge_card_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_recharge_config` +-- +ALTER TABLE `tt_recharge_config` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_recharge_prod` +-- +ALTER TABLE `tt_recharge_prod` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_recharge_ranking_reward` +-- +ALTER TABLE `tt_recharge_ranking_reward` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_recharge_ranking_reward_0812` +-- +ALTER TABLE `tt_recharge_ranking_reward_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_recharge_record` +-- +ALTER TABLE `tt_recharge_record` + MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_recharge_record_0812` +-- +ALTER TABLE `tt_recharge_record_0812` + MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_red_pack` +-- +ALTER TABLE `tt_red_pack` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT '主键'; + +-- +-- 使用表AUTO_INCREMENT `tt_red_packet` +-- +ALTER TABLE `tt_red_packet` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_red_packet_0812` +-- +ALTER TABLE `tt_red_packet_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_red_packet_record` +-- +ALTER TABLE `tt_red_packet_record` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_red_packet_record_0812` +-- +ALTER TABLE `tt_red_packet_record_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_red_pack_0812` +-- +ALTER TABLE `tt_red_pack_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT '主键'; + +-- +-- 使用表AUTO_INCREMENT `tt_replacement_record` +-- +ALTER TABLE `tt_replacement_record` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'id'; + +-- +-- 使用表AUTO_INCREMENT `tt_replacement_record_0812` +-- +ALTER TABLE `tt_replacement_record_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'id'; + +-- +-- 使用表AUTO_INCREMENT `tt_robot_fight_group` +-- +ALTER TABLE `tt_robot_fight_group` + MODIFY `group_id` int NOT NULL AUTO_INCREMENT COMMENT '分组ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_robot_fight_group_0812` +-- +ALTER TABLE `tt_robot_fight_group_0812` + MODIFY `group_id` int NOT NULL AUTO_INCREMENT COMMENT '分组ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_roll` +-- +ALTER TABLE `tt_roll` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ROLL房ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_roll_0812` +-- +ALTER TABLE `tt_roll_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ROLL房ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_roll_jackpot` +-- +ALTER TABLE `tt_roll_jackpot` + MODIFY `jackpot_id` int NOT NULL AUTO_INCREMENT COMMENT '奖池ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_roll_jackpot_0812` +-- +ALTER TABLE `tt_roll_jackpot_0812` + MODIFY `jackpot_id` int NOT NULL AUTO_INCREMENT COMMENT '奖池ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_roll_jackpot_ornaments` +-- +ALTER TABLE `tt_roll_jackpot_ornaments` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_roll_jackpot_ornaments_0812` +-- +ALTER TABLE `tt_roll_jackpot_ornaments_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_roll_user` +-- +ALTER TABLE `tt_roll_user` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_roll_user_prize` +-- +ALTER TABLE `tt_roll_user_prize` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_roll_user_prize_0812` +-- +ALTER TABLE `tt_roll_user_prize_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_task_center` +-- +ALTER TABLE `tt_task_center` + MODIFY `task_id` int NOT NULL AUTO_INCREMENT COMMENT '任务ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_time_roll` +-- +ALTER TABLE `tt_time_roll` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_time_roll_0812` +-- +ALTER TABLE `tt_time_roll_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_time_roll_user` +-- +ALTER TABLE `tt_time_roll_user` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_upgrade_fail_ornaments` +-- +ALTER TABLE `tt_upgrade_fail_ornaments` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_upgrade_fail_ornaments_0812` +-- +ALTER TABLE `tt_upgrade_fail_ornaments_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_upgrade_ornaments` +-- +ALTER TABLE `tt_upgrade_ornaments` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_upgrade_ornaments_0812` +-- +ALTER TABLE `tt_upgrade_ornaments_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_upgrade_record` +-- +ALTER TABLE `tt_upgrade_record` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_upgrade_record_0812` +-- +ALTER TABLE `tt_upgrade_record_0812` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_user` +-- +ALTER TABLE `tt_user` + MODIFY `user_id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_user_0812` +-- +ALTER TABLE `tt_user_0812` + MODIFY `user_id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_user_amount_records` +-- +ALTER TABLE `tt_user_amount_records` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_user_amount_records_0812` +-- +ALTER TABLE `tt_user_amount_records_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_user_avatar` +-- +ALTER TABLE `tt_user_avatar` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_user_blend_ercash` +-- +ALTER TABLE `tt_user_blend_ercash` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_user_blend_ercash_0812` +-- +ALTER TABLE `tt_user_blend_ercash_0812` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_user_credits_records` +-- +ALTER TABLE `tt_user_credits_records` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_user_credits_records_0812` +-- +ALTER TABLE `tt_user_credits_records_0812` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_user_label` +-- +ALTER TABLE `tt_user_label` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_vip_level` +-- +ALTER TABLE `tt_vip_level` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `tt_welfare` +-- +ALTER TABLE `tt_welfare` + MODIFY `welfare_id` int NOT NULL AUTO_INCREMENT COMMENT '福利ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_welfare_0812` +-- +ALTER TABLE `tt_welfare_0812` + MODIFY `welfare_id` int NOT NULL AUTO_INCREMENT COMMENT '福利ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_welfare_monthly_recharges` +-- +ALTER TABLE `tt_welfare_monthly_recharges` + MODIFY `welfare_id` int NOT NULL AUTO_INCREMENT COMMENT '福利ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_welfare_monthly_recharges_0812` +-- +ALTER TABLE `tt_welfare_monthly_recharges_0812` + MODIFY `welfare_id` int NOT NULL AUTO_INCREMENT COMMENT '福利ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_welfare_record` +-- +ALTER TABLE `tt_welfare_record` + MODIFY `welfare_record_id` int NOT NULL AUTO_INCREMENT COMMENT '福利领取记录ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_welfare_record_0812` +-- +ALTER TABLE `tt_welfare_record_0812` + MODIFY `welfare_record_id` int NOT NULL AUTO_INCREMENT COMMENT '福利领取记录ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_welfare_vip_level` +-- +ALTER TABLE `tt_welfare_vip_level` + MODIFY `welfare_id` int NOT NULL AUTO_INCREMENT COMMENT '福利ID'; + +-- +-- 使用表AUTO_INCREMENT `tt_welfare_vip_level_0812` +-- +ALTER TABLE `tt_welfare_vip_level_0812` + MODIFY `welfare_id` int NOT NULL AUTO_INCREMENT COMMENT '福利ID'; + +-- +-- 限制导出的表 +-- + +-- +-- 限制表 `qrtz_blob_triggers` +-- +ALTER TABLE `qrtz_blob_triggers` + ADD CONSTRAINT `QRTZ_BLOB_TRIGGERS_ibfk_1` FOREIGN KEY (`sched_name`,`trigger_name`,`trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- +-- 限制表 `qrtz_cron_triggers` +-- +ALTER TABLE `qrtz_cron_triggers` + ADD CONSTRAINT `QRTZ_CRON_TRIGGERS_ibfk_1` FOREIGN KEY (`sched_name`,`trigger_name`,`trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- +-- 限制表 `qrtz_simple_triggers` +-- +ALTER TABLE `qrtz_simple_triggers` + ADD CONSTRAINT `QRTZ_SIMPLE_TRIGGERS_ibfk_1` FOREIGN KEY (`sched_name`,`trigger_name`,`trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- +-- 限制表 `qrtz_simprop_triggers` +-- +ALTER TABLE `qrtz_simprop_triggers` + ADD CONSTRAINT `QRTZ_SIMPROP_TRIGGERS_ibfk_1` FOREIGN KEY (`sched_name`,`trigger_name`,`trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- +-- 限制表 `qrtz_triggers` +-- +ALTER TABLE `qrtz_triggers` + ADD CONSTRAINT `QRTZ_TRIGGERS_ibfk_1` FOREIGN KEY (`sched_name`,`job_name`,`job_group`) REFERENCES `qrtz_job_details` (`sched_name`, `job_name`, `job_group`) ON DELETE RESTRICT ON UPDATE RESTRICT; +COMMIT; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/odcs2.com/.env b/odcs2.com/.env new file mode 100644 index 0000000..10e00e3 --- /dev/null +++ b/odcs2.com/.env @@ -0,0 +1 @@ +VUE_APP_BASE_URL='http://154.12.94.229:8080/' \ No newline at end of file diff --git a/odcs2.com/.gitignore b/odcs2.com/.gitignore new file mode 100644 index 0000000..403adbc --- /dev/null +++ b/odcs2.com/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules +/dist + + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/odcs2.com/README.en.md b/odcs2.com/README.en.md new file mode 100644 index 0000000..e7fe074 --- /dev/null +++ b/odcs2.com/README.en.md @@ -0,0 +1,36 @@ +# demo + +#### Description +{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} + +#### Software Architecture +Software architecture description + +#### Installation + +1. xxxx +2. xxxx +3. xxxx + +#### Instructions + +1. xxxx +2. xxxx +3. xxxx + +#### Contribution + +1. Fork the repository +2. Create Feat_xxx branch +3. Commit your code +4. Create Pull Request + + +#### Gitee Feature + +1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md +2. Gitee blog [blog.gitee.com](https://blog.gitee.com) +3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) +4. The most valuable open source project [GVP](https://gitee.com/gvp) +5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) +6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/odcs2.com/README.md b/odcs2.com/README.md new file mode 100644 index 0000000..b741db0 --- /dev/null +++ b/odcs2.com/README.md @@ -0,0 +1,39 @@ +# demo + +#### 介绍 +{**以下是 Gitee 平台说明,您可以替换此简介** +Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 +无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} + +#### 软件架构 +软件架构说明 + + +#### 安装教程 + +1. xxxx +2. xxxx +3. xxxx + +#### 使用说明 + +1. xxxx +2. xxxx +3. xxxx + +#### 参与贡献 + +1. Fork 本仓库 +2. 新建 Feat_xxx 分支 +3. 提交代码 +4. 新建 Pull Request + + +#### 特技 + +1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md +2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) +3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 +4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 +5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) +6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/odcs2.com/babel.config.js b/odcs2.com/babel.config.js new file mode 100644 index 0000000..126cabe --- /dev/null +++ b/odcs2.com/babel.config.js @@ -0,0 +1,12 @@ +const plugins = [] + +if (process.env.NODE_ENV === 'production') { + plugins.push(['transform-remove-console', { 'exclude': ['error', 'warn'] }]) + console.log('plugins',plugins); +} +module.exports = { + presets: [ + '@vue/cli-plugin-babel/preset' + ], + plugins: [...plugins] +} diff --git a/odcs2.com/jsconfig.json b/odcs2.com/jsconfig.json new file mode 100644 index 0000000..587b289 --- /dev/null +++ b/odcs2.com/jsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "esnext", + "baseUrl": "./", + "moduleResolution": "node", + "paths": { + "@/*": [ + "src/*" + ] + }, + "lib": [ + "esnext", + "dom", + "dom.iterable", + "scripthost" + ] + } +} \ No newline at end of file diff --git a/odcs2.com/package-lock.json b/odcs2.com/package-lock.json new file mode 100644 index 0000000..c185f72 --- /dev/null +++ b/odcs2.com/package-lock.json @@ -0,0 +1,18972 @@ +{ + "name": "f2cs2", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "f2cs2", + "version": "0.1.0", + "dependencies": { + "@babel/core": "^7.22.9", + "@babel/preset-env": "^7.22.9", + "animate.css": "^4.1.1", + "axios": "^1.4.0", + "core-js": "^3.6.5", + "element-ui": "^2.15.13", + "iscroll": "^5.2.0", + "js-cookie": "^3.0.5", + "qrcodejs2": "^0.0.2", + "sass-loader": "^8.0.2", + "socket.io": "^4.7.4", + "swiper": "^4.5.1", + "video.js": "^8.10.0", + "vue": "^2.6.11", + "vue-agile": "^2.0.0", + "vue-count-to": "^1.0.13", + "vue-cropper": "^0.6.4", + "vue-router": "^3.2.0", + "vue-seamless-scroll": "^1.1.23", + "vuex": "^3.4.0", + "vuex-persistedstate": "^4.1.0" + }, + "devDependencies": { + "@vue/cli-plugin-babel": "~4.5.19", + "@vue/cli-plugin-eslint": "~4.5.19", + "@vue/cli-plugin-router": "~4.5.19", + "@vue/cli-plugin-vuex": "~4.5.19", + "@vue/cli-service": "~4.5.19", + "babel-eslint": "^10.1.0", + "babel-plugin-transform-remove-console": "^6.9.4", + "eslint": "^6.7.2", + "eslint-plugin-vue": "^6.2.2", + "qrcode": "^1.5.3", + "sass": "^1.99.0", + "vue-template-compiler": "^2.6.11" + } + }, + "node_modules/@achrinza/node-ipc": { + "version": "9.2.2", + "resolved": "https://registry.npmmirror.com/@achrinza/node-ipc/-/node-ipc-9.2.2.tgz", + "integrity": "sha512-b90U39dx0cU6emsOvy5hxU4ApNXnE3+Tuo8XQZfiKTGelDwpMwBVgBP7QX6dGTcJgu/miyJuNJ/2naFBliNWEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@node-ipc/js-queue": "2.0.3", + "event-pubsub": "4.3.0", + "js-message": "1.0.7" + }, + "engines": { + "node": "8 || 10 || 12 || 14 || 16 || 17" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", + "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.8", + "resolved": "https://registry.npmmirror.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.11" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", + "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", + "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.0.tgz", + "integrity": "sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-syntax-decorators": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.28.6.tgz", + "integrity": "sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", + "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", + "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", + "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", + "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", + "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", + "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", + "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", + "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/template": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", + "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", + "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", + "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", + "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", + "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz", + "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", + "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", + "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", + "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", + "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", + "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", + "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", + "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", + "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", + "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz", + "integrity": "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", + "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", + "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", + "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.29.2", + "resolved": "https://registry.npmmirror.com/@babel/preset-env/-/preset-env-7.29.2.tgz", + "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.28.6", + "@babel/plugin-syntax-import-attributes": "^7.28.6", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.29.0", + "@babel/plugin-transform-async-to-generator": "^7.28.6", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.6", + "@babel/plugin-transform-class-properties": "^7.28.6", + "@babel/plugin-transform-class-static-block": "^7.28.6", + "@babel/plugin-transform-classes": "^7.28.6", + "@babel/plugin-transform-computed-properties": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-dotall-regex": "^7.28.6", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.6", + "@babel/plugin-transform-exponentiation-operator": "^7.28.6", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.28.6", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.28.6", + "@babel/plugin-transform-modules-systemjs": "^7.29.0", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", + "@babel/plugin-transform-numeric-separator": "^7.28.6", + "@babel/plugin-transform-object-rest-spread": "^7.28.6", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.28.6", + "@babel/plugin-transform-optional-chaining": "^7.28.6", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.28.6", + "@babel/plugin-transform-private-property-in-object": "^7.28.6", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.29.0", + "@babel/plugin-transform-regexp-modifiers": "^7.28.6", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.28.6", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.28.6", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", + "core-js-compat": "^3.48.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmmirror.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@hapi/address": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/@hapi/address/-/address-2.1.4.tgz", + "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==", + "deprecated": "Moved to 'npm install @sideway/address'", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/bourne": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/@hapi/bourne/-/bourne-1.3.2.tgz", + "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==", + "deprecated": "This version has been deprecated and is no longer supported or maintained", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/hoek": { + "version": "8.5.1", + "resolved": "https://registry.npmmirror.com/@hapi/hoek/-/hoek-8.5.1.tgz", + "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==", + "deprecated": "This version has been deprecated and is no longer supported or maintained", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/joi": { + "version": "15.1.1", + "resolved": "https://registry.npmmirror.com/@hapi/joi/-/joi-15.1.1.tgz", + "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", + "deprecated": "Switch to 'npm install joi'", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/address": "2.x.x", + "@hapi/bourne": "1.x.x", + "@hapi/hoek": "8.x.x", + "@hapi/topo": "3.x.x" + } + }, + "node_modules/@hapi/topo": { + "version": "3.1.6", + "resolved": "https://registry.npmmirror.com/@hapi/topo/-/topo-3.1.6.tgz", + "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", + "deprecated": "This version has been deprecated and is no longer supported or maintained", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^8.3.0" + } + }, + "node_modules/@intervolga/optimize-cssnano-plugin": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/@intervolga/optimize-cssnano-plugin/-/optimize-cssnano-plugin-1.0.6.tgz", + "integrity": "sha512-zN69TnSr0viRSU6cEDIcuPcP67QcpQ6uHACg58FiN9PDrU6SLyGW3MR4tiISbYxy1kDWAVPwD+XwQTWE5cigAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano": "^4.0.0", + "cssnano-preset-default": "^4.0.0", + "postcss": "^7.0.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@node-ipc/js-queue": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/@node-ipc/js-queue/-/js-queue-2.0.3.tgz", + "integrity": "sha512-fL1wpr8hhD5gT2dA1qifeVaoDFlQR5es8tFuKqjHX+kdOtdNHnxkVZbtIrR2rxnMFvehkjaZRNV2H/gPXlb0hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "easy-stack": "1.0.1" + }, + "engines": { + "node": ">=1.0.0" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@soda/friendly-errors-webpack-plugin": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz", + "integrity": "sha512-h2ooWqP8XuFqTXT+NyAFbrArzfQA7R6HTezADrvD9Re8fxMLTPPniLdqVTdDaO0eIoLaAwKT+d6w+5GeTk7Vbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^3.0.0", + "error-stack-parser": "^2.0.6", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/@soda/get-current-script": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/@soda/get-current-script/-/get-current-script-1.0.2.tgz", + "integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmmirror.com/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmmirror.com/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmmirror.com/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.17", + "resolved": "https://registry.npmmirror.com/@types/http-proxy/-/http-proxy-1.17.17.tgz", + "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.6.0", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.19.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmmirror.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/q": { + "version": "1.5.8", + "resolved": "https://registry.npmmirror.com/@types/q/-/q-1.5.8.tgz", + "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/source-list-map": { + "version": "0.1.6", + "resolved": "https://registry.npmmirror.com/@types/source-list-map/-/source-list-map-0.1.6.tgz", + "integrity": "sha512-5JcVt1u5HDmlXkwOD2nslZVllBBc7HDuOICfiZah2Z0is8M8g+ddAEawbmd3VjedfDHBzxCaXLs07QEmb7y54g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tapable": { + "version": "1.0.12", + "resolved": "https://registry.npmmirror.com/@types/tapable/-/tapable-1.0.12.tgz", + "integrity": "sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/uglify-js": { + "version": "3.17.5", + "resolved": "https://registry.npmmirror.com/@types/uglify-js/-/uglify-js-3.17.5.tgz", + "integrity": "sha512-TU+fZFBTBcXj/GpDpDaBmgWk/gn96kMZ+uocaFUlV2f8a6WdMzzI44QBCmGcCiYR0Y6ZlNRiyUyKKt5nl/lbzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/@types/webpack": { + "version": "4.41.40", + "resolved": "https://registry.npmmirror.com/@types/webpack/-/webpack-4.41.40.tgz", + "integrity": "sha512-u6kMFSBM9HcoTpUXnL6mt2HSzftqb3JgYV6oxIgL2dl6sX6aCa5k6SOkzv5DuZjBTPUE/dJltKtwwuqrkZHpfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tapable": "^1", + "@types/uglify-js": "*", + "@types/webpack-sources": "*", + "anymatch": "^3.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@types/webpack-dev-server": { + "version": "3.11.6", + "resolved": "https://registry.npmmirror.com/@types/webpack-dev-server/-/webpack-dev-server-3.11.6.tgz", + "integrity": "sha512-XCph0RiiqFGetukCTC3KVnY1jwLcZ84illFRMbyFzCcWl90B/76ew0tSqF46oBhnLC4obNDG7dMO0JfTN0MgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect-history-api-fallback": "*", + "@types/express": "*", + "@types/serve-static": "*", + "@types/webpack": "^4", + "http-proxy-middleware": "^1.0.0" + } + }, + "node_modules/@types/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/@types/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-4nZOdMwSPHZ4pTEZzSp0AsTM4K7Qmu40UKW4tJDiOVs20UzYF9l+qUe4s0ftfN0pin06n+5cWWDJXH+sbhAiDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.7.3" + } + }, + "node_modules/@types/webpack-sources/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@videojs/http-streaming": { + "version": "3.17.4", + "resolved": "https://registry.npmmirror.com/@videojs/http-streaming/-/http-streaming-3.17.4.tgz", + "integrity": "sha512-XAvdG2dolBuV2Fx8bu1kjmQ2D4TonGzZH68Pgv/O9xMSFWdZtITSMFismeQLEAtMmGwze8qNJp3RgV+jStrJqg==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "^4.1.1", + "aes-decrypter": "^4.0.2", + "global": "^4.4.0", + "m3u8-parser": "^7.2.0", + "mpd-parser": "^1.3.1", + "mux.js": "7.1.0", + "video.js": "^7 || ^8" + }, + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "peerDependencies": { + "video.js": "^8.19.0" + } + }, + "node_modules/@videojs/vhs-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/@videojs/vhs-utils/-/vhs-utils-4.1.1.tgz", + "integrity": "sha512-5iLX6sR2ownbv4Mtejw6Ax+naosGvoT9kY+gcuHzANyUZZ+4NpeNdKMUhb6ag0acYej1Y7cmr/F2+4PrggMiVA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "global": "^4.4.0" + }, + "engines": { + "node": ">=8", + "npm": ">=5" + } + }, + "node_modules/@videojs/xhr": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/@videojs/xhr/-/xhr-2.7.0.tgz", + "integrity": "sha512-giab+EVRanChIupZK7gXjHy90y3nncA2phIOyG3Ne5fvpiMJzvqYwiTOnEVW2S4CoYcuKJkomat7bMXA/UoUZQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "global": "~4.4.0", + "is-function": "^1.0.1" + } + }, + "node_modules/@vue/babel-helper-vue-jsx-merge-props": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz", + "integrity": "sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz", + "integrity": "sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz", + "integrity": "sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@vue/babel-helper-vue-transform-on": "1.5.0", + "@vue/babel-plugin-resolve-type": "1.5.0", + "@vue/shared": "^3.5.18" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz", + "integrity": "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.0", + "@vue/compiler-sfc": "^3.5.18" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-plugin-transform-vue-jsx": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.4.0.tgz", + "integrity": "sha512-Fmastxw4MMx0vlgLS4XBX0XiBbUFzoMGeVXuMV08wyOfXdikAFqBTuYPR0tlk+XskL19EzHc39SgjrPGY23JnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0", + "html-tags": "^2.0.0", + "lodash.kebabcase": "^4.1.1", + "svg-tags": "^1.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-preset-app": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/babel-preset-app/-/babel-preset-app-4.5.19.tgz", + "integrity": "sha512-VCNRiAt2P/bLo09rYt3DLe6xXUMlhJwrvU18Ddd/lYJgC7s8+wvhgYs+MTx4OiAXdu58drGwSBO9SPx7C6J82Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.0", + "@babel/helper-compilation-targets": "^7.9.6", + "@babel/helper-module-imports": "^7.8.3", + "@babel/plugin-proposal-class-properties": "^7.8.3", + "@babel/plugin-proposal-decorators": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-jsx": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.11.0", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.0", + "@vue/babel-plugin-jsx": "^1.0.3", + "@vue/babel-preset-jsx": "^1.2.4", + "babel-plugin-dynamic-import-node": "^2.3.3", + "core-js": "^3.6.5", + "core-js-compat": "^3.6.5", + "semver": "^6.1.0" + }, + "peerDependencies": { + "@babel/core": "*", + "core-js": "^3", + "vue": "^2 || ^3.0.0-0" + }, + "peerDependenciesMeta": { + "core-js": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/babel-preset-jsx": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-preset-jsx/-/babel-preset-jsx-1.4.0.tgz", + "integrity": "sha512-QmfRpssBOPZWL5xw7fOuHNifCQcNQC1PrOo/4fu6xlhlKJJKSA3HqX92Nvgyx8fqHZTUGMPHmFA+IDqwXlqkSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.4.0", + "@vue/babel-sugar-composition-api-inject-h": "^1.4.0", + "@vue/babel-sugar-composition-api-render-instance": "^1.4.0", + "@vue/babel-sugar-functional-vue": "^1.4.0", + "@vue/babel-sugar-inject-h": "^1.4.0", + "@vue/babel-sugar-v-model": "^1.4.0", + "@vue/babel-sugar-v-on": "^1.4.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0", + "vue": "*" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/babel-sugar-composition-api-inject-h": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-composition-api-inject-h/-/babel-sugar-composition-api-inject-h-1.4.0.tgz", + "integrity": "sha512-VQq6zEddJHctnG4w3TfmlVp5FzDavUSut/DwR0xVoe/mJKXyMcsIibL42wPntozITEoY90aBV0/1d2KjxHU52g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-composition-api-render-instance": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-composition-api-render-instance/-/babel-sugar-composition-api-render-instance-1.4.0.tgz", + "integrity": "sha512-6ZDAzcxvy7VcnCjNdHJ59mwK02ZFuP5CnucloidqlZwVQv5CQLijc3lGpR7MD3TWFi78J7+a8J56YxbCtHgT9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-functional-vue": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.4.0.tgz", + "integrity": "sha512-lTEB4WUFNzYt2In6JsoF9sAYVTo84wC4e+PoZWSgM6FUtqRJz7wMylaEhSRgG71YF+wfLD6cc9nqVeXN2rwBvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-inject-h": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.4.0.tgz", + "integrity": "sha512-muwWrPKli77uO2fFM7eA3G1lAGnERuSz2NgAxuOLzrsTlQl8W4G+wwbM4nB6iewlKbwKRae3nL03UaF5ffAPMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-v-model": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.4.0.tgz", + "integrity": "sha512-0t4HGgXb7WHYLBciZzN5s0Hzqan4Ue+p/3FdQdcaHAb7s5D9WZFGoSxEZHrR1TFVZlAPu1bejTKGeAzaaG3NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.4.0", + "camelcase": "^5.0.0", + "html-tags": "^2.0.0", + "svg-tags": "^1.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-v-on": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.4.0.tgz", + "integrity": "sha512-m+zud4wKLzSKgQrWwhqRObWzmTuyzl6vOP7024lrpeJM4x2UhQtRDLgYjXAw9xBXjCwS0pP9kXjg91F9ZNo9JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.4.0", + "camelcase": "^5.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/cli-overlay": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/cli-overlay/-/cli-overlay-4.5.19.tgz", + "integrity": "sha512-GdxvNSmOw7NHIazCO8gTK+xZbaOmScTtxj6eHVeMbYpDYVPJ+th3VMLWNpw/b6uOjwzzcyKlA5dRQ1DAb+gF/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/cli-plugin-babel": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-babel/-/cli-plugin-babel-4.5.19.tgz", + "integrity": "sha512-8ebXzaMW9KNTMAN6+DzkhFsjty1ieqT7hIW5Lbk4v30Qhfjkms7lBWyXPGkoq+wAikXFa1Gnam2xmWOBqDDvWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.0", + "@vue/babel-preset-app": "^4.5.19", + "@vue/cli-shared-utils": "^4.5.19", + "babel-loader": "^8.1.0", + "cache-loader": "^4.1.0", + "thread-loader": "^2.1.3", + "webpack": "^4.0.0" + }, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0-0" + } + }, + "node_modules/@vue/cli-plugin-eslint": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.5.19.tgz", + "integrity": "sha512-53sa4Pu9j5KajesFlj494CcO8vVo3e3nnZ1CCKjGGnrF90id1rUeepcFfz5XjwfEtbJZp2x/NoX/EZE6zCzSFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/cli-shared-utils": "^4.5.19", + "eslint-loader": "^2.2.1", + "globby": "^9.2.0", + "inquirer": "^7.1.0", + "webpack": "^4.0.0", + "yorkie": "^2.0.0" + }, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0-0", + "eslint": ">= 1.6.0 < 7.0.0" + } + }, + "node_modules/@vue/cli-plugin-router": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-router/-/cli-plugin-router-4.5.19.tgz", + "integrity": "sha512-3icGzH1IbVYmMMsOwYa0lal/gtvZLebFXdE5hcQJo2mnTwngXGMTyYAzL56EgHBPjbMmRpyj6Iw9k4aVInVX6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/cli-shared-utils": "^4.5.19" + }, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0-0" + } + }, + "node_modules/@vue/cli-plugin-vuex": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.19.tgz", + "integrity": "sha512-DUmfdkG3pCdkP7Iznd87RfE9Qm42mgp2hcrNcYQYSru1W1gX2dG/JcW8bxmeGSa06lsxi9LEIc/QD1yPajSCZw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0-0" + } + }, + "node_modules/@vue/cli-service": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/cli-service/-/cli-service-4.5.19.tgz", + "integrity": "sha512-+Wpvj8fMTCt9ZPOLu5YaLkFCQmB4MrZ26aRmhhKiCQ/4PMoL6mLezfqdt6c+m2htM+1WV5RunRo+0WHl2DfwZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@intervolga/optimize-cssnano-plugin": "^1.0.5", + "@soda/friendly-errors-webpack-plugin": "^1.7.1", + "@soda/get-current-script": "^1.0.0", + "@types/minimist": "^1.2.0", + "@types/webpack": "^4.0.0", + "@types/webpack-dev-server": "^3.11.0", + "@vue/cli-overlay": "^4.5.19", + "@vue/cli-plugin-router": "^4.5.19", + "@vue/cli-plugin-vuex": "^4.5.19", + "@vue/cli-shared-utils": "^4.5.19", + "@vue/component-compiler-utils": "^3.1.2", + "@vue/preload-webpack-plugin": "^1.1.0", + "@vue/web-component-wrapper": "^1.2.0", + "acorn": "^7.4.0", + "acorn-walk": "^7.1.1", + "address": "^1.1.2", + "autoprefixer": "^9.8.6", + "browserslist": "^4.12.0", + "cache-loader": "^4.1.0", + "case-sensitive-paths-webpack-plugin": "^2.3.0", + "cli-highlight": "^2.1.4", + "clipboardy": "^2.3.0", + "cliui": "^6.0.0", + "copy-webpack-plugin": "^5.1.1", + "css-loader": "^3.5.3", + "cssnano": "^4.1.10", + "debug": "^4.1.1", + "default-gateway": "^5.0.5", + "dotenv": "^8.2.0", + "dotenv-expand": "^5.1.0", + "file-loader": "^4.2.0", + "fs-extra": "^7.0.1", + "globby": "^9.2.0", + "hash-sum": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "launch-editor-middleware": "^2.2.1", + "lodash.defaultsdeep": "^4.6.1", + "lodash.mapvalues": "^4.6.0", + "lodash.transform": "^4.6.0", + "mini-css-extract-plugin": "^0.9.0", + "minimist": "^1.2.5", + "pnp-webpack-plugin": "^1.6.4", + "portfinder": "^1.0.26", + "postcss-loader": "^3.0.0", + "ssri": "^8.0.1", + "terser-webpack-plugin": "^1.4.4", + "thread-loader": "^2.1.3", + "url-loader": "^2.2.0", + "vue-loader": "^15.9.2", + "vue-style-loader": "^4.1.2", + "webpack": "^4.0.0", + "webpack-bundle-analyzer": "^3.8.0", + "webpack-chain": "^6.4.0", + "webpack-dev-server": "^3.11.0", + "webpack-merge": "^4.2.2" + }, + "bin": { + "vue-cli-service": "bin/vue-cli-service.js" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "vue-loader-v16": "npm:vue-loader@^16.1.0" + }, + "peerDependencies": { + "@vue/compiler-sfc": "^3.0.0-beta.14", + "vue-template-compiler": "^2.0.0" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + }, + "less-loader": { + "optional": true + }, + "pug-plain-loader": { + "optional": true + }, + "raw-loader": { + "optional": true + }, + "sass-loader": { + "optional": true + }, + "stylus-loader": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/@vue/cli-shared-utils": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/cli-shared-utils/-/cli-shared-utils-4.5.19.tgz", + "integrity": "sha512-JYpdsrC/d9elerKxbEUtmSSU6QRM60rirVubOewECHkBHj+tLNznWq/EhCjswywtePyLaMUK25eTqnTSZlEE+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@achrinza/node-ipc": "9.2.2", + "@hapi/joi": "^15.0.1", + "chalk": "^2.4.2", + "execa": "^1.0.0", + "launch-editor": "^2.2.1", + "lru-cache": "^5.1.1", + "open": "^6.3.0", + "ora": "^3.4.0", + "read-pkg": "^5.1.1", + "request": "^2.88.2", + "semver": "^6.1.0", + "strip-ansi": "^6.0.0" + } + }, + "node_modules/@vue/cli-shared-utils/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vue/cli-shared-utils/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vue/cli-shared-utils/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@vue/cli-shared-utils/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/cli-shared-utils/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@vue/cli-shared-utils/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.33", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.33.tgz", + "integrity": "sha512-3PZLQwFw4Za3TC8t0FvTy3wI16Kt+pmwcgNZca4Pj9iWL2E72a/gZlpBtAJvEdDMdCxdG/qq0C7PN0bsJuv0Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.2", + "@vue/shared": "3.5.33", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.33", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.33.tgz", + "integrity": "sha512-PXq0yrfCLzzL07rbXO4awtXY1Z06LG2eu6Adg3RJFa/j3Cii217XxxLXG22N330gw7GmALCY0Z8RgXEviwgpjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.33", + "@vue/shared": "3.5.33" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.33", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.33.tgz", + "integrity": "sha512-UTUvRO9cY+rROrx/pvN9P5Z7FgA6QGfokUCfhQE4EnmUj3rVnK+CHI0LsEO1pg+I7//iRYMUfcNcCPe7tg0CoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.2", + "@vue/compiler-core": "3.5.33", + "@vue/compiler-dom": "3.5.33", + "@vue/compiler-ssr": "3.5.33", + "@vue/shared": "3.5.33", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.10", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/postcss": { + "version": "8.5.10", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.33", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.33.tgz", + "integrity": "sha512-IErjYdnj1qIupG5xxiVIYiiRvDhGWV4zuh/RCrwfYpuL+HWQzeU6lCk/nF9r7olWMnjKxCAkOctT2qFWFkzb1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.33", + "@vue/shared": "3.5.33" + } + }, + "node_modules/@vue/component-compiler-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", + "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.36", + "postcss-selector-parser": "^6.0.2", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vue/preload-webpack-plugin": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz", + "integrity": "sha512-LIZMuJk38pk9U9Ur4YzHjlIyMuxPlACdBIHH9/nGYVTsaGKOSnSuELiE8vS9wa+dJpIYspYUOqk+L1Q4pgHQHQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "html-webpack-plugin": ">=2.26.0", + "webpack": ">=4.0.0" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.33", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.33.tgz", + "integrity": "sha512-5vR2QIlmaLG77Ygd4pMP6+SGQ5yox9VhtnbDWTy9DzMzdmeLxZ1QqxrywEZ9sa1AVubfIJyaCG3ytyWU81ufcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/web-component-wrapper": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz", + "integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "node_modules/@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "license": "ISC" + }, + "node_modules/@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "license": "MIT", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.13", + "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.13.tgz", + "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "license": "Apache-2.0" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aes-decrypter": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/aes-decrypter/-/aes-decrypter-4.0.2.tgz", + "integrity": "sha512-lc+/9s6iJvuaRe5qDlMTpCFjnwpkeOXp8qP3oiZ5jsj1MRg+SBVUmmICrhxHvc8OELSmc+fEyyxAuppY6hrWzw==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "^4.1.1", + "global": "^4.4.0", + "pkcs7": "^1.0.4" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "license": "MIT", + "peerDependencies": { + "ajv": ">=5.0.0" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/animate.css": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/animate.css/-/animate.css-4.1.1.tgz", + "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==", + "license": "MIT" + }, + "node_modules/ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmmirror.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "license": "ISC" + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/array.prototype.reduce/-/array.prototype.reduce-1.0.8.tgz", + "integrity": "sha512-DwuEqgXFBwbmZSRqt3BpQigWNUoqw9Ml2dTWdF3B2zQlQX4OeUE0zyuzX0fX0IbTvjdkZbcBTU3idgpO78qkTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-array-method-boxes-properly": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "is-string": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmmirror.com/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmmirror.com/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", + "license": "MIT" + }, + "node_modules/assert": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/assert/-/assert-1.5.1.tgz", + "integrity": "sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==", + "license": "MIT", + "dependencies": { + "object.assign": "^4.1.4", + "util": "^0.10.4" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assert/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "license": "ISC" + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmmirror.com/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "license": "MIT", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmmirror.com/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-each": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/async-each/-/async-each-1.0.6.tgz", + "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-validator": { + "version": "1.8.5", + "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-1.8.5.tgz", + "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==", + "dependencies": { + "babel-runtime": "6.x" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/autoprefixer/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmmirror.com/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.15.2", + "resolved": "https://registry.npmmirror.com/axios/-/axios-1.15.2.tgz", + "integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "eslint": ">= 4.12.1" + } + }, + "node_modules/babel-helper-vue-jsx-merge-props": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz", + "integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==", + "license": "MIT" + }, + "node_modules/babel-loader": { + "version": "8.4.1", + "resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-8.4.1.tgz", + "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.4", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.17", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.14.2", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8", + "core-js-compat": "^3.48.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.8", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-transform-remove-console": { + "version": "6.9.4", + "resolved": "https://registry.npmmirror.com/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", + "integrity": "sha512-88blrUrMX3SPiGkT1GnvVY8E/7A+k6oj3MNvUtTIxJflFzXTw1bHkuJ/y039ouhFMp2prRn5cQGzokViYi1dsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmmirror.com/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "license": "MIT", + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "node_modules/babel-runtime/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmmirror.com/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "license": "MIT", + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.21", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.21.tgz", + "integrity": "sha512-Q+rUQ7Uz8AHM7DEaNdwvfFCTq7a43lNTzuS94eiWqwyxfV/wJv+oUivef51T91mmRY4d4A1u9rcSvkeufCVXlA==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bfj": { + "version": "6.1.2", + "resolved": "https://registry.npmmirror.com/bfj/-/bfj-6.1.2.tgz", + "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.5.5", + "check-types": "^8.0.3", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "license": "MIT" + }, + "node_modules/bn.js": { + "version": "5.2.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-5.2.3.tgz", + "integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/bonjour": { + "version": "3.5.1", + "resolved": "https://registry.npmmirror.com/bonjour/-/bonjour-3.5.1.tgz", + "integrity": "sha512-xONzj4PfpPJw6xSqCcT2SmQkBOXpUINUz3o3qXcWJwYlXbkZNcNaUae0o5lle7tKt4HHV6dTgkIRhAXZ3nBMsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^7.2.3", + "multicast-dns-service-types": "^1.1.0" + } + }, + "node_modules/bonjour/node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/braces/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "license": "MIT" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "license": "MIT", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "license": "MIT", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/browserify-rsa/-/browserify-rsa-4.1.1.tgz", + "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==", + "license": "MIT", + "dependencies": { + "bn.js": "^5.2.1", + "randombytes": "^2.1.0", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/browserify-rsa/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/browserify-sign": { + "version": "4.2.5", + "resolved": "https://registry.npmmirror.com/browserify-sign/-/browserify-sign-4.2.5.tgz", + "integrity": "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==", + "license": "ISC", + "dependencies": { + "bn.js": "^5.2.2", + "browserify-rsa": "^4.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.6.1", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.9", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/browserify-sign/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "license": "MIT", + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmmirror.com/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-json": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/buffer-json/-/buffer-json-2.0.0.tgz", + "integrity": "sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "license": "MIT" + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmmirror.com/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "license": "ISC", + "dependencies": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "node_modules/cacache/node_modules/ssri": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "license": "ISC", + "dependencies": { + "figgy-pudding": "^3.5.1" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "license": "MIT", + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cache-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/cache-loader/-/cache-loader-4.1.0.tgz", + "integrity": "sha512-ftOayxve0PwKzBF/GLsZNC9fJBXl8lkZE3TOsjkboHfVHVkL39iUEs1FO07A33mizmci5Dudt38UZrrYXDtbhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-json": "^2.0.0", + "find-cache-dir": "^3.0.0", + "loader-utils": "^1.2.3", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.1", + "schema-utils": "^2.0.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/cache-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/cache-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001790", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001790.tgz", + "integrity": "sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmmirror.com/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/check-types": { + "version": "8.0.3", + "resolved": "https://registry.npmmirror.com/check-types/-/check-types-8.0.3.tgz", + "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "optional": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "optional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar/node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "optional": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chokidar/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/chokidar/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cipher-base": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/cipher-base/-/cipher-base-1.0.7.tgz", + "integrity": "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cipher-base/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmmirror.com/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "license": "MIT", + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "license": "MIT", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmmirror.com/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmmirror.com/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/clipboardy": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-2.3.0.tgz", + "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "arch": "^2.1.1", + "execa": "^1.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/coa/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/coa/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/coa/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "license": "MIT", + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.17.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true, + "license": "MIT" + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "license": "MIT" + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/comutils": { + "version": "1.1.19", + "resolved": "https://registry.npmmirror.com/comutils/-/comutils-1.1.19.tgz", + "integrity": "sha512-JxXB67juILiwhdLwOsYyjUqwWEhHdObI0EClOPk+JDtEuTbac59s0pxGpfCBnNNQ5JommifmcMGneW/4Cg7YWw==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmmirror.com/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "node_modules/consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmmirror.com/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "deprecated": "Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.1.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz", + "integrity": "sha512-Uh7crJAco3AjBvgAy9Z75CjK8IG+gxaErro71THQ+vv/bl4HaQcpkexAY8KVW/T6D2W2IRr+couF/knIRkZMIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cacache": "^12.0.3", + "find-cache-dir": "^2.1.0", + "glob-parent": "^3.1.0", + "globby": "^7.1.1", + "is-glob": "^4.0.1", + "loader-utils": "^1.2.3", + "minimatch": "^3.0.4", + "normalize-path": "^3.0.0", + "p-limit": "^2.2.1", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "webpack-log": "^2.0.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/copy-webpack-plugin/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/globby/-/globby-7.1.1.tgz", + "integrity": "sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/globby/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-webpack-plugin/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/copy-webpack-plugin/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/copy-webpack-plugin/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/copy-webpack-plugin/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/copy-webpack-plugin/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js": { + "version": "3.49.0", + "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.49.0", + "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", + "license": "MIT" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmmirror.com/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.1", + "resolved": "https://registry.npmmirror.com/crypto-browserify/-/crypto-browserify-3.12.1.tgz", + "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==", + "license": "MIT", + "dependencies": { + "browserify-cipher": "^1.0.1", + "browserify-sign": "^4.2.3", + "create-ecdh": "^4.0.4", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "diffie-hellman": "^5.0.3", + "hash-base": "~3.0.4", + "inherits": "^2.0.4", + "pbkdf2": "^3.1.2", + "public-encrypt": "^4.0.3", + "randombytes": "^2.1.0", + "randomfill": "^1.0.4" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmmirror.com/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + }, + "engines": { + "node": ">4" + } + }, + "node_modules/css-loader": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/css-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/css-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "dev": true, + "license": "MIT" + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "4.1.11", + "resolved": "https://registry.npmmirror.com/cssnano/-/cssnano-4.1.11.tgz", + "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.8", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz", + "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.3", + "postcss-unique-selectors": "^4.0.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha512-6RIcwmV3/cBMG8Aj5gucQRsJb4vv4I4rn6YjPbVWd5+Pn/fuG+YseGvXGk00XLkoZkaj31QOD7vMUpNPC4FIuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha512-JPMZ1TSMRUPVIqEalIBNoBtAYbi8okvcFns4O0YIhcdGebeYZK7dMyHJiQ6GqNBA9kE0Hym4Aqym5rPdsV/4Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/cyclist": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/cyclist/-/cyclist-1.0.2.tgz", + "integrity": "sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA==", + "license": "MIT" + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "5.0.5", + "resolved": "https://registry.npmmirror.com/default-gateway/-/default-gateway-5.0.5.tgz", + "integrity": "sha512-z2RnruVmj8hVMmAnEJMTIJNijhKCDiGjbLP+BHJFOT7ld3Bo5qcIBpVYDniqhbMIIf+jZDlkP2MkPXiQy/DBLA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "execa": "^3.3.0" + }, + "engines": { + "node": "^8.12.0 || >=9.7.0" + } + }, + "node_modules/default-gateway/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/default-gateway/node_modules/execa": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": "^8.12.0 || >=9.7.0" + } + }, + "node_modules/default-gateway/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/default-gateway/node_modules/p-finally": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/default-gateway/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/default-gateway/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/default-gateway/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/default-gateway/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmmirror.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", + "license": "MIT" + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true, + "license": "MIT" + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmmirror.com/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "node_modules/dom7": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/dom7/-/dom7-2.1.5.tgz", + "integrity": "sha512-xnhwVgyOh3eD++/XGtH+5qBwYTgCm0aW91GFgPJ3XG+jlsRLyJivnbP0QmUBFhI+Oaz9FV0s7cxgXHezwOEBYA==", + "license": "MIT", + "dependencies": { + "ssr-window": "^2.0.0" + } + }, + "node_modules/dom7/node_modules/ssr-window": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ssr-window/-/ssr-window-2.0.0.tgz", + "integrity": "sha512-NXzN+/HPObKAx191H3zKlYomE5WrVIkoCB5IaSdvKokxTpjBdWfr0RaP+1Z5KOfDT0ZVz+2tdtiBkhsEQ9p+0A==", + "license": "MIT" + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "license": "MIT", + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmmirror.com/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmmirror.com/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/easy-stack": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/easy-stack/-/easy-stack-1.0.1.tgz", + "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmmirror.com/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.344", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz", + "integrity": "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==", + "license": "ISC" + }, + "node_modules/element-ui": { + "version": "2.15.14", + "resolved": "https://registry.npmmirror.com/element-ui/-/element-ui-2.15.14.tgz", + "integrity": "sha512-2v9fHL0ZGINotOlRIAJD5YuVB8V7WKxrE9Qy7dXhRipa035+kF7WuU/z+tEmLVPBcJ0zt8mOu1DKpWcVzBK8IA==", + "license": "MIT", + "dependencies": { + "async-validator": "~1.8.1", + "babel-helper-vue-jsx-merge-props": "^2.0.0", + "deepmerge": "^1.2.0", + "normalize-wheel": "^1.0.1", + "resize-observer-polyfill": "^1.5.0", + "throttle-debounce": "^1.0.1" + }, + "peerDependencies": { + "vue": "^2.5.17" + } + }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmmirror.com/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.6.6", + "resolved": "https://registry.npmmirror.com/engine.io/-/engine.io-6.6.6.tgz", + "integrity": "sha512-U2SN0w3OpjFRVlrc17E6TMDmH58Xl9rai1MblNjAdwWp07Kk+llmzX0hjDpQdrDGzwmvOtgM5yI+meYX6iZ2xA==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "@types/ws": "^8.5.12", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.18.3" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmmirror.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/enhanced-resolve/node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmmirror.com/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "license": "MIT", + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "license": "MIT", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-loader": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/eslint-loader/-/eslint-loader-2.2.1.tgz", + "integrity": "sha512-RLgV9hoCVsMLvOxCuNjdqOrUqIj9oJg8hF44vzJaYqsAHuY9G2YAeN3joQ9nxP0p5Th9iFSIpKo+SD8KISxXRg==", + "deprecated": "This loader has been deprecated. Please use eslint-webpack-plugin", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-fs-cache": "^1.0.0", + "loader-utils": "^1.0.2", + "object-assign": "^4.0.1", + "object-hash": "^1.1.4", + "rimraf": "^2.6.1" + }, + "peerDependencies": { + "eslint": ">=1.6.0 <7.0.0", + "webpack": ">=2.0.0 <5.0.0" + } + }, + "node_modules/eslint-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/eslint-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eslint-plugin-vue": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-6.2.2.tgz", + "integrity": "sha512-Nhc+oVAHm0uz/PkJAWscwIT4ijTrK5fqNqz9QB1D35SbbuMG1uB6Yr5AJpvPSWg+WOw7nYNswerYh0kOk64gqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "natural-compare": "^1.4.0", + "semver": "^5.6.0", + "vue-eslint-parser": "^7.0.0" + }, + "engines": { + "node": ">=8.10" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/eslint-plugin-vue/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/espree": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-pubsub": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/event-pubsub/-/event-pubsub-4.3.0.tgz", + "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "license": "MIT", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "license": "MIT", + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "license": "MIT", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmmirror.com/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "license": "MIT", + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmmirror.com/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmmirror.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "deprecated": "This module is no longer supported.", + "license": "ISC" + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/file-loader/-/file-loader-4.3.0.tgz", + "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/file-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/file-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT", + "optional": true + }, + "node_modules/filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmmirror.com/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true, + "license": "ISC" + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "license": "MIT", + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==", + "dev": true, + "license": "BSD" + }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmmirror.com/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "license": "MIT", + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "node_modules/globals": { + "version": "12.4.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "9.2.0", + "resolved": "https://registry.npmmirror.com/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmmirror.com/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "license": "MIT", + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "license": "MIT", + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-base": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/hash-base/-/hash-base-3.0.5.tgz", + "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/hash-base/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/hash-sum": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", + "dev": true, + "license": "MIT" + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmmirror.com/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmmirror.com/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A==", + "dev": true, + "license": "MIT" + }, + "node_modules/hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmmirror.com/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "bin": { + "html-minifier": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-Br4ifmjQojUP4EmHnRBoUIYcZ9J7M4bTMcm7u6xoIAIuq2Nte4TzXX0533owvkQKQD1WeMTTTyD4Ni4QKxS0Bg==", + "deprecated": "3.x is no longer supported", + "dev": true, + "license": "MIT", + "dependencies": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "engines": { + "node": ">=6.9" + }, + "peerDependencies": { + "webpack": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/html-webpack-plugin/node_modules/big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/html-webpack-plugin/node_modules/emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/html-webpack-plugin/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmmirror.com/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/html-webpack-plugin/node_modules/loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmmirror.com/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmmirror.com/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz", + "integrity": "sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.5", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/http-proxy-middleware/node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/http-proxy-middleware/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss": "^7.0.14" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmmirror.com/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==", + "license": "MIT" + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "5.1.5", + "resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.1.5.tgz", + "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha512-Ew5AZzJQFqrOV5BTW3EIoHAnoie1LojZLXKcCQ/yTRyVZosBhK1x1ViYjHGf5pAFOq8ZyChZp6m/fSN7pJyZtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-from": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha512-0vdnLL2wSGnhlRmzHJAg5JHjt1l2vYhzJ7tNLGbeVg0fse56tpGaH0uzH+r9Slej+BSXXEHvBKDEnVSLLE9/+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==", + "dev": true, + "license": "MIT" + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmmirror.com/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/internal-ip/node_modules/default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip": { + "version": "1.1.9", + "resolved": "https://registry.npmmirror.com/ip/-/ip-1.1.9.tgz", + "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", + "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "optional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^1.5.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha512-H1U8Vz0cfXNujrJzEcvvwMDW9Ra+biSYA3ThdQvAnMLJkEHQXn6bWzLkxHtVYJ+Sdbx0b6finn3jZiaVe7MAHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz", + "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "license": "MIT" + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-path-inside": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-is-inside": "^1.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/iscroll": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/iscroll/-/iscroll-5.2.0.tgz", + "integrity": "sha512-HyEu3cR8ulc0JONuQq6yht7s4iYgIPpYvy0h8IJflOUMDB95pwZ7e5FQtA6qfCESxhfxv7JZb9kIQe0JFPL05g==", + "deprecated": "deprecated", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/javascript-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz", + "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-message": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/js-message/-/js-message-1.0.7.tgz", + "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/killable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true, + "license": "ISC" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/launch-editor": { + "version": "2.13.2", + "resolved": "https://registry.npmmirror.com/launch-editor/-/launch-editor-2.13.2.tgz", + "integrity": "sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.1", + "shell-quote": "^1.8.3" + } + }, + "node_modules/launch-editor-middleware": { + "version": "2.13.2", + "resolved": "https://registry.npmmirror.com/launch-editor-middleware/-/launch-editor-middleware-2.13.2.tgz", + "integrity": "sha512-kI7VqA9g6mhoeQ6YdNgd+gKLaeuWHAUR8oDM8vFtt924wG8HbI2XO0n/hSX2ML4hcJbTgUihuPHfpnPjIKMdJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "launch-editor": "^2.13.2" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/loader-fs-cache": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz", + "integrity": "sha512-ldcgZpjNJj71n+2Mf6yetz+c9bM4xpKtNds4LbqXzU/PTdeAX0g3ytnU1AJMEcTk2Lex4Smpe3Q/eCTsvUBxbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-cache-dir": "^0.1.1", + "mkdirp": "^0.5.1" + } + }, + "node_modules/loader-fs-cache/node_modules/find-cache-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz", + "integrity": "sha512-Z9XSBoNE7xQiV6MSgPuCfyMokH2K7JdpRkOYE1+mu3d4BFJtx3GW+f6Bo4q8IX6rlf5MYbLBKW0pjl2cWdkm2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "mkdirp": "^0.5.1", + "pkg-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-fs-cache/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-fs-cache/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-fs-cache/node_modules/pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha512-c6pv3OE78mcZ92ckebVDqg0aWSoKhOTbwCV6qbCWMk546mAL9pZln0+QsN/yQ7fkucd4+yJPLrCBXNt8Ruk+Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "license": "MIT", + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.defaultsdeep": { + "version": "4.6.1", + "resolved": "https://registry.npmmirror.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.orderby": { + "version": "4.18.0", + "resolved": "https://registry.npmmirror.com/lodash.orderby/-/lodash.orderby-4.18.0.tgz", + "integrity": "sha512-XSSpOxgihAM5kawpay9vl0e9r73l+LJIh03NzJBF33DWb8XgSM9Bvl1mEpA0ydrvoOeTVbNZBo2gY7zfw22EQQ==", + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" + }, + "node_modules/lodash.transform": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/lodash.transform/-/lodash.transform-4.6.0.tgz", + "integrity": "sha512-LO37ZnhmBVx0GvOU/caQuipEh4GN82TcWv3yHlebGDgOxbxiwwzW5Pcx2AcvpIv2WmvmSMoC492yQFNhy/l/UQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmmirror.com/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/m3u8-parser": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/m3u8-parser/-/m3u8-parser-7.2.0.tgz", + "integrity": "sha512-CRatFqpjVtMiMaKXxNvuI3I++vUumIXVVT/JpCpdU/FynV/ceVw1qpPyyBNindL+JlPMSesx+WX1QJaZEJSaMQ==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "^4.1.1", + "global": "^4.4.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "license": "MIT", + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmmirror.com/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", + "license": "MIT", + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "license": "MIT", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", + "license": "MIT" + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-document": { + "version": "2.19.2", + "resolved": "https://registry.npmmirror.com/min-document/-/min-document-2.19.2.tgz", + "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", + "license": "MIT", + "dependencies": { + "dom-walk": "^0.1.0" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "0.9.0", + "resolved": "https://registry.npmmirror.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", + "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.4.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "license": "BSD-2-Clause", + "dependencies": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "license": "MIT", + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "node_modules/mpd-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/mpd-parser/-/mpd-parser-1.3.1.tgz", + "integrity": "sha512-1FuyEWI5k2HcmhS1HkKnUAQV7yFPfXPht2DnRRGtoiiAAW+ESTbtEXIDpRkwdU+XyrQuwrIym7UkoPKsZ0SyFw==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@videojs/vhs-utils": "^4.0.0", + "@xmldom/xmldom": "^0.8.3", + "global": "^4.4.0" + }, + "bin": { + "mpd-to-m3u8-json": "bin/parse.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmmirror.com/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmmirror.com/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/mux.js": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/mux.js/-/mux.js-7.1.0.tgz", + "integrity": "sha512-NTxawK/BBELJrYsZThEulyUMDVlLizKdxyAsMuzoCD1eFj97BVaA8D/CvKsKu6FOLYkFojN5CbM9h++ZTZtknA==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.11.2", + "global": "^4.4.0" + }, + "bin": { + "muxjs-transmux": "bin/transmux.js" + }, + "engines": { + "node": ">=8", + "npm": ">=5" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nan": { + "version": "2.26.2", + "resolved": "https://registry.npmmirror.com/nan/-/nan-2.26.2.tgz", + "integrity": "sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw==", + "license": "MIT", + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmmirror.com/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "license": "MIT", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", + "optional": true + }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmmirror.com/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "license": "MIT", + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.38", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "license": "MIT" + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize-wheel": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz", + "integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==", + "license": "BSD-3-Clause" + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmmirror.com/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "license": "MIT", + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "license": "MIT", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-1.3.1.tgz", + "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmmirror.com/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.9.tgz", + "integrity": "sha512-mt8YM6XwsTTovI+kdZdHSxoyF2DI59up034orlC9NfweclcWOt7CVascNNLp6U+bjFVCVCIh9PwS76tDM/rH8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.reduce": "^1.0.8", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "gopd": "^1.2.0", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "6.4.0", + "resolved": "https://registry.npmmirror.com/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/open/node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmmirror.com/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/opn/node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/ora/-/ora-3.4.0.tgz", + "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-spinners": "^2.0.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ora/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ora/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "license": "MIT" + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "license": "MIT", + "dependencies": { + "retry": "^0.12.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "license": "MIT", + "dependencies": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module/node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.9", + "resolved": "https://registry.npmmirror.com/parse-asn1/-/parse-asn1-5.1.9.tgz", + "integrity": "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==", + "license": "ISC", + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "pbkdf2": "^3.1.5", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-asn1/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "license": "MIT" + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-type/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/pbkdf2/-/pbkdf2-3.1.5.tgz", + "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==", + "license": "MIT", + "dependencies": { + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "ripemd160": "^2.0.3", + "safe-buffer": "^5.2.1", + "sha.js": "^2.4.12", + "to-buffer": "^1.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pbkdf2/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkcs7": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/pkcs7/-/pkcs7-1.0.4.tgz", + "integrity": "sha512-afRERtHn54AlwaF2/+LFszyAANTCggGilmcmILUzEjvs3XgFZT+xE6+QWQcAGmu4xajy+Xtj7acLOPdx5/eXWQ==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.5.5" + }, + "bin": { + "pkcs7": "bin/cli.js" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/pnp-webpack-plugin": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.7.0.tgz", + "integrity": "sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ts-pnp": "^1.1.6" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/portfinder": { + "version": "1.0.38", + "resolved": "https://registry.npmmirror.com/portfinder/-/portfinder-1.0.38.tgz", + "integrity": "sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^3.2.6", + "debug": "^4.3.6" + }, + "engines": { + "node": ">= 10.12" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmmirror.com/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-colormin/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-convert-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-load-config": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz", + "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/postcss-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-loader/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmmirror.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-params/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss": "^7.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-positions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-string/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-url/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/postcss-svgo/-/postcss-svgo-4.0.3.tgz", + "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-svgo/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dev": true, + "license": "MIT", + "dependencies": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "license": "MIT", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-error": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^2.0.4" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmmirror.com/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "license": "ISC" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "license": "MIT" + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmmirror.com/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.3", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "license": "MIT", + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcodejs2": { + "version": "0.0.2", + "resolved": "https://registry.npmmirror.com/qrcodejs2/-/qrcodejs2-0.0.2.tgz", + "integrity": "sha512-+Y4HA+cb6qUzdgvI3KML8GYpMFwB24dFwzMkS/yXq6hwtUGNUnZQdUnksrV1XGMc2mid5ROw5SAuY9XhI3ValA==", + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.5.5", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.5.5.tgz", + "integrity": "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmmirror.com/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "license": "MIT", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.2", + "resolved": "https://registry.npmmirror.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "license": "MIT" + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.5.0" + } + }, + "node_modules/regexpu-core": { + "version": "6.4.0", + "resolved": "https://registry.npmmirror.com/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.2", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmmirror.com/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.13.1", + "resolved": "https://registry.npmmirror.com/regjsparser/-/regjsparser-0.13.1.tgz", + "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmmirror.com/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/renderkid": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/renderkid/-/renderkid-2.0.7.tgz", + "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^3.0.1" + } + }, + "node_modules/renderkid/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/renderkid/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmmirror.com/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmmirror.com/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true, + "license": "ISC" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "license": "MIT" + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmmirror.com/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "license": "MIT", + "engines": { + "node": ">=0.12" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/ripemd160/-/ripemd160-2.0.3.tgz", + "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==", + "license": "MIT", + "dependencies": { + "hash-base": "^3.1.2", + "inherits": "^2.0.4" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ripemd160/node_modules/hash-base": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/hash-base/-/hash-base-3.1.2.tgz", + "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ripemd160/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", + "license": "ISC", + "dependencies": { + "aproba": "^1.1.1" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "license": "MIT", + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.99.0", + "resolved": "https://registry.npmmirror.com/sass/-/sass-1.99.0.tgz", + "integrity": "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass-loader": { + "version": "8.0.2", + "resolved": "https://registry.npmmirror.com/sass-loader/-/sass-loader-8.0.2.tgz", + "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==", + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.2.3", + "neo-async": "^2.6.1", + "schema-utils": "^2.6.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0", + "sass": "^1.3.0", + "webpack": "^4.36.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/sass-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/sass-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true, + "license": "ISC" + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true, + "license": "MIT" + }, + "node_modules/selfsigned": { + "version": "1.10.14", + "resolved": "https://registry.npmmirror.com/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-forge": "^0.10.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmmirror.com/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.2", + "resolved": "https://registry.npmmirror.com/serve-index/-/serve-index-1.9.2.tgz", + "integrity": "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.8.0", + "mime-types": "~2.1.35", + "parseurl": "~1.3.3" + }, + "engines": { + "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmmirror.com/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sha.js/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shvl": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/shvl/-/shvl-2.0.3.tgz", + "integrity": "sha512-V7C6S9Hlol6SzOJPnQ7qzOVEWUQImt3BNmmzh40wObhla3XOYMe4gGiYzLrJd5TFa+cI2f9LKIRJTTKZSTbWgw==", + "deprecated": "older versions vulnerable to prototype pollution", + "license": "MIT" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmmirror.com/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "license": "MIT", + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "license": "MIT", + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "license": "MIT", + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "license": "MIT", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.3", + "resolved": "https://registry.npmmirror.com/socket.io/-/socket.io-4.8.3.tgz", + "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.6", + "resolved": "https://registry.npmmirror.com/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz", + "integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==", + "license": "MIT", + "dependencies": { + "debug": "~4.4.1", + "ws": "~8.18.3" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.6", + "resolved": "https://registry.npmmirror.com/socket.io-parser/-/socket.io-parser-4.2.6.tgz", + "integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs-client": { + "version": "1.6.1", + "resolved": "https://registry.npmmirror.com/sockjs-client/-/sockjs-client-1.6.1.tgz", + "integrity": "sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "eventsource": "^2.0.2", + "faye-websocket": "^0.11.4", + "inherits": "^2.0.4", + "url-parse": "^1.5.10" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://tidelift.com/funding/github/npm/sockjs-client" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-keys/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmmirror.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "license": "MIT", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "license": "MIT" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmmirror.com/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssr-window": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/ssr-window/-/ssr-window-1.0.1.tgz", + "integrity": "sha512-dgFqB+f00LJTEgb6UXhx0h+SrG50LJvti2yMKMqAgzfUmUXZrLSv2fjULF7AWGwK25EXu8+smLR3jYsJQChPsg==", + "license": "MIT" + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", + "dev": true, + "license": "MIT" + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmmirror.com/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "dev": true, + "license": "MIT" + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "license": "MIT", + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "license": "MIT", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmmirror.com/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "license": "MIT", + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" + }, + "node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/stylehacks/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/svgo/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/svgo/node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/svgo/node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmmirror.com/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/svgo/node_modules/domutils/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/svgo/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/svgo/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/svgo/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/swiper": { + "version": "4.5.1", + "resolved": "https://registry.npmmirror.com/swiper/-/swiper-4.5.1.tgz", + "integrity": "sha512-se6I7PWWu950NAMXXT+ENtF/6SVb8mPyO+bTfNxbQBILSeLqsYp3Ndap+YOA0EczOIUlea274PKejT6gKZDseA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "dom7": "^2.1.3", + "ssr-window": "^1.0.1" + }, + "engines": { + "node": ">= 4.7.0" + } + }, + "node_modules/table": { + "version": "5.4.6", + "resolved": "https://registry.npmmirror.com/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "4.8.1", + "resolved": "https://registry.npmmirror.com/terser/-/terser-4.8.1.tgz", + "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "license": "BSD-2-Clause", + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "1.4.6", + "resolved": "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.6.tgz", + "integrity": "sha512-2lBVf/VMVIddjSn3GqbT90GvIJ/eYXJkt8cTzU7NbjKqK8fwv18Ftr4PlbF46b/e88743iZFL5Dtr/rC4hjIeA==", + "license": "MIT", + "dependencies": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/terser-webpack-plugin/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/terser-webpack-plugin/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "license": "MIT", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/terser-webpack-plugin/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/thread-loader": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/thread-loader/-/thread-loader-2.1.3.tgz", + "integrity": "sha512-wNrVKH2Lcf8ZrWxDF/khdlLlsTMczdcwPA9VEK4c2exlEPynYWxi9op3nPTo5lAnDIkE0rQEB3VBP+4Zncc9Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-runner": "^2.3.1", + "loader-utils": "^1.1.0", + "neo-async": "^2.6.0" + }, + "engines": { + "node": ">= 6.9.0 <7.0.0 || >= 8.9.0" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/thread-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/thread-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/throttle-debounce": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-1.1.0.tgz", + "integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmmirror.com/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmmirror.com/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "license": "MIT", + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==", + "license": "MIT" + }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-buffer/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/to-buffer/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "license": "MIT", + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "license": "MIT", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha512-FclLrw8b9bMWf4QlCJuHBEVhSRsqDj6u3nIjAzPeJvgl//1hBlffdlk0MALceL14+koWEdU4ofRAXofbODxQzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-pnp": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/ts-pnp/-/ts-pnp-1.2.0.tgz", + "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmmirror.com/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==", + "license": "MIT" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmmirror.com/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmmirror.com/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglify-js/node_modules/commander": { + "version": "2.19.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "7.19.2", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "license": "MIT", + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "dev": true, + "license": "MIT" + }, + "node_modules/uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "license": "ISC", + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "license": "MIT", + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "license": "MIT", + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "license": "MIT", + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "license": "MIT" + }, + "node_modules/url": { + "version": "0.11.4", + "resolved": "https://registry.npmmirror.com/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", + "license": "MIT", + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/url-loader": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/url-loader/-/url-loader-2.3.0.tgz", + "integrity": "sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^1.2.3", + "mime": "^2.4.4", + "schema-utils": "^2.5.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/url-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "license": "MIT" + }, + "node_modules/url/node_modules/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npmmirror.com/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "license": "MIT", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "license": "ISC" + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmmirror.com/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/video.js": { + "version": "8.23.7", + "resolved": "https://registry.npmmirror.com/video.js/-/video.js-8.23.7.tgz", + "integrity": "sha512-cG4HOygYt+Z8j6Sf5DuK6OgEOoM+g9oGP6vpqoZRaD13aHE4PMITbyjJUXZcIQbgB0wJEadBRaVm5lJIzo2jAA==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@videojs/http-streaming": "^3.17.3", + "@videojs/vhs-utils": "^4.1.1", + "@videojs/xhr": "2.7.0", + "aes-decrypter": "^4.0.2", + "global": "4.4.0", + "m3u8-parser": "^7.2.0", + "mpd-parser": "^1.3.1", + "mux.js": "^7.0.1", + "videojs-contrib-quality-levels": "4.1.0", + "videojs-font": "4.2.0", + "videojs-vtt.js": "0.15.5" + } + }, + "node_modules/videojs-contrib-quality-levels": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/videojs-contrib-quality-levels/-/videojs-contrib-quality-levels-4.1.0.tgz", + "integrity": "sha512-TfrXJJg1Bv4t6TOCMEVMwF/CoS8iENYsWNKip8zfhB5kTcegiFYezEA0eHAJPU64ZC8NQbxQgOwAsYU8VXbOWA==", + "license": "Apache-2.0", + "dependencies": { + "global": "^4.4.0" + }, + "engines": { + "node": ">=16", + "npm": ">=8" + }, + "peerDependencies": { + "video.js": "^8" + } + }, + "node_modules/videojs-font": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/videojs-font/-/videojs-font-4.2.0.tgz", + "integrity": "sha512-YPq+wiKoGy2/M7ccjmlvwi58z2xsykkkfNMyIg4xb7EZQQNwB71hcSsB3o75CqQV7/y5lXkXhI/rsGAS7jfEmQ==", + "license": "Apache-2.0" + }, + "node_modules/videojs-vtt.js": { + "version": "0.15.5", + "resolved": "https://registry.npmmirror.com/videojs-vtt.js/-/videojs-vtt.js-0.15.5.tgz", + "integrity": "sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==", + "license": "Apache-2.0", + "dependencies": { + "global": "^4.3.1" + } + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "license": "MIT" + }, + "node_modules/vue": { + "version": "2.7.16", + "resolved": "https://registry.npmmirror.com/vue/-/vue-2.7.16.tgz", + "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==", + "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.", + "license": "MIT", + "dependencies": { + "@vue/compiler-sfc": "2.7.16", + "csstype": "^3.1.0" + } + }, + "node_modules/vue-agile": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/vue-agile/-/vue-agile-2.0.0.tgz", + "integrity": "sha512-5xkSLJQNRdQ7qpEnXj5FgLg33XKRHaTZKGP5qkvteOc/uGJX89MYCjPSgdNqJ1GYFGfdGAp0jvhihW8OMuXS3g==", + "license": "MIT", + "dependencies": { + "lodash.orderby": "^4.6.0", + "lodash.throttle": "^4.1.1" + } + }, + "node_modules/vue-count-to": { + "version": "1.0.13", + "resolved": "https://registry.npmmirror.com/vue-count-to/-/vue-count-to-1.0.13.tgz", + "integrity": "sha512-6R4OVBVNtQTlcbXu6SJ8ENR35M2/CdWt3Jmv57jOUM+1ojiFmjVGvZPH8DfHpMDSA+ITs+EW5V6qthADxeyYOQ==", + "license": "MIT" + }, + "node_modules/vue-cropper": { + "version": "0.6.5", + "resolved": "https://registry.npmmirror.com/vue-cropper/-/vue-cropper-0.6.5.tgz", + "integrity": "sha512-lSvY6IpeA/Tv/iPZ/FOkMHVRBPSlm7t57nuHEZFBMRNOH8ElvfqVlnHGDOAMlvPhh9gHiddiQoASS+fY0MFX0g==", + "license": "ISC" + }, + "node_modules/vue-eslint-parser": { + "version": "7.11.0", + "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-7.11.0.tgz", + "integrity": "sha512-qh3VhDLeh773wjgNTl7ss0VejY9bMMa0GoDG2fQVyDzRFdiU3L7fw74tWZDHNQXdZqxO3EveQroa9ct39D2nqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "eslint-scope": "^5.1.1", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.2.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8.10" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5.0.0" + } + }, + "node_modules/vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmmirror.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-loader": { + "version": "15.11.1", + "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-15.11.1.tgz", + "integrity": "sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "peerDependencies": { + "css-loader": "*", + "webpack": "^3.0.0 || ^4.1.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "cache-loader": { + "optional": true + }, + "prettier": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/vue-loader-v16": { + "name": "vue-loader", + "version": "16.8.3", + "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-16.8.3.tgz", + "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "loader-utils": "^2.0.0" + }, + "peerDependencies": { + "webpack": "^4.1.0 || ^5.0.0-0" + } + }, + "node_modules/vue-loader-v16/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/vue-loader/node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/vue-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/vue-router": { + "version": "3.6.5", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-3.6.5.tgz", + "integrity": "sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==", + "license": "MIT" + }, + "node_modules/vue-seamless-scroll": { + "version": "1.1.23", + "resolved": "https://registry.npmmirror.com/vue-seamless-scroll/-/vue-seamless-scroll-1.1.23.tgz", + "integrity": "sha512-HBjUub8WwsKJzbFCrwKPDrZn4e+SSbkKgwWtjKtfLwesiFGwSsVxP44/Z6d3kpXy94qIFOiflJH6l0/9pj7SGA==", + "license": "ISC", + "dependencies": { + "comutils": "^1.1.9" + } + }, + "node_modules/vue-style-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmmirror.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz", + "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "node_modules/vue-style-loader/node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-style-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/vue-style-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.7.16", + "resolved": "https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue/node_modules/@vue/compiler-sfc": { + "version": "2.7.16", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz", + "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==", + "dependencies": { + "@babel/parser": "^7.23.5", + "postcss": "^8.4.14", + "source-map": "^0.6.1" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" + } + }, + "node_modules/vue/node_modules/postcss": { + "version": "8.5.10", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/vuex": { + "version": "3.6.2", + "resolved": "https://registry.npmmirror.com/vuex/-/vuex-3.6.2.tgz", + "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==", + "license": "MIT", + "peerDependencies": { + "vue": "^2.0.0" + } + }, + "node_modules/vuex-persistedstate": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/vuex-persistedstate/-/vuex-persistedstate-4.1.0.tgz", + "integrity": "sha512-3SkEj4NqwM69ikJdFVw6gObeB0NHyspRYMYkR/EbhR0hbvAKyR5gksVhtAfY1UYuWUOCCA0QNGwv9pOwdj+XUQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2", + "shvl": "^2.0.3" + }, + "peerDependencies": { + "vuex": "^3.0 || ^4.0.0-rc" + } + }, + "node_modules/vuex-persistedstate/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "optionalDependencies": { + "chokidar": "^3.4.1", + "watchpack-chokidar2": "^2.0.1" + } + }, + "node_modules/watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "license": "MIT", + "optional": true, + "dependencies": { + "chokidar": "^2.1.8" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "license": "ISC", + "optional": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "license": "MIT", + "optional": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "license": "MIT", + "optional": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/watchpack-chokidar2/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmmirror.com/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webpack": { + "version": "4.47.0", + "resolved": "https://registry.npmmirror.com/webpack/-/webpack-4.47.0.tgz", + "integrity": "sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.5.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + }, + "webpack-command": { + "optional": true + } + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "3.9.0", + "resolved": "https://registry.npmmirror.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.0.tgz", + "integrity": "sha512-Ob8amZfCm3rMB1ScjQVlbYYUEJyEjdEtQ92jqiFUYt5VkEeO2v5UMbv49P/gnmCZm3A6yaFQzCBvpZqN4MUsdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.19", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 6.14.4" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-bundle-analyzer/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/webpack-chain": { + "version": "6.5.1", + "resolved": "https://registry.npmmirror.com/webpack-chain/-/webpack-chain-6.5.1.tgz", + "integrity": "sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "deepmerge": "^1.5.2", + "javascript-stringify": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "3.7.3", + "resolved": "https://registry.npmmirror.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-server": { + "version": "3.11.3", + "resolved": "https://registry.npmmirror.com/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz", + "integrity": "sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-html-community": "0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.8", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "sockjs-client": "^1.5.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 6.11.5" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "license": "ISC", + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/webpack-dev-server/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/webpack-dev-server/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-dev-server/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-dev-server/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/webpack-dev-server/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmmirror.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-server/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/webpack-dev-server/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmmirror.com/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "license": "MIT", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/webpack/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "license": "MIT", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmmirror.com/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "license": "MIT", + "dependencies": { + "errno": "~0.1.7" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^0.5.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmmirror.com/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/yargs/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/yargs/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yorkie": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/yorkie/-/yorkie-2.0.0.tgz", + "integrity": "sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "execa": "^0.8.0", + "is-ci": "^1.0.10", + "normalize-path": "^1.0.0", + "strip-indent": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yorkie/node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/yorkie/node_modules/execa": { + "version": "0.8.0", + "resolved": "https://registry.npmmirror.com/execa/-/execa-0.8.0.tgz", + "integrity": "sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yorkie/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/yorkie/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/yorkie/node_modules/normalize-path": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-1.0.0.tgz", + "integrity": "sha512-7WyT0w8jhpDStXRq5836AMmihQwq2nrUVQrgjvUo/p/NZf9uy/MeJ246lBJVmWuYXMlJuG9BNZHF0hWjfTbQUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yorkie/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/odcs2.com/package.json b/odcs2.com/package.json new file mode 100644 index 0000000..332d3a7 --- /dev/null +++ b/odcs2.com/package.json @@ -0,0 +1,71 @@ +{ + "name": "f2cs2", + "description": "欢迎访问f2cs2,高爆开箱网,饰品交易站", + "version": "0.1.0", + "private": true, + "scripts": { + "serve": "vue-cli-service serve", + "build": "vue-cli-service build", + "lint": "vue-cli-service lint" + }, + "dependencies": { + "@babel/core": "^7.22.9", + "@babel/preset-env": "^7.22.9", + "animate.css": "^4.1.1", + "axios": "^1.4.0", + "core-js": "^3.6.5", + "element-ui": "^2.15.13", + "iscroll": "^5.2.0", + "js-cookie": "^3.0.5", + "qrcodejs2": "^0.0.2", + "sass-loader": "^8.0.2", + "socket.io": "^4.7.4", + "swiper": "^4.5.1", + "video.js": "^8.10.0", + "vue": "^2.6.11", + "vue-agile": "^2.0.0", + "vue-count-to": "^1.0.13", + "vue-cropper": "^0.6.4", + "vue-router": "^3.2.0", + "vue-seamless-scroll": "^1.1.23", + "vuex": "^3.4.0", + "vuex-persistedstate": "^4.1.0" + }, + "devDependencies": { + "@vue/cli-plugin-babel": "~4.5.19", + "@vue/cli-plugin-eslint": "~4.5.19", + "@vue/cli-plugin-router": "~4.5.19", + "@vue/cli-plugin-vuex": "~4.5.19", + "@vue/cli-service": "~4.5.19", + "babel-eslint": "^10.1.0", + "babel-plugin-transform-remove-console": "^6.9.4", + "eslint": "^6.7.2", + "eslint-plugin-vue": "^6.2.2", + "qrcode": "^1.5.3", + "sass": "^1.99.0", + "vue-template-compiler": "^2.6.11" + }, + "eslintConfig": { + "root": true, + "env": { + "node": true + }, + "extends": [ + "plugin:vue/essential", + "eslint:recommended" + ], + "parserOptions": { + "parser": "babel-eslint" + }, + "rules": { + "vue/no-unused-components": "off", + "no-unused-vars": "off", + "no-debugger": "off" + } + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not dead" + ] +} diff --git a/odcs2.com/public/1.jpg b/odcs2.com/public/1.jpg new file mode 100644 index 0000000..5294a33 Binary files /dev/null and b/odcs2.com/public/1.jpg differ diff --git a/odcs2.com/public/123.png b/odcs2.com/public/123.png new file mode 100644 index 0000000..5848656 Binary files /dev/null and b/odcs2.com/public/123.png differ diff --git a/odcs2.com/public/2.mp3 b/odcs2.com/public/2.mp3 new file mode 100644 index 0000000..0387c30 Binary files /dev/null and b/odcs2.com/public/2.mp3 differ diff --git a/odcs2.com/public/Group 1.png b/odcs2.com/public/Group 1.png new file mode 100644 index 0000000..828c336 Binary files /dev/null and b/odcs2.com/public/Group 1.png differ diff --git a/odcs2.com/public/Group 16.png b/odcs2.com/public/Group 16.png new file mode 100644 index 0000000..7d20bd0 Binary files /dev/null and b/odcs2.com/public/Group 16.png differ diff --git a/odcs2.com/public/Group 5.png b/odcs2.com/public/Group 5.png new file mode 100644 index 0000000..e2a9427 Binary files /dev/null and b/odcs2.com/public/Group 5.png differ diff --git a/odcs2.com/public/Group 6.png b/odcs2.com/public/Group 6.png new file mode 100644 index 0000000..41e3fcd Binary files /dev/null and b/odcs2.com/public/Group 6.png differ diff --git a/odcs2.com/public/and.png b/odcs2.com/public/and.png new file mode 100644 index 0000000..e797c88 Binary files /dev/null and b/odcs2.com/public/and.png differ diff --git a/odcs2.com/public/apple.png b/odcs2.com/public/apple.png new file mode 100644 index 0000000..7c16d37 Binary files /dev/null and b/odcs2.com/public/apple.png differ diff --git a/odcs2.com/public/config.json b/odcs2.com/public/config.json new file mode 100644 index 0000000..66e93f8 --- /dev/null +++ b/odcs2.com/public/config.json @@ -0,0 +1,8 @@ +{ + "customerServiceAddress":{ + "url":"http://kefu.odcs2.com/chat/pc", + "noCanClose":1, + "kefu_id":1, + "token":"d6fdece3a8e12c5605457bab3ad67afd" + } +} \ No newline at end of file diff --git a/odcs2.com/public/fail.png b/odcs2.com/public/fail.png new file mode 100644 index 0000000..6a0bc29 Binary files /dev/null and b/odcs2.com/public/fail.png differ diff --git a/odcs2.com/public/fail_2.png b/odcs2.com/public/fail_2.png new file mode 100644 index 0000000..7913e2d Binary files /dev/null and b/odcs2.com/public/fail_2.png differ diff --git a/odcs2.com/public/favicon.ico b/odcs2.com/public/favicon.ico new file mode 100644 index 0000000..5e3a44d Binary files /dev/null and b/odcs2.com/public/favicon.ico differ diff --git a/odcs2.com/public/hongbao.png b/odcs2.com/public/hongbao.png new file mode 100644 index 0000000..92f82f6 Binary files /dev/null and b/odcs2.com/public/hongbao.png differ diff --git a/odcs2.com/public/index.html b/odcs2.com/public/index.html new file mode 100644 index 0000000..bed4206 --- /dev/null +++ b/odcs2.com/public/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + +
+ + + diff --git a/odcs2.com/public/pk全套音_01.mp3 b/odcs2.com/public/pk全套音_01.mp3 new file mode 100644 index 0000000..bebc642 Binary files /dev/null and b/odcs2.com/public/pk全套音_01.mp3 differ diff --git a/odcs2.com/public/win.png b/odcs2.com/public/win.png new file mode 100644 index 0000000..eada50b Binary files /dev/null and b/odcs2.com/public/win.png differ diff --git a/odcs2.com/public/you.jpg b/odcs2.com/public/you.jpg new file mode 100644 index 0000000..c9560a2 Binary files /dev/null and b/odcs2.com/public/you.jpg differ diff --git a/odcs2.com/public/zuo.jpg b/odcs2.com/public/zuo.jpg new file mode 100644 index 0000000..43c45f6 Binary files /dev/null and b/odcs2.com/public/zuo.jpg differ diff --git a/odcs2.com/public/点击音_02.mp3 b/odcs2.com/public/点击音_02.mp3 new file mode 100644 index 0000000..1b0c239 Binary files /dev/null and b/odcs2.com/public/点击音_02.mp3 differ diff --git a/odcs2.com/public/胜利音效_01.mp3 b/odcs2.com/public/胜利音效_01.mp3 new file mode 100644 index 0000000..33c06aa Binary files /dev/null and b/odcs2.com/public/胜利音效_01.mp3 differ diff --git a/odcs2.com/src/App.vue b/odcs2.com/src/App.vue new file mode 100644 index 0000000..67ac99c --- /dev/null +++ b/odcs2.com/src/App.vue @@ -0,0 +1,334 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/api/basurl.js b/odcs2.com/src/api/basurl.js new file mode 100644 index 0000000..ea2b04a --- /dev/null +++ b/odcs2.com/src/api/basurl.js @@ -0,0 +1,9 @@ +// const DEV_BASE_URL = 'ws://localhost:8082' +// const DEV_BASE_URL = "ws://123.60.170.219:8082"; + + +//const DEV_BASE_URL = "ws://1.92.85.51:8080"; +const DEV_BASE_URL = "ws://127.0.0.1:8081"; +export default { + BASE_API: DEV_BASE_URL, +}; diff --git a/odcs2.com/src/api/cookie.js b/odcs2.com/src/api/cookie.js new file mode 100644 index 0000000..d153bf5 --- /dev/null +++ b/odcs2.com/src/api/cookie.js @@ -0,0 +1,13 @@ +import Cookies from 'js-cookie' +const TokenKey = 'token' +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token) { + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} \ No newline at end of file diff --git a/odcs2.com/src/api/index.js b/odcs2.com/src/api/index.js new file mode 100644 index 0000000..2c28c58 --- /dev/null +++ b/odcs2.com/src/api/index.js @@ -0,0 +1,975 @@ +import axios from "axios"; +import { getToken, removeToken } from "./cookie"; +// import { Loading } from 'element-ui'; +import { Message } from "element-ui"; +import store from "@/vuex/store"; +import Vue from "vue"; +let vue = new Vue(); +// import Vue from 'vue' +// import App from '../App.vue' +// import router from '@/router' +// var vm = new Vue({ +// router, +// render: h => h(App) +// }).$mount('#app') + +const reqAxios = axios.create({ + // baseURL: "http://192.168.1.3:8081", + // baseURL: "http://192.168.1.3:8080", + // baseURL: "http://123.60.170.219:8082", + // baseURL: "http://localhost:8082", + //baseURL: "http://192.168.1.39:8082", + + +//baseURL:'http://1.92.85.51:8080', +// baseURL:'http://3.92.67.237:8080', +baseURL:'http://127.0.0.1:8081/', + + // baseURL: "http://beecsgo.com/prod-api/", + // baseURL: "http://www.f2cs2.com/prod-api/", + // baseURL: 'http://127.0.0.1/prod-api', + // baseURL: "http://101.200.132.56/prod-api/", + + timeout: 60000, // 请求超时时间 这里的意思是当请求时间超过5秒还未取得结果时 提示用户请求超时 +}); +// 拦截请求(拦截器) +const timer = new Map(); +reqAxios.interceptors.request.use( + function (request) { + if (request.url !== "/api/luckyUpgrade/getUpgradeRecord") { + vue.$toast("加载中"); + } + // 处理请求公共数据,如添加请求头令牌... + let token = getToken(); + //请求头设置 + request.headers = { + "content-type": "application/json;", + userType: "user", + Authorization: token, + }; + // // console.log('request',request); + if (request.url == "/api/luckyUpgrade/upgrade") { + return throttle(request, 3500); + } + if ( + request.url == "/api/luckyUpgrade/getUpgradeRecord" || + request.url == "/api/roll/getRollList" || + request.url == "/api/userAmountRecords/blendErcashRank" || + request.url == "/api/boxRecords/historyByCondition" || + request.url == "/api/userAmountRecords/deliveryRecords" || + request.url == "/api/userAmountRecords/userAccountDetail" || + request.url == "/api/userPackSack/getPackSack"|| + request.url == "/api/roll/getRollPlayers" + ) { + return request; + } + return throttle(request, 500); + // return request; + }, + function (error) { + console.log("requesterror", error); + return Promise.reject(error); + } +); +function throttle(request, time) { + // console.log(request); + let lastTime = timer.get(request.url); + let nowTime = new Date().getTime(); + if (request.method == "get") { + return request; + } else { + if (lastTime && nowTime - lastTime < time) { + console.log("overRequest", request); + return Promise.reject("请勿频繁操作"); + // return false + } else { + timer.set(request.url, nowTime); + return request; + } + } +} + +//响应拦截器 +reqAxios.interceptors.response.use( + (res) => { + // console.log('response',res) + vue.$hide(); + if (res.data.code == 401) { + Message({ + message: "登录已过期,请重新登陆", + type: "warning", + customClass: "log_warning", + }); + removeToken(); + store.commit("LOGIN_IS_SHOW", true); + store.commit("ADVERT_IS_SHOW", true); + return; + } else if (res.status == 200) { + return res; + } else if (res.data.code == 200) { + return res; + } else { + Message({ + message: res.data.msg, + type: "warning", + customClass: "log_warning", + }); + return res; + } + }, + (err) => { + console.log("err", err); + if (err == "请勿频繁操作") { + Message({ + message: "您的操作太频繁,请1秒后再试", + type: "warning", + customClass: "log_warning", + }); + } + if (err.response.status == 500) { + Message({ + message: err.response.data.message, + type: "warning", + customClass: "log_warning", + }); + } + if (err.response.status == 401) { + removeToken(); + store.commit("LOGIN_IS_SHOW", true); + store.commit("ADVERT_IS_SHOW", true); + Message({ + message: "登录过期,请重新登陆", + type: "warning", + customClass: "log_warning", + }); + } + } +); + +//登录 +export const getLogin = (data) => { + return reqAxios.post("/api/login", data); +}; +//退出登录 +export const logOut = () => { + return reqAxios.post("/api/logout"); +}; +//注册 +export const getRegister = (data) => { + return reqAxios({ + method: "post", + url: "/api/register", + data, + }); +}; + +// 实名认证 +export const realNameAuthentication = (data) => { + return reqAxios({ + url: `/api/user/realNameAuthentication`, + method: "post", + data: data, + }); +}; + +//获取下级人数 +export const getUserSubusser = () => { + return reqAxios({ + url: "/api/user/rechargeCount", + method: "get", + }); +}; +export const getUserSubRechange = () => { + return reqAxios({ + url: "/api/user/getOrdersAmounts", + method: "get", + }); +}; + +// 判断是否充值 +export const IsRecharge = () => { + return reqAxios.get("/api/user/getUserIsRecharge"); +}; + +// 刷新用户金币 +export const updateUserData = () => { + return reqAxios.post("/api/user/updateUserData"); +}; + +// 获取用户信息 +export const getUserInfo = () => { + return reqAxios.get("/api/getInfo"); +}; + +//roll房列表 +export const getRollList = (data) => { +/* return reqAxios.get("/api/roll/getRollList",data); */ + return reqAxios({ + url: `/api/roll/getRollList`, + method: "get", + params: data, + }); +}; +//获取roll房信息 +export const getRollDetial = (rollId) => { + return reqAxios({ + url: `/api/roll/getRollDetails/${rollId}`, + method: "get", + }); +}; + +//加入roll房 +export const joinRoll = (rollId, rollPassword = "") => { + return reqAxios({ + url: `/api/roll/joinRoll`, + method: "post", + data: { + rollId, + rollPassword, + }, + }); +}; + +// 获取用户参与Roll房 +// export const getMyPartRollList = (data) => { +// return reqAxios({ +// url: `/api/roll/getRollList`, +// method: "get", +// params: data, +// }); +// }; + +// 个人明细 +export const getUserLogList = (data) => { + return reqAxios({ + url: `/api/userAmountRecords/userAccountDetail`, + method: "post", + data, + }); +}; +//可充值钱数 +export const getRechargelist = (data) => { + return reqAxios({ + url: `/api/recharge/list`, + method: "get", + params: data, + }); +}; +//充值记录 +export const orderList = () => { + return reqAxios({ + url: `/admin/order/list`, + method: "get", + }); +}; +// +export const jiuJiaPay = (data) => { + return reqAxios({ + url: `/api/jiuJiaPay/createPay`, + method: "post", + data: data, + }); +}; +// +export const kamiPay = (data) => { + return reqAxios({ + url: `/api/recharge/cardPay`, + method: "post", + params: data, + }); +}; + +// 签到 +export const attendance = () => { + return reqAxios({ + url: `/api/attendanceRecord/attendance`, + method: "get", + }); +}; +// 七日签到 +export const attendance7day = () => { + return reqAxios({ + url: `/api/attendanceRecord/sevenAttendance`, + method: "get", + }); +}; + +//当日排行榜 +export const creditsRank = (data) => { + return reqAxios({ + url: `api/userCreditsRecords/creditsRank/2/1/5`, + method: "get", + params: data, + }); +}; +// 获取下级流水列表 +export const getUserxList = () => { + return reqAxios({ + url: `/api/user/getLsjlList`, + method: "get", + }); +}; + +// 推广 +export const userCreditsRecords = (page, size) => { + return reqAxios({ + url: `api/userAmountRecords/pWelfareRecords/${page}/${size}`, + method: "get", + }); +}; + +//站内信-获取站内信列表 + +export const getMessageList = (data) => { + return reqAxios({ + url: `/api/message/getMessageList`, + method: "get", + params: data, + }); +}; + +//任务列表 +export const gettaskList = () => { + return reqAxios({ + url: `/api/ttTask/taskOfme`, + method: "get", + }); +}; +//领取任务 +export const claimTasks = (tid) => { + return reqAxios({ + url: `/api/ttTask/getAward/${tid}`, + method: "get", + }); +}; + +// 头像列表 +export const getavatar = () => { + return reqAxios({ + url: "/api/userAvatar/list", + method: "get", + }); +}; +//站内信-查看内容 + +export const getMessageView = (data) => { + return reqAxios({ + url: `/api/message/view?id=${data}`, + method: "get", + }); +}; +//站内信-批量操作 +export const getMessageOperation = (data) => { + return reqAxios({ + url: `/api/message/batchOperation?ids=${data.ids}&status=${data.status}`, + method: "post", + // data: data, + }); +}; + +//首页顶部轮播 +export const getWeapon = (options) => { + return reqAxios({ + url: `/api/boxRecords/historyByCondition`, + method: "POST", + data: options, + }); +}; + +//首页中间轮播 +export const getHomeBanner = (options) => { + return reqAxios.get("/api/websiteSetup/getBannerList", options); +}; +//获取广告 +export const getAdvertisement = () => { + return reqAxios.get("/api/advertisement/1"); +}; + +//开箱 根据宝箱ID获取宝箱数据 + +//开箱 历史掉落 +export const getBoxhistory = (options) => { + return reqAxios({ + url: `/api/boxRecords/historyByCondition`, + method: "POST", + data: options, + }); +}; + +//开箱接口 +export const openBox = (options) => { + return reqAxios({ + method: "post", + url: `/api/bindbox/openBox`, + params: options, + }); +}; + +//开箱,武器分解 // 背包分解 +export const decompose = (options) => { + return reqAxios({ + method: "post", + url: `/api/userPackSack/decompose`, + data: options, + }); +}; +// 分解记录 + +export const userdecompseList = (data) => { + return reqAxios({ + method: "post", + url: `/api/userPackSack/decomposeLog`, + data, + }); +}; + +// 提取饰品 +export const delivery = (options) => { + return reqAxios({ + method: "post", + url: `/api/userPackSack/delivery`, + data: options, + }); +}; + +// 转赠饰品 +export const transfer = (options) => { + return reqAxios({ + method: "post", + url: `/api/userPackSack/transfer`, + data: options, + }); +}; + +//全部对战,查询所有对战列表 +export const getFightList = () => { + return reqAxios({ + url: "/api/fight/getFightList", + method: "get", + data: { + page: 1, + size: 10, + statusList: [1, 0], + }, + }); +}; + +//创建对战 +export const createFight = (data) => { + return reqAxios({ + method: "post", + url: "/api/fight/createFight", + data, + }); +}; +//玩家准备 +/* /api/fight/seatrReady */ +export const ApiSeatReady = (fightId) => { + return reqAxios({ + url: `/api/fight/seatrReady?fightId=${fightId}`, + method: "get", + }); +}; +//房主开始游戏 +export const Playthegame = (fightId) => { + return reqAxios({ + url: `/api/fight/fightBegin?fightId=${fightId}`, + method: "get", + }); +}; +//游戏结束 +/* /api/fight/fightEnd */ +export const Gameover = (fightId) => { + return reqAxios({ + method: "get", + url: `/api/fight/fightEnd?fightId=${fightId}`, + }); +}; + +//保存对战回合 +export const saveFightBoutData = (data) => { + return reqAxios({ + method: "post", + url: "/api/fight/saveFightBoutData", + data: data, + }); +}; +//获取对战回合 +export const getFightBoutNum = (fightId) => { + return reqAxios({ + method: "post", + url: `/api/fight/getFightBoutNum/${fightId}`, + }); +}; +//获取对战数据 +export const getFightData = (fightId) => { + return reqAxios({ + url: `/api/fight/getFightData/${fightId}`, + method: "get", + }); +}; +//获取对战结果 +export const getFightResult = (fightId) => { + return reqAxios({ + url: `/api/fight/getFightResult/${fightId}`, + method: "get", + }); +}; +//查询对战宝箱列表 +export const getFightBoxList = (data) => { + return reqAxios({ + url: `/api/fight/getFightBoxList`, + method: "get", + params: data, + }); +}; +//加入对战的验证 +export const joinFightverify = (fightId) => { + return reqAxios({ + url: `/api/fight/joinFightRoom`, + method: "post", + params: fightId, + }); +}; + +//加入对战 +export const joinFight = (data) => { + return reqAxios({ + url: `/api/fight/joinFight`, + method: "post", + params: data, + }); +}; +//对战宝箱饰品 +export const boxOrnament = (id) => { + return reqAxios.get( + `/api/fightAgainstBData/getFightAgainstBoxOrnamentsById?bid=${id}` + ); +}; + +//我的对战 +export const getMyPartFightList = (data) => { + return reqAxios({ + url: `/api/fight/getMyPartFightList`, + method: "get", + params: data, + }); +}; + +//获取每一回合的对战结果 +export const getpkrealdetial = (data) => { + return reqAxios({ + url: "/api/fight/getFightRecord", + method: "get", + params: data, + }); +}; + +//追梦 获取饰品列表 + +export const deramlist = (data) => { + return reqAxios({ + method: "get", + url: `/api/luckyUpgrade/getOrnamentsList`, + params: data, + }); +}; + +//追梦 +export const deramrecord = (data) => { + return reqAxios({ + method: "post", + url: `/api/luckyUpgrade/getUpgradeRecord`, + data, + }); +}; + +//追梦 +export const deramupgrade = (data) => { + return reqAxios({ + method: "post", + url: `/api/luckyUpgrade/upgrade`, + data: data, + }); +}; + +//用户签到post +export const getUserSign = (data) => { + return reqAxios({ + method: "post", + url: `/api/user/userSigin`, + data, + }); +}; + +//用户签到查询 +export const getUserSigndata = (data) => { + return reqAxios.get(`/api/user/userSigninData?signDate=${data}`); +}; + +//发送验证码 1注册 2登录 3修改手机号 4忘记密码 +export const getcode = (data) => { + return reqAxios.post(`/api/sms/getVerifyCode`, data); + // return reqAxios.post(`/sms/getVerifyCode?phonenumber=${data.phonenumber}&type=${data.type}`) +}; + +//修改密码 +export const changePass = (data) => { + return reqAxios.post("/api/user/forgetPassword", data); +}; + +//充值查询 +export const getUserchong = () => { + return reqAxios.get(`/api/ttCoinItem/getCoinItemList`); +}; + +export const Recgargezfb = (data) => { + return reqAxios({ + method: "post", + url: `/api/jiujiaOrder/createOrder`, + data, + }); +}; +export const Recgargewx = (data) => { + return reqAxios({ + method: "post", + url: `/api/tianxinOrder/createOrder`, + data, + }); +}; + +//商城列表 +export const shoplist = (options) => { + return reqAxios({ + method: "get", + url: `/api/shopping/list`, + params: options, + }); +}; +//商城查询条件 +export const shopcondition = (value) => { + return reqAxios({ + url: `/api/shopping/getShoppingQuery?value=` + value, + method: "get", + }); +}; + +// 商城物品兑换 +export const shopconvert = (options) => { + return reqAxios({ + method: "post", + url: `/api/shopping/exchange`, + params: options, + }); +}; +// 背包 +export const getPackSack = (data) => { + return reqAxios({ + url: `/api/userPackSack/getPackSack`, + method: "post", + data, + }); +}; + +// 汰换 +export const updateReplacement = (data) => { + return reqAxios({ + method: "post", + url: `/api/skinsservice/replacementRecord/synthesizeItems`, + data, + }); +}; +// 收支明细 +export const getdetail = (pageSize = 15, pageNum = 1) => { + return reqAxios.get( + `/api/user/getBeanChangeRecords?pageSize=${pageSize}&pageNum=${pageNum}` + ); +}; + +// 绑定交易链接 +export const giveMoneyAPI = (options) => { + return reqAxios.post(`/api/user/bindSteamLink?steamLink=${options}`); +}; +// 更新用户信息 +export const updateUserDetails = (data) => { + return reqAxios({ + method: "post", + url: "/api/user/updateUserDetails", + data, + }); +}; +// 更新用户头像信息 +export const updataavatar = (data) => { + return reqAxios({ + method: "post", + url: "/api/user/profilePictureUpload", + data: data, + }); +}; +// 绑定推广链接 +export const giveUserAPI = (options) => { + return reqAxios({ + method: "post", + url: `/api/user/bindInvitationCode?invitationCode=${options}`, + }); +}; + +// 提货账单 +export const getExtractBillAPI = (data) => { + return reqAxios.get( + `/api/user/getDeliveryRecordList?status=${data}&pageSize=1000&pageNum=1` + ); +}; + +//文章 +export const getArticle = (type) => { + return reqAxios.get(`/api/websiteSetup/getContentByType/?alias=${type} `); +}; +//查询宝箱分类 +export const boxesType = () => { + return reqAxios.get("/api/bindbox/getBoxList"); +}; + +///api/battleRoyale/joinRoom +export const getbigkillroom = (rankId, roomid, copies) => { + return reqAxios.get( + `/api/battleRoyale/joinRoom?rankId=${rankId}&roomId=${roomid}&copies=${copies}` + ); +}; +export const getbigkilHistroy = (rankId) => { + return reqAxios.get(`/api/battleRoyale/getHistoricalRecord?rankId=${rankId}`); +}; + +//卡密 /prod-api/api/ttCoinItem/checkKM/{key} +export const getkami = (id) => { + return reqAxios.post(`/api/ttCoinItem/checkKM/${id}`); +}; + +// 查询用户金币 /api/user/getUserRealTimeBean + +export const getuserbean = () => { + return reqAxios.get(`/api/user/getUserRealTimeBean`); +}; + +// type 1 昨天 2 今天 + +export const getdiaoluo = () => { + return reqAxios.get(`/api/user/propRankOfDay/${1}/1`); +}; + +// /api/user/propRankOfDay + +////api/websocket/skins +//协议 + +export const getagreement = (id) => { + return reqAxios.get(`/admin/content/${id}`); +}; +//宝箱详情 +export const simpleBoxDetail = (boxId) => { + // return reqAxios.get(`/api/fight/simpleBoxDetail/boxId=${boxId}`); + return reqAxios({ + method: "get", + url: "/api/fight/simpleBoxDetail", + params: { boxId }, + // data:{boxId} + }); +}; +//观战获取房间数据 +export const audience = (id) => { + return reqAxios.get(`/api/fight/audience?fightId=${id}`); +}; +//极速竞技退出房间/api/fight/fightRoomExit +export const fightRoomExit = (data) => { + return reqAxios({ + method: "post", + url: "/api/fight/fightRoomExit", + params: data, + }); +}; +//我参与的 +export const fightOnMyOwn = (data) => { + return reqAxios({ + method: "post", + url: "/api/fight/fightOnMyOwn", + data, + }); +}; +//支付接口 +export const ApiAddTrans = (data) => { + return reqAxios({ + method: "post", + url: "/api/zyZFB/ApiAddTrans", + params: data, + }); +}; +//国富汇通 +export const ApiAdddgfht = (data) => { + return reqAxios({ + method: "post", + url: "/api/gfht/pay", + data, + }); +}; + +//排行榜 +export const blendErcashRank = (data) => { + return reqAxios({ + method: "post", + url: "/api/userAmountRecords/blendErcashRank", + data, + }); +}; +//获取团队信息 +export const teamUsers = (data) => { + return reqAxios({ + method: "post", + url: "/api/userAmountRecords/teamUsers", + data, + }); +}; +export const changePW = (data) => { + return reqAxios({ + method: "post", + url: "/api/user/changePW", + data, + }); +}; +//获取个人提货记录 +export const deliveryRecords = (data) => { + return reqAxios({ + method: "post", + url: "/api/userAmountRecords/deliveryRecords", + data, + }); +}; +//获取个人充值明细 +export const rechargeRecords = (data) => { + return reqAxios({ + method: "get", + url: "/api/order/list", + params: data, + }); +}; +//历史详情 +export const fightDetail = (data) => { + return reqAxios({ + method: "post", + url: "/api/fight/fightDetail", + data, + }); +}; +//roll房参与人员 +export const getRollPlayers = (data) => { + return reqAxios({ + method: "post", + url: "/api/roll/getRollPlayers", + data, + }); +}; +//roll房开奖详情 +export const getRollOpenPrize = (data) => { + return reqAxios({ + method: "post", + url: "/api/roll/getRollOpenPrize", + data, + }); +}; +//roll房奖池 +export const getRollPrizePool = (data) => { + return reqAxios({ + method: "post", + url: "/api/roll/getRollPrizePool", + data, + }); +}; +export const earlierHistory = (data) => { + return reqAxios({ + method: "post", + url: "/api/fight/earlierHistory", + data, + }); +}; +export const endRoll = (data) => { + return reqAxios({ + method: "get", + url: `/api/roll/endRoll/${data}`, + }); +}; +export const packSackGlobalData = () => { + return reqAxios({ + method: "get", + url: `/api/userPackSack/packSackGlobalData`, + }); +}; + +export const bindBoss = (data) => { + return reqAxios({ + method: "post", + url: `/api/user/bindBoss`, + data, + }); +}; +export const receiveRedPacket = (data) => { + return reqAxios({ + method: "get", + url: `/api/bonus/receiveRedPacket/${data}`, + }); +}; +export const mayi = (data) => { + return reqAxios({ + method: "post", + url: `/api/mayi/ApiAddTrans`, + data, + }); +}; +export const getBoxList = (data) => { + return reqAxios({ + method: "get", + url: `/api/bindbox/getBoxList`, + params:data, + }); +}; +export const getBoxTypeList = (data) => { + return reqAxios({ + method: "get", + url: `/api/box/getBoxTypeList/${data.page}/${data.size}`, + }); +}; +/* /api/bindbox/{boxId} */ +export const getDetail = (boxId) => { + return reqAxios({ + method: "get", + url: `/api/bindbox/${boxId}`, + }); +}; +//vip等级 +export const welfareList = () => { + return reqAxios({ + method: "get", + url: `/api/welfare/getWelfareList`, + }); +} +///api/userAmountRecords/rechargeRanking 获取充值排行榜 +export const rechargeRanking = () => { + return reqAxios({ + method: "get", + url: `/api/userAmountRecords/rechargeRanking `, + }); +} +///api/userAmountRecords/rechargeRankingRewardsIntroduction +export const rechargeRankingRewardsIntroduction = () => { + return reqAxios({ + method: "get", + url: `/api/userAmountRecords/rechargeRankingRewardsIntroduction`, + }); +} + + +// /api/user/vipLevelIntroduction +export const getviplist = () => { + return reqAxios({ + method: "get", + url: `/api/user/vipLevelIntroduction`, + }); +} \ No newline at end of file diff --git a/odcs2.com/src/api/scoket.js b/odcs2.com/src/api/scoket.js new file mode 100644 index 0000000..efaa02e --- /dev/null +++ b/odcs2.com/src/api/scoket.js @@ -0,0 +1,37 @@ +// websocket.js + +const WebSocketPlugin = { + install(Vue, options) { + + + const socket = new WebSocket(options.url); + + Vue.prototype.$socket = socket; + + socket.addEventListener('open', () => { + console.log('WebSocket连接成功'); + }); + + socket.addEventListener('message', (event) => { + console.log('Received message:', event.data); + Vue.prototype.$emit('websocket-message', event.data); + }); + + socket.addEventListener('close', (event) => { + console.log('WebSocket连接关闭', event); + Vue.prototype.$emit('websocket-close', event); + }); + + socket.addEventListener('error', (error) => { + console.error('WebSocket连接错误', error); + Vue.prototype.$emit('websocket-error', error); + }); + socket.close(); + this.$sockets.onclose = () => { + console.log('WebSocket 连接已关闭'); + }; + + }, +}; + +export default WebSocketPlugin; \ No newline at end of file diff --git a/odcs2.com/src/assets.zip b/odcs2.com/src/assets.zip new file mode 100644 index 0000000..a4983b8 Binary files /dev/null and b/odcs2.com/src/assets.zip differ diff --git a/odcs2.com/src/assets/01.mp3 b/odcs2.com/src/assets/01.mp3 new file mode 100644 index 0000000..33c06aa Binary files /dev/null and b/odcs2.com/src/assets/01.mp3 differ diff --git a/odcs2.com/src/assets/02.mp3 b/odcs2.com/src/assets/02.mp3 new file mode 100644 index 0000000..1b0c239 Binary files /dev/null and b/odcs2.com/src/assets/02.mp3 differ diff --git a/odcs2.com/src/assets/1.mp3 b/odcs2.com/src/assets/1.mp3 new file mode 100644 index 0000000..bebc642 Binary files /dev/null and b/odcs2.com/src/assets/1.mp3 differ diff --git a/odcs2.com/src/assets/1.png b/odcs2.com/src/assets/1.png new file mode 100644 index 0000000..ec4ccaf Binary files /dev/null and b/odcs2.com/src/assets/1.png differ diff --git a/odcs2.com/src/assets/2.mp3 b/odcs2.com/src/assets/2.mp3 new file mode 100644 index 0000000..0387c30 Binary files /dev/null and b/odcs2.com/src/assets/2.mp3 differ diff --git a/odcs2.com/src/assets/3.mp3 b/odcs2.com/src/assets/3.mp3 new file mode 100644 index 0000000..9147496 Binary files /dev/null and b/odcs2.com/src/assets/3.mp3 differ diff --git a/odcs2.com/src/assets/Ranking.png b/odcs2.com/src/assets/Ranking.png new file mode 100644 index 0000000..862148b Binary files /dev/null and b/odcs2.com/src/assets/Ranking.png differ diff --git a/odcs2.com/src/assets/aaa.png b/odcs2.com/src/assets/aaa.png new file mode 100644 index 0000000..322357a Binary files /dev/null and b/odcs2.com/src/assets/aaa.png differ diff --git a/odcs2.com/src/assets/add.png b/odcs2.com/src/assets/add.png new file mode 100644 index 0000000..5a2a2d1 Binary files /dev/null and b/odcs2.com/src/assets/add.png differ diff --git a/odcs2.com/src/assets/add2.png b/odcs2.com/src/assets/add2.png new file mode 100644 index 0000000..897e135 Binary files /dev/null and b/odcs2.com/src/assets/add2.png differ diff --git a/odcs2.com/src/assets/beibao.png b/odcs2.com/src/assets/beibao.png new file mode 100644 index 0000000..b124d23 Binary files /dev/null and b/odcs2.com/src/assets/beibao.png differ diff --git a/odcs2.com/src/assets/beibao_sel2.png b/odcs2.com/src/assets/beibao_sel2.png new file mode 100644 index 0000000..d75c46c Binary files /dev/null and b/odcs2.com/src/assets/beibao_sel2.png differ diff --git a/odcs2.com/src/assets/bg-ByBHE7gk.png b/odcs2.com/src/assets/bg-ByBHE7gk.png new file mode 100644 index 0000000..06b8bdd Binary files /dev/null and b/odcs2.com/src/assets/bg-ByBHE7gk.png differ diff --git a/odcs2.com/src/assets/bottom-active.png b/odcs2.com/src/assets/bottom-active.png new file mode 100644 index 0000000..346744c Binary files /dev/null and b/odcs2.com/src/assets/bottom-active.png differ diff --git a/odcs2.com/src/assets/bottom-bg.png b/odcs2.com/src/assets/bottom-bg.png new file mode 100644 index 0000000..f2727b4 Binary files /dev/null and b/odcs2.com/src/assets/bottom-bg.png differ diff --git a/odcs2.com/src/assets/bottom/active-bg.png b/odcs2.com/src/assets/bottom/active-bg.png new file mode 100644 index 0000000..c985de7 Binary files /dev/null and b/odcs2.com/src/assets/bottom/active-bg.png differ diff --git a/odcs2.com/src/assets/bottom/beibao.png b/odcs2.com/src/assets/bottom/beibao.png new file mode 100644 index 0000000..7571623 Binary files /dev/null and b/odcs2.com/src/assets/bottom/beibao.png differ diff --git a/odcs2.com/src/assets/bottom/chongzhi.png b/odcs2.com/src/assets/bottom/chongzhi.png new file mode 100644 index 0000000..1aa2675 Binary files /dev/null and b/odcs2.com/src/assets/bottom/chongzhi.png differ diff --git a/odcs2.com/src/assets/bottom/shangcheng.png b/odcs2.com/src/assets/bottom/shangcheng.png new file mode 100644 index 0000000..4f2112a Binary files /dev/null and b/odcs2.com/src/assets/bottom/shangcheng.png differ diff --git a/odcs2.com/src/assets/bottom/shouye.png b/odcs2.com/src/assets/bottom/shouye.png new file mode 100644 index 0000000..68e5583 Binary files /dev/null and b/odcs2.com/src/assets/bottom/shouye.png differ diff --git a/odcs2.com/src/assets/bottom/wode.png b/odcs2.com/src/assets/bottom/wode.png new file mode 100644 index 0000000..b96d3ac Binary files /dev/null and b/odcs2.com/src/assets/bottom/wode.png differ diff --git a/odcs2.com/src/assets/box_moeny_bg.png b/odcs2.com/src/assets/box_moeny_bg.png new file mode 100644 index 0000000..f131f50 Binary files /dev/null and b/odcs2.com/src/assets/box_moeny_bg.png differ diff --git a/odcs2.com/src/assets/box_open.png b/odcs2.com/src/assets/box_open.png new file mode 100644 index 0000000..29905d5 Binary files /dev/null and b/odcs2.com/src/assets/box_open.png differ diff --git a/odcs2.com/src/assets/btn_bg.png b/odcs2.com/src/assets/btn_bg.png new file mode 100644 index 0000000..016d375 Binary files /dev/null and b/odcs2.com/src/assets/btn_bg.png differ diff --git a/odcs2.com/src/assets/bushiyingjia.png b/odcs2.com/src/assets/bushiyingjia.png new file mode 100644 index 0000000..1f030e6 Binary files /dev/null and b/odcs2.com/src/assets/bushiyingjia.png differ diff --git a/odcs2.com/src/assets/c9bd5d06-2d1b-4e6f-a5e2-a3cdd00872ac.71ee8.png b/odcs2.com/src/assets/c9bd5d06-2d1b-4e6f-a5e2-a3cdd00872ac.71ee8.png new file mode 100644 index 0000000..2b66a87 Binary files /dev/null and b/odcs2.com/src/assets/c9bd5d06-2d1b-4e6f-a5e2-a3cdd00872ac.71ee8.png differ diff --git a/odcs2.com/src/assets/createRoom.png b/odcs2.com/src/assets/createRoom.png new file mode 100644 index 0000000..982ab74 Binary files /dev/null and b/odcs2.com/src/assets/createRoom.png differ diff --git a/odcs2.com/src/assets/dengdai.png b/odcs2.com/src/assets/dengdai.png new file mode 100644 index 0000000..609a801 Binary files /dev/null and b/odcs2.com/src/assets/dengdai.png differ diff --git a/odcs2.com/src/assets/dream-bg.png b/odcs2.com/src/assets/dream-bg.png new file mode 100644 index 0000000..7e4b4f5 Binary files /dev/null and b/odcs2.com/src/assets/dream-bg.png differ diff --git a/odcs2.com/src/assets/dream-btn-bg-active.png b/odcs2.com/src/assets/dream-btn-bg-active.png new file mode 100644 index 0000000..3e97861 Binary files /dev/null and b/odcs2.com/src/assets/dream-btn-bg-active.png differ diff --git a/odcs2.com/src/assets/dream-btn-bg.png b/odcs2.com/src/assets/dream-btn-bg.png new file mode 100644 index 0000000..08361b0 Binary files /dev/null and b/odcs2.com/src/assets/dream-btn-bg.png differ diff --git a/odcs2.com/src/assets/dream.mp4 b/odcs2.com/src/assets/dream.mp4 new file mode 100644 index 0000000..7cb8101 Binary files /dev/null and b/odcs2.com/src/assets/dream.mp4 differ diff --git a/odcs2.com/src/assets/font/YouSheBiaoTiHei-2.ttf b/odcs2.com/src/assets/font/YouSheBiaoTiHei-2.ttf new file mode 100644 index 0000000..3729151 Binary files /dev/null and b/odcs2.com/src/assets/font/YouSheBiaoTiHei-2.ttf differ diff --git a/odcs2.com/src/assets/font/demo.css b/odcs2.com/src/assets/font/demo.css new file mode 100644 index 0000000..a67054a --- /dev/null +++ b/odcs2.com/src/assets/font/demo.css @@ -0,0 +1,539 @@ +/* Logo 字体 */ +@font-face { + font-family: "iconfont logo"; + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); +} + +.logo { + font-family: "iconfont logo"; + font-size: 160px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* tabs */ +.nav-tabs { + position: relative; +} + +.nav-tabs .nav-more { + position: absolute; + right: 0; + bottom: 0; + height: 42px; + line-height: 42px; + color: #666; +} + +#tabs { + border-bottom: 1px solid #eee; +} + +#tabs li { + cursor: pointer; + width: 100px; + height: 40px; + line-height: 40px; + text-align: center; + font-size: 16px; + border-bottom: 2px solid transparent; + position: relative; + z-index: 1; + margin-bottom: -1px; + color: #666; +} + + +#tabs .active { + border-bottom-color: #f00; + color: #222; +} + +.tab-container .content { + display: none; +} + +/* 页面布局 */ +.main { + padding: 30px 100px; + width: 960px; + margin: 0 auto; +} + +.main .logo { + color: #333; + text-align: left; + margin-bottom: 30px; + line-height: 1; + height: 110px; + margin-top: -50px; + overflow: hidden; + *zoom: 1; +} + +.main .logo a { + font-size: 160px; + color: #333; +} + +.helps { + margin-top: 40px; +} + +.helps pre { + padding: 20px; + margin: 10px 0; + border: solid 1px #e7e1cd; + background-color: #fffdef; + overflow: auto; +} + +.icon_lists { + width: 100% !important; + overflow: hidden; + *zoom: 1; +} + +.icon_lists li { + width: 100px; + margin-bottom: 10px; + margin-right: 20px; + text-align: center; + list-style: none !important; + cursor: default; +} + +.icon_lists li .code-name { + line-height: 1.2; +} + +.icon_lists .icon { + display: block; + height: 100px; + line-height: 100px; + font-size: 42px; + margin: 10px auto; + color: #333; + -webkit-transition: font-size 0.25s linear, width 0.25s linear; + -moz-transition: font-size 0.25s linear, width 0.25s linear; + transition: font-size 0.25s linear, width 0.25s linear; +} + +.icon_lists .icon:hover { + font-size: 100px; +} + +.icon_lists .svg-icon { + /* 通过设置 font-size 来改变图标大小 */ + width: 1em; + /* 图标和文字相邻时,垂直对齐 */ + vertical-align: -0.15em; + /* 通过设置 color 来改变 SVG 的颜色/fill */ + fill: currentColor; + /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 + normalize.css 中也包含这行 */ + overflow: hidden; +} + +.icon_lists li .name, +.icon_lists li .code-name { + color: #666; +} + +/* markdown 样式 */ +.markdown { + color: #666; + font-size: 14px; + line-height: 1.8; +} + +.highlight { + line-height: 1.5; +} + +.markdown img { + vertical-align: middle; + max-width: 100%; +} + +.markdown h1 { + color: #404040; + font-weight: 500; + line-height: 40px; + margin-bottom: 24px; +} + +.markdown h2, +.markdown h3, +.markdown h4, +.markdown h5, +.markdown h6 { + color: #404040; + margin: 1.6em 0 0.6em 0; + font-weight: 500; + clear: both; +} + +.markdown h1 { + font-size: 28px; +} + +.markdown h2 { + font-size: 22px; +} + +.markdown h3 { + font-size: 16px; +} + +.markdown h4 { + font-size: 14px; +} + +.markdown h5 { + font-size: 12px; +} + +.markdown h6 { + font-size: 12px; +} + +.markdown hr { + height: 1px; + border: 0; + background: #e9e9e9; + margin: 16px 0; + clear: both; +} + +.markdown p { + margin: 1em 0; +} + +.markdown>p, +.markdown>blockquote, +.markdown>.highlight, +.markdown>ol, +.markdown>ul { + width: 80%; +} + +.markdown ul>li { + list-style: circle; +} + +.markdown>ul li, +.markdown blockquote ul>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown>ul li p, +.markdown>ol li p { + margin: 0.6em 0; +} + +.markdown ol>li { + list-style: decimal; +} + +.markdown>ol li, +.markdown blockquote ol>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown code { + margin: 0 3px; + padding: 0 5px; + background: #eee; + border-radius: 3px; +} + +.markdown strong, +.markdown b { + font-weight: 600; +} + +.markdown>table { + border-collapse: collapse; + border-spacing: 0px; + empty-cells: show; + border: 1px solid #e9e9e9; + width: 95%; + margin-bottom: 24px; +} + +.markdown>table th { + white-space: nowrap; + color: #333; + font-weight: 600; +} + +.markdown>table th, +.markdown>table td { + border: 1px solid #e9e9e9; + padding: 8px 16px; + text-align: left; +} + +.markdown>table th { + background: #F7F7F7; +} + +.markdown blockquote { + font-size: 90%; + color: #999; + border-left: 4px solid #e9e9e9; + padding-left: 0.8em; + margin: 1em 0; +} + +.markdown blockquote p { + margin: 0; +} + +.markdown .anchor { + opacity: 0; + transition: opacity 0.3s ease; + margin-left: 8px; +} + +.markdown .waiting { + color: #ccc; +} + +.markdown h1:hover .anchor, +.markdown h2:hover .anchor, +.markdown h3:hover .anchor, +.markdown h4:hover .anchor, +.markdown h5:hover .anchor, +.markdown h6:hover .anchor { + opacity: 1; + display: inline-block; +} + +.markdown>br, +.markdown>p>br { + clear: both; +} + + +.hljs { + display: block; + background: white; + padding: 0.5em; + color: #333333; + overflow-x: auto; +} + +.hljs-comment, +.hljs-meta { + color: #969896; +} + +.hljs-string, +.hljs-variable, +.hljs-template-variable, +.hljs-strong, +.hljs-emphasis, +.hljs-quote { + color: #df5000; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-type { + color: #a71d5d; +} + +.hljs-literal, +.hljs-symbol, +.hljs-bullet, +.hljs-attribute { + color: #0086b3; +} + +.hljs-section, +.hljs-name { + color: #63a35c; +} + +.hljs-tag { + color: #333333; +} + +.hljs-title, +.hljs-attr, +.hljs-selector-id, +.hljs-selector-class, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #795da3; +} + +.hljs-addition { + color: #55a532; + background-color: #eaffea; +} + +.hljs-deletion { + color: #bd2c00; + background-color: #ffecec; +} + +.hljs-link { + text-decoration: underline; +} + +/* 代码高亮 */ +/* PrismJS 1.15.0 +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre)>code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre)>code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/odcs2.com/src/assets/font/demo_index.html b/odcs2.com/src/assets/font/demo_index.html new file mode 100644 index 0000000..085a698 --- /dev/null +++ b/odcs2.com/src/assets/font/demo_index.html @@ -0,0 +1,306 @@ + + + + + iconfont Demo + + + + + + + + + + + + + +
+

+ + + 彩色字体 + +

+ +
+
+
    + +
  • + +
    小丑
    +
    &#xe656;
    +
  • + +
  • + +
    皇冠
    +
    &#xe607;
    +
  • + +
  • + +
    皇冠
    +
    &#xe676;
    +
  • + +
  • + +
    背景音乐-调皮
    +
    &#xe626;
    +
  • + +
  • + +
    皇冠
    +
    &#xe654;
    +
  • + +
+
+

Unicode 引用

+
+ +

Unicode 是字体在网页端最原始的应用方式,特点是:

+
    +
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • +
  • 默认情况下不支持多色,直接添加多色图标会自动去色。
  • +
+
+

注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)

+
+

Unicode 使用步骤如下:

+

第一步:拷贝项目下面生成的 @font-face

+
@font-face {
+  font-family: 'iconfont';
+  src: 
+       url('iconfont.woff2?t=1699606514058') format('woff2'),
+       url('iconfont.woff?t=1699606514058') format('woff'),
+       url('iconfont.ttf?t=1699606514058') format('truetype');
+}
+
+

第二步:定义使用 iconfont 的样式

+
.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+

第三步:挑选相应图标并获取字体编码,应用于页面

+
+<span class="iconfont">&#x33;</span>
+
+
+

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    + 小丑 +
    +
    .icon-xiaochou +
    +
  • + +
  • + +
    + 皇冠 +
    +
    .icon-huangguan +
    +
  • + +
  • + +
    + 皇冠 +
    +
    .icon-huangguan1 +
    +
  • + +
  • + +
    + 背景音乐-调皮 +
    +
    .icon-beijingyinle-tiaopi +
    +
  • + +
  • + +
    + 皇冠 +
    +
    .icon-huangguan2 +
    +
  • + +
+
+

font-class 引用

+
+ +

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

+

与 Unicode 使用方式相比,具有如下特点:

+
    +
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • +
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 fontclass 代码:

+
<link rel="stylesheet" href="./iconfont.css">
+
+

第二步:挑选相应图标并获取类名,应用于页面:

+
<span class="iconfont icon-xxx"></span>
+
+
+

" + iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    小丑
    +
    #icon-xiaochou
    +
  • + +
  • + +
    皇冠
    +
    #icon-huangguan
    +
  • + +
  • + +
    皇冠
    +
    #icon-huangguan1
    +
  • + +
  • + +
    背景音乐-调皮
    +
    #icon-beijingyinle-tiaopi
    +
  • + +
  • + +
    皇冠
    +
    #icon-huangguan2
    +
  • + +
+
+

Symbol 引用

+
+ +

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 + 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

+
    +
  • 支持多色图标了,不再受单色限制。
  • +
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • +
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • +
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 symbol 代码:

+
<script src="./iconfont.js"></script>
+
+

第二步:加入通用 CSS 代码(引入一次就行):

+
<style>
+.icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+</style>
+
+

第三步:挑选相应图标并获取类名,应用于页面:

+
<svg class="icon" aria-hidden="true">
+  <use xlink:href="#icon-xxx"></use>
+</svg>
+
+
+
+ +
+
+ + + diff --git a/odcs2.com/src/assets/font/iconfont.css b/odcs2.com/src/assets/font/iconfont.css new file mode 100644 index 0000000..8bee2ab --- /dev/null +++ b/odcs2.com/src/assets/font/iconfont.css @@ -0,0 +1,37 @@ +@font-face { + font-family: "iconfont"; /* Project id 4325432 */ + /* Color fonts */ + src: + url('iconfont.woff2?t=1699606514058') format('woff2'), + url('iconfont.woff?t=1699606514058') format('woff'), + url('iconfont.ttf?t=1699606514058') format('truetype'); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-xiaochou:before { + content: "\e656"; +} + +.icon-huangguan:before { + content: "\e607"; +} + +.icon-huangguan1:before { + content: "\e676"; +} + +.icon-beijingyinle-tiaopi:before { + content: "\e626"; +} + +.icon-huangguan2:before { + content: "\e654"; +} + diff --git a/odcs2.com/src/assets/font/iconfont.js b/odcs2.com/src/assets/font/iconfont.js new file mode 100644 index 0000000..28a8ee6 --- /dev/null +++ b/odcs2.com/src/assets/font/iconfont.js @@ -0,0 +1 @@ +window._iconfont_svg_string_4325432='',function(t){var a=(a=document.getElementsByTagName("script"))[a.length-1],l=a.getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var h,c,i,e,p,d=function(a,l){l.parentNode.insertBefore(a,l)};if(l&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}h=function(){var a,l=document.createElement("div");l.innerHTML=t._iconfont_svg_string_4325432,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?d(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(c=function(){document.removeEventListener("DOMContentLoaded",c,!1),h()},document.addEventListener("DOMContentLoaded",c,!1)):document.attachEvent&&(i=h,e=t.document,p=!1,F(),e.onreadystatechange=function(){"complete"==e.readyState&&(e.onreadystatechange=null,n())})}function n(){p||(p=!0,i())}function F(){try{e.documentElement.doScroll("left")}catch(a){return void setTimeout(F,50)}n()}}(window); \ No newline at end of file diff --git a/odcs2.com/src/assets/font/iconfont.json b/odcs2.com/src/assets/font/iconfont.json new file mode 100644 index 0000000..7b8a385 --- /dev/null +++ b/odcs2.com/src/assets/font/iconfont.json @@ -0,0 +1,44 @@ +{ + "id": "4325432", + "name": "小丑", + "font_family": "iconfont", + "css_prefix_text": "icon-", + "description": "", + "glyphs": [ + { + "icon_id": "5456858", + "name": "小丑", + "font_class": "xiaochou", + "unicode": "e656", + "unicode_decimal": 58966 + }, + { + "icon_id": "7676678", + "name": "皇冠", + "font_class": "huangguan", + "unicode": "e607", + "unicode_decimal": 58887 + }, + { + "icon_id": "8993665", + "name": "皇冠", + "font_class": "huangguan1", + "unicode": "e676", + "unicode_decimal": 58998 + }, + { + "icon_id": "18063756", + "name": "背景音乐-调皮", + "font_class": "beijingyinle-tiaopi", + "unicode": "e626", + "unicode_decimal": 58918 + }, + { + "icon_id": "33439583", + "name": "皇冠", + "font_class": "huangguan2", + "unicode": "e654", + "unicode_decimal": 58964 + } + ] +} diff --git a/odcs2.com/src/assets/font/iconfont.ttf b/odcs2.com/src/assets/font/iconfont.ttf new file mode 100644 index 0000000..74e38a3 Binary files /dev/null and b/odcs2.com/src/assets/font/iconfont.ttf differ diff --git a/odcs2.com/src/assets/font/iconfont.woff b/odcs2.com/src/assets/font/iconfont.woff new file mode 100644 index 0000000..f0801c0 Binary files /dev/null and b/odcs2.com/src/assets/font/iconfont.woff differ diff --git a/odcs2.com/src/assets/font/iconfont.woff2 b/odcs2.com/src/assets/font/iconfont.woff2 new file mode 100644 index 0000000..b25336c Binary files /dev/null and b/odcs2.com/src/assets/font/iconfont.woff2 differ diff --git a/odcs2.com/src/assets/font/tuiguangbg.png b/odcs2.com/src/assets/font/tuiguangbg.png new file mode 100644 index 0000000..f0794ab Binary files /dev/null and b/odcs2.com/src/assets/font/tuiguangbg.png differ diff --git a/odcs2.com/src/assets/game_bg.png b/odcs2.com/src/assets/game_bg.png new file mode 100644 index 0000000..ce2b22f Binary files /dev/null and b/odcs2.com/src/assets/game_bg.png differ diff --git a/odcs2.com/src/assets/gen-box-bg-C9N-vsd0.png b/odcs2.com/src/assets/gen-box-bg-C9N-vsd0.png new file mode 100644 index 0000000..e25ac3e Binary files /dev/null and b/odcs2.com/src/assets/gen-box-bg-C9N-vsd0.png differ diff --git a/odcs2.com/src/assets/goods-bg-W3mkDdCy.png b/odcs2.com/src/assets/goods-bg-W3mkDdCy.png new file mode 100644 index 0000000..3a58c34 Binary files /dev/null and b/odcs2.com/src/assets/goods-bg-W3mkDdCy.png differ diff --git a/odcs2.com/src/assets/guanzhan.png b/odcs2.com/src/assets/guanzhan.png new file mode 100644 index 0000000..480755a Binary files /dev/null and b/odcs2.com/src/assets/guanzhan.png differ diff --git a/odcs2.com/src/assets/guanzhan2.png b/odcs2.com/src/assets/guanzhan2.png new file mode 100644 index 0000000..b1a9fc0 Binary files /dev/null and b/odcs2.com/src/assets/guanzhan2.png differ diff --git a/odcs2.com/src/assets/head-bg.png b/odcs2.com/src/assets/head-bg.png new file mode 100644 index 0000000..3a693ae Binary files /dev/null and b/odcs2.com/src/assets/head-bg.png differ diff --git a/odcs2.com/src/assets/home (1).png b/odcs2.com/src/assets/home (1).png new file mode 100644 index 0000000..4afac11 Binary files /dev/null and b/odcs2.com/src/assets/home (1).png differ diff --git a/odcs2.com/src/assets/home.gif b/odcs2.com/src/assets/home.gif new file mode 100644 index 0000000..8126a36 Binary files /dev/null and b/odcs2.com/src/assets/home.gif differ diff --git a/odcs2.com/src/assets/home.mp3 b/odcs2.com/src/assets/home.mp3 new file mode 100644 index 0000000..7d06155 Binary files /dev/null and b/odcs2.com/src/assets/home.mp3 differ diff --git a/odcs2.com/src/assets/home.png b/odcs2.com/src/assets/home.png new file mode 100644 index 0000000..9a667eb Binary files /dev/null and b/odcs2.com/src/assets/home.png differ diff --git a/odcs2.com/src/assets/home.webp b/odcs2.com/src/assets/home.webp new file mode 100644 index 0000000..fce38dc Binary files /dev/null and b/odcs2.com/src/assets/home.webp differ diff --git a/odcs2.com/src/assets/home2.png b/odcs2.com/src/assets/home2.png new file mode 100644 index 0000000..1851d4a Binary files /dev/null and b/odcs2.com/src/assets/home2.png differ diff --git a/odcs2.com/src/assets/home_sel2.png b/odcs2.com/src/assets/home_sel2.png new file mode 100644 index 0000000..ea182eb Binary files /dev/null and b/odcs2.com/src/assets/home_sel2.png differ diff --git a/odcs2.com/src/assets/hong.png b/odcs2.com/src/assets/hong.png new file mode 100644 index 0000000..c1a9901 Binary files /dev/null and b/odcs2.com/src/assets/hong.png differ diff --git a/odcs2.com/src/assets/hudong.png b/odcs2.com/src/assets/hudong.png new file mode 100644 index 0000000..3344696 Binary files /dev/null and b/odcs2.com/src/assets/hudong.png differ diff --git a/odcs2.com/src/assets/image/avatar/0e491040df45832cee35314f374c9b14.jpg b/odcs2.com/src/assets/image/avatar/0e491040df45832cee35314f374c9b14.jpg new file mode 100644 index 0000000..7e6d963 Binary files /dev/null and b/odcs2.com/src/assets/image/avatar/0e491040df45832cee35314f374c9b14.jpg differ diff --git a/odcs2.com/src/assets/image/avatar/325ab15e8ecfeb7113edc9ebaeb6bb73.jpg b/odcs2.com/src/assets/image/avatar/325ab15e8ecfeb7113edc9ebaeb6bb73.jpg new file mode 100644 index 0000000..c97ac6e Binary files /dev/null and b/odcs2.com/src/assets/image/avatar/325ab15e8ecfeb7113edc9ebaeb6bb73.jpg differ diff --git a/odcs2.com/src/assets/image/avatar/3d7afd41a03e69dcbdf75a17cb5bd0ea.jpg b/odcs2.com/src/assets/image/avatar/3d7afd41a03e69dcbdf75a17cb5bd0ea.jpg new file mode 100644 index 0000000..a4a17b5 Binary files /dev/null and b/odcs2.com/src/assets/image/avatar/3d7afd41a03e69dcbdf75a17cb5bd0ea.jpg differ diff --git a/odcs2.com/src/assets/image/avatar/68f01a004889bffbd65019ef28d021b5.jpg b/odcs2.com/src/assets/image/avatar/68f01a004889bffbd65019ef28d021b5.jpg new file mode 100644 index 0000000..92ad485 Binary files /dev/null and b/odcs2.com/src/assets/image/avatar/68f01a004889bffbd65019ef28d021b5.jpg differ diff --git a/odcs2.com/src/assets/image/avatar/884b1494c29dce6b4aad2161a9b27055.jpg b/odcs2.com/src/assets/image/avatar/884b1494c29dce6b4aad2161a9b27055.jpg new file mode 100644 index 0000000..8a5461c Binary files /dev/null and b/odcs2.com/src/assets/image/avatar/884b1494c29dce6b4aad2161a9b27055.jpg differ diff --git a/odcs2.com/src/assets/image/avatar/a5b3397dc886cee5a91dca5921e2679b.jpg b/odcs2.com/src/assets/image/avatar/a5b3397dc886cee5a91dca5921e2679b.jpg new file mode 100644 index 0000000..e56afd0 Binary files /dev/null and b/odcs2.com/src/assets/image/avatar/a5b3397dc886cee5a91dca5921e2679b.jpg differ diff --git a/odcs2.com/src/assets/image/avatar/ac464e5bf9d9a679b2bcec70441176e0.jpg b/odcs2.com/src/assets/image/avatar/ac464e5bf9d9a679b2bcec70441176e0.jpg new file mode 100644 index 0000000..b99a738 Binary files /dev/null and b/odcs2.com/src/assets/image/avatar/ac464e5bf9d9a679b2bcec70441176e0.jpg differ diff --git a/odcs2.com/src/assets/image/avatar/b56f2c46b0075ab0627997d8b79fb766.jpg b/odcs2.com/src/assets/image/avatar/b56f2c46b0075ab0627997d8b79fb766.jpg new file mode 100644 index 0000000..4bf0ec5 Binary files /dev/null and b/odcs2.com/src/assets/image/avatar/b56f2c46b0075ab0627997d8b79fb766.jpg differ diff --git a/odcs2.com/src/assets/image/avatar/c08c85b9f4df9573116ae393d6292ab9.jpg b/odcs2.com/src/assets/image/avatar/c08c85b9f4df9573116ae393d6292ab9.jpg new file mode 100644 index 0000000..31be00b Binary files /dev/null and b/odcs2.com/src/assets/image/avatar/c08c85b9f4df9573116ae393d6292ab9.jpg differ diff --git a/odcs2.com/src/assets/image/avatar/cf74fa052c5aa129bcf958ca333de148.jpg b/odcs2.com/src/assets/image/avatar/cf74fa052c5aa129bcf958ca333de148.jpg new file mode 100644 index 0000000..89a5359 Binary files /dev/null and b/odcs2.com/src/assets/image/avatar/cf74fa052c5aa129bcf958ca333de148.jpg differ diff --git a/odcs2.com/src/assets/image/close.svg b/odcs2.com/src/assets/image/close.svg new file mode 100644 index 0000000..7782144 --- /dev/null +++ b/odcs2.com/src/assets/image/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/odcs2.com/src/assets/image/gongan.png b/odcs2.com/src/assets/image/gongan.png new file mode 100644 index 0000000..2252c02 Binary files /dev/null and b/odcs2.com/src/assets/image/gongan.png differ diff --git a/odcs2.com/src/assets/image/index/logo.png b/odcs2.com/src/assets/image/index/logo.png new file mode 100644 index 0000000..7fe72be Binary files /dev/null and b/odcs2.com/src/assets/image/index/logo.png differ diff --git a/odcs2.com/src/assets/image/loggin_button.png b/odcs2.com/src/assets/image/loggin_button.png new file mode 100644 index 0000000..e0a5a55 Binary files /dev/null and b/odcs2.com/src/assets/image/loggin_button.png differ diff --git a/odcs2.com/src/assets/image/logo_login.png b/odcs2.com/src/assets/image/logo_login.png new file mode 100644 index 0000000..7aa6925 Binary files /dev/null and b/odcs2.com/src/assets/image/logo_login.png differ diff --git a/odcs2.com/src/assets/image/pk_watch.png b/odcs2.com/src/assets/image/pk_watch.png new file mode 100644 index 0000000..2b5f081 Binary files /dev/null and b/odcs2.com/src/assets/image/pk_watch.png differ diff --git a/odcs2.com/src/assets/images/add.png b/odcs2.com/src/assets/images/add.png new file mode 100644 index 0000000..34ad37b Binary files /dev/null and b/odcs2.com/src/assets/images/add.png differ diff --git a/odcs2.com/src/assets/images/app_header.png b/odcs2.com/src/assets/images/app_header.png new file mode 100644 index 0000000..157e057 Binary files /dev/null and b/odcs2.com/src/assets/images/app_header.png differ diff --git a/odcs2.com/src/assets/images/avatar.png b/odcs2.com/src/assets/images/avatar.png new file mode 100644 index 0000000..cf627ca Binary files /dev/null and b/odcs2.com/src/assets/images/avatar.png differ diff --git a/odcs2.com/src/assets/images/bars.png b/odcs2.com/src/assets/images/bars.png new file mode 100644 index 0000000..5e86b2b Binary files /dev/null and b/odcs2.com/src/assets/images/bars.png differ diff --git a/odcs2.com/src/assets/images/default-header.png b/odcs2.com/src/assets/images/default-header.png new file mode 100644 index 0000000..85b5600 Binary files /dev/null and b/odcs2.com/src/assets/images/default-header.png differ diff --git a/odcs2.com/src/assets/images/h_right.png b/odcs2.com/src/assets/images/h_right.png new file mode 100644 index 0000000..fb31224 Binary files /dev/null and b/odcs2.com/src/assets/images/h_right.png differ diff --git a/odcs2.com/src/assets/images/header-item.png b/odcs2.com/src/assets/images/header-item.png new file mode 100644 index 0000000..938b36f Binary files /dev/null and b/odcs2.com/src/assets/images/header-item.png differ diff --git a/odcs2.com/src/assets/images/jifen.png b/odcs2.com/src/assets/images/jifen.png new file mode 100644 index 0000000..2181071 Binary files /dev/null and b/odcs2.com/src/assets/images/jifen.png differ diff --git a/odcs2.com/src/assets/images/jinbi.png b/odcs2.com/src/assets/images/jinbi.png new file mode 100644 index 0000000..4ef2b25 Binary files /dev/null and b/odcs2.com/src/assets/images/jinbi.png differ diff --git a/odcs2.com/src/assets/images/line.png b/odcs2.com/src/assets/images/line.png new file mode 100644 index 0000000..b23e089 Binary files /dev/null and b/odcs2.com/src/assets/images/line.png differ diff --git a/odcs2.com/src/assets/images/login_n.png b/odcs2.com/src/assets/images/login_n.png new file mode 100644 index 0000000..efc45b5 Binary files /dev/null and b/odcs2.com/src/assets/images/login_n.png differ diff --git a/odcs2.com/src/assets/images/login_register.png b/odcs2.com/src/assets/images/login_register.png new file mode 100644 index 0000000..548a9a9 Binary files /dev/null and b/odcs2.com/src/assets/images/login_register.png differ diff --git a/odcs2.com/src/assets/images/need_login1.png b/odcs2.com/src/assets/images/need_login1.png new file mode 100644 index 0000000..76efbe0 Binary files /dev/null and b/odcs2.com/src/assets/images/need_login1.png differ diff --git a/odcs2.com/src/assets/index/duizhan.webp b/odcs2.com/src/assets/index/duizhan.webp new file mode 100644 index 0000000..50c1bb4 Binary files /dev/null and b/odcs2.com/src/assets/index/duizhan.webp differ diff --git a/odcs2.com/src/assets/index/fuli.webp b/odcs2.com/src/assets/index/fuli.webp new file mode 100644 index 0000000..3927faf Binary files /dev/null and b/odcs2.com/src/assets/index/fuli.webp differ diff --git a/odcs2.com/src/assets/index/fuli1.webp b/odcs2.com/src/assets/index/fuli1.webp new file mode 100644 index 0000000..47d76f1 Binary files /dev/null and b/odcs2.com/src/assets/index/fuli1.webp differ diff --git a/odcs2.com/src/assets/index/icon-bg.webp b/odcs2.com/src/assets/index/icon-bg.webp new file mode 100644 index 0000000..a4c0113 Binary files /dev/null and b/odcs2.com/src/assets/index/icon-bg.webp differ diff --git a/odcs2.com/src/assets/index/jingdian.webp b/odcs2.com/src/assets/index/jingdian.webp new file mode 100644 index 0000000..74a7507 Binary files /dev/null and b/odcs2.com/src/assets/index/jingdian.webp differ diff --git a/odcs2.com/src/assets/index/kefu.webp b/odcs2.com/src/assets/index/kefu.webp new file mode 100644 index 0000000..d4d03bb Binary files /dev/null and b/odcs2.com/src/assets/index/kefu.webp differ diff --git a/odcs2.com/src/assets/index/paihang.webp b/odcs2.com/src/assets/index/paihang.webp new file mode 100644 index 0000000..b2a52b8 Binary files /dev/null and b/odcs2.com/src/assets/index/paihang.webp differ diff --git a/odcs2.com/src/assets/index/roll.webp b/odcs2.com/src/assets/index/roll.webp new file mode 100644 index 0000000..60ebca2 Binary files /dev/null and b/odcs2.com/src/assets/index/roll.webp differ diff --git a/odcs2.com/src/assets/index/xingyun.webp b/odcs2.com/src/assets/index/xingyun.webp new file mode 100644 index 0000000..94ccd4f Binary files /dev/null and b/odcs2.com/src/assets/index/xingyun.webp differ diff --git a/odcs2.com/src/assets/index/xinxi.webp b/odcs2.com/src/assets/index/xinxi.webp new file mode 100644 index 0000000..bdd8320 Binary files /dev/null and b/odcs2.com/src/assets/index/xinxi.webp differ diff --git a/odcs2.com/src/assets/index/zhishu.webp b/odcs2.com/src/assets/index/zhishu.webp new file mode 100644 index 0000000..9546ad5 Binary files /dev/null and b/odcs2.com/src/assets/index/zhishu.webp differ diff --git a/odcs2.com/src/assets/index/图层 82@2x.webp b/odcs2.com/src/assets/index/图层 82@2x.webp new file mode 100644 index 0000000..fe1b63c Binary files /dev/null and b/odcs2.com/src/assets/index/图层 82@2x.webp differ diff --git a/odcs2.com/src/assets/integral.png b/odcs2.com/src/assets/integral.png new file mode 100644 index 0000000..edc7c1b Binary files /dev/null and b/odcs2.com/src/assets/integral.png differ diff --git a/odcs2.com/src/assets/item_light.png b/odcs2.com/src/assets/item_light.png new file mode 100644 index 0000000..f334cfc Binary files /dev/null and b/odcs2.com/src/assets/item_light.png differ diff --git a/odcs2.com/src/assets/jiaru.png b/odcs2.com/src/assets/jiaru.png new file mode 100644 index 0000000..8a18762 Binary files /dev/null and b/odcs2.com/src/assets/jiaru.png differ diff --git a/odcs2.com/src/assets/jiaru2.png b/odcs2.com/src/assets/jiaru2.png new file mode 100644 index 0000000..846b700 Binary files /dev/null and b/odcs2.com/src/assets/jiaru2.png differ diff --git a/odcs2.com/src/assets/jin.png b/odcs2.com/src/assets/jin.png new file mode 100644 index 0000000..ad00c52 Binary files /dev/null and b/odcs2.com/src/assets/jin.png differ diff --git a/odcs2.com/src/assets/jiuxu.png b/odcs2.com/src/assets/jiuxu.png new file mode 100644 index 0000000..018e21b Binary files /dev/null and b/odcs2.com/src/assets/jiuxu.png differ diff --git a/odcs2.com/src/assets/jsjj.jpg b/odcs2.com/src/assets/jsjj.jpg new file mode 100644 index 0000000..480b4a6 Binary files /dev/null and b/odcs2.com/src/assets/jsjj.jpg differ diff --git a/odcs2.com/src/assets/jsjj.png b/odcs2.com/src/assets/jsjj.png new file mode 100644 index 0000000..2c535d4 Binary files /dev/null and b/odcs2.com/src/assets/jsjj.png differ diff --git a/odcs2.com/src/assets/kf_bg.png b/odcs2.com/src/assets/kf_bg.png new file mode 100644 index 0000000..b2f2d89 Binary files /dev/null and b/odcs2.com/src/assets/kf_bg.png differ diff --git a/odcs2.com/src/assets/loginBtn.png b/odcs2.com/src/assets/loginBtn.png new file mode 100644 index 0000000..9d18efb Binary files /dev/null and b/odcs2.com/src/assets/loginBtn.png differ diff --git a/odcs2.com/src/assets/logo.png b/odcs2.com/src/assets/logo.png new file mode 100644 index 0000000..7aa6925 Binary files /dev/null and b/odcs2.com/src/assets/logo.png differ diff --git a/odcs2.com/src/assets/logo/log.png b/odcs2.com/src/assets/logo/log.png new file mode 100644 index 0000000..6c41a83 Binary files /dev/null and b/odcs2.com/src/assets/logo/log.png differ diff --git a/odcs2.com/src/assets/logo/logo.jpg b/odcs2.com/src/assets/logo/logo.jpg new file mode 100644 index 0000000..922ff55 Binary files /dev/null and b/odcs2.com/src/assets/logo/logo.jpg differ diff --git a/odcs2.com/src/assets/logo/logo.png b/odcs2.com/src/assets/logo/logo.png new file mode 100644 index 0000000..2d29c2d Binary files /dev/null and b/odcs2.com/src/assets/logo/logo.png differ diff --git a/odcs2.com/src/assets/logo/微信图片_20240708223032-恢复的-恢复的.jpg b/odcs2.com/src/assets/logo/微信图片_20240708223032-恢复的-恢复的.jpg new file mode 100644 index 0000000..60c2b52 Binary files /dev/null and b/odcs2.com/src/assets/logo/微信图片_20240708223032-恢复的-恢复的.jpg differ diff --git a/odcs2.com/src/assets/logo/未标题-1_画板 1.jpg b/odcs2.com/src/assets/logo/未标题-1_画板 1.jpg new file mode 100644 index 0000000..bd79abe Binary files /dev/null and b/odcs2.com/src/assets/logo/未标题-1_画板 1.jpg differ diff --git a/odcs2.com/src/assets/luck.png b/odcs2.com/src/assets/luck.png new file mode 100644 index 0000000..0f98039 Binary files /dev/null and b/odcs2.com/src/assets/luck.png differ diff --git a/odcs2.com/src/assets/luckweapon.png b/odcs2.com/src/assets/luckweapon.png new file mode 100644 index 0000000..8ca4229 Binary files /dev/null and b/odcs2.com/src/assets/luckweapon.png differ diff --git a/odcs2.com/src/assets/lucky-room-center-bg.png b/odcs2.com/src/assets/lucky-room-center-bg.png new file mode 100644 index 0000000..0121305 Binary files /dev/null and b/odcs2.com/src/assets/lucky-room-center-bg.png differ diff --git a/odcs2.com/src/assets/luckyJewelry_bg.png b/odcs2.com/src/assets/luckyJewelry_bg.png new file mode 100644 index 0000000..5607631 Binary files /dev/null and b/odcs2.com/src/assets/luckyJewelry_bg.png differ diff --git a/odcs2.com/src/assets/lv.png b/odcs2.com/src/assets/lv.png new file mode 100644 index 0000000..44bfa0f Binary files /dev/null and b/odcs2.com/src/assets/lv.png differ diff --git a/odcs2.com/src/assets/main-btn-bg-active.png b/odcs2.com/src/assets/main-btn-bg-active.png new file mode 100644 index 0000000..d8bd2e1 Binary files /dev/null and b/odcs2.com/src/assets/main-btn-bg-active.png differ diff --git a/odcs2.com/src/assets/main-btn-bg.png b/odcs2.com/src/assets/main-btn-bg.png new file mode 100644 index 0000000..9349be1 Binary files /dev/null and b/odcs2.com/src/assets/main-btn-bg.png differ diff --git a/odcs2.com/src/assets/mall.png b/odcs2.com/src/assets/mall.png new file mode 100644 index 0000000..4b830cb Binary files /dev/null and b/odcs2.com/src/assets/mall.png differ diff --git a/odcs2.com/src/assets/mall_sel2.png b/odcs2.com/src/assets/mall_sel2.png new file mode 100644 index 0000000..079fdc7 Binary files /dev/null and b/odcs2.com/src/assets/mall_sel2.png differ diff --git a/odcs2.com/src/assets/mine-bg.png b/odcs2.com/src/assets/mine-bg.png new file mode 100644 index 0000000..d2cc856 Binary files /dev/null and b/odcs2.com/src/assets/mine-bg.png differ diff --git a/odcs2.com/src/assets/mine.png b/odcs2.com/src/assets/mine.png new file mode 100644 index 0000000..0d063b7 Binary files /dev/null and b/odcs2.com/src/assets/mine.png differ diff --git a/odcs2.com/src/assets/mine_sel2.png b/odcs2.com/src/assets/mine_sel2.png new file mode 100644 index 0000000..ecd1222 Binary files /dev/null and b/odcs2.com/src/assets/mine_sel2.png differ diff --git a/odcs2.com/src/assets/moba.png b/odcs2.com/src/assets/moba.png new file mode 100644 index 0000000..b0eb827 Binary files /dev/null and b/odcs2.com/src/assets/moba.png differ diff --git a/odcs2.com/src/assets/moba1.png b/odcs2.com/src/assets/moba1.png new file mode 100644 index 0000000..5c6a751 Binary files /dev/null and b/odcs2.com/src/assets/moba1.png differ diff --git a/odcs2.com/src/assets/money.png b/odcs2.com/src/assets/money.png new file mode 100644 index 0000000..c28b7bc Binary files /dev/null and b/odcs2.com/src/assets/money.png differ diff --git a/odcs2.com/src/assets/need_login.png b/odcs2.com/src/assets/need_login.png new file mode 100644 index 0000000..0efa283 Binary files /dev/null and b/odcs2.com/src/assets/need_login.png differ diff --git a/odcs2.com/src/assets/notice-bar.png b/odcs2.com/src/assets/notice-bar.png new file mode 100644 index 0000000..572eafa Binary files /dev/null and b/odcs2.com/src/assets/notice-bar.png differ diff --git a/odcs2.com/src/assets/num1.png b/odcs2.com/src/assets/num1.png new file mode 100644 index 0000000..a8049bf Binary files /dev/null and b/odcs2.com/src/assets/num1.png differ diff --git a/odcs2.com/src/assets/num2.png b/odcs2.com/src/assets/num2.png new file mode 100644 index 0000000..556cd11 Binary files /dev/null and b/odcs2.com/src/assets/num2.png differ diff --git a/odcs2.com/src/assets/num3.png b/odcs2.com/src/assets/num3.png new file mode 100644 index 0000000..dd5f845 Binary files /dev/null and b/odcs2.com/src/assets/num3.png differ diff --git a/odcs2.com/src/assets/num4.png b/odcs2.com/src/assets/num4.png new file mode 100644 index 0000000..8164eb3 Binary files /dev/null and b/odcs2.com/src/assets/num4.png differ diff --git a/odcs2.com/src/assets/num5.png b/odcs2.com/src/assets/num5.png new file mode 100644 index 0000000..e9f0276 Binary files /dev/null and b/odcs2.com/src/assets/num5.png differ diff --git a/odcs2.com/src/assets/open-btn-bg2-D3rUtn-q.png b/odcs2.com/src/assets/open-btn-bg2-D3rUtn-q.png new file mode 100644 index 0000000..c4845b0 Binary files /dev/null and b/odcs2.com/src/assets/open-btn-bg2-D3rUtn-q.png differ diff --git a/odcs2.com/src/assets/openbox.jpg b/odcs2.com/src/assets/openbox.jpg new file mode 100644 index 0000000..fb6d61f Binary files /dev/null and b/odcs2.com/src/assets/openbox.jpg differ diff --git a/odcs2.com/src/assets/openbox.png b/odcs2.com/src/assets/openbox.png new file mode 100644 index 0000000..e74995f Binary files /dev/null and b/odcs2.com/src/assets/openbox.png differ diff --git a/odcs2.com/src/assets/pinxiangBottom_text_bg.png b/odcs2.com/src/assets/pinxiangBottom_text_bg.png new file mode 100644 index 0000000..c350d46 Binary files /dev/null and b/odcs2.com/src/assets/pinxiangBottom_text_bg.png differ diff --git a/odcs2.com/src/assets/pinxiangBottom_top.png b/odcs2.com/src/assets/pinxiangBottom_top.png new file mode 100644 index 0000000..6317355 Binary files /dev/null and b/odcs2.com/src/assets/pinxiangBottom_top.png differ diff --git a/odcs2.com/src/assets/pk_center_user_bg.png b/odcs2.com/src/assets/pk_center_user_bg.png new file mode 100644 index 0000000..450e4b3 Binary files /dev/null and b/odcs2.com/src/assets/pk_center_user_bg.png differ diff --git a/odcs2.com/src/assets/pk_player_bg.png b/odcs2.com/src/assets/pk_player_bg.png new file mode 100644 index 0000000..a5c4fcd Binary files /dev/null and b/odcs2.com/src/assets/pk_player_bg.png differ diff --git a/odcs2.com/src/assets/pk_top_bg.png b/odcs2.com/src/assets/pk_top_bg.png new file mode 100644 index 0000000..3cfb2b7 Binary files /dev/null and b/odcs2.com/src/assets/pk_top_bg.png differ diff --git a/odcs2.com/src/assets/rankbg.png b/odcs2.com/src/assets/rankbg.png new file mode 100644 index 0000000..dda04bd Binary files /dev/null and b/odcs2.com/src/assets/rankbg.png differ diff --git a/odcs2.com/src/assets/recharge.jpg b/odcs2.com/src/assets/recharge.jpg new file mode 100644 index 0000000..5986324 Binary files /dev/null and b/odcs2.com/src/assets/recharge.jpg differ diff --git a/odcs2.com/src/assets/recharge.png b/odcs2.com/src/assets/recharge.png new file mode 100644 index 0000000..1a7b815 Binary files /dev/null and b/odcs2.com/src/assets/recharge.png differ diff --git a/odcs2.com/src/assets/recharge_sel2.png b/odcs2.com/src/assets/recharge_sel2.png new file mode 100644 index 0000000..efaf518 Binary files /dev/null and b/odcs2.com/src/assets/recharge_sel2.png differ diff --git a/odcs2.com/src/assets/red.png b/odcs2.com/src/assets/red.png new file mode 100644 index 0000000..779efa8 Binary files /dev/null and b/odcs2.com/src/assets/red.png differ diff --git a/odcs2.com/src/assets/red1.png b/odcs2.com/src/assets/red1.png new file mode 100644 index 0000000..4243e7a Binary files /dev/null and b/odcs2.com/src/assets/red1.png differ diff --git a/odcs2.com/src/assets/roll-official-bg-DRp1UF5D.png b/odcs2.com/src/assets/roll-official-bg-DRp1UF5D.png new file mode 100644 index 0000000..129dad9 Binary files /dev/null and b/odcs2.com/src/assets/roll-official-bg-DRp1UF5D.png differ diff --git a/odcs2.com/src/assets/roll-official-bg-DRp1UF5D.pxr b/odcs2.com/src/assets/roll-official-bg-DRp1UF5D.pxr new file mode 100644 index 0000000..4ca8477 Binary files /dev/null and b/odcs2.com/src/assets/roll-official-bg-DRp1UF5D.pxr differ diff --git a/odcs2.com/src/assets/roll.jpg b/odcs2.com/src/assets/roll.jpg new file mode 100644 index 0000000..62775d0 Binary files /dev/null and b/odcs2.com/src/assets/roll.jpg differ diff --git a/odcs2.com/src/assets/roll.png b/odcs2.com/src/assets/roll.png new file mode 100644 index 0000000..a6e79c6 Binary files /dev/null and b/odcs2.com/src/assets/roll.png differ diff --git a/odcs2.com/src/assets/roll_jieshu.png b/odcs2.com/src/assets/roll_jieshu.png new file mode 100644 index 0000000..18ab08f Binary files /dev/null and b/odcs2.com/src/assets/roll_jieshu.png differ diff --git a/odcs2.com/src/assets/roll_serch.png b/odcs2.com/src/assets/roll_serch.png new file mode 100644 index 0000000..e53c784 Binary files /dev/null and b/odcs2.com/src/assets/roll_serch.png differ diff --git a/odcs2.com/src/assets/roll_status.png b/odcs2.com/src/assets/roll_status.png new file mode 100644 index 0000000..c2b3fba Binary files /dev/null and b/odcs2.com/src/assets/roll_status.png differ diff --git a/odcs2.com/src/assets/room-bottom.png b/odcs2.com/src/assets/room-bottom.png new file mode 100644 index 0000000..415d5cc Binary files /dev/null and b/odcs2.com/src/assets/room-bottom.png differ diff --git a/odcs2.com/src/assets/room-content.png b/odcs2.com/src/assets/room-content.png new file mode 100644 index 0000000..aeab968 Binary files /dev/null and b/odcs2.com/src/assets/room-content.png differ diff --git a/odcs2.com/src/assets/room-top.png b/odcs2.com/src/assets/room-top.png new file mode 100644 index 0000000..785fd5b Binary files /dev/null and b/odcs2.com/src/assets/room-top.png differ diff --git a/odcs2.com/src/assets/room_bg.png b/odcs2.com/src/assets/room_bg.png new file mode 100644 index 0000000..eb80c37 Binary files /dev/null and b/odcs2.com/src/assets/room_bg.png differ diff --git a/odcs2.com/src/assets/room_bg2.png b/odcs2.com/src/assets/room_bg2.png new file mode 100644 index 0000000..83306b4 Binary files /dev/null and b/odcs2.com/src/assets/room_bg2.png differ diff --git a/odcs2.com/src/assets/room_btn_yjs.png b/odcs2.com/src/assets/room_btn_yjs.png new file mode 100644 index 0000000..6266bbb Binary files /dev/null and b/odcs2.com/src/assets/room_btn_yjs.png differ diff --git a/odcs2.com/src/assets/status_btn_active.png b/odcs2.com/src/assets/status_btn_active.png new file mode 100644 index 0000000..9bcde9a Binary files /dev/null and b/odcs2.com/src/assets/status_btn_active.png differ diff --git a/odcs2.com/src/assets/style/iconfont.css b/odcs2.com/src/assets/style/iconfont.css new file mode 100644 index 0000000..f583b22 --- /dev/null +++ b/odcs2.com/src/assets/style/iconfont.css @@ -0,0 +1,33 @@ +@font-face { + font-family: "iconfont"; /* Project id */ + src: url('iconfont.ttf?t=1730627224351') format('truetype'); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-chongzhi:before { + content: "\e69c"; +} + +.icon-shouye:before { + content: "\e624"; +} + +.icon-wode:before { + content: "\e630"; +} + +.icon-shangcheng:before { + content: "\e78c"; +} + +.icon-beibao:before { + content: "\e610"; +} + diff --git a/odcs2.com/src/assets/style/iconfont.ttf b/odcs2.com/src/assets/style/iconfont.ttf new file mode 100644 index 0000000..380cbce Binary files /dev/null and b/odcs2.com/src/assets/style/iconfont.ttf differ diff --git a/odcs2.com/src/assets/style/success.css b/odcs2.com/src/assets/style/success.css new file mode 100644 index 0000000..5cf8ac1 --- /dev/null +++ b/odcs2.com/src/assets/style/success.css @@ -0,0 +1,338 @@ +.mesinfo { + background-color: red; +} + +.log_success { + padding: 0; +} + +.log_success { + min-width: 210px; + height: 42px; + padding: 5px 10px; + background: #1F2D36; + border-radius: 35px; + opacity: 1; + border: none; + justify-content: center; + font-size: 18px; +} + +.log_success .el-message__content { + color: #3a97e4; + font-size: 18px; +} + +.log_success .el-icon-success { + color: #3a97e4; + font-size: 18px; +} + +@media (max-width: 550px) { + .log_success { + min-width: 150px; + height: 30px; + opacity: 1; + font-size: 13px; + } + .log_success .el-message__content { + color: #3a97e4; + font-size: 13px; + } + .log_success .el-icon-success { + color: #3a97e4; + font-size: 13px; + } +} + +.log_warning { + height: 42px; + background: rgba(219, 171, 85, 0.1); + border-radius: 35px; + opacity: 1; + border: none; + min-width: 210px; + justify-content: center; + font-size: 18px; + padding: 0 10px; +} + +.log_warning .el-message__content { + color: #dbab55; + font-size: 18px; +} + +.log_warning .el-icon-warning { + color: #dbab55; + font-size: 18px; +} + +@media (max-width: 550px) { + .log_warning { + min-width: 170px; + height: 30px; + font-size: 13px; + } + .log_warning .el-message__content { + color: #dbab55; + font-size: 13px; + } + .log_warning .el-icon-success { + color: #dbab55; + font-size: 13px; + } +} + +.dcc { + display: flex; + align-items: center; + justify-content: center; +} + +.money { + width: 20px; + height: 20px; + margin-right: 5px; +} + +.integral { + width: 20px; + height: 20px; + margin-right: 5px; +} + +.d-c { + display: flex; + align-items: center; +} + +.mh20 { + height: 20px; + color: #3fa2c7; +} + +.ih20 { + height: 20px; + color: #DBAB55; +} + +@media (max-width: 550px) { + .money, + .integral { + width: 15px; + height: 15px; + } +} + +.mengban { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 177px; + background: linear-gradient(-225deg, #1f3a58 0%, rgba(32, 29, 48, 0) 30%, rgba(248, 125, 81, 0) 100%); + z-index: 0; +} + +.textover-f { + overflow: hidden; + width: 90%; + white-space: nowrap; + text-overflow: ellipsis; + margin: 0 auto; + text-align: center; +} + +.money-money { + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + color: #fff; +} + +.integral-integral { + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + color: #DBAB55; +} + +.center { + display: flex; + align-items: center; + justify-content: center; +} + +.space_between { + display: flex; + align-items: center; + justify-content: space-between; +} + +.space_around { + display: flex; + align-items: center; + justify-content: space-around; +} + +.left { + display: flex; + align-items: center; + justify-content: left; +} + +.btn_click { + transition: all .2s ease-in; +} + +.btn_click:active { + transform: scale(0.8); + position: relative; +} + +.grade2 { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 9px; +} + +.grade3 { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 9px; +} + +.grade4 { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 9px; +} + +.column { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} + +.columns { + display: flex; + align-items: center; + justify-content: space-between; + flex-direction: column; +} + +.el-loading-spinner .path { + stroke: #787878; +} + +.left50 { + position: absolute; + left: 50%; + transform: translateX(-50%); +} + +.selectbg1 { + background-color: rgba(219, 171, 85, 0.25) !important; +} + +.selectbg2 { + background-color: rgba(194, 61, 212, 0.25) !important; +} + +.selectbg3 { + background-color: rgba(111, 79, 255, 0.25) !important; +} + +.selectbg4 { + background-color: rgba(58, 151, 228, 0.25) !important; +} + +.selectbg5 { + background-color: rgba(143, 143, 143, 0.25) !important; +} + +.loader { + --size-loader: 10px; + --size-orbe: 1px; + width: var(--size-loader); + height: var(--size-loader); + position: relative; + transform: rotate(45deg); +} + +.orbe { + position: absolute; + width: 100%; + height: 100%; + --delay: calc(var(--index) * 0.1s); + animation: orbit7456 ease-in-out 1.5s var(--delay) infinite; + opacity: calc(1 - calc(0.2 * var(--index))); +} + +.orbe::after { + position: absolute; + content: ""; + top: 0; + left: 0; + width: var(--size-orbe); + height: var(--size-orbe); + background-color: #3ae374; + box-shadow: 0px 0px 0.020px 0.2px #3ae374; + border-radius: 50%; +} + +@keyframes orbit7456 { + 0% { + } + 80% { + transform: rotate(360deg); + } + 100% { + transform: rotate(360deg); + } +} + +@keyframes infinite-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@keyframes boxscola { + 0% { + transform: scaleX(1); + transform: translateX(0); + } + 10%, + 20% { + transform: scale3d(0.9, 0.9, 0.9) rotate(-3deg); + } + 30%, + 50%, + 70%, + 90% { + transform: scale3d(1.1, 1.1, 1.1) rotate(3deg); + } + 40%, + 60%, + 80% { + transform: scale3d(1.1, 1.1, 1.1) rotate(-3deg); + } + 100% { + transform: scaleX(1); + } +} + +.roomV2 { + width: 100%; +} + +.setV2 { + overflow: hidden; +} diff --git a/odcs2.com/src/assets/style/success.min.css b/odcs2.com/src/assets/style/success.min.css new file mode 100644 index 0000000..1ae44a9 --- /dev/null +++ b/odcs2.com/src/assets/style/success.min.css @@ -0,0 +1 @@ +.mesinfo{background-color:red}.log_success{padding:0}.log_success{min-width:210px;height:42px;padding:5px 10px;background:#1F2D36;border-radius:35px;opacity:1;border:none;justify-content:center;font-size:18px}.log_success .el-message__content{color:#3a97e4;font-size:18px}.log_success .el-icon-success{color:#3a97e4;font-size:18px}@media (max-width: 550px){.log_success{min-width:150px;height:30px;opacity:1;font-size:13px}.log_success .el-message__content{color:#3a97e4;font-size:13px}.log_success .el-icon-success{color:#3a97e4;font-size:13px}}.log_warning{height:42px;background:rgba(219,171,85,0.1);border-radius:35px;opacity:1;border:none;min-width:210px;justify-content:center;font-size:18px;padding:0 10px}.log_warning .el-message__content{color:#dbab55;font-size:18px}.log_warning .el-icon-warning{color:#dbab55;font-size:18px}@media (max-width: 550px){.log_warning{min-width:170px;height:30px;font-size:13px}.log_warning .el-message__content{color:#dbab55;font-size:13px}.log_warning .el-icon-success{color:#dbab55;font-size:13px}}.dcc{display:flex;align-items:center;justify-content:center}.money{width:20px;height:20px;margin-right:5px}.integral{width:20px;height:20px;margin-right:5px}.d-c{display:flex;align-items:center}.mh20{height:20px;color:#3fa2c7}.ih20{height:20px;color:#DBAB55}@media (max-width: 550px){.money,.integral{width:15px;height:15px}}.mengban{position:absolute;left:0;top:0;width:100%;height:177px;background:linear-gradient(-225deg, rgba(37,244,132,0.2) 0%, rgba(32,29,48,0) 30%, rgba(248,125,81,0) 100%);z-index:0}.textover-f{overflow:hidden;width:90%;white-space:nowrap;text-overflow:ellipsis;margin:0 auto;text-align:center}.money-money{display:flex;align-items:center;justify-content:center;line-height:1;color:#3fa2c7}.integral-integral{display:flex;align-items:center;justify-content:center;line-height:1;color:#DBAB55}.center{display:flex;align-items:center;justify-content:center}.space_between{display:flex;align-items:center;justify-content:space-between}.space_around{display:flex;align-items:center;justify-content:space-around}.left{display:flex;align-items:center;justify-content:left}.btn_click{transition:all .2s ease-in}.btn_click:active{transform:scale(0.8);position:relative}.grade2{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:9px}.grade3{display:grid;grid-template-columns:repeat(3, minmax(0, 1fr));gap:9px}.grade4{display:grid;grid-template-columns:repeat(4, minmax(0, 1fr));gap:9px}.column{display:flex;align-items:center;justify-content:center;flex-direction:column}.columns{display:flex;align-items:center;justify-content:space-between;flex-direction:column}.el-loading-spinner .path{stroke:#787878}.left50{position:absolute;left:50%;transform:translateX(-50%)}.selectbg1{background-color:rgba(219,171,85,0.25) !important}.selectbg2{background-color:rgba(194,61,212,0.25) !important}.selectbg3{background-color:rgba(111,79,255,0.25) !important}.selectbg4{background-color:rgba(58,151,228,0.25) !important}.selectbg5{background-color:rgba(143,143,143,0.25) !important}.loader{--size-loader:10px;--size-orbe:1px;width:var(--size-loader);height:var(--size-loader);position:relative;transform:rotate(45deg)}.orbe{position:absolute;width:100%;height:100%;--delay:calc(var(--index) * 0.1s);animation:orbit7456 ease-in-out 1.5s var(--delay) infinite;opacity:calc(1 - calc(0.2 * var(--index)))}.orbe::after{position:absolute;content:"";top:0;left:0;width:var(--size-orbe);height:var(--size-orbe);background-color:#3ae374;box-shadow:0px 0px 0.020px 0.2px #3ae374;border-radius:50%}@keyframes orbit7456{0%{}80%{transform:rotate(360deg)}100%{transform:rotate(360deg)}}@keyframes infinite-spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}@keyframes boxscola{0%{transform:scaleX(1);transform:translateX(0)}10%,20%{transform:scale3d(0.9, 0.9, 0.9) rotate(-3deg)}30%,50%,70%,90%{transform:scale3d(1.1, 1.1, 1.1) rotate(3deg)}40%,60%,80%{transform:scale3d(1.1, 1.1, 1.1) rotate(-3deg)}100%{transform:scaleX(1)}}.roomV2{width:100%}.setV2{overflow:hidden} diff --git a/odcs2.com/src/assets/style/success.scss b/odcs2.com/src/assets/style/success.scss new file mode 100644 index 0000000..6998b31 --- /dev/null +++ b/odcs2.com/src/assets/style/success.scss @@ -0,0 +1,358 @@ +.mesinfo { + background-color: red; +} + +.log_success { + padding: 0; +} + +.log_success { + min-width: 210px; + height: 42px; + padding: 5px 10px; + background: #1F2D36; + border-radius: 35px; + opacity: 1; + border: none; + justify-content: center; + font-size: 18px; + + .el-message__content { + color: #3a97e4; + font-size: 18px; + } + + .el-icon-success { + color: #3a97e4; + font-size: 18px; + } + + @media (max-width: 550px) { + min-width: 150px; + height: 30px; + opacity: 1; + font-size: 13px; + + .el-message__content { + color: #3a97e4; + font-size: 13px; + } + + .el-icon-success { + color: #3a97e4; + font-size: 13px; + } + } +} + +.log_warning { + height: 42px; + background: rgba(219, 171, 85, 0.10); + border-radius: 35px; + opacity: 1; + border: none; + min-width: 210px; + justify-content: center; + font-size: 18px; + padding: 0 10px; + + .el-message__content { + color: #dbab55; + font-size: 18px; + } + + .el-icon-warning { + color: #dbab55; + font-size: 18px; + } + + @media (max-width: 550px) { + min-width: 170px; + height: 30px; + font-size: 13px; + + .el-message__content { + color: #dbab55; + font-size: 13px; + } + + .el-icon-success { + color: #dbab55; + font-size: 13px; + } + } +} + +.dcc { + display: flex; + align-items: center; + justify-content: center; +} + +.money { + width: 20px; + height: 20px; + margin-right: 5px; +} + +.integral { + width: 20px; + height: 20px; + margin-right: 5px; +} + +.d-c { + display: flex; + align-items: center; +} + +.mh20 { + height: 20px; + color: #3fa2c7; +} + +.ih20 { + height: 20px; + color: #DBAB55; +} + +@media (max-width: 550px) { + + .money, + .integral { + width: 15px; + height: 15px; + } +} + +.mengban { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 177px; + background: linear-gradient(-225deg, + #1f3a58 0%, + rgba(32, 29, 48, 0) 30%, + rgba(248, 125, 81, 0) 100%); + z-index: 0; +} + +.textover-f { + overflow: hidden; + width: 90%; + white-space: nowrap; //溢出不换行 + text-overflow: ellipsis; + margin: 0 auto; + text-align: center; +} + +.money-money { + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + color: #fff; + // font-size: 13px; +} + +.integral-integral { + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + color: #DBAB55; +} + +.center { + display: flex; + align-items: center; + justify-content: center; +} + +.space_between { + display: flex; + align-items: center; + justify-content: space-between; +} + +.space_around { + display: flex; + align-items: center; + justify-content: space-around; +} + +.left { + display: flex; + align-items: center; + justify-content: left; +} + +.btn_click { + transition: all .2s ease-in; +} + +// .btn_click:hover { + +// box-shadow: inset 0 0 10px rgba(27, 253, 156, 0.6), 0 0 9px 3px rgba(27, 253, 156, 0.2); +// } + + +.btn_click:active { + // box-shadow: inset 0 -100px 0 0 #725AC1; + transform: scale(0.8); + position: relative; +} + +.grade2 { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 9px; +} + +.grade3 { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 9px; +} + +.grade4 { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 9px; +} + +.column { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} + +.columns { + display: flex; + align-items: center; + justify-content: space-between; + flex-direction: column; +} + +.el-loading-spinner .path { + stroke: #787878; +} + +.left50 { + position: absolute; + left: 50%; + transform: translateX(-50%); +} + +.selectbg1 { + background-color: rgba(219, 171, 85, 0.25) !important; +} + +.selectbg2 { + background-color: rgba(194, 61, 212, 0.25) !important; +} + +.selectbg3 { + background-color: rgba(111, 79, 255, 0.25) !important; +} + +.selectbg4 { + background-color: rgba(58, 151, 228, 0.25) !important; +} + +.selectbg5 { + background-color: rgba(143, 143, 143, 0.25) !important; +} + +.loader { + --size-loader: 10px; + --size-orbe: 1px; + width: var(--size-loader); + height: var(--size-loader); + position: relative; + transform: rotate(45deg); +} + +.orbe { + position: absolute; + width: 100%; + height: 100%; + --delay: calc(var(--index) * 0.1s); + animation: orbit7456 ease-in-out 1.5s var(--delay) infinite; + opacity: calc(1 - calc(0.2 * var(--index))); +} + +.orbe::after { + position: absolute; + content: ""; + top: 0; + left: 0; + width: var(--size-orbe); + height: var(--size-orbe); + background-color: #3ae374; + box-shadow: 0px 0px 0.020px 0.2px #3ae374; + border-radius: 50%; +} + +@keyframes orbit7456 { + 0% {} + + 80% { + transform: rotate(360deg); + } + + 100% { + transform: rotate(360deg); + } +} + +@keyframes infinite-spin { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} + +@keyframes boxscola { + + 0% { + transform: scaleX(1); + transform: translateX(0); + } + + 10%, + 20% { + transform: scale3d(.9, .9, .9) rotate(-3deg); + } + + 30%, + 50%, + 70%, + 90% { + transform: scale3d(1.1, 1.1, 1.1) rotate(3deg); + } + + 40%, + 60%, + 80% { + transform: scale3d(1.1, 1.1, 1.1) rotate(-3deg); + } + + 100% { + transform: scaleX(1); + } +} + +// pk 公共样式 +.roomV2 { + width: 100%; +} + +.setV2 { + overflow: hidden; +} \ No newline at end of file diff --git a/odcs2.com/src/assets/tab-active.png b/odcs2.com/src/assets/tab-active.png new file mode 100644 index 0000000..cac8769 Binary files /dev/null and b/odcs2.com/src/assets/tab-active.png differ diff --git a/odcs2.com/src/assets/tab-select-bg-DCl8rbkb.png b/odcs2.com/src/assets/tab-select-bg-DCl8rbkb.png new file mode 100644 index 0000000..39a814b Binary files /dev/null and b/odcs2.com/src/assets/tab-select-bg-DCl8rbkb.png differ diff --git a/odcs2.com/src/assets/tuiguangbg.png b/odcs2.com/src/assets/tuiguangbg.png new file mode 100644 index 0000000..a8f771b Binary files /dev/null and b/odcs2.com/src/assets/tuiguangbg.png differ diff --git a/odcs2.com/src/assets/wlogo.png b/odcs2.com/src/assets/wlogo.png new file mode 100644 index 0000000..7fe72be Binary files /dev/null and b/odcs2.com/src/assets/wlogo.png differ diff --git a/odcs2.com/src/assets/yingjia.png b/odcs2.com/src/assets/yingjia.png new file mode 100644 index 0000000..1556605 Binary files /dev/null and b/odcs2.com/src/assets/yingjia.png differ diff --git a/odcs2.com/src/assets/zmqx.jpg b/odcs2.com/src/assets/zmqx.jpg new file mode 100644 index 0000000..238faf7 Binary files /dev/null and b/odcs2.com/src/assets/zmqx.jpg differ diff --git a/odcs2.com/src/assets/zmqx.png b/odcs2.com/src/assets/zmqx.png new file mode 100644 index 0000000..d3af590 Binary files /dev/null and b/odcs2.com/src/assets/zmqx.png differ diff --git a/odcs2.com/src/components/HelloWorld.vue b/odcs2.com/src/components/HelloWorld.vue new file mode 100644 index 0000000..e69de29 diff --git a/odcs2.com/src/components/WipeSidebar.vue b/odcs2.com/src/components/WipeSidebar.vue new file mode 100644 index 0000000..bc1da36 --- /dev/null +++ b/odcs2.com/src/components/WipeSidebar.vue @@ -0,0 +1,165 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/components/bannerUser.vue b/odcs2.com/src/components/bannerUser.vue new file mode 100644 index 0000000..e27735b --- /dev/null +++ b/odcs2.com/src/components/bannerUser.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/odcs2.com/src/components/bottomUser copy.vue b/odcs2.com/src/components/bottomUser copy.vue new file mode 100644 index 0000000..0b0f9fd --- /dev/null +++ b/odcs2.com/src/components/bottomUser copy.vue @@ -0,0 +1,154 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/components/bottomUser.vue b/odcs2.com/src/components/bottomUser.vue new file mode 100644 index 0000000..dfffd60 --- /dev/null +++ b/odcs2.com/src/components/bottomUser.vue @@ -0,0 +1,86 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/components/common/Swiper.vue b/odcs2.com/src/components/common/Swiper.vue new file mode 100644 index 0000000..be16d52 --- /dev/null +++ b/odcs2.com/src/components/common/Swiper.vue @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/components/common/back.vue b/odcs2.com/src/components/common/back.vue new file mode 100644 index 0000000..d5ed446 --- /dev/null +++ b/odcs2.com/src/components/common/back.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/odcs2.com/src/components/common/boxicon.vue b/odcs2.com/src/components/common/boxicon.vue new file mode 100644 index 0000000..4e296d2 --- /dev/null +++ b/odcs2.com/src/components/common/boxicon.vue @@ -0,0 +1,27 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/components/common/integral.vue b/odcs2.com/src/components/common/integral.vue new file mode 100644 index 0000000..14de481 --- /dev/null +++ b/odcs2.com/src/components/common/integral.vue @@ -0,0 +1,35 @@ + \ No newline at end of file diff --git a/odcs2.com/src/components/common/money.vue b/odcs2.com/src/components/common/money.vue new file mode 100644 index 0000000..10ac7ac --- /dev/null +++ b/odcs2.com/src/components/common/money.vue @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/odcs2.com/src/components/common/plusreduce.vue b/odcs2.com/src/components/common/plusreduce.vue new file mode 100644 index 0000000..887955f --- /dev/null +++ b/odcs2.com/src/components/common/plusreduce.vue @@ -0,0 +1,40 @@ + + + + diff --git a/odcs2.com/src/components/headerTop copy 2.vue b/odcs2.com/src/components/headerTop copy 2.vue new file mode 100644 index 0000000..292e630 --- /dev/null +++ b/odcs2.com/src/components/headerTop copy 2.vue @@ -0,0 +1,904 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/components/headerTop copy.vue b/odcs2.com/src/components/headerTop copy.vue new file mode 100644 index 0000000..be7feeb --- /dev/null +++ b/odcs2.com/src/components/headerTop copy.vue @@ -0,0 +1,308 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/components/headerTop.vue b/odcs2.com/src/components/headerTop.vue new file mode 100644 index 0000000..ccaad62 --- /dev/null +++ b/odcs2.com/src/components/headerTop.vue @@ -0,0 +1,913 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/components/illustrate.vue b/odcs2.com/src/components/illustrate.vue new file mode 100644 index 0000000..15ec27b --- /dev/null +++ b/odcs2.com/src/components/illustrate.vue @@ -0,0 +1,207 @@ + + + + + diff --git a/odcs2.com/src/components/pkHome/lottery.vue b/odcs2.com/src/components/pkHome/lottery.vue new file mode 100644 index 0000000..eb06725 --- /dev/null +++ b/odcs2.com/src/components/pkHome/lottery.vue @@ -0,0 +1,169 @@ + + + diff --git a/odcs2.com/src/components/pkHome/lotteryRounds.vue b/odcs2.com/src/components/pkHome/lotteryRounds.vue new file mode 100644 index 0000000..9008adc --- /dev/null +++ b/odcs2.com/src/components/pkHome/lotteryRounds.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/odcs2.com/src/components/pkHome/lotterySeat.vue b/odcs2.com/src/components/pkHome/lotterySeat.vue new file mode 100644 index 0000000..54d99ad --- /dev/null +++ b/odcs2.com/src/components/pkHome/lotterySeat.vue @@ -0,0 +1,543 @@ + + + + + diff --git a/odcs2.com/src/components/sliderUser.vue b/odcs2.com/src/components/sliderUser.vue new file mode 100644 index 0000000..bff1df1 --- /dev/null +++ b/odcs2.com/src/components/sliderUser.vue @@ -0,0 +1,287 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/components/swiperUser.vue b/odcs2.com/src/components/swiperUser.vue new file mode 100644 index 0000000..e9b3dda --- /dev/null +++ b/odcs2.com/src/components/swiperUser.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/odcs2.com/src/main.js b/odcs2.com/src/main.js new file mode 100644 index 0000000..a172f9c --- /dev/null +++ b/odcs2.com/src/main.js @@ -0,0 +1,66 @@ +import Vue from "vue"; +import App from "./App.vue"; +import router from "./router"; +import store from "@/vuex/store"; +import "@/api/scoket"; + +import ElementUI from "element-ui"; +import "element-ui/lib/theme-chalk/index.css"; +import "@/assets/font/iconfont.css"; +import "animate.css"; +import "@/assets/style/iconfont.css" + +import "@/assets/style/success.scss"; +import money from "@/components/common/money.vue"; +import integral from "@/components/common/integral.vue"; +import boxicon from "@/components/common/boxicon.vue"; +import handleback from "@/components/common/back.vue"; +import plusreduce from "@/components/common/plusreduce.vue"; +import illustrate from "@/components/illustrate.vue"; +// import vueSeamlessScroll from "vue-seamless-scroll"; +import { socket } from "socket.io"; +import VueAgile from 'vue-agile' +import Video from 'video.js' +import 'video.js/dist/video-js.css' +import toastRegistry from './views/toast/index' +Vue.prototype.$video = Video +// Vue.use(vueSeamlessScroll); + +Vue.use(ElementUI); +Vue.prototype.$socket = socket +Vue.component("money", money); +Vue.component("integral", integral); +Vue.component("handleback", handleback); +Vue.component("plusreduce", plusreduce); +Vue.component("boxicon", boxicon); +Vue.component("illustrate", illustrate); +Vue.config.productionTip = false; +Vue.use(VueAgile) +Vue.use(toastRegistry) +import Router from "vue-router"; + +const originalPush = Router.prototype.push; +Router.prototype.push = function push(location) { + return originalPush.call(this, location).catch((err) => err); +}; +Vue.filter('fullPath', (path) => { + if (typeof path === 'string' && path.trim()) { + const baseUrl = 'http://95.40.65.4:8081'; + // 检查 path 是否以 http 或者 https 开头 + if (/^(http|https):\/\//i.test(path)) { + return path.trim(); + } else { + return baseUrl + path.trim(); + } + } + return ''; // 返回空字符串或默认值 +}); +new Vue({ + router, + store, + beforeCreate() { + // Vue.prototype.$ws=ws + Vue.prototype.$bus = this; + }, + render: (h) => h(App), +}).$mount("#app"); diff --git a/odcs2.com/src/router/index.js b/odcs2.com/src/router/index.js new file mode 100644 index 0000000..7cd16d7 --- /dev/null +++ b/odcs2.com/src/router/index.js @@ -0,0 +1,166 @@ +import Vue from "vue"; +import VueRouter from "vue-router"; +import RollHome from "../views/rollHome/index.vue"; + +Vue.use(VueRouter); + +const routes = [ + { + path: "/", + name: "home", + component: RollHome, + }, + { + path: "/replacing", + name: "replacing", + + component: () => import("../views/replacing/index.vue"), + }, + { + path: "/pk", + name: "pk", + component: () => import("../views/pkHome/index.vue"), + }, + { + path: "/login", + name: "login", + component: () => import("../views/Home/common/login.vue"), + }, + { + path: "/roll", + redirect: "/", + }, + { + path: "/shopping", + name: "shopping", + component: () => import("../views/shopping/index.vue"), + }, + { + path: "/login", + name: "login", + component: () => import("../views/Home/common/login.vue"), + }, + { + path: "/knapsack", + name: "knapsack", + component: () => import("../views/Home/common/knapsack.vue"), + }, + { + path: "/recharge", + name: "recharge", + component: () => import("../views/Home/common/recharge.vue"), + }, + { + path: "/notifications", + name: "notifications", + component: () => import("../views/Home/common/notifications.vue"), + }, + { + path: "/personal", + name: "personal", + component: () => import("../views/Home/common/personal.vue"), + }, + { + path: "/help", + name: "help", + component: () => import("../views/Home/common/help.vue"), + }, + { + path: "/account", + name: "account", + component: () => import("../views/Home/common/account.vue"), + }, + { + path: "/task", + name: "task", + component: () => import("../views/task/index.vue"), + }, + { + path: "/welfare", + name: "welfare", + component: () => import("../views/welfare/index.vue"), + }, + { + path: "/open", + name: "open", + component: () => import("../views/openbox/index.vue"), + meta:{ + iskeep:true + } + }, + { + path: "/openbox", + name: "openbox", + component: () => import("../views/openbox/common/openbox.vue"), + }, + { + path: "/dream", + name: "dream", + component: () => import("../views/dream/index.vue"), + }, + { + path: "/hongbao", + name: "hongbao", + component: () => import("../views/hongbao/hongbao.vue"), + }, + { + path: "/downloadApp", + name: "downloadApp", + component: () => import("../views/downloadApp/downloadApp.vue"), + }, + { + path: "/home", + name: "home2", + component: () => import("../views/Home/index.vue"), + }, + { + path: "/battleroom", + name: "battleroom", + component: () => import("../views/pkHome/battleroom.vue"), + }, + { + path: "/creatroom", + name: "creatroom", + component: () => import("../views/pkHome/creatroom.vue"), + }, + { + path: "/roomDetail", + name: "roomDetail", + component: () => import("../views/pkHome/roomDetail.vue"), + }, + { + path: "/battleroom", + name: "battleroom", + component: () => import("../views/pkHome/battleroom.vue"), + }, + { + // 测试页面 + path: "/lottery", + name: "lottery", + component: () => import("../views/pkHome/test01.vue"), + }, + { + // 测试页面 + path: "/lotterySeat", + name: "lotterySeat", + component: () => import("../views/pkHome/test02.vue"), + }, + { + path: "/rolldetial", + name: "rolldetial", + component: () => import("../views/rollHome/common/rolldetial.vue"), + }, + { + path: "/index2", + name: "ceshi", + component: () => import("../views/rollHome/index2.vue"), + }, +]; + +const router = new VueRouter({ + mode: "history", + base: process.env.BASE_URL, + routes, +}); + +export default router; diff --git a/odcs2.com/src/utils/brouser.js b/odcs2.com/src/utils/brouser.js new file mode 100644 index 0000000..9f69ba6 --- /dev/null +++ b/odcs2.com/src/utils/brouser.js @@ -0,0 +1,20 @@ +const browser = () => { + const u = navigator.userAgent + return { + edge: u.indexOf('Edge') > -1, // Edge内核 + trident: u.indexOf('Trident') > -1, // IE内核 + presto: u.indexOf('Presto') > -1, // opera内核 + webKit: u.indexOf('AppleWebKit') > -1, // 苹果、谷歌内核 + gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') === -1, // 火狐内核 + mobile: !!u.match(/AppleWebKit.*Mobile.*/), // 是否为移动终端 + ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), // ios终端 + android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, // android终端 + iPhone: u.indexOf('iPhone') > -1, // 是否为iPhone或者QQHD浏览器 + iPad: u.indexOf('iPad') > -1, // 是否iPad + webApp: u.indexOf('Safari') === -1, // 是否web应用程序,没有头部与底部 + weixin: u.indexOf('MicroMessenger') > -1, // 是否微信 (2015-01-22新增) + qq: u.match(/\sQQ/i) === 'qq' // 是否QQ + } + } + export default browser + \ No newline at end of file diff --git a/odcs2.com/src/utils/bus.js b/odcs2.com/src/utils/bus.js new file mode 100644 index 0000000..70ab24e --- /dev/null +++ b/odcs2.com/src/utils/bus.js @@ -0,0 +1,4 @@ +// @/utils/bus.js +import Vue from "vue"; +const bus = new Vue(); +export default bus; diff --git a/odcs2.com/src/utils/webSocket.js b/odcs2.com/src/utils/webSocket.js new file mode 100644 index 0000000..9bd7b6e --- /dev/null +++ b/odcs2.com/src/utils/webSocket.js @@ -0,0 +1,111 @@ +import { Message } from 'element-ui' +/* import { getToken } from '@/api/index' */ // 与后端的协商,websocket请求需要带上token参数 +let websock = null +let messageCallback = null +let errorCallback = null +let wsUrl = '' +let tryTime = 0 + +// 接收ws后端返回的数据 +function websocketonmessage (e) { + /* messageCallback(JSON.parse(e.data)) */ + messageCallback(e) +} + +/** + * 发起websocket连接 + * @param {Object} agentData 需要向后台传递的参数数据 + */ +function websocketSend (agentData) { + // 加延迟是为了尽量让ws连接状态变为OPEN + setTimeout(() => { + // 添加状态判断,当为OPEN时,发送消息 + if (websock.readyState === websock.OPEN) { // websock.OPEN = 1 + // 发给后端的数据需要字符串化 + websock.send(JSON.stringify(agentData)) + } + if (websock.readyState === websock.CLOSED) { // websock.CLOSED = 3 + console.log('websock.readyState=3') + Message.error('ws连接异常,请稍候重试') + errorCallback() + } + }, 500) +} + +// 关闭ws连接 +function websocketclose (e) { + // e.code === 1000 表示正常关闭。 无论为何目的而创建, 该链接都已成功完成任务。 + // e.code !== 1000 表示非正常关闭。 + if (e && e.code !== 1000) { + Message.error('ws连接异常,请稍候重试') + errorCallback() + // // 如果需要设置异常重连则可替换为下面的代码,自行进行测试 + // if (tryTime < 10) { + // setTimeout(function() { + // websock = null + // tryTime++ + // initWebSocket() + // console.log(`第${tryTime}次重连`) + // }, 3 * 1000) + //} else { + // Message.error('重连失败!请稍后重试') + //} + } +} +// 建立ws连接 +function websocketOpen (e) { + console.log('ws连接成功') +} + +// 初始化weosocket +function initWebSocket () { + if (typeof (WebSocket) === 'undefined') { + Message.error('您的浏览器不支持WebSocket,无法获取数据') + return false + } + + const token = 'JWT=' + getToken() + // ws请求完整地址 + const requstWsUrl = wsUrl + '?' + token + websock = new WebSocket(requstWsUrl) + + websock.onmessage = function (e) { + websocketonmessage(e) + } + websock.onopen = function () { + websocketOpen() + } + websock.onerror = function () { + Message.error('ws连接异常,请稍候重试') + errorCallback() + } + websock.onclose = function (e) { + websocketclose(e) + } +} + +/** + * 发起websocket请求函数 + * @param {string} url ws连接地址 + * @param {Object} agentData 传给后台的参数 + * @param {function} successCallback 接收到ws数据,对数据进行处理的回调函数 + * @param {function} errCallback ws连接错误的回调函数 + */ +export function sendWebsocket (url, agentData, successCallback, errCallback) { + wsUrl = url + initWebSocket() + messageCallback = successCallback + errorCallback = errCallback + websocketSend(agentData) +} + +/** + * 关闭websocket函数 + */ +export function closeWebsocket () { + if (websock) { + websock.close() // 关闭websocket + websock.onclose() // 关闭websocket + } +} + diff --git a/odcs2.com/src/views/Home/common/account.vue b/odcs2.com/src/views/Home/common/account.vue new file mode 100644 index 0000000..19b2b95 --- /dev/null +++ b/odcs2.com/src/views/Home/common/account.vue @@ -0,0 +1,365 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/views/Home/common/advert.vue b/odcs2.com/src/views/Home/common/advert.vue new file mode 100644 index 0000000..3e3fcca --- /dev/null +++ b/odcs2.com/src/views/Home/common/advert.vue @@ -0,0 +1,75 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/views/Home/common/help.vue b/odcs2.com/src/views/Home/common/help.vue new file mode 100644 index 0000000..20a884c --- /dev/null +++ b/odcs2.com/src/views/Home/common/help.vue @@ -0,0 +1,393 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/views/Home/common/knapsack.vue b/odcs2.com/src/views/Home/common/knapsack.vue new file mode 100644 index 0000000..6409737 --- /dev/null +++ b/odcs2.com/src/views/Home/common/knapsack.vue @@ -0,0 +1,844 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/views/Home/common/login.vue b/odcs2.com/src/views/Home/common/login.vue new file mode 100644 index 0000000..2cfb955 --- /dev/null +++ b/odcs2.com/src/views/Home/common/login.vue @@ -0,0 +1,665 @@ + + + + + diff --git a/odcs2.com/src/views/Home/common/notifications.vue b/odcs2.com/src/views/Home/common/notifications.vue new file mode 100644 index 0000000..1241a88 --- /dev/null +++ b/odcs2.com/src/views/Home/common/notifications.vue @@ -0,0 +1,343 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/views/Home/common/personal.vue b/odcs2.com/src/views/Home/common/personal.vue new file mode 100644 index 0000000..2660da7 --- /dev/null +++ b/odcs2.com/src/views/Home/common/personal.vue @@ -0,0 +1,1290 @@ + + + + + diff --git a/odcs2.com/src/views/Home/common/recharge.vue b/odcs2.com/src/views/Home/common/recharge.vue new file mode 100644 index 0000000..8aff458 --- /dev/null +++ b/odcs2.com/src/views/Home/common/recharge.vue @@ -0,0 +1,1234 @@ + + + + + diff --git a/odcs2.com/src/views/Home/index.vue b/odcs2.com/src/views/Home/index.vue new file mode 100644 index 0000000..ead84c5 --- /dev/null +++ b/odcs2.com/src/views/Home/index.vue @@ -0,0 +1,1651 @@ + + + + + diff --git a/odcs2.com/src/views/aindex.vue b/odcs2.com/src/views/aindex.vue new file mode 100644 index 0000000..674ba84 --- /dev/null +++ b/odcs2.com/src/views/aindex.vue @@ -0,0 +1,366 @@ + + + + diff --git a/odcs2.com/src/views/downloadApp/downloadApp.vue b/odcs2.com/src/views/downloadApp/downloadApp.vue new file mode 100644 index 0000000..b8c4ab2 --- /dev/null +++ b/odcs2.com/src/views/downloadApp/downloadApp.vue @@ -0,0 +1,298 @@ + + + + + diff --git a/odcs2.com/src/views/dream/index.vue b/odcs2.com/src/views/dream/index.vue new file mode 100644 index 0000000..96a119f --- /dev/null +++ b/odcs2.com/src/views/dream/index.vue @@ -0,0 +1,1468 @@ + + + + + diff --git a/odcs2.com/src/views/hongbao/hongbao.vue b/odcs2.com/src/views/hongbao/hongbao.vue new file mode 100644 index 0000000..f0321a8 --- /dev/null +++ b/odcs2.com/src/views/hongbao/hongbao.vue @@ -0,0 +1,338 @@ + + + + + diff --git a/odcs2.com/src/views/index.vue b/odcs2.com/src/views/index.vue new file mode 100644 index 0000000..3782be5 --- /dev/null +++ b/odcs2.com/src/views/index.vue @@ -0,0 +1,347 @@ + + + + diff --git a/odcs2.com/src/views/openbox/common/openbox.vue b/odcs2.com/src/views/openbox/common/openbox.vue new file mode 100644 index 0000000..3db7e72 --- /dev/null +++ b/odcs2.com/src/views/openbox/common/openbox.vue @@ -0,0 +1,1530 @@ + + + + + diff --git a/odcs2.com/src/views/openbox/index.vue b/odcs2.com/src/views/openbox/index.vue new file mode 100644 index 0000000..6748a00 --- /dev/null +++ b/odcs2.com/src/views/openbox/index.vue @@ -0,0 +1,471 @@ + + + + + diff --git a/odcs2.com/src/views/pkHome/aaa.js b/odcs2.com/src/views/pkHome/aaa.js new file mode 100644 index 0000000..e89d58b --- /dev/null +++ b/odcs2.com/src/views/pkHome/aaa.js @@ -0,0 +1,390 @@ +getDetial() { + getFightData(this.id).then(res => { ///////////获取对战数据 + this.detialList = res.data.data;///////初始化数据 + this.roomstatus = res.data.data.status;/////房间的状态0为等待 + this.detialList.boxData = JSON.parse(res.data.data.boxData);////所有宝箱 + this.detialList.userData = JSON.parse(res.data.data.userData);///所有用户 + this.detialList.currentPeople = res.data.data.userData.length;///人数 + this.detialList.boxData = res.data.data.boxData.flatMap(item => + Array.from({ length: item.boxNum }, () => item) + );//////////////////处理宝箱数据 + this.totalround = this.detialList.boxData.length; /////几个宝箱就是几个回合 + + this.waitpeople = { people: [] };/////等待的人 + + for (let index = 0; index < this.detialList.playerNum; index++) { + const obj = (this.joinSeatNumV2.people[index] = { realget: [] }); + obj.joinSeatNum = index + 1; + if (this.detialList.userData[index]) { + this.waitpeople.people.push(this.detialList.userData[index]); + this.waitpeople.people[index].status = "1"; + this.waitpeople.people[index].joinSeatNum = index + 1; + } else { + this.waitpeople.people.push({ + status: "0", + joinSeatNum: index + 1 + }); + } + }///人数判断有几个人 + // v2 将用户分配到指定座位 + // 遍历arr1中的每一项 + this.joinSeatNumV2.status = res.data.data.status; + this.joinSeatNumV2.people.forEach(item1 => { + // 遍历arr2中的每一项,寻找joinSeatNum相等的项 + res.data.data.userData.forEach(item2 => { + if (item1.joinSeatNum === item2.joinSeatNum) { + // 如果找到相等的项,在arr1的当前项中添加realuser属性,并设置其值为arr2的当前项 + item1.realuser = item2; + } + }); + }); + //每个人 + this.joinSeatNumV2.people = this.joinSeatNumV2.people.map(item => ({ + ...item, + realdrop: [] + })); + if (this.roomstatus == "0") { /////////如果他的状态时0的话就是等待中 + ///this.get(this.totalround);//总回合数 + this.deng = setTimeout(() => { + this.getDetial() + }, 1000); + } else if (this.roomstatus == "1") { + getpkrealdetial({ + fightId: this.$route.query.id, + rounds: this.totalround + }).then(res => { + const roundresult = res.data.data.playerGainsOrnamentsData; + if (res.data.code === 500) { + console.log("结束") + } else { + this.realround = res.data.data.round;///第几回合 + if (res.data.data.playerGainsOrnamentsData == null) { + this.getpkResult() + } else { + getBoxdetial({//根据宝箱的id获取宝箱的数据 + id: this.detialList.boxData[Number(res.data.data.round - 1)].boxId + }).then(res => { + // const money= + this.joinSeatNumV2.people.forEach(item1 => { + roundresult.forEach(item2 => { + if (item1.joinSeatNum === item2.joinSeatNum) { + let drawList = this.generateRandomDrawList( + res.data.data.boxOrnamentsList, + 20 + ); + item1.roundresult = drawList; + item1.roundresult.push(item2.ornamentsData); + item1.realmoney = 0; + this.$forceUpdate(); + setTimeout(() => { + item1.realmoney += Number( + item2.ornamentsData.ornamentsPrice + ); + item1.realget.unshift(item2.ornamentsData); + }, 6200); + + } + }); + }); + this.begin = false; + setTimeout(() => { + this.begin = true; + }, 10); + }); + } + } + }); + this.aaaaa = setTimeout(() => { + this.get(this.totalround);//传总回合数 + + }, 6000); + } else if (this.roomstatus == "2") { + /* this.refresh() */ + clearTimeout(this.deng); + this.getpkResult(); + clearTimeout(this.aaaaa) + clearTimeout(this.aaa) + this.aaa = null + + clearTimeout(this.timerv2) + clearTimeout(this.ding) + this.timerv2 = null + } + }); +}, + + + + + +cleantimer(){ + clearTimeout(this.timerv2); + this.timerv2 = null; +}, +refresh() { + location.reload(); +}, + +get(round) { + this.timerv2 = setInterval(() => { + if (this.realround == null) { + + this.cleantimer(); + this.getpkResult(); + + + this.refresh() + + } else { + + + + getpkrealdetial({ + fightId: this.$route.query.id, + rounds: round + }).then(res => { + if (res.data.code == 500) { + this.cleantimer(); + + /* this.aaaa=setTimeout(() => { */ + this.getpkResult(); + /* this.getDetial(); */ + /* this.clearTimeout(this.aaaa) + }, 6500); */ + // + } else { + const roundresult = res.data.data.playerGainsOrnamentsData; + + this.realround = res.data.data.round; + if (res.data.data.playerGainsOrnamentsData == null) { + clearTimeout(this.timerv2); + this.timerv2 = null; + this.getDetial(); + } else { + getBoxdetial({ + id: this.detialList.boxData[Number(res.data.data.round - 1)] + .boxId + }).then(res => { + // const money= + + this.joinSeatNumV2.people.forEach(item1 => { + roundresult.forEach(item2 => { + if (item1.joinSeatNum === item2.joinSeatNum) { + let drawList = this.generateRandomDrawList( + res.data.data.boxOrnamentsList, + 20 + ); + item1.roundresult = drawList; + item1.roundresult.push(item2.ornamentsData); + this.$forceUpdate(); + setTimeout(() => { + item1.realmoney += Number( + item2.ornamentsData.ornamentsPrice + ); + item1.realget.unshift(item2.ornamentsData); + }, 6200); + + } + }); + }); + this.begin = false; + setTimeout(() => { + this.begin = true; + }, 100); + }); + } + } + }); + } + }, 5000); +}, + +generateRandomDrawList(ornamentsArray, drawListLength) { + const drawList = []; + + for (let i = 0; i < drawListLength; i++) { + const randomIndex = Math.floor(Math.random() * ornamentsArray.length); + drawList.push(ornamentsArray[randomIndex]); + } + + return drawList; +}, +formatter(value) { + // 格式化函数,可以根据需要自定义 + return value.toFixed(2); // 格式化为本地数字字符串 +}, + + +handlejointrue() { + let obj = { + fightId: this.$route.query.id, + joinSeatNum: this.peopleselect, + rounds: this.totalround + }; + joinFight(obj).then(() => { + this.isjoin = false; + this.dings = setTimeout(() => { + this.getDetial(); + clearTimeout(this.dings) + }, 500); + }); +}, +handleJoin(res) { + this.peopleselect = res; + this.title = "加入房间"; + this.isjoin = true; +}, + + + + + +///复制分享链接 +async handlecopy() { + const url = window.location.href; // 获取当前地址栏的URL + const textarea = document.createElement("textarea"); + textarea.value = url; + document.body.appendChild(textarea); + textarea.focus(); + textarea.select(); + // 执行复制操作 + try { + const successful = document.execCommand("copy"); + if (successful) { + // 复制成功,使用 Element UI 的 $message 显示通知 + this.$message({ + message: "复制成功", + type: "success", + customClass: "log_success" + }); + } else { + // 复制失败,显示错误信息 + this.$message({ + message: "浏览器版本过低,请升级浏览器", + type: "warning", + customClass: "log_warning" + }); + } + } catch (err) { + this.$message({ + message: "浏览器版本过低,请升级浏览器", + type: "warning", + customClass: "log_warning" + }); + } + + // 移除textarea元素 + document.body.removeChild(textarea); +}, +getrealPeople() { }, +getpkResult() { + clearTimeout(this.aaa) + clearTimeout(this.deng); + this.aaa = null + getFightResult(this.id).then(res => { + this.jieshuList = res.data.data; + this.jieshuList.winnerGainsOrnamentsData.forEach(item => { + let totalPrice = 0; + if (item.ornamentsDataList) { + item.ornamentsDataList.forEach(ornament => { + totalPrice += ornament.ornamentsPrice; + }); + item.totalprize = totalPrice; + } + }); + this.ding = setTimeout(() => { + if (this.detialList.status == "2") { + for (let i = 0; i < this.waitpeople.people.length; i++) { + // 遍历 arr2 + for ( + let j = 0; + j < this.jieshuList.winnerGainsOrnamentsData.length; + j++ + ) { + // 如果 joinSeatNum 相等 + if ( + this.waitpeople.people[i].joinSeatNum === + this.jieshuList.winnerGainsOrnamentsData[j].joinSeatNum + ) { + // 如果 arr1 的当前项没有 result 属性,则添加该属性 + + // 将 arr2 的当前项添加到 arr1 的 result 属性中 + this.waitpeople.people[ + i + ].result = this.jieshuList.winnerGainsOrnamentsData[j]; + this.waitpeople.people[i].winner = true; + } else { + this.waitpeople.people[i].winner = false; + this.waitpeople.people[i].resultcoin = "0.00"; + } + } + } + this.waitpeople.people.forEach(item => { + let resultcoin = 0; + if (item.result) { + item.result.ornamentsDataList.forEach(ornament => { + resultcoin += ornament.ornamentsPrice; + }); + item.resultcoin = resultcoin; + } + }); + this.loading = false; + this.$forceUpdate(); + } + if (this.joinSeatNumV2.status == "2") { + for (let i = 0; i < this.joinSeatNumV2.people.length; i++) { + // 遍历 arr2 + for ( + let j = 0; + j < this.jieshuList.winnerGainsOrnamentsData.length; + j++ + ) { + // 如果 joinSeatNum 相等 + if ( + this.joinSeatNumV2.people[i].joinSeatNum === + this.jieshuList.winnerGainsOrnamentsData[j].joinSeatNum + ) { + // 如果 arr1 的当前项没有 result 属性,则添加该属性 + + // 将 arr2 的当前项添加到 arr1 的 result 属性中 + this.joinSeatNumV2.people[ + i + ].result = this.jieshuList.winnerGainsOrnamentsData[j]; + this.joinSeatNumV2.people[i].winner = true; + } else { + this.joinSeatNumV2.people[i].winner = false; + this.joinSeatNumV2.people[i].resultcoin = "0.00"; + } + } + } + this.joinSeatNumV2.people.forEach(item => { + let resultcoin = 0; + if (item.result) { + item.result.ornamentsDataList.forEach(ornament => { + resultcoin += ornament.ornamentsPrice; + }); + item.resultcoin = resultcoin; + } + }); + } + }, 450); + + /* clearTimeout(this.ding) */ + + }); + // 对战结果 + setInterval(() => { + // 遍历arr2,找到arr1中对应的项,并将ornamentsDataList添加到realdrop属性中 + this.jieshuList.playerGainsOrnamentsData.forEach(item2 => { + const foundIndex = this.joinSeatNumV2.people.findIndex( + item1 => item1.joinSeatNum === item2.joinSeatNum + ); + if (foundIndex !== -1) { + // 如果找到了对应的项,则添加ornamentsDataList到realdrop中 + this.joinSeatNumV2.people[foundIndex].realdrop.push( + ...item2.ornamentsDataList + ); + } + }); + }, 700); +} \ No newline at end of file diff --git a/odcs2.com/src/views/pkHome/battleroom.vue b/odcs2.com/src/views/pkHome/battleroom.vue new file mode 100644 index 0000000..68aa13a --- /dev/null +++ b/odcs2.com/src/views/pkHome/battleroom.vue @@ -0,0 +1,1300 @@ + + + + + diff --git a/odcs2.com/src/views/pkHome/boxDetail.vue b/odcs2.com/src/views/pkHome/boxDetail.vue new file mode 100644 index 0000000..01b3a27 --- /dev/null +++ b/odcs2.com/src/views/pkHome/boxDetail.vue @@ -0,0 +1,311 @@ + + + + + diff --git a/odcs2.com/src/views/pkHome/creatroom.vue b/odcs2.com/src/views/pkHome/creatroom.vue new file mode 100644 index 0000000..29091f7 --- /dev/null +++ b/odcs2.com/src/views/pkHome/creatroom.vue @@ -0,0 +1,905 @@ + + + + + diff --git a/odcs2.com/src/views/pkHome/index.vue b/odcs2.com/src/views/pkHome/index.vue new file mode 100644 index 0000000..42a58e7 --- /dev/null +++ b/odcs2.com/src/views/pkHome/index.vue @@ -0,0 +1,1346 @@ + + + + + diff --git a/odcs2.com/src/views/pkHome/roomDetail.vue b/odcs2.com/src/views/pkHome/roomDetail.vue new file mode 100644 index 0000000..455aad3 --- /dev/null +++ b/odcs2.com/src/views/pkHome/roomDetail.vue @@ -0,0 +1,1190 @@ + + + + + diff --git a/odcs2.com/src/views/pkHome/test01.vue b/odcs2.com/src/views/pkHome/test01.vue new file mode 100644 index 0000000..7c68153 --- /dev/null +++ b/odcs2.com/src/views/pkHome/test01.vue @@ -0,0 +1,676 @@ + + + + + diff --git a/odcs2.com/src/views/pkHome/test02.vue b/odcs2.com/src/views/pkHome/test02.vue new file mode 100644 index 0000000..0ff4216 --- /dev/null +++ b/odcs2.com/src/views/pkHome/test02.vue @@ -0,0 +1,2648 @@ + + + + + diff --git a/odcs2.com/src/views/replacing/index.vue b/odcs2.com/src/views/replacing/index.vue new file mode 100644 index 0000000..b721ebd --- /dev/null +++ b/odcs2.com/src/views/replacing/index.vue @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/views/rollHome/common/rolldetial.vue b/odcs2.com/src/views/rollHome/common/rolldetial.vue new file mode 100644 index 0000000..60c23bd --- /dev/null +++ b/odcs2.com/src/views/rollHome/common/rolldetial.vue @@ -0,0 +1,1119 @@ + + + + + diff --git a/odcs2.com/src/views/rollHome/index.vue b/odcs2.com/src/views/rollHome/index.vue new file mode 100644 index 0000000..0b2bf1a --- /dev/null +++ b/odcs2.com/src/views/rollHome/index.vue @@ -0,0 +1,675 @@ + + + + + diff --git a/odcs2.com/src/views/rollHome/index2.vue b/odcs2.com/src/views/rollHome/index2.vue new file mode 100644 index 0000000..9b7039d --- /dev/null +++ b/odcs2.com/src/views/rollHome/index2.vue @@ -0,0 +1,459 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/views/shopping/index.vue b/odcs2.com/src/views/shopping/index.vue new file mode 100644 index 0000000..ef4d053 --- /dev/null +++ b/odcs2.com/src/views/shopping/index.vue @@ -0,0 +1,1438 @@ + + + + + diff --git a/odcs2.com/src/views/task/index.vue b/odcs2.com/src/views/task/index.vue new file mode 100644 index 0000000..c71ad8e --- /dev/null +++ b/odcs2.com/src/views/task/index.vue @@ -0,0 +1,377 @@ + + + + + \ No newline at end of file diff --git a/odcs2.com/src/views/toast/index.js b/odcs2.com/src/views/toast/index.js new file mode 100644 index 0000000..3a7c537 --- /dev/null +++ b/odcs2.com/src/views/toast/index.js @@ -0,0 +1,44 @@ +import vue from 'vue' + +// 这里就是我们刚刚创建的那个静态组件 +import toastComponent from './toast.vue' + +// 返回一个 扩展实例构造器 +const ToastConstructor = vue.extend(toastComponent) +const toastDom = new ToastConstructor({ + el: document.createElement('div'), + data() { + return { + text:'', + showWrap:true, // 是否显示组件 + showContent:true, + duration:0 // 作用:在隐藏组件之前,显示隐藏动画 + } + } +}) +// 定义弹出组件的函数 接收2个参数, 要显示的文本 和 显示时间 +function showToast(text, duration=2000) { + toastDom.text=text + toastDom.duration=duration + toastDom.showWrap=true + toastDom.showContent=true + document.body.appendChild(toastDom.$el) +} +function hideToast() { + + + // 提前 250ms 执行淡出动画(因为我们再css里面设置的隐藏动画持续是250ms) + setTimeout(() => {toastDom.showContent = false} ,toastDom.duration - 2250) + // 过了 duration 时间后隐藏整个组件 + setTimeout(() => {toastDom.showWrap = false} ,toastDom.duration) +} + +// 注册为全局组件的函数 +function registryToast() { + // 将组件注册到 vue 的 原型链里去, + // 这样就可以在所有 vue 的实例里面使用 this.$toast() + vue.prototype.$toast = showToast + vue.prototype.$hide = hideToast +} + +export default registryToast \ No newline at end of file diff --git a/odcs2.com/src/views/toast/toast.vue b/odcs2.com/src/views/toast/toast.vue new file mode 100644 index 0000000..cb215f8 --- /dev/null +++ b/odcs2.com/src/views/toast/toast.vue @@ -0,0 +1,39 @@ + + + \ No newline at end of file diff --git a/odcs2.com/src/views/welfare/index.vue b/odcs2.com/src/views/welfare/index.vue new file mode 100644 index 0000000..41e8168 --- /dev/null +++ b/odcs2.com/src/views/welfare/index.vue @@ -0,0 +1,1812 @@ + + + + + diff --git a/odcs2.com/src/vuex/store.js b/odcs2.com/src/vuex/store.js new file mode 100644 index 0000000..1f1f378 --- /dev/null +++ b/odcs2.com/src/vuex/store.js @@ -0,0 +1,52 @@ +import Vue from "vue"; +import Vuex from "vuex"; + +Vue.use(Vuex); + +export default new Vuex.Store({ + state: { + showstate:false, + user_info: {}, + websocketMessages: {}, + LOGIN_IS_SHOW: false, + USER_INFO: "", + battlefinallmessage: {}, + remotelogin: [], + voicefile: new Audio(require("../assets/02.mp3")), + // 是否显示广告 + ADVERT_IS_SHOW: false, + }, + mutations: { + playAudio(state){ + if(!state.voicefile.paused){ + state.voicefile.load() + return + } + state.voicefile.play() + }, + loadAudio(state){ + + state.voicefile.load() + }, + USER_INFO(state, message) { + state.USER_INFO = message; + }, + LOGIN_IS_SHOW(state, message) { + state.LOGIN_IS_SHOW = message; + }, + addMessage(state, message) { + state.websocketMessages = message; + }, + battlefinall(state, message) { + state.battlefinallmessage = message; + }, + ADVERT_IS_SHOW(state, value) { + state.ADVERT_IS_SHOW = value; + }, + }, + actions: { + processMessage({ commit }, message) { + commit("addMessage", message); + }, + }, +}); diff --git a/odcs2.com/vue.config.js b/odcs2.com/vue.config.js new file mode 100644 index 0000000..c659d2b --- /dev/null +++ b/odcs2.com/vue.config.js @@ -0,0 +1,6 @@ +module.exports = { + //指定输出路径 + outputDir: 'dist', + parallel: false + } + \ No newline at end of file diff --git a/package.sh b/package.sh new file mode 100644 index 0000000..acf73f4 --- /dev/null +++ b/package.sh @@ -0,0 +1,6 @@ +#!/bin/bash +MVN=/root/apache-maven-3.6.3/bin/mvn +export JAVA_HOME=/root/jdk-21.0.10 +export PATH=$JAVA_HOME/bin:$PATH +$MVN -version +git pull && $MVN clean && $MVN package -Dmaven.test.skip=true && cp ./ruoyi-admin/target/ruoyi-admin.jar ./ \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4b5dd2d --- /dev/null +++ b/pom.xml @@ -0,0 +1,328 @@ + + + 4.0.0 + + com.ruoyi + ruoyi + 4.8.2 + + ruoyi + http://www.ruoyi.vip + 若依管理系统 + + + 4.8.2 + UTF-8 + UTF-8 + 21 + 3.1.1 + 4.0.3 + 2.1.0 + 4.0.1 + 2.1.0 + 1.2.28 + 8.1.0 + 1.21 + 3.0.0 + 2.3.3 + 2.1.1 + 2.0.53 + 6.10.0 + 2.21.0 + 3.2.2 + 4.1.2 + 2.3 + 0.9.1 + 3.0.2 + + + + + + + io.micrometer + micrometer-bom + 1.16.3 + pom + import + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + com.alibaba + druid-spring-boot-4-starter + ${druid.version} + + + + + pro.fessional + kaptcha + ${kaptcha.version} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-spring-boot.version} + + + + + nl.basjes.parse.useragent + yauaa + ${yauaa.version} + + + eu.bitwalker + UserAgentUtils + ${bitwalker.version} + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.boot.version} + + + + + com.github.oshi + oshi-core + ${oshi.version} + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc.version} + + + + + io.springfox + springfox-boot-starter + ${swagger.version} + + + + + + + + + + + commons-io + commons-io + ${commons.io.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + + + commons-collections + commons-collections + ${commons.collections.version} + + + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson.version} + + + + + io.jsonwebtoken + jjwt + ${jwt.version} + + + + + pro.fessional + kaptcha + ${kaptcha.version} + + + + + com.ruoyi + ruoyi-quartz + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-generator + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-framework + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-system + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-common + ${ruoyi.version} + + + + com.ruoyi + skins-service + 4.8.2 + + + + + + + ruoyi-admin + ruoyi-framework + ruoyi-system + ruoyi-quartz + ruoyi-generator + ruoyi-common + skins-model + skins-service + skins-promo + + pom + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + true + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + false + + + + + \ No newline at end of file diff --git a/promo-ui2/.browserslistrc b/promo-ui2/.browserslistrc new file mode 100644 index 0000000..e7c12fd --- /dev/null +++ b/promo-ui2/.browserslistrc @@ -0,0 +1,5 @@ +Android 4.1 +IOS 7.1 +Chrome > 31 +ff > 31 +ie >= 11 \ No newline at end of file diff --git a/promo-ui2/.env.base b/promo-ui2/.env.base new file mode 100644 index 0000000..a879427 --- /dev/null +++ b/promo-ui2/.env.base @@ -0,0 +1,23 @@ +# 环境 +VITE_NODE_ENV=development + +# 接口前缀 +VITE_API_BASE_PATH=http://localhost:8081 + +# 打包路径 +VITE_BASE_PATH=/ + +# 标题 +VITE_APP_TITLE=推广后台 + +# 是否全量引入element-plus样式 +VITE_USE_ALL_ELEMENT_PLUS_STYLE=true + +# 是否开启mock +VITE_USE_MOCK=true + +# 是否使用在线图标 +VITE_USE_ONLINE_ICON=true + +# 是否隐藏全局设置按钮 +VITE_HIDE_GLOBAL_SETTING=false diff --git a/promo-ui2/.env.dev b/promo-ui2/.env.dev new file mode 100644 index 0000000..792c9ea --- /dev/null +++ b/promo-ui2/.env.dev @@ -0,0 +1,41 @@ +# 环境 +VITE_NODE_ENV=production + +# 接口前缀 +VITE_API_BASE_PATH= + +# 打包路径 +VITE_BASE_PATH=/dist-dev/ + +# 是否删除debugger +VITE_DROP_DEBUGGER=false + +# 是否删除console.log +VITE_DROP_CONSOLE=false + +# 是否sourcemap +VITE_SOURCEMAP=true + +# 输出路径 +VITE_OUT_DIR=dist-dev + +# 标题 +VITE_APP_TITLE=ElementAdmin + +# 是否包分析 +VITE_USE_BUNDLE_ANALYZER=false + +# 是否全量引入element-plus样式 +VITE_USE_ALL_ELEMENT_PLUS_STYLE=false + +# 是否开启mock +VITE_USE_MOCK=true + +# 是否切割css +VITE_USE_CSS_SPLIT=true + +# 是否使用在线图标 +VITE_USE_ONLINE_ICON=true + +# 是否隐藏全局设置按钮 +VITE_HIDE_GLOBAL_SETTING=false diff --git a/promo-ui2/.env.gitee b/promo-ui2/.env.gitee new file mode 100644 index 0000000..5536574 --- /dev/null +++ b/promo-ui2/.env.gitee @@ -0,0 +1,41 @@ +# 环境 +VITE_NODE_ENV=production + +# 接口前缀 +VITE_API_BASE_PATH= + +# 打包路径 +VITE_BASE_PATH=/vue-element-plus-admin/ + +# 是否删除debugger +VITE_DROP_DEBUGGER=true + +# 是否删除console.log +VITE_DROP_CONSOLE=true + +# 是否sourcemap +VITE_SOURCEMAP=false + +# 输出路径 +VITE_OUT_DIR=dist-pro + +# 标题 +VITE_APP_TITLE=ElementAdmin + +# 是否包分析 +VITE_USE_BUNDLE_ANALYZER=false + +# 是否全量引入element-plus样式 +VITE_USE_ALL_ELEMENT_PLUS_STYLE=false + +# 是否开启mock +VITE_USE_MOCK=true + +# 是否切割css +VITE_USE_CSS_SPLIT=true + +# 是否使用在线图标 +VITE_USE_ONLINE_ICON=true + +# 是否隐藏全局设置按钮 +VITE_HIDE_GLOBAL_SETTING=false diff --git a/promo-ui2/.env.pro b/promo-ui2/.env.pro new file mode 100644 index 0000000..1bfa1da --- /dev/null +++ b/promo-ui2/.env.pro @@ -0,0 +1,41 @@ +# 环境 +VITE_NODE_ENV=production + +# 接口前缀 +VITE_API_BASE_PATH=https://zb.wildcs2.com + +# 打包路径 +VITE_BASE_PATH=/ + +# 是否删除debugger +VITE_DROP_DEBUGGER=true + +# 是否删除console.log +VITE_DROP_CONSOLE=true + +# 是否sourcemap +VITE_SOURCEMAP=false + +# 输出路径 +VITE_OUT_DIR=dist-pro + +# 标题 +VITE_APP_TITLE=推广后台 + +# 是否包分析 +VITE_USE_BUNDLE_ANALYZER=true + +# 是否全量引入element-plus样式 +VITE_USE_ALL_ELEMENT_PLUS_STYLE=false + +# 是否开启mock +VITE_USE_MOCK=false + +# 是否切割css +VITE_USE_CSS_SPLIT=true + +# 是否使用在线图标 +VITE_USE_ONLINE_ICON=false + +# 是否隐藏全局设置按钮 +VITE_HIDE_GLOBAL_SETTING=true diff --git a/promo-ui2/.env.test b/promo-ui2/.env.test new file mode 100644 index 0000000..d0a425b --- /dev/null +++ b/promo-ui2/.env.test @@ -0,0 +1,41 @@ +# 环境 +VITE_NODE_ENV=production + +# 接口前缀 +VITE_API_BASE_PATH=http://8.160.181.6:8081 + +# 打包路径 +VITE_BASE_PATH=/ + +# 是否删除debugger +VITE_DROP_DEBUGGER=true + +# 是否删除console.log +VITE_DROP_CONSOLE=true + +# 是否sourcemap +VITE_SOURCEMAP=false + +# 输出路径 +VITE_OUT_DIR=dist-pro + +# 标题 +VITE_APP_TITLE=推广后台 + +# 是否包分析 +VITE_USE_BUNDLE_ANALYZER=true + +# 是否全量引入element-plus样式 +VITE_USE_ALL_ELEMENT_PLUS_STYLE=false + +# 是否开启mock +VITE_USE_MOCK=false + +# 是否切割css +VITE_USE_CSS_SPLIT=true + +# 是否使用在线图标 +VITE_USE_ONLINE_ICON=false + +# 是否隐藏全局设置按钮 +VITE_HIDE_GLOBAL_SETTING=true diff --git a/promo-ui2/.github/workflows/auto-merge.yml b/promo-ui2/.github/workflows/auto-merge.yml new file mode 100644 index 0000000..f9cd3d7 --- /dev/null +++ b/promo-ui2/.github/workflows/auto-merge.yml @@ -0,0 +1,131 @@ +name: Automerge + +on: + pull_request: + types: + - labeled + - unlabeled + - synchronize + - opened + - edited + - ready_for_review + - reopened + - unlocked + pull_request_review: + types: + - submitted + status: {} + +jobs: + # 合并发布版本的 pr 到 master + auto-merge: + runs-on: ubuntu-latest + steps: + - name: Automerge + uses: 'pascalgn/automerge-action@v0.14.3' + env: + BASE_BRANCHES: 'release' + GITHUB_TOKEN: '${{ secrets.TOKEN }}' + MERGE_LABELS: '' + MERGE_FILTER_AUTHOR: 'kailong321200875' + + push-to-gh-pages: + needs: [auto-merge] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Pnpm + uses: pnpm/action-setup@v2 + with: + version: latest + + - name: use Node.js 18 + uses: actions/setup-node@v2.1.2 + with: + node-version: '18.x' + + - name: Set SSH Environment + env: + DOCS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }} + run: | + mkdir -p ~/.ssh/ + echo "$ACTIONS_DEPLOY_KEY" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan github.com > ~/.ssh/known_hosts + chmod 700 ~/.ssh && chmod 600 ~/.ssh/* + git config --local user.email "321200875@qq.com" + git config --local user.name "kailong321200875" + + # 发布到 github + - name: Build Github + run: | + pnpm install --no-frozen-lockfile + pnpm run build:pro + + - name: Deploy Github + uses: peaceiris/actions-gh-pages@v3 + with: + deploy_key: ${{secrets.ACTIONS_DEPLOY_KEY}} + publish_branch: gh-pages + publish_dir: ./dist-pro + force_orphan: true + cname: element-plus-admin.cn + + push-to-gh-pages-gitee: + needs: [auto-merge] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Pnpm + uses: pnpm/action-setup@v2 + with: + version: latest + + - name: use Node.js 18 + uses: actions/setup-node@v2.1.2 + with: + node-version: '18.x' + + - name: Set SSH Environment + env: + DOCS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }} + run: | + mkdir -p ~/.ssh/ + echo "$ACTIONS_DEPLOY_KEY" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan github.com > ~/.ssh/known_hosts + chmod 700 ~/.ssh && chmod 600 ~/.ssh/* + git config --local user.email "321200875@qq.com" + git config --local user.name "kailong321200875" + + - name: Build Gitee + run: | + pnpm install --no-frozen-lockfile + pnpm run build:gitee + + # 发布到 gitee + - name: Deploy Gitee + uses: peaceiris/actions-gh-pages@v3 + with: + deploy_key: ${{secrets.ACTIONS_DEPLOY_KEY}} + publish_branch: gh-pages-gitee + publish_dir: ./dist-pro + force_orphan: true + + - name: Sync Github Repos To Gitee # 名字随便起 + uses: Yikun/hub-mirror-action@v1.1 # 使用Yikun/hub-mirror-action + with: + src: github/kailong321200875 # 源端账户名(github) + dst: gitee/kailong110120130 # 目的端账户名(gitee) + dst_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} # SSH密钥对中的私钥 + dst_token: ${{ secrets.GITEE_TOKEN }} # Gitee账户的私人令牌 + account_type: user # 账户类型 + clone_style: 'https' # 使用https方式进行clone,也可以使用ssh + debug: true # 启用后会显示所有执行命令 + force_update: true # 启用后,强制同步,即强制覆盖目的端仓库 + static_list: 'vue-element-plus-admin' # 静态同步列表,在此填写需要同步的仓库名称,可填写多个 + timeout: '600s' # git超时设置,超时后会自动重试git操作 diff --git a/promo-ui2/.github/workflows/release.yml b/promo-ui2/.github/workflows/release.yml new file mode 100644 index 0000000..2c11444 --- /dev/null +++ b/promo-ui2/.github/workflows/release.yml @@ -0,0 +1,19 @@ +on: + push: + branches: + - release + +name: Release + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - uses: GoogleCloudPlatform/release-please-action@v3 + id: release + with: + token: ${{ secrets.TOKEN }} + default-branch: release + release-type: node + package-name: standard-version + changelog-types: '[{"type": "types", "section":"Types", "hidden": false},{"type": "revert", "section":"Reverts", "hidden": false},{"type": "feat", "section": "Features", "hidden": false},{"type": "fix", "section": "Bug Fixes", "hidden": false},{"type": "improvement", "section": "Feature Improvements", "hidden": false},{"type": "docs", "section":"Docs", "hidden": false},{"type": "style", "section":"Styling", "hidden": false},{"type": "refactor", "section":"Code Refactoring", "hidden": false},{"type": "perf", "section":"Performance Improvements", "hidden": false},{"type": "test", "section":"Tests", "hidden": false},{"type": "build", "section":"Build System", "hidden": false},{"type": "ci", "section":"CI", "hidden":false}]' diff --git a/promo-ui2/.gitignore b/promo-ui2/.gitignore new file mode 100644 index 0000000..2c274c8 --- /dev/null +++ b/promo-ui2/.gitignore @@ -0,0 +1,9 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +/dist* +pnpm-debug +stats.html +.idea diff --git a/promo-ui2/.husky/commit-msg b/promo-ui2/.husky/commit-msg new file mode 100644 index 0000000..34eed8b --- /dev/null +++ b/promo-ui2/.husky/commit-msg @@ -0,0 +1 @@ +npx --no -- commitlint --edit $1 \ No newline at end of file diff --git a/promo-ui2/.husky/lintstagedrc.cjs b/promo-ui2/.husky/lintstagedrc.cjs new file mode 100644 index 0000000..fd96fcb --- /dev/null +++ b/promo-ui2/.husky/lintstagedrc.cjs @@ -0,0 +1,9 @@ +module.exports = { + '*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'], + '{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': ['prettier --parser json --write'], + 'package.json': ['prettier --write'], + '*.vue': ['prettier --write', 'stylelint --fix'], + '*.{scss,less,styl,css,html}': ['stylelint --fix', 'prettier --write'], + '*.md': ['prettier --write'], + '*.hbs': ['prettier --write'] +} diff --git a/promo-ui2/.husky/pre-commit b/promo-ui2/.husky/pre-commit new file mode 100644 index 0000000..d96ed69 --- /dev/null +++ b/promo-ui2/.husky/pre-commit @@ -0,0 +1,2 @@ +npm run ts:check +npm run lint:lint-staged \ No newline at end of file diff --git a/promo-ui2/.prettierignore b/promo-ui2/.prettierignore new file mode 100644 index 0000000..4ff8c9d --- /dev/null +++ b/promo-ui2/.prettierignore @@ -0,0 +1,9 @@ +/node_modules/** +/dist/ +/dist* +/public/* +/docs/* +/src/types/env.d.ts +/docs/**/* +/plop/**/* +CHANGELOG diff --git a/promo-ui2/.stylelintignore b/promo-ui2/.stylelintignore new file mode 100644 index 0000000..aa605b4 --- /dev/null +++ b/promo-ui2/.stylelintignore @@ -0,0 +1,6 @@ +/dist/* +/public/* +public/* +/dist* +/src/types/env.d.ts +/docs/**/* diff --git a/promo-ui2/CHANGELOG.md b/promo-ui2/CHANGELOG.md new file mode 100644 index 0000000..9d8dadd --- /dev/null +++ b/promo-ui2/CHANGELOG.md @@ -0,0 +1,1421 @@ +# Changelog + +All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +## [2.9.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.8.1...v2.9.0) (2024-09-07) + + +### Features + +* The dialog supports custom-defined window size. [#527](https://github.com/kailong321200875/vue-element-plus-admin/issues/527) ([e6affad](https://github.com/kailong321200875/vue-element-plus-admin/commit/e6affad67fed5e815563396e7ce6ab816ee737ce)) +* 全局默认box-sizing: border-box;降低元素布局难度。 ([a27b14e](https://github.com/kailong321200875/vue-element-plus-admin/commit/a27b14e7ffeeeae6a212f262d20303db02b017f1)) + + +### Bug Fixes + +* VideoPlayer 的实例未赋值 [#524](https://github.com/kailong321200875/vue-element-plus-admin/issues/524) ([0e99f83](https://github.com/kailong321200875/vue-element-plus-admin/commit/0e99f8374a6c4aac19da9105c9b0c1f4950f87c7)) +* 修复husky问题 ([7b8e58e](https://github.com/kailong321200875/vue-element-plus-admin/commit/7b8e58e0ec81e99517cbca41e04930f013773380)) +* 修复isUrl判断错误bug [#526](https://github.com/kailong321200875/vue-element-plus-admin/issues/526) ([3cd89bd](https://github.com/kailong321200875/vue-element-plus-admin/commit/3cd89bdd09ee45d97d1ea41a5ee7686ca56c4ff2)) +* 修复在表格中给按钮添加link属性后,字体颜色变成白色 [#490](https://github.com/kailong321200875/vue-element-plus-admin/issues/490) ([5d9ca8a](https://github.com/kailong321200875/vue-element-plus-admin/commit/5d9ca8ac629adeb3d9c5fbf908502e597da7a168)) +* 修复新增权限时 id 缺失前端判断错误问题 ([88a0440](https://github.com/kailong321200875/vue-element-plus-admin/commit/88a04404d429e0a7e258fc8974e328dcbc04ae7a)) +* 修复示例图标错误 ([a07f4e9](https://github.com/kailong321200875/vue-element-plus-admin/commit/a07f4e9925ba15b3a94ddbea1908e42c2184b4cb)) +* 修复非一级子菜单显示位置错误;修改滚动条样式和系统滚动条样式一致 ([e0596ef](https://github.com/kailong321200875/vue-element-plus-admin/commit/e0596ef9f1f3ed022396961bf2a82665bebaecf7)) +* 左侧菜单收起后,组件菜单的子菜单显示不全 ([574055c](https://github.com/kailong321200875/vue-element-plus-admin/commit/574055c2749b9a183af44a0aaacab45f898cfff2)) + + +### Docs + +* 修改readme ([367b350](https://github.com/kailong321200875/vue-element-plus-admin/commit/367b3508e8efaf4b2b8383f44c3fea0e1dd09e8e)) + + +### Performance Improvements + +* 移除scrollbar__view高度限定。 ([18b08e2](https://github.com/kailong321200875/vue-element-plus-admin/commit/18b08e2983c116228de5282cd6e0c84884b24238)) + +## [2.8.1](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.8.0...v2.8.1) (2024-06-20) + + +### Bug Fixes + +* [#458](https://github.com/kailong321200875/vue-element-plus-admin/issues/458) ([49451ae](https://github.com/kailong321200875/vue-element-plus-admin/commit/49451ae606009d1f5ab0b98f84535892d3fd7646)) +* [#481](https://github.com/kailong321200875/vue-element-plus-admin/issues/481) ([c77586c](https://github.com/kailong321200875/vue-element-plus-admin/commit/c77586c5670cdc63978b032bbda694a14e875838)) +* 修复 search组件的收起展开 和重置 Bug ([9a5c7bc](https://github.com/kailong321200875/vue-element-plus-admin/commit/9a5c7bcb5b51e76eac6bc1d3aebc287593b13ca1)) +* 修复css前缀无法应用问题([#482](https://github.com/kailong321200875/vue-element-plus-admin/issues/482)) ([4b43c87](https://github.com/kailong321200875/vue-element-plus-admin/commit/4b43c87949fe4a68b4be004a06dfff4c7f87fbd4)) +* 修复表格default-expand-all属性无效BUG ([6657bbc](https://github.com/kailong321200875/vue-element-plus-admin/commit/6657bbc9f11f22cbfb04a57f5629bc810575496e)) +* 修复表格合计报错问题 ([9c44006](https://github.com/kailong321200875/vue-element-plus-admin/commit/9c44006ec26bee446dc5c90b6a4546cdd84ba4dc)) + + +### Styling + +* 修改登录页样式 ([9f98b7b](https://github.com/kailong321200875/vue-element-plus-admin/commit/9f98b7be266825612f93135c460d7db2d6a8beb0)) + + +### Performance Improvements + +* 优化使用离线图标后运行慢问题 ([7e9c4a6](https://github.com/kailong321200875/vue-element-plus-admin/commit/7e9c4a6109b417a577d9ac9ecf02db52eb1964af)) + +## [2.8.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.7.0...v2.8.0) (2024-06-01) + + +### Types + +* 修复Table类型错误 ([79b917a](https://github.com/kailong321200875/vue-element-plus-admin/commit/79b917af4957aa4b47db46e034385477828f5fca)) + + +### Features + +* Add a new component CodeEditor ([#466](https://github.com/kailong321200875/vue-element-plus-admin/issues/466)) ([00989b7](https://github.com/kailong321200875/vue-element-plus-admin/commit/00989b7ac9b92685be495c15c1f11dd2546eb6be)) + + +### Bug Fixes + +* [#427](https://github.com/kailong321200875/vue-element-plus-admin/issues/427) ([a00d76e](https://github.com/kailong321200875/vue-element-plus-admin/commit/a00d76e4149b430e19c985a78b9d89ce992dba3f)) +* [#428](https://github.com/kailong321200875/vue-element-plus-admin/issues/428) ([97a1cd4](https://github.com/kailong321200875/vue-element-plus-admin/commit/97a1cd41de82dad8855c95cec6bb106541fd53a7)) +* [#432](https://github.com/kailong321200875/vue-element-plus-admin/issues/432) ([df5b716](https://github.com/kailong321200875/vue-element-plus-admin/commit/df5b7166b48b7e0e77a1fb10ab6dd353d186547e)) +* [#438](https://github.com/kailong321200875/vue-element-plus-admin/issues/438) ([f977fdb](https://github.com/kailong321200875/vue-element-plus-admin/commit/f977fdb05d018ee07baeb6db454b9a77acb89f07)) +* [#451](https://github.com/kailong321200875/vue-element-plus-admin/issues/451) ([08665a3](https://github.com/kailong321200875/vue-element-plus-admin/commit/08665a35ac606549322039d073daf8072053eef4)) +* [#465](https://github.com/kailong321200875/vue-element-plus-admin/issues/465) ([8996e01](https://github.com/kailong321200875/vue-element-plus-admin/commit/8996e01ca35bfad8c13bef321f86bdd711202e12)) +* less 变量命名与 css 关键字冲突 [#475](https://github.com/kailong321200875/vue-element-plus-admin/issues/475) ([1c56e13](https://github.com/kailong321200875/vue-element-plus-admin/commit/1c56e13c5523a86d77464eccee26b5408db028c7)) +* 修复 lint-staged 中 prettier 以 json 格式美化代码的无效命令问题 ([7b2eae1](https://github.com/kailong321200875/vue-element-plus-admin/commit/7b2eae1d6aa813e162c3ad4a0553d2df480c765f)) +* 修复 Transfer 组件 optionApi 不生效 ([198718b](https://github.com/kailong321200875/vue-element-plus-admin/commit/198718b8749a036263d756f928b5dd38cfb47701)) +* 修复富文本编辑器初始化时, 报错 Error: Cannot find a descendant at path [0,1] in node ([a65d5fd](https://github.com/kailong321200875/vue-element-plus-admin/commit/a65d5fd20334307a56cb469361e8f9bd838510c9)) +* 修复组件-查询界面:收起和展开功能bug [#473](https://github.com/kailong321200875/vue-element-plus-admin/issues/473) ([8e58eae](https://github.com/kailong321200875/vue-element-plus-admin/commit/8e58eaeed6ea9beb749afaed75edc5a4f6d9867a)) + + +### Docs + +* 更新群二维码 ([2c89dbc](https://github.com/kailong321200875/vue-element-plus-admin/commit/2c89dbc884c38511d40c92480f65aef46511cefb)) + + +### Performance Improvements + +* 已经是 FormData 对象的不用再次转换 ([d582ad4](https://github.com/kailong321200875/vue-element-plus-admin/commit/d582ad428f4b378014d063635c4afbbad944a71a)) + +## [2.7.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.6.0...v2.7.0) (2024-02-29) + + +### Features + +* IAgree ([abb6906](https://github.com/kailong321200875/vue-element-plus-admin/commit/abb69064dfdb979e2843e3a1b62a2510f6ed3637)) +* 头像列表 ([3bf28a5](https://github.com/kailong321200875/vue-element-plus-admin/commit/3bf28a5d4555bf2a10754474db81d70b04ee432a)) +* 新增个人中心页 ([4146716](https://github.com/kailong321200875/vue-element-plus-admin/commit/4146716655bfbe4ae5b780e5b52a6377efd914ec)) + + +### Bug Fixes + +* 修复启动慢问题 ([61d7ef6](https://github.com/kailong321200875/vue-element-plus-admin/commit/61d7ef642a027e9e1f942bc84322233be3ca9a82)) +* 修复第四种布局样式层级问题([#424](https://github.com/kailong321200875/vue-element-plus-admin/issues/424)) ([78aeb89](https://github.com/kailong321200875/vue-element-plus-admin/commit/78aeb897fc93cfb998f94578d1fbe4480426843f)) + + +### Docs + +* 更新群二维码 ([c8c1a1b](https://github.com/kailong321200875/vue-element-plus-admin/commit/c8c1a1b6357105da73e23adff968c3f2fad7d837)) + +## [2.6.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.5.6...v2.6.0) (2024-02-07) + + +### Features + +* add vite-plugin-url-copy ([f5ab977](https://github.com/kailong321200875/vue-element-plus-admin/commit/f5ab9776a90e0136b243601571f4619c20da3ccd)) + + +### Bug Fixes + +* Table组件中size属性的validator设置错误 ([f30e37e](https://github.com/kailong321200875/vue-element-plus-admin/commit/f30e37ee777d4f30d4ae58c4a016a1392d41c25f)) +* Table组件注册为全局组件报错问题,存在对pinia的提前引用 ([1e209a7](https://github.com/kailong321200875/vue-element-plus-admin/commit/1e209a702a5114943a615063eefd0c00f1a6a003)) +* Table组件设置 align="center" 导致横向滚动条位置错误问题 ([22f071d](https://github.com/kailong321200875/vue-element-plus-admin/commit/22f071d9268806f7abd23ab2d08e9392e377a426)) +* 修复 element-plus 2.5版本以上,el-form-item inline模式下,select宽度问题 ([f44e48d](https://github.com/kailong321200875/vue-element-plus-admin/commit/f44e48d08d3f8dd347b829166107dd62e5e18c72)) +* 修复 prettier 报错 ([f5f08f8](https://github.com/kailong321200875/vue-element-plus-admin/commit/f5f08f8f87b063d489f55ea8f19c7c802acf15f7)) +* 修复 useCrudSchemas 详情组件数据结构文案不匹配问题 ([d94fc0a](https://github.com/kailong321200875/vue-element-plus-admin/commit/d94fc0a701bcbc9343ab3e7b630e3db8f6d61623)) +* 修复cutMenu布局刷新样式问题 ([03580b0](https://github.com/kailong321200875/vue-element-plus-admin/commit/03580b0ca0c8d088589ae1d8426b1535f654361b)) +* 修复Menu组件缩略菜单弹窗内样式不统一问题 ([d5dc4e3](https://github.com/kailong321200875/vue-element-plus-admin/commit/d5dc4e32d5978fcd271e841832c9cbf1e0c87db7)) +* 修复TagsView右键菜单逻辑错误 ([901c891](https://github.com/kailong321200875/vue-element-plus-admin/commit/901c891872ef6164e3517eb8e798d6039b7b7f4e)) +* 修复test打包VITE_USE_ONLINE_ICON无效问题 ([a3436a3](https://github.com/kailong321200875/vue-element-plus-admin/commit/a3436a32c6fd746e9e2af67c3cc5a8872aabf919)) +* 修复本地化图标空白问题 ([14ff83a](https://github.com/kailong321200875/vue-element-plus-admin/commit/14ff83affcd267fbdb405d2f46e9f929a1fbfaeb)) +* 修复请求示例中,mock开启时无法取消单个请求的问题 ([d6d70a4](https://github.com/kailong321200875/vue-element-plus-admin/commit/d6d70a443cccb2fe12161b57a1f227d1ed63384a)) +* 修改兼容方式,兼容Form 组件中contentMap中类输入框或下拉选择的所有组件,特殊兼容 InputNumber 组件 ([ab98ceb](https://github.com/kailong321200875/vue-element-plus-admin/commit/ab98ceb85f52c5f7b87c2114997c63f1b80f216f)) +* 更换判断条件 ([b5cb626](https://github.com/kailong321200875/vue-element-plus-admin/commit/b5cb626bfac4df8b1a0741b5000d5b22f6cd4555)) + + +### Docs + +* 修改群二维码 ([395ff68](https://github.com/kailong321200875/vue-element-plus-admin/commit/395ff68412ff71a9b8ce670c2399da285cfed67d)) +* 更新群二维码 ([c8ccaa8](https://github.com/kailong321200875/vue-element-plus-admin/commit/c8ccaa8d49b5bf7a2784a29af6e126657ce54cda)) + + +### Styling + +* 添加TabMenu边框 ([feb3d9a](https://github.com/kailong321200875/vue-element-plus-admin/commit/feb3d9a8d07f6444c39ca89f6eb63245c06783a0)) + +## [2.5.6](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.5.5...v2.5.6) (2024-01-18) + + +### Bug Fixes + +* [#396](https://github.com/kailong321200875/vue-element-plus-admin/issues/396) ([9b2b4d4](https://github.com/kailong321200875/vue-element-plus-admin/commit/9b2b4d42a6d5fffd5012506b7cac3892774c8595)) +* [#399](https://github.com/kailong321200875/vue-element-plus-admin/issues/399) ([59d4ed4](https://github.com/kailong321200875/vue-element-plus-admin/commit/59d4ed4dd9d6b6f0d5881b4d466e7a621770ad75)) +* 修复Form组件设置了hidden还是会出现占位空白 ([0f531fd](https://github.com/kailong321200875/vue-element-plus-admin/commit/0f531fd1d0469ddd56327b0a9a7956a0d6076c91)) +* 修复无法登录问题 ([8ce00ab](https://github.com/kailong321200875/vue-element-plus-admin/commit/8ce00ab247de4061cb56f9c2f6d3079abd39aefd)) +* 修复菜单管理回显问题 ([d9ca9ba](https://github.com/kailong321200875/vue-element-plus-admin/commit/d9ca9ba5e8111b7cc3758a8bba14f7fac45c9446)) +* 升级依赖,修复vue警告 ([eafb507](https://github.com/kailong321200875/vue-element-plus-admin/commit/eafb5075d587feac0501a1adae90a176a72c240f)) + + +### Docs + +* 更新README ([28bd10f](https://github.com/kailong321200875/vue-element-plus-admin/commit/28bd10f26373ad6e139b412e08d1e2afacc4ab92)) + + +### Styling + +* 调整样式 ([09b96c7](https://github.com/kailong321200875/vue-element-plus-admin/commit/09b96c75425cd2d931e7df4ef3f330b78bf74f9e)) + + +### Performance Improvements + +* request请求根据ContentType自动转换数据 ([ef9aa62](https://github.com/kailong321200875/vue-element-plus-admin/commit/ef9aa625724b754afc565b8b1f2589376f4d5c50)) +* 使用flex布局,优化section区域min-height的繁琐计算 ([fbb6f9a](https://github.com/kailong321200875/vue-element-plus-admin/commit/fbb6f9ad4b6d5fac9bb95d0a9250b5a318680d99)) + +## [2.5.5](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.5.4...v2.5.5) (2024-01-06) + + +### Bug Fixes + +* [#276](https://github.com/kailong321200875/vue-element-plus-admin/issues/276) ([6fbc2b0](https://github.com/kailong321200875/vue-element-plus-admin/commit/6fbc2b0243e4aec0463a734c37591dc3de40f7db)) +* el-button组件和其他部分使用到相关变量的组件无法适配主题色变化问题 ([00cac6a](https://github.com/kailong321200875/vue-element-plus-admin/commit/00cac6a831c2a0bb2f8a9df8b9264f1cad13ddde)) + + +### Styling + +* 菜单支持超出省略号 ([a926c56](https://github.com/kailong321200875/vue-element-plus-admin/commit/a926c5607a162145f77d35762b3d6730d67b23f6)) +* 菜单背景支持跟随暗黑模式 ([b34aeba](https://github.com/kailong321200875/vue-element-plus-admin/commit/b34aeba10a464a0f92752fc966386286443df53a)) + + +### Performance Improvements + +* 优化ImageCropping ([069777c](https://github.com/kailong321200875/vue-element-plus-admin/commit/069777c8801c51ab28c070b2ba3f10000e9c91b4)) +* 图标选择器逻辑优化 ([c2dde25](https://github.com/kailong321200875/vue-element-plus-admin/commit/c2dde252297c94036221d5d9971781182bc2998e)) +* 表格组件预览字段拆分 ([8c5858e](https://github.com/kailong321200875/vue-element-plus-admin/commit/8c5858e2c5d42db1de37d5290ea2ca784f4d4612)) + +## [2.5.4](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.5.3...v2.5.4) (2023-12-26) + + +### Types + +* 修复全局组件属性类型无法推导 ([94160c0](https://github.com/kailong321200875/vue-element-plus-admin/commit/94160c0418816e560f440e259e1f0fd4742e0143)) + + +### Bug Fixes + +* Menu菜单组件显示bug,renderMenuItem返回的数组存在undefined数据,导致省略菜单显示问题 ([1c63757](https://github.com/kailong321200875/vue-element-plus-admin/commit/1c63757d55076d15ffdf21d647de393ca3c6b0be)) +* useClipboard在ip地址下不能使用问题 ([f3593c4](https://github.com/kailong321200875/vue-element-plus-admin/commit/f3593c453a8b8d5eb8cbd0ed5402132b027461b9)) +* 修复request请求自定义headers类型错误 ([bf2cd72](https://github.com/kailong321200875/vue-element-plus-admin/commit/bf2cd720d0e5801603848a0b1520a928443ac549)) +* 修复切换主题色缓存失败 ([1074520](https://github.com/kailong321200875/vue-element-plus-admin/commit/10745207e64d2d444636cb9d877cec9a0bebf1eb)) +* 修复非正式环境打包报错 ([3a5db42](https://github.com/kailong321200875/vue-element-plus-admin/commit/3a5db42c97f382f3fc701b8f113385b38a214583)) + + +### Styling + +* 本地化图标 ([608bf50](https://github.com/kailong321200875/vue-element-plus-admin/commit/608bf50e1cae49b7f97587395f794ae351f833f0)) +* 添加常见问题链接 ([16b9375](https://github.com/kailong321200875/vue-element-plus-admin/commit/16b93757d32c8ce2f611a62d6015072b0ecfc09a)) + + +### Code Refactoring + +* 新增列设置 ([7314065](https://github.com/kailong321200875/vue-element-plus-admin/commit/7314065c907f8ef4d184c1f3c724b67c30410ab9)) +* 重写useEventBus ([8035151](https://github.com/kailong321200875/vue-element-plus-admin/commit/80351516ced0ec2d67c30405d4a644aca8ca4bc2)) + + +### Performance Improvements + +* 优化启动速度 ([379b340](https://github.com/kailong321200875/vue-element-plus-admin/commit/379b340750eb0d4f7816f5d7c25cbd2983fd33b9)) +* 还原mock.js ([83de387](https://github.com/kailong321200875/vue-element-plus-admin/commit/83de387e2a0124804a9c99080ac841a9d6676fca)) + +## [2.5.3](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.5.2...v2.5.3) (2023-12-17) + + +### Bug Fixes + +* [#374](https://github.com/kailong321200875/vue-element-plus-admin/issues/374) ([30fb2de](https://github.com/kailong321200875/vue-element-plus-admin/commit/30fb2de6f37fe0bb00b0f364da31b07a292d59a1)) +* 修复cutMenu布局和top布局内容高度计算错误问题 ([8badd48](https://github.com/kailong321200875/vue-element-plus-admin/commit/8badd48a699aabd8fe510052d098fa6848ff5cbd)) +* 修复动态路由多开标签页404问题 ([1c5b16f](https://github.com/kailong321200875/vue-element-plus-admin/commit/1c5b16f529d2e60a1eefcadf3f416585d1adb93b)) +* 修复类型推导错误 ([649fb17](https://github.com/kailong321200875/vue-element-plus-admin/commit/649fb17d000c0d500ffcfe1f9ab6ddd73ab7ecfa)) +* 修复项目配置清楚缓存无效 ([a09ee60](https://github.com/kailong321200875/vue-element-plus-admin/commit/a09ee60bb123f5bc4bbe6d80539145d5c4b94cb8)) + + +### Code Refactoring + +* 重构描述组件样式 ([c7658d8](https://github.com/kailong321200875/vue-element-plus-admin/commit/c7658d8c70618045a7527156444ba1d564963325)) + + +### Performance Improvements + +* 优化登录记住我流程 ([2009594](https://github.com/kailong321200875/vue-element-plus-admin/commit/2009594f089722151b739598dbad5ee7fb062b6e)) + +## [2.5.2](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.5.1...v2.5.2) (2023-12-10) + + +### Bug Fixes + +* 修复mock无法使用问题 ([319aaef](https://github.com/kailong321200875/vue-element-plus-admin/commit/319aaef7eec6287a0e80f25a479918d43c051810)) + +## [2.5.1](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.5.0...v2.5.1) (2023-12-10) + + +### Bug Fixes + +* 修复表单回车刷新页面 ([2f64836](https://github.com/kailong321200875/vue-element-plus-admin/commit/2f6483652b5d130057b4422b0f3350542b4b4b1d)) +* 表单布局方式为top时,查询组件按钮位置错位 ([ca98359](https://github.com/kailong321200875/vue-element-plus-admin/commit/ca983590da72cb13392cb8897f4045fbacbc6c8d)) + + +### Docs + +* 更新Readme ([81d2dc6](https://github.com/kailong321200875/vue-element-plus-admin/commit/81d2dc6a43df8fd5799461cdafc1b7e6054cf1e1)) + + +### Styling + +* 修改视频表格样式 ([93767b6](https://github.com/kailong321200875/vue-element-plus-admin/commit/93767b65aa7c41e28a8a79a82edd2a91d20bd176)) +* 抽离BaseButton,支持按钮修改主题色 ([69539ee](https://github.com/kailong321200875/vue-element-plus-admin/commit/69539ee2d34ddfcb83cbfb25e218b94891196e76)) + + +### Performance Improvements + +* [#344](https://github.com/kailong321200875/vue-element-plus-admin/issues/344) ([7fa533b](https://github.com/kailong321200875/vue-element-plus-admin/commit/7fa533b8ba0d886c0009b350a3b5fe4b027a9126)) + +## [2.5.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.4.1...v2.5.0) (2023-12-03) + + +### Types + +* 删除无用类型 ([30e4214](https://github.com/kailong321200875/vue-element-plus-admin/commit/30e421438793b8283a0113ba50eb9aef90cfed4e)) + + +### Features + +* VideoPlayer ([7b5bbed](https://github.com/kailong321200875/vue-element-plus-admin/commit/7b5bbedbccf56049ff611005ba17a0f07b07034d)) +* 持久化缓存 ([893459d](https://github.com/kailong321200875/vue-element-plus-admin/commit/893459da7cf819b6b94477cd76fdfeeecacc287f)) +* 新增ImageCropping ([b0a43a7](https://github.com/kailong321200875/vue-element-plus-admin/commit/b0a43a70e6c93690ba4b0779527316f40297a45d)) +* 新增userStore ([77c962e](https://github.com/kailong321200875/vue-element-plus-admin/commit/77c962ea91de68299a01680a7941cf7a73c7e4a7)) +* 新增表格视频预览 ([cfc2d54](https://github.com/kailong321200875/vue-element-plus-admin/commit/cfc2d54586e73353295e7b73e2bf39e4e4d03c96)) +* 替换mock-server插件 ([b8f9a99](https://github.com/kailong321200875/vue-element-plus-admin/commit/b8f9a9940d5eb3f532421b1b85aeb1f3d9afb4b1)) +* 替换mockjs ([7c76d94](https://github.com/kailong321200875/vue-element-plus-admin/commit/7c76d945be8c46b427fe65c728ae0e70ab7a5e91)) +* 重新整理目录结构,mock请求 ([179ab26](https://github.com/kailong321200875/vue-element-plus-admin/commit/179ab2672fe7fff55c8a9c55fae22a4b6c362623)) + + +### Bug Fixes + +* [#367](https://github.com/kailong321200875/vue-element-plus-admin/issues/367) ([c8400ab](https://github.com/kailong321200875/vue-element-plus-admin/commit/c8400abd9f37405127890be1c9a559edf9f251f8)) + + +### Styling + +* 格式化代码 ([31ea31d](https://github.com/kailong321200875/vue-element-plus-admin/commit/31ea31dde8a149f4fc805c08e4fca4e755c36752)) +* 表单项宽度默认100% ([416de2b](https://github.com/kailong321200875/vue-element-plus-admin/commit/416de2b4d644f68d7db379c7cb1139c8a17f64d7)) + + +### Performance Improvements + +* 新增token过期示例 ([bdc8d35](https://github.com/kailong321200875/vue-element-plus-admin/commit/bdc8d358a1ca8f5fc6b43990899834791364e4f2)) + +## [2.4.1](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.4.0...v2.4.1) (2023-11-12) + + +### Bug Fixes + +* [#361](https://github.com/kailong321200875/vue-element-plus-admin/issues/361) ([2e7797b](https://github.com/kailong321200875/vue-element-plus-admin/commit/2e7797be68b2469d979231e6588b43d0b5bdb88b)) +* Default currentSize ([af583c7](https://github.com/kailong321200875/vue-element-plus-admin/commit/af583c71b0d1760ba4ed4cfa12458820c3f4db52)) +* 修复瀑布流示例图片无法展示 ([3477173](https://github.com/kailong321200875/vue-element-plus-admin/commit/3477173b7649eb43a1e64c91135b0e657a3c7888)) +* 修复自动格式化无效 ([bd82108](https://github.com/kailong321200875/vue-element-plus-admin/commit/bd8210858126f945bad31b3f1e0416aa178afef1)) + + +### Styling + +* 修改样式 ([92d436b](https://github.com/kailong321200875/vue-element-plus-admin/commit/92d436b8bb95c94831fcfe30678d384c3debc052)) + + +### Performance Improvements + +* 优化权限管理 ([efc1c25](https://github.com/kailong321200875/vue-element-plus-admin/commit/efc1c25db86d28438a2c324a3dc302501e1fdf8f)) +* 优化瀑布流组件 ([82eb7f1](https://github.com/kailong321200875/vue-element-plus-admin/commit/82eb7f16ad3f663be602a747b55a78f6b986da30)) + +## [2.4.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.3.0...v2.4.0) (2023-10-14) + + +### Types + +* 修改类型错误 ([4760733](https://github.com/kailong321200875/vue-element-plus-admin/commit/4760733bbe39b547285894555754bae6539190f9)) + + +### Features + +* Waterfall ([d543e56](https://github.com/kailong321200875/vue-element-plus-admin/commit/d543e56efb3b3e5800ab3ec24eda25565311eda2)) + + +### Bug Fixes + +* [#342](https://github.com/kailong321200875/vue-element-plus-admin/issues/342) ([1c51221](https://github.com/kailong321200875/vue-element-plus-admin/commit/1c512216453b17c64a09f97263fd481816badf7c)) +* [#346](https://github.com/kailong321200875/vue-element-plus-admin/issues/346) ([d392868](https://github.com/kailong321200875/vue-element-plus-admin/commit/d392868c2799c2066ba606b0cdad95c011399559)) +* [#355](https://github.com/kailong321200875/vue-element-plus-admin/issues/355) ([03d5e13](https://github.com/kailong321200875/vue-element-plus-admin/commit/03d5e130146a662a8a312e6c49f995f85ea0f9d3)) +* **Descriptions:** Add a default value ([83b09f0](https://github.com/kailong321200875/vue-element-plus-admin/commit/83b09f09ffafb2a6273a1c5274e22f842c202c32)) +* table column 中定义 selectable 无效 ([b8e043c](https://github.com/kailong321200875/vue-element-plus-admin/commit/b8e043c09c74fe00521ac0d7390331b9f223c797)) +* Table的addColumn不能添加首列 ([240178f](https://github.com/kailong321200875/vue-element-plus-admin/commit/240178fd380402571fc056ddb9c8ae44ccb1e265)) +* 修复Waterfall列数错误BUG ([1c2befa](https://github.com/kailong321200875/vue-element-plus-admin/commit/1c2befa4ddc76c625774100e3f5dd5a68a6faa45)) +* 去除控制台警告 ([4d14246](https://github.com/kailong321200875/vue-element-plus-admin/commit/4d14246de50d2ba9d652ec5ef038f4fd3597006a)) + + +### Styling + +* Descriptions样式调整 ([be73f4d](https://github.com/kailong321200875/vue-element-plus-admin/commit/be73f4da3e4bbbacf3f748f7ebfd70f825e0d15e)) +* formDemo集成图标选择器 ([99ffe6a](https://github.com/kailong321200875/vue-element-plus-admin/commit/99ffe6a86ac9961ad5b9be0171b01acdfa0cf994)) +* 修改 Search 组件图标错误 ([7c93b74](https://github.com/kailong321200875/vue-element-plus-admin/commit/7c93b74e8f3e69d6c88ef2891eb6accc99a6a1e8)) + + +### Performance Improvements + +* IconPicker新增搜索功能 ([a4d1391](https://github.com/kailong321200875/vue-element-plus-admin/commit/a4d1391390bb33d498f2ec2cc64965f1a0b0aaab)) +* useClipboard ([1db2248](https://github.com/kailong321200875/vue-element-plus-admin/commit/1db22482b43f6fb7ca8321b838fb41a5b0aff62e)) +* useNetwork ([88be3ee](https://github.com/kailong321200875/vue-element-plus-admin/commit/88be3eea10196054596945af0eb9910e998dfd42)) +* 优化请求例子 ([6b3d2e1](https://github.com/kailong321200875/vue-element-plus-admin/commit/6b3d2e14985c1a7a3c68001e17820d0e7a833a56)) +* 完善demo ([2c4ff7d](https://github.com/kailong321200875/vue-element-plus-admin/commit/2c4ff7d190c816a92d92f9c2dbe048436b2bf964)) +* 新增请求示例 ([2762aaf](https://github.com/kailong321200875/vue-element-plus-admin/commit/2762aaf09b3616944476797a6e112c350c12a0ec)) + +## [2.3.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.2.0...v2.3.0) (2023-09-24) + + +### Features + +* IconPicker ([4490d5e](https://github.com/kailong321200875/vue-element-plus-admin/commit/4490d5eeeb4389f94f90c9c45a30343324db2250)) +* 表格工具栏新增列设置功能 ([9d10ba8](https://github.com/kailong321200875/vue-element-plus-admin/commit/9d10ba821feca414b9b020322859ca4a47291005)) + + +### Bug Fixes + +* [#326](https://github.com/kailong321200875/vue-element-plus-admin/issues/326) ([c95a4e0](https://github.com/kailong321200875/vue-element-plus-admin/commit/c95a4e0763838e843cf5ce174110a01f2baa8000)) +* default interceptor response return ([c3d8540](https://github.com/kailong321200875/vue-element-plus-admin/commit/c3d8540ab284312f24d9355072f6fb4506ed6d1d)) +* 修复IconPicker BUG ([1e3aa78](https://github.com/kailong321200875/vue-element-plus-admin/commit/1e3aa789260773b1caecdaa32e1cafede22733e3)) +* 修复useCrudSchemas无法自定义label ([aa5deb1](https://github.com/kailong321200875/vue-element-plus-admin/commit/aa5deb13904e45e7cb6ec7285e936b9ebae57273)) + + +### Docs + +* 更新README ([4947c82](https://github.com/kailong321200875/vue-element-plus-admin/commit/4947c82d6770f0dce2845682f0c41d853268cf82)) +* 更新README ([c3624ce](https://github.com/kailong321200875/vue-element-plus-admin/commit/c3624cee588457e7fedaab360746500337c1b2a7)) +* 更新群二维码 ([ead1ab8](https://github.com/kailong321200875/vue-element-plus-admin/commit/ead1ab8c88c05593d539b56a811809382675faf5)) + + +### Styling + +* 修复样式层级问题 ([f92d2b6](https://github.com/kailong321200875/vue-element-plus-admin/commit/f92d2b60a956e1963b63e23b446a9d42096704e0)) +* 修改登录样式 ([bdd31f0](https://github.com/kailong321200875/vue-element-plus-admin/commit/bdd31f0621712af89d89b87ac439c3e0b398605a)) + +## [2.2.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.1.0...v2.2.0) (2023-08-27) + + +### Features + +* JsonEditor ([c0f4517](https://github.com/kailong321200875/vue-element-plus-admin/commit/c0f4517b87de5a0172a057fb9da141f758cca1fa)) +* 新增 useCrudSchemas demo ([ae0628e](https://github.com/kailong321200875/vue-element-plus-admin/commit/ae0628e3af3466c7c9d7b593b825f776843de5ec)) +* 新增useTagsView ([a869a45](https://github.com/kailong321200875/vue-element-plus-admin/commit/a869a457e6a8052531ce3040ae0d332d7afbb478)) + + +### Bug Fixes + +* [#316](https://github.com/kailong321200875/vue-element-plus-admin/issues/316) ([7582e4d](https://github.com/kailong321200875/vue-element-plus-admin/commit/7582e4d12f18ae86f5ef4ff36211c364afca5763)) +* [#317](https://github.com/kailong321200875/vue-element-plus-admin/issues/317) ([2095caa](https://github.com/kailong321200875/vue-element-plus-admin/commit/2095caaa854b686b57f47ee183419f42563a5a95)) +* [#318](https://github.com/kailong321200875/vue-element-plus-admin/issues/318) ([4169e52](https://github.com/kailong321200875/vue-element-plus-admin/commit/4169e52baaaa43765848c29c5ce222d019e81c35)) +* [#319](https://github.com/kailong321200875/vue-element-plus-admin/issues/319) ([b6ee4e5](https://github.com/kailong321200875/vue-element-plus-admin/commit/b6ee4e5d48deb3a07f289366ed3700baa3674cd6)) +* 修复useValidator报错 ([4912f6c](https://github.com/kailong321200875/vue-element-plus-admin/commit/4912f6c0586249b3de7ac7d365c8ea98af7923c7)) +* 修复动态路由无效 ([1452a1a](https://github.com/kailong321200875/vue-element-plus-admin/commit/1452a1afc77eb3f64cd3de91a05ddc15e40f4a06)) + + +### Docs + +* 更新README ([5b4defa](https://github.com/kailong321200875/vue-element-plus-admin/commit/5b4defa8c4be2de894b2cb50ae9ea739a10cf7d9)) +* 更新群二维码 ([13aa71c](https://github.com/kailong321200875/vue-element-plus-admin/commit/13aa71c5bd5b5076599501961a24a171a9133c57)) +* 更新群二维码 ([ae29e97](https://github.com/kailong321200875/vue-element-plus-admin/commit/ae29e974bfed2214d1beda703b976cdfa63070ab)) + + +### Styling + +* 修改Descriptions样式 ([cd0e05a](https://github.com/kailong321200875/vue-element-plus-admin/commit/cd0e05a6b9146af7ae64be62613724cd58e6c2a3)) + + +### Code Refactoring + +* 重构useValidator ([b8849da](https://github.com/kailong321200875/vue-element-plus-admin/commit/b8849dabe2b306831f69e84db167a367570d992a)) + + +### Performance Improvements + +* 优化动态路由 ([8793588](https://github.com/kailong321200875/vue-element-plus-admin/commit/879358821d02d5e4575dfee0d189b9fee7f2e217)) +* 完善useTagsView ([e0c55f4](https://github.com/kailong321200875/vue-element-plus-admin/commit/e0c55f40d4c1c47e29de6c4c7e9433efa978bf7f)) +* 完善useTagsView ([175abd0](https://github.com/kailong321200875/vue-element-plus-admin/commit/175abd0aa3388e8473f6ecbf63e28133fce55bd3)) +* 更新demo ([2c99cd2](https://github.com/kailong321200875/vue-element-plus-admin/commit/2c99cd20f0c25a740ac7a3a8319f7a112e69c0d3)) + +## [2.1.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v2.0.0...v2.1.0) (2023-08-12) + + +### Features + +* 新增多开标签页Demo ([5c253ce](https://github.com/kailong321200875/vue-element-plus-admin/commit/5c253ce803a9ef7ce03534ddd5f0865db4602378)) + + +### Bug Fixes + +* [#307](https://github.com/kailong321200875/vue-element-plus-admin/issues/307) ([4ce07e1](https://github.com/kailong321200875/vue-element-plus-admin/commit/4ce07e150c0bd3903cc5f43fcd88c2cb292d7690)) +* [#311](https://github.com/kailong321200875/vue-element-plus-admin/issues/311) ([bdde4cc](https://github.com/kailong321200875/vue-element-plus-admin/commit/bdde4ccd39d5d698d68b299c6e80546d4a8be89f)) +* 修复eslint错误 ([b5e47e0](https://github.com/kailong321200875/vue-element-plus-admin/commit/b5e47e04d8f5f889e0c46a2dced108d058ded94e)) +* 修复Table插槽传参错误 ([97344e6](https://github.com/kailong321200875/vue-element-plus-admin/commit/97344e68f5abb144d9e5d4ad273108858dbcfba2)) +* 修复Table组件插槽传参错误 ([c83a026](https://github.com/kailong321200875/vue-element-plus-admin/commit/c83a026d559e2854fead17d2e28fbebcf25490de)) + + +### Docs + +* 修改Readme ([ee059b7](https://github.com/kailong321200875/vue-element-plus-admin/commit/ee059b7619ad01ded9d3be20287086ddbcce3253)) +* 修改Readme ([e05f5a7](https://github.com/kailong321200875/vue-element-plus-admin/commit/e05f5a77edc175daa267e4fc6abbcfc8fec2e291)) +* 修改Readme ([b0e561d](https://github.com/kailong321200875/vue-element-plus-admin/commit/b0e561d8acd36e8780087e317cc34257956981fd)) +* 修改Readme ([fced2e0](https://github.com/kailong321200875/vue-element-plus-admin/commit/fced2e0087694445a89cf360e5e3e3013d8ca604)) +* 修改README ([dce76f0](https://github.com/kailong321200875/vue-element-plus-admin/commit/dce76f042d5243039540828a3fd982af25f37531)) +* 更新群二维码 ([607ef58](https://github.com/kailong321200875/vue-element-plus-admin/commit/607ef585d010c9ade6f54d96c2a12b36099ece74)) + + +### Styling + +* 修改TabMenu样式 ([e8cd6f9](https://github.com/kailong321200875/vue-element-plus-admin/commit/e8cd6f9e1c4387c582e461cde4d59796bf17c1bd)) + +## [2.0.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.10.0...v2.0.0) (2023-08-06) + + +### ⚠ BREAKING CHANGES + +* 重构完成 + +### Features + +* 重构完成 ([76e971e](https://github.com/kailong321200875/vue-element-plus-admin/commit/76e971ef96ad4f5cc7df58abd0559898ce70207d)) + + +### Code Refactoring + +* 重构完成 ([85f8cda](https://github.com/kailong321200875/vue-element-plus-admin/commit/85f8cda19d8cafb951f211b845aad970a661dd1e)) +* 重构完成 ([5d55597](https://github.com/kailong321200875/vue-element-plus-admin/commit/5d55597cca6c9d2bc6cb6211a01c161fa5f086ba)) + +## [1.10.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.9.9...v1.10.0) (2023-08-06) + + +### Types + +* Form类型调整 ([a0f4aeb](https://github.com/kailong321200875/vue-element-plus-admin/commit/a0f4aebc5a685366cd56b1a7bb39fa614976e3bb)) +* Form类型调整 ([674d760](https://github.com/kailong321200875/vue-element-plus-admin/commit/674d760029b451c0c6fc23a2aeac5c83992a0b27)) +* 修改类型 ([c3ac191](https://github.com/kailong321200875/vue-element-plus-admin/commit/c3ac1915045d4d59bca09ec6d19151bc5da342f1)) +* 修改类型 ([7d0476f](https://github.com/kailong321200875/vue-element-plus-admin/commit/7d0476f47c5858019db871cff2bdd19f0210f0d4)) +* 类型优化 ([283bc58](https://github.com/kailong321200875/vue-element-plus-admin/commit/283bc58d46151a8954bb81ee6bf8f499177b15fc)) +* 调整类型 ([24c8af9](https://github.com/kailong321200875/vue-element-plus-admin/commit/24c8af91835fb2c8c00e7c2673fff01f098c9944)) +* 迁移types ([ccbec86](https://github.com/kailong321200875/vue-element-plus-admin/commit/ccbec865568b1c9b3c3321d7071c164fdc350a0f)) +* 迁移types ([46b35e4](https://github.com/kailong321200875/vue-element-plus-admin/commit/46b35e48b3e7876c74159625b5149ef663396f5c)) + + +### Features + +* axios 改造 ([3238140](https://github.com/kailong321200875/vue-element-plus-admin/commit/32381408bbe418eeaca2a975305bac80ddaa03f5)) +* axios 改造 ([5807db1](https://github.com/kailong321200875/vue-element-plus-admin/commit/5807db1dc12a7ff2dbf66801a742a78974ad8f9c)) +* Descriptions组件重构 ([49e415d](https://github.com/kailong321200875/vue-element-plus-admin/commit/49e415d27788cb468c96f2a828f1df7ae65b7a3c)) +* Dialog组件重构 ([3701a04](https://github.com/kailong321200875/vue-element-plus-admin/commit/3701a04231af02ec7f7ef73533f3a22e707380fb)) +* Form useForm 完成 ([3e4e27c](https://github.com/kailong321200875/vue-element-plus-admin/commit/3e4e27c21fd59c944229856bee929f005d2ee140)) +* Form改造 ([9c724dc](https://github.com/kailong321200875/vue-element-plus-admin/commit/9c724dc9aad18397d5ecd00e53c3c24e142a34b5)) +* Icon改版 ([882f162](https://github.com/kailong321200875/vue-element-plus-admin/commit/882f162ff21c74239b638f284f52161e5791722d)) +* Radio改造 ([deeee73](https://github.com/kailong321200875/vue-element-plus-admin/commit/deeee73bcb3ad912844fddee62b1155d95d4b42b)) +* Radio改造 ([83513d5](https://github.com/kailong321200875/vue-element-plus-admin/commit/83513d519d4b6b8fbfd48db266b9bd7b3a998d63)) +* Search组件重构 ([a7f3702](https://github.com/kailong321200875/vue-element-plus-admin/commit/a7f370214481577ab82bf2871191dda717c7978a)) +* SelectV2改造完成 ([4d04734](https://github.com/kailong321200875/vue-element-plus-admin/commit/4d04734e13f6926c16aeee421feecb0d339534f0)) +* Table重构 ([94800b0](https://github.com/kailong321200875/vue-element-plus-admin/commit/94800b0120ee05ca7d534dda3e59653f38d7fda0)) +* 完善search组件demo ([cdf44a4](https://github.com/kailong321200875/vue-element-plus-admin/commit/cdf44a43a05010dbcba3a3ec0cb7c8251f16fce3)) +* 拖拽表格 ([b69b8ed](https://github.com/kailong321200875/vue-element-plus-admin/commit/b69b8ed1bde36100fc86e51fcc63805d4ea21210)) +* 新增TreeSelect表单项 ([de0cb43](https://github.com/kailong321200875/vue-element-plus-admin/commit/de0cb43566b9065250abbc71548ffeca4c8e8bf1)) +* 新增Uload ([c181887](https://github.com/kailong321200875/vue-element-plus-admin/commit/c181887f7f0c5eecc9584edfe99e9065440bdc56)) +* 新增useStorage ([dfea91c](https://github.com/kailong321200875/vue-element-plus-admin/commit/dfea91c7e1d18fa299067c62557cac61723ea861)) +* 新增权限测试页 ([3fe40ba](https://github.com/kailong321200875/vue-element-plus-admin/commit/3fe40ba62df29c2ffea9adfd65fc559489481e24)) +* 新增锁屏功能 ([e2fd349](https://github.com/kailong321200875/vue-element-plus-admin/commit/e2fd349070147c57f9400fa9a413260b7707bda2)) +* 用户列表重构 ([755cea0](https://github.com/kailong321200875/vue-element-plus-admin/commit/755cea0990d9e3b64c936f29c02e4053393a1a19)) +* 登录页改造 ([5312951](https://github.com/kailong321200875/vue-element-plus-admin/commit/5312951359b5d919b6c1a03783aa6bbaf8ec0044)) +* 综合示例重构 ([9a0259d](https://github.com/kailong321200875/vue-element-plus-admin/commit/9a0259de5c47970502db95f4dda24998ad5d9efe)) +* 菜单管理 ([c72b3a3](https://github.com/kailong321200875/vue-element-plus-admin/commit/c72b3a33aab7d3605770a64d23b8a84ef4ad68d2)) +* 角色管理 ([47016a5](https://github.com/kailong321200875/vue-element-plus-admin/commit/47016a535f2b7a22ab498bee197bc30a983f507d)) +* 部门管理 ([28d0785](https://github.com/kailong321200875/vue-element-plus-admin/commit/28d0785be842022cae7808c23e1f19eaab5fb996)) +* 重构Dialog组件示例 ([9a78ac9](https://github.com/kailong321200875/vue-element-plus-admin/commit/9a78ac977eb0cfb3bd6c2a9b96e69d9f010017f4)) + + +### Bug Fixes + +* mock数据 ([8bdac71](https://github.com/kailong321200875/vue-element-plus-admin/commit/8bdac7152f463cd98c50c9893a46bb6c111fd428)) +* 修复Form已知问题 ([097b32e](https://github.com/kailong321200875/vue-element-plus-admin/commit/097b32e1a9d92a609a66179d68b3dabe12f96b66)) +* 修复Table组件已知问题 ([b1a83f6](https://github.com/kailong321200875/vue-element-plus-admin/commit/b1a83f601838cb82fb29c036654a4cdc729997cd)) +* 修复类型错误 ([26dc886](https://github.com/kailong321200875/vue-element-plus-admin/commit/26dc886f8ccb5cf1ffc10e1d9601c827a1f960c2)) +* 样式问题修复 ([cdc7c76](https://github.com/kailong321200875/vue-element-plus-admin/commit/cdc7c76eb5ac3ccb79f5f55ff5b7ce6b8c4955e1)) +* 解决类型检测报错 ([9d93496](https://github.com/kailong321200875/vue-element-plus-admin/commit/9d9349600b3d2008e4216d49c9fa1c1b9995fa79)) +* 解决类型检测报错 ([513108c](https://github.com/kailong321200875/vue-element-plus-admin/commit/513108c00e622812e2e70dfe833435f6b5462d6e)) +* 解决类型检测报错 ([28bf8be](https://github.com/kailong321200875/vue-element-plus-admin/commit/28bf8bee45e3cc8575a356623abdbe56e30991f8)) + + +### Styling + +* Table样式修改 ([5fc57bd](https://github.com/kailong321200875/vue-element-plus-admin/commit/5fc57bdb08488f6898eafd6f28289b0567d6d9e2)) +* Table样式修改 ([411c0f7](https://github.com/kailong321200875/vue-element-plus-admin/commit/411c0f792ae8359c49e81974d8193f049120985b)) +* Table样式修改 ([d487c6a](https://github.com/kailong321200875/vue-element-plus-admin/commit/d487c6a93ec0281d76a3938e6e23ea2a4a7940c1)) +* Table样式修改 ([c7d21e3](https://github.com/kailong321200875/vue-element-plus-admin/commit/c7d21e36d012377ba863ac848d77abb5db4f475a)) +* Table样式修改 ([7f5078a](https://github.com/kailong321200875/vue-element-plus-admin/commit/7f5078a436c4d5abcaf7a420df35d2be9b3680c5)) +* 修改Dialog样式 ([e451bfc](https://github.com/kailong321200875/vue-element-plus-admin/commit/e451bfcde6e5a47d4b3022e240ffcc0576ebb9a8)) +* 修改样式 ([207c5b3](https://github.com/kailong321200875/vue-element-plus-admin/commit/207c5b3fc4e52bb06baa36cd4b659e14893785ba)) +* 完善角色管理 ([c4576bd](https://github.com/kailong321200875/vue-element-plus-admin/commit/c4576bd57bcf504733f20188202ea7d33ab1c184)) +* 布局样式优化 ([962689a](https://github.com/kailong321200875/vue-element-plus-admin/commit/962689a8bd0ed5eb17d946b8a21dec4a197f13a7)) +* 样式布局调整完成 ([7193176](https://github.com/kailong321200875/vue-element-plus-admin/commit/719317694f71e22692256bb557070343f034ffe5)) +* 用户管理样式修改 ([57a5fa7](https://github.com/kailong321200875/vue-element-plus-admin/commit/57a5fa7b82ae9f3d7a1f8ec5391f14b1d1cd32e8)) +* 移除不必要样式 ([7ef1d1e](https://github.com/kailong321200875/vue-element-plus-admin/commit/7ef1d1e3013cc5bf7fc574e67c2004f50792e66d)) +* 移除不必要样式 ([366db45](https://github.com/kailong321200875/vue-element-plus-admin/commit/366db4528254d18659e6a922817702b5b92a57b0)) +* 调整Icon悬停样式 ([64c7e48](https://github.com/kailong321200875/vue-element-plus-admin/commit/64c7e48bd18ba83e605daccbc4c2f4cc6b58695d)) +* 调整工作台样式错乱 ([cc18f29](https://github.com/kailong321200875/vue-element-plus-admin/commit/cc18f297ef50655d5773d01fcfddabc365dc53e7)) + + +### Performance Improvements + +* Dialog默认高度修改 ([0e04fce](https://github.com/kailong321200875/vue-element-plus-admin/commit/0e04fce4367d6829e8de97a249318b0309e06fd5)) +* Form Table Search Descriptions 支持嵌套赋值 ([46ddf62](https://github.com/kailong321200875/vue-element-plus-admin/commit/46ddf62d2d4ce1a653f47695cb0bb3475aa16bd8)) +* ImageViewer组件优化 ([3b9c3d8](https://github.com/kailong321200875/vue-element-plus-admin/commit/3b9c3d8b757646eaf74625403112a969bfd15e55)) +* 优化Form事件传递 ([69cafb3](https://github.com/kailong321200875/vue-element-plus-admin/commit/69cafb3b7b2ce7ecbd9f2e8ef09e250817e9a55c)) +* 优化Search组件 ([e548668](https://github.com/kailong321200875/vue-element-plus-admin/commit/e548668ccef8c41d9ac7d9fe39ffe66471d160d2)) +* 优化表单组件 ([77a3866](https://github.com/kailong321200875/vue-element-plus-admin/commit/77a38662488ab9ff4cbe5ff3cf9b65eea34abca1)) +* 优化锁屏组件 ([4f8330a](https://github.com/kailong321200875/vue-element-plus-admin/commit/4f8330a4faf6cc98a9bac17bd3e1719ae1b30c81)) + +## [1.9.9](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.9.8...v1.9.9) (2023-04-13) + + +### Bug Fixes + +* 使用动态路由时,多级路由只有一个子路且父路由未使用alwaysShow时,子路由未添加至路由中 ([9b330a1](https://github.com/kailong321200875/vue-element-plus-admin/commit/9b330a1f513d3af9233b9a9dde6bdfeeefbc3393)) + +## [1.9.8](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.9.7...v1.9.8) (2023-04-12) + + +### Bug Fixes + +* 修复已知问题 ([0a6f306](https://github.com/kailong321200875/vue-element-plus-admin/commit/0a6f306686ea024e30bcdccac34e485b8526e38f)) + +## [1.9.7](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.9.6...v1.9.7) (2023-03-28) + + +### Bug Fixes + +* 修复表格与搜索框字段不能不一致的问题 ([5c1cd29](https://github.com/kailong321200875/vue-element-plus-admin/commit/5c1cd298defefb36b858adc766b776a0a7b9bd66)) + +## [1.9.6](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.9.5...v1.9.6) (2023-03-22) + + +### Bug Fixes + +* 修改 Editor 的 z-index 使其不会遮挡『综合示例 - 新增』界面的下拉菜单 ([c046e45](https://github.com/kailong321200875/vue-element-plus-admin/commit/c046e4554ba8fd99614484d8fb636650072b833e)) + +## [1.9.5](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.9.4...v1.9.5) (2023-03-13) + + +### Bug Fixes + +* 面包屑:1.修复使用动态路由的时候,无法显示的bug ([8790c8c](https://github.com/kailong321200875/vue-element-plus-admin/commit/8790c8cbd8d63ea0f8f276fd5af006f39b06e7d3)) + +## [1.9.4](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.9.3...v1.9.4) (2023-03-03) + + +### Bug Fixes + +* 修复已知BUG ([782b8e2](https://github.com/kailong321200875/vue-element-plus-admin/commit/782b8e2f94c867c3ee282287c37c888fff93fc55)) + +## [1.9.3](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.9.2...v1.9.3) (2023-03-01) + + +### Types + +* 修复类型错误 ([297b2c6](https://github.com/kailong321200875/vue-element-plus-admin/commit/297b2c69a239b487126c3b9316645a1b5f06bb7c)) + + +### Styling + +* 抽屉弹出样式问题 ([16a3eef](https://github.com/kailong321200875/vue-element-plus-admin/commit/16a3eef85a1ffb296bd44f67d89a911ecaf1c25e)) +* 调整主题切换样式 ([6e6beef](https://github.com/kailong321200875/vue-element-plus-admin/commit/6e6beefc3c380f7297985adcabcf966fbd2c5197)) + +## [1.9.2](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.9.1...v1.9.2) (2023-01-16) + + +### Bug Fixes + +* 修复TS类型错误 ([1c06a27](https://github.com/kailong321200875/vue-element-plus-admin/commit/1c06a27b900a891cd0b47098062cebc984ff6505)) + +## [1.9.1](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.9.0...v1.9.1) (2023-01-11) + + +### Bug Fixes + +* dark mode toggle ([bbc7646](https://github.com/kailong321200875/vue-element-plus-admin/commit/bbc764601ec864c2fdbe3ad78c083c5ae80615e0)) + +## [1.9.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.8.7...v1.9.0) (2022-12-28) + + +### Features + +* 添加打包进度条 ([354e87f](https://github.com/kailong321200875/vue-element-plus-admin/commit/354e87f7c533ad8e93ef484b47d0fe16f17048c9)) + + +### Bug Fixes + +* husky ([6fe5b2e](https://github.com/kailong321200875/vue-element-plus-admin/commit/6fe5b2e6c781b251bff5f0ac936c04dcfe5ef95f)) + +## [1.8.7](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.8.6...v1.8.7) (2022-12-05) + + +### Bug Fixes + +* 解决iframe无法正常工作 ([4fcc46f](https://github.com/kailong321200875/vue-element-plus-admin/commit/4fcc46fccf747b47909e2079c4f2abc5dbfb1a0c)) + +## [1.8.6](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.8.5...v1.8.6) (2022-11-21) + + +### Bug Fixes + +* 修复Search组件无法默认值 ([3368fda](https://github.com/kailong321200875/vue-element-plus-admin/commit/3368fda251bd3ff5a8e0059b3b33f9c0339d236b)) + +## [1.8.5](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.8.4...v1.8.5) (2022-11-17) + + +### Bug Fixes + +* 修复Form赋值问题 ([f37cc1b](https://github.com/kailong321200875/vue-element-plus-admin/commit/f37cc1b5801add3ada168dbbcf4cd2c340f0e30d)) + +## [1.8.4](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.8.3...v1.8.4) (2022-11-07) + + +### Bug Fixes + +* 修复option禁用属性无效 ([0b671e9](https://github.com/kailong321200875/vue-element-plus-admin/commit/0b671e914e396c7666ad5e34768a6e29f7dfbd33)) + + +### Styling + +* input默认宽度与select对齐 ([0b671e9](https://github.com/kailong321200875/vue-element-plus-admin/commit/0b671e914e396c7666ad5e34768a6e29f7dfbd33)) + +## [1.8.3](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.8.2...v1.8.3) (2022-10-28) + + +### Performance Improvements + +* 优化描述组件 ([73ecc98](https://github.com/kailong321200875/vue-element-plus-admin/commit/73ecc98671d430013920246d98ce9ab1752e56eb)) + +## [1.8.2](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.8.1...v1.8.2) (2022-10-18) + + +### Bug Fixes + +* Correct spelling of words(aciton →action) ([eb405b2](https://github.com/kailong321200875/vue-element-plus-admin/commit/eb405b2a9041ca0ad4455db79bf617ec910dc485)) +* Correct spelling of words(tigger →trigger) ([c2ca2d7](https://github.com/kailong321200875/vue-element-plus-admin/commit/c2ca2d736c92e02380923a6741450844acb41a38)) + +## [1.8.1](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.8.0...v1.8.1) (2022-10-11) + + +### Bug Fixes + +* 修复cutMenu收起时 ([993af6b](https://github.com/kailong321200875/vue-element-plus-admin/commit/993af6bb6576249e66e0c0ea592ebf851f65ab8c)) + + +### Styling + +* cutMenu层级样式 ([32d2408](https://github.com/kailong321200875/vue-element-plus-admin/commit/32d2408588c487cff2cf73e3cc132e5105ff4459)) + +## [1.8.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.7.1...v1.8.0) (2022-10-10) + + +### Features + +* types优化 ([3351155](https://github.com/kailong321200875/vue-element-plus-admin/commit/33511553cd9055b036b2d7491f9c2eda123f8b22)) + + +### Styling + +* 优化第四种布局 ([122fa62](https://github.com/kailong321200875/vue-element-plus-admin/commit/122fa62d859413d16175e0d97c7bf13f232dbb3a)) + +## [1.7.1](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.7.0...v1.7.1) (2022-10-10) + + +### Bug Fixes + +* 修正types提示错误 ([ef3e006](https://github.com/kailong321200875/vue-element-plus-admin/commit/ef3e006859dcd8b93ffb7cffcaeae24cbb330f2a)) + +## [1.7.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.6.6...v1.7.0) (2022-10-09) + + +### Features + +* type抽离 ([8b4fa1a](https://github.com/kailong321200875/vue-element-plus-admin/commit/8b4fa1aa21aa2c1379288315ccd64a6f3375be51)) + +## [1.6.6](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.6.5...v1.6.6) (2022-10-09) + + +### Bug Fixes + +* table search params ([a62929a](https://github.com/kailong321200875/vue-element-plus-admin/commit/a62929a8dac21028d3dd1cddf98189492c33b093)) + +## [1.6.5](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.6.4...v1.6.5) (2022-10-08) + + +### Bug Fixes + +* The attribute of option does not work ([d946920](https://github.com/kailong321200875/vue-element-plus-admin/commit/d946920e61ed81beacf9f1f8be7ee1f50505f64d)) + + +### Performance Improvements + +* perf store ([d416178](https://github.com/kailong321200875/vue-element-plus-admin/commit/d416178d69ca6100be4b635922b1a22d27629f08)) +* token test ([b320e65](https://github.com/kailong321200875/vue-element-plus-admin/commit/b320e658d1a559a6eaebdf374d63649c223c2ecd)) + +## [1.6.4](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.6.3...v1.6.4) (2022-09-21) + + +### Bug Fixes + +* fix bug ([da39f3b](https://github.com/kailong321200875/vue-element-plus-admin/commit/da39f3bc904ca2d80f432a31709725f9a57deb19)) + +## [1.6.3](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.6.2...v1.6.3) (2022-08-20) + + +### Bug Fixes + +* 修复重定向错误 ([89d03fd](https://github.com/kailong321200875/vue-element-plus-admin/commit/89d03fd067e7aca565ceb84ea9276f340bbfcb60)) + + +### Styling + +* 调整样式 ([d29e151](https://github.com/kailong321200875/vue-element-plus-admin/commit/d29e151f8a660031a685f6ef9f789532b1b7b58b)) + +## [1.6.2](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.6.1...v1.6.2) (2022-08-13) + + +### Styling + +* Misspelling ([c43e833](https://github.com/kailong321200875/vue-element-plus-admin/commit/c43e833582e4f14ac78b0683f1eb3bdeb9fb4821)) +* perfect tableDemo ([c589edd](https://github.com/kailong321200875/vue-element-plus-admin/commit/c589edd960b23ad0c8b56d2c7880b61014114d45)) + + +### Code Refactoring + +* refactor axios ([0980640](https://github.com/kailong321200875/vue-element-plus-admin/commit/0980640f65fc80e3e58ba49e98db10b7b1b80077)) + +## [1.6.1](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.6.0...v1.6.1) (2022-07-30) + + +### Bug Fixes + +* fix menu active bug ([ff59fc7](https://github.com/kailong321200875/vue-element-plus-admin/commit/ff59fc7e133202945360a7e210f9cdf3a4a89dd7)) +* Pie chart data not updated ([55d4ce7](https://github.com/kailong321200875/vue-element-plus-admin/commit/55d4ce7e35ff9a0c5590bc3589160cfd304d3ae5)) + + +### Performance Improvements + +* add static router ([55522b0](https://github.com/kailong321200875/vue-element-plus-admin/commit/55522b0661a8df3ad3c8afafcc9f8fcb162c5a00)) + + +### Styling + +* tagviews styles update ([bff7d93](https://github.com/kailong321200875/vue-element-plus-admin/commit/bff7d9370db7a9c171828721bb99643dac2f235d)) + +## [1.6.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.5.4...v1.6.0) (2022-07-18) + + +### Features + +* 添加按钮权限 ([7bef662](https://github.com/kailong321200875/vue-element-plus-admin/commit/7bef662db1e91aa8164e9f7a92de3fe4480a3c3b)) + + +### Performance Improvements + +* 移除md5依赖 ([a123097](https://github.com/kailong321200875/vue-element-plus-admin/commit/a123097f1f38eac45e945c7d3fdccafc16ea9b69)) + + +### Styling + +* 压缩图片尺寸 ([ae3c565](https://github.com/kailong321200875/vue-element-plus-admin/commit/ae3c5657b604ffbdae5ce3ce3603626ad4acc5e5)) + + +### Docs + +* vite2 to vite3 ([b3918b9](https://github.com/kailong321200875/vue-element-plus-admin/commit/b3918b9c3c5de4b48811ec95967851cfb3c231e1)) +* vite2 to vite3 ([aaf07de](https://github.com/kailong321200875/vue-element-plus-admin/commit/aaf07de77aa600332880a894faa35757f787c012)) + +## [1.6.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.5.4...v1.6.0) (2022-07-18) + + +### Features + +* 添加按钮权限 ([7bef662](https://github.com/kailong321200875/vue-element-plus-admin/commit/7bef662db1e91aa8164e9f7a92de3fe4480a3c3b)) + + +### Performance Improvements + +* 移除md5依赖 ([a123097](https://github.com/kailong321200875/vue-element-plus-admin/commit/a123097f1f38eac45e945c7d3fdccafc16ea9b69)) + + +### Styling + +* 压缩图片尺寸 ([ae3c565](https://github.com/kailong321200875/vue-element-plus-admin/commit/ae3c5657b604ffbdae5ce3ce3603626ad4acc5e5)) + + +### Docs + +* vite2 to vite3 ([b3918b9](https://github.com/kailong321200875/vue-element-plus-admin/commit/b3918b9c3c5de4b48811ec95967851cfb3c231e1)) +* vite2 to vite3 ([aaf07de](https://github.com/kailong321200875/vue-element-plus-admin/commit/aaf07de77aa600332880a894faa35757f787c012)) + +## [1.5.4](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.5.3...v1.5.4) (2022-07-16) + + +### Bug Fixes + +* fix build:test error ([14530cf](https://github.com/kailong321200875/vue-element-plus-admin/commit/14530cf790bfbbe37c72fa831f0376bbb4209e9d)) + +## [1.5.3](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.5.2...v1.5.3) (2022-07-01) + + +### Bug Fixes + +* fix useCrudSchemas not work ([0a855b9](https://github.com/kailong321200875/vue-element-plus-admin/commit/0a855b93e282dfe7863b3fce31dde5d7e0d3e2b6)) + +## [1.5.2](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.5.1...v1.5.2) (2022-07-01) + + +### Performance Improvements + +* add useCrudSchemas demo ([ca3ce54](https://github.com/kailong321200875/vue-element-plus-admin/commit/ca3ce54630b723d87415b14c642440d6734876ff)) + +## [1.5.1](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.5.0...v1.5.1) (2022-07-01) + + +### Bug Fixes + +* change showMainRoute to canTo ([5e292f8](https://github.com/kailong321200875/vue-element-plus-admin/commit/5e292f8a2b2ded7297a2a76893e113ac81517d23)) + + +### Performance Improvements + +* dialog combine with form ([34aefb6](https://github.com/kailong321200875/vue-element-plus-admin/commit/34aefb64ab9237521a1225925264818eebff9ad3)) +* dynamic options demo ([1acb4d7](https://github.com/kailong321200875/vue-element-plus-admin/commit/1acb4d7e8f449ba342699f1b4387ac2404a4c1fb)) +* dynamic options demo ([9a3b617](https://github.com/kailong321200875/vue-element-plus-admin/commit/9a3b6177aa0fbc99c86c5073a1c6c696e1eaf890)) +* useCrudSchemas cutom label ([7864d83](https://github.com/kailong321200875/vue-element-plus-admin/commit/7864d830e2134d814609e722b7bad1754ea9460e)) + +## [1.5.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.4.5...v1.5.0) (2022-06-25) + + +### Features + +* refactoring API ([37b7583](https://github.com/kailong321200875/vue-element-plus-admin/commit/37b75839a591648b145065432efb1dc8c7a3b917)) + + +### Bug Fixes + +* 修复axios已知问题 ([537af57](https://github.com/kailong321200875/vue-element-plus-admin/commit/537af57a0aaa24c88ebe75acf52dc0403a58b04b)) + + +### Performance Improvements + +* perf axios config ([39edd84](https://github.com/kailong321200875/vue-element-plus-admin/commit/39edd84023109a84683c21ea33e41bd024756520)) + +## [1.4.5](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.4.4...v1.4.5) (2022-06-09) + + +### Bug Fixes + +* fix tagsview not work ([d88e051](https://github.com/kailong321200875/vue-element-plus-admin/commit/d88e0514349e877f1c5280a48f9b1bd2bfd622bf)) +* fix tagsview not work ([1bf2d4c](https://github.com/kailong321200875/vue-element-plus-admin/commit/1bf2d4c77287fdca7ed1cb8c9998a53f1375dc6f)) + + +### Types + +* fix types error ([586486a](https://github.com/kailong321200875/vue-element-plus-admin/commit/586486a68d4bf2a024e50a79945b4007324f642d)) + +## [1.4.4](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.4.3...v1.4.4) (2022-06-06) + + +### Types + +* fix type error ([d66f12e](https://github.com/kailong321200875/vue-element-plus-admin/commit/d66f12e0e77f6acf485bae06509d9ea4abcd1eaa)) + +### [1.4.3](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.4.2...v1.4.3) (2022-06-01) + + +### Bug Fixes + +* multiple requests when pageSize change and currentPage isn't 1 ([f71a250](https://github.com/kailong321200875/vue-element-plus-admin/commit/f71a2503bc521c01e7102feecf4ec39a5224a6bb)) + +### [1.4.2](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.4.1...v1.4.2) (2022-05-15) + + +### Styling + +* fix dark mode bug ([2f9fd5d](https://github.com/kailong321200875/vue-element-plus-admin/commit/2f9fd5d21550d771ec12ae3540b975e2eebcd25b)) + +### [1.4.1](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.4.0...v1.4.1) (2022-05-12) + + +### Bug Fixes + +* the warning of VSCode extensions ([a368c21](https://github.com/kailong321200875/vue-element-plus-admin/commit/a368c21fb9c41f98f31f51586a2023076a8a9132)) + +## [1.4.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.3.2...v1.4.0) (2022-05-10) + + +### Features + +* add dark mode ([0758a6a](https://github.com/kailong321200875/vue-element-plus-admin/commit/0758a6a9d83170e53d45d39c3313e52ff5885746)) + +### [1.3.2](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.3.1...v1.3.2) (2022-05-07) + + +### Bug Fixes + +* fix the problem that the page is stuck in top mode ([8d01f48](https://github.com/kailong321200875/vue-element-plus-admin/commit/8d01f48d5098195b10c03b3cb3a0f485ebc9e018)) + +### [1.3.1](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.3.0...v1.3.1) (2022-05-06) + + +### Bug Fixes + +* spelling 'useRenderChcekbox' ([ee92f03](https://github.com/kailong321200875/vue-element-plus-admin/commit/ee92f039bea4307ccfb819728d3e2ed04fa00e03)) + +## [1.3.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.13...v1.3.0) (2022-04-26) + + +### Features + +* add useCrudSchemas hook ([00d947e](https://github.com/kailong321200875/vue-element-plus-admin/commit/00d947e2f81105194b0622d33768f999e37ad28a)) + + +### Bug Fixes + +* fix Table slot warning ([0eac05d](https://github.com/kailong321200875/vue-element-plus-admin/commit/0eac05d4f973ff7b15e00973f6e96595a3cd6d43)) + + +### Code Refactoring + +* refactor useAxios ([185f1e6](https://github.com/kailong321200875/vue-element-plus-admin/commit/185f1e6e210ecaac28ebfdee4198b7ca2eaa0933)) + + +### Build System + +* add url ([ab0f59a](https://github.com/kailong321200875/vue-element-plus-admin/commit/ab0f59ac91a077cf060923fa76e6d57e05d0b21b)) +* update plugins ([c475a61](https://github.com/kailong321200875/vue-element-plus-admin/commit/c475a610c19094034306f2dc665e240c7c117f87)) +* update plugins ([dfedbc7](https://github.com/kailong321200875/vue-element-plus-admin/commit/dfedbc74fdb2c819a96b6263849bdaab59b9e337)) + +### [1.2.13](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.12...v1.2.13) (2022-04-18) + + +### Performance Improvements + +* Editor component support v-model ([d77f8e3](https://github.com/kailong321200875/vue-element-plus-admin/commit/d77f8e334d77ee43c9ee0f411733f7397b278bc0)) + +### [1.2.12](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.11...v1.2.12) (2022-04-17) + + +### Bug Fixes + +* fixed spelling 'ElememtPlusSzie' ([5dbbc60](https://github.com/kailong321200875/vue-element-plus-admin/commit/5dbbc608640d93fe68fec6f58fdb30a43e02aada)) + +### [1.2.11](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.10...v1.2.11) (2022-04-14) + + +### Bug Fixes + +* fix the error reported by the Editor component ([7dc6d8a](https://github.com/kailong321200875/vue-element-plus-admin/commit/7dc6d8a9d7289bfaf27f972e9ca1773c0a1ddd7d)) +* fix the error reported by the Editor component ([90ef985](https://github.com/kailong321200875/vue-element-plus-admin/commit/90ef9856a0885fa812339cb7047ecc98b86c7b73)) + + +### Performance Improvements + +* add tagsViewIcon setting ([d395f03](https://github.com/kailong321200875/vue-element-plus-admin/commit/d395f03a57a9265f1d39b3220fc7c9b983efee30)) +* add uniqueopened setting ([b060319](https://github.com/kailong321200875/vue-element-plus-admin/commit/b0603199a5ae0ee923483dad449f49220d36f444)) + + +### Build System + +* update plugins ([2ee4954](https://github.com/kailong321200875/vue-element-plus-admin/commit/2ee49549e7b601af26ef5204f7648d271f3348f2)) + + +### Styling + +* add layout background color ([9b614fe](https://github.com/kailong321200875/vue-element-plus-admin/commit/9b614fe89288538197c50f164586aeed7836b7a8)) + +### [1.2.10](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.9...v1.2.10) (2022-04-12) + + +### Bug Fixes + +* fix bug ([327522f](https://github.com/kailong321200875/vue-element-plus-admin/commit/327522f2b73ae0e11f8ebbc39394b06029ce0b65)) + + +### Styling + +* modify the commitlint package manager ([ba7e722](https://github.com/kailong321200875/vue-element-plus-admin/commit/ba7e7224ab58612548519415f5429c32827a61de)) + +### [1.2.9](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.8...v1.2.9) (2022-04-12) + + +### Docs + +* update changlog ([e37273d](https://github.com/kailong321200875/vue-element-plus-admin/commit/e37273d95d29a3bb752604658d550264aacdc979)) + +### [1.2.8](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.7...v1.2.8) (2022-04-11) + +### Build System + +- update plugins ([00a573a](https://github.com/kailong321200875/vue-element-plus-admin/commit/00a573af3f455395b4ee2ab99a03f3103d466e9c)) + +### Docs + +- update changlog ([62fc183](https://github.com/kailong321200875/vue-element-plus-admin/commit/62fc1839fdff3a4d06a7db4cf3f8ce2cb9aee681)) + +### [1.2.7](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.6...v1.2.7) (2022-04-10) + +### Build System + +- update plugins ([f13a91d](https://github.com/kailong321200875/vue-element-plus-admin/commit/f13a91dd460b1dcdbd17aef723ab3ca2b01c34f0)) + +### Styling + +- .bhs code formatting ([57b2707](https://github.com/kailong321200875/vue-element-plus-admin/commit/57b27071e9a33423c46542a5d0e5d5c2e9a3b718)) + +### [1.2.6](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.5...v1.2.6) (2022-04-08) + +### Build System + +- update plugins ([d645892](https://github.com/kailong321200875/vue-element-plus-admin/commit/d645892cde2f7f43215a2ba1776ee94a322437bf)) + +### [1.2.5](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.4...v1.2.5) (2022-04-08) + +### Performance Improvements + +- add plop ([fa54a17](https://github.com/kailong321200875/vue-element-plus-admin/commit/fa54a1704ffd93f7b42dbeb1229bc4868d2d3a6a)) + +### Build System + +- update plugins ([18c6bd8](https://github.com/kailong321200875/vue-element-plus-admin/commit/18c6bd868622d954b51ea34e37516361ad4eb540)) + +### Styling + +- fix padding and background color ([f8c9d54](https://github.com/kailong321200875/vue-element-plus-admin/commit/f8c9d54687edafd92f5b61bf5288bb1188c73f01)) + +### [1.2.4](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.3...v1.2.4) (2022-04-06) + +### Bug Fixes + +- add Sticky props comment ([46133b3](https://github.com/kailong321200875/vue-element-plus-admin/commit/46133b3ff39d48d11cbcaa1f20a271118f48eb29)) +- fix bug ([179ca06](https://github.com/kailong321200875/vue-element-plus-admin/commit/179ca064ba8adbb3b063d9798ec1930ccc68e459)) +- fix remove unnecessary variables ([ca01cbf](https://github.com/kailong321200875/vue-element-plus-admin/commit/ca01cbfd98b63a0d76190fe8d43097fdc9df74e6)) +- fix style ([17c8fea](https://github.com/kailong321200875/vue-element-plus-admin/commit/17c8fea93811d9d9b708808484f5c907d761fcf1)) +- remove ContentDetailWrap style ([4ceaa9d](https://github.com/kailong321200875/vue-element-plus-admin/commit/4ceaa9d7816369d0dcaf3e18e4cdbbd6165cef88)) + +### [1.2.3](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.2...v1.2.3) (2022-03-31) + +### Bug Fixes + +- fix refresh with query ([e94020f](https://github.com/kailong321200875/vue-element-plus-admin/commit/e94020ff541a061599486c0003258f1dbf13aba8)) + +### [1.2.2](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.1...v1.2.2) (2022-03-30) + +### Bug Fixes + +- fix avatar height bug ([cd4ab76](https://github.com/kailong321200875/vue-element-plus-admin/commit/cd4ab767018941777174d7837045f5259d1cc403)) +- fix parmas to params ([2c7211c](https://github.com/kailong321200875/vue-element-plus-admin/commit/2c7211c89d7299ffc0a36bef8999b3c201dbaf4a)) + +### [1.2.1](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.2.0...v1.2.1) (2022-03-29) + +### Bug Fixes + +- fix invalid paging ([ad184ee](https://github.com/kailong321200875/vue-element-plus-admin/commit/ad184ee9c0619da36f1ca3f26e67f18f88488523)) + +### Build System + +- update plugins ([0c7276f](https://github.com/kailong321200875/vue-element-plus-admin/commit/0c7276feadaedef83e6a4ad9d457e26d408698a8)) + +## [1.2.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.14...v1.2.0) (2022-03-27) + +### Features + +- add hooks demo ([c43f39e](https://github.com/kailong321200875/vue-element-plus-admin/commit/c43f39efef296266c64cc24690717d07fa0bcb85)) +- add inputPassword demo ([8f8b126](https://github.com/kailong321200875/vue-element-plus-admin/commit/8f8b1260e75df6998ebea617f62d4ab6be81d721)) + +### Docs + +- update LICENSE ([69d3dcc](https://github.com/kailong321200875/vue-element-plus-admin/commit/69d3dcc7edf69e9b4e3042ddb11faa85ec7d39e2)) + +### Styling + +- modify the function name to make it more semantic ([046ae51](https://github.com/kailong321200875/vue-element-plus-admin/commit/046ae512f02df2d3f08134949b9376a061c1eef3)) +- update Footer component presentation ([d4a9ba3](https://github.com/kailong321200875/vue-element-plus-admin/commit/d4a9ba3aa6758b8aac18b30e1a6b9501baff826c)) +- update Icon demo ([8597122](https://github.com/kailong321200875/vue-element-plus-admin/commit/85971227cd3055ea280cf493c7c42b250c1515da)) + +### Tests + +- test push first commit ([a67bb48](https://github.com/kailong321200875/vue-element-plus-admin/commit/a67bb48f269651a2dcd01b9e33d10f20c42d76ee)) + +### Build System + +- update plugins ([9c13d92](https://github.com/kailong321200875/vue-element-plus-admin/commit/9c13d92b36a2a7c95b9edb7821367fc8f0ac6658)) +- update server port ([d2be8c1](https://github.com/kailong321200875/vue-element-plus-admin/commit/d2be8c1a307a3c5daf363bd7f1d21e574598de5c)) + +### [1.1.14](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.13...v1.1.14) (2022-03-22) + +### Bug Fixes + +- fix the bug that the form search function is invalid of the example-dialog page ([9ec30e7](https://github.com/kailong321200875/vue-element-plus-admin/commit/9ec30e719f89865497dbb1321be1df906f59f14e)) + +### [1.1.13](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.12...v1.1.13) (2022-03-17) + +### Build System + +- update plugins ([a2d0313](https://github.com/kailong321200875/vue-element-plus-admin/commit/a2d03137899f9b16fc1d4a09a23576cd74e7950e)) + +### [1.1.12](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.11...v1.1.12) (2022-03-15) + +### Build System + +- update plugins ([fee2252](https://github.com/kailong321200875/vue-element-plus-admin/commit/fee2252930b05b709d0c012e809568c4ed32bd89)) + +### [1.1.11](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.10...v1.1.11) (2022-03-15) + +### Bug Fixes + +- fix the problem of blank bar when toggle the TagsView component ([b1d9771](https://github.com/kailong321200875/vue-element-plus-admin/commit/b1d9771c750709fe45061d13299a85dbbd6ead25)) +- fix the problem that no reaction when copy setting config in http page ([61e0e33](https://github.com/kailong321200875/vue-element-plus-admin/commit/61e0e33c64d6a889fe6ed80d27a10cf8b201d21a)) + +### [1.1.10](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.9...v1.1.10) (2022-03-13) + +### Build System + +- update plugins ([0b525c8](https://github.com/kailong321200875/vue-element-plus-admin/commit/0b525c875075a28288e92243b205b337f85ab550)) + +### [1.1.9](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.8...v1.1.9) (2022-03-07) + +### Build System + +- update plugins ([1456fd4](https://github.com/kailong321200875/vue-element-plus-admin/commit/1456fd49ec9abbfe1f25aeadfe5fed54fec07394)) + +### [1.1.8](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.7...v1.1.8) (2022-03-07) + +### Docs + +- update changelog ([bf09441](https://github.com/kailong321200875/vue-element-plus-admin/commit/bf09441852e59b0d07d4949a33de75958696817f)) + +### [1.1.7](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.6...v1.1.7) (2022-03-06) + +### Styling + +- add labelMessage attribute to Form component ([8c42790](https://github.com/kailong321200875/vue-element-plus-admin/commit/8c427907843ccb2dfd882d27c1e8a894c5616487)) + +### [1.1.6](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.5...v1.1.6) (2022-03-04) + +### Bug Fixes + +- fix the problem that the tree data of Table component cannot be displayed ([bf83d3e](https://github.com/kailong321200875/vue-element-plus-admin/commit/bf83d3efbad9097f245c32cc07d1178580cec4e3)) + +### [1.1.5](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.4...v1.1.5) (2022-03-02) + +### Bug Fixes + +- fix the problem of tagsview error when loginout ([835d76a](https://github.com/kailong321200875/vue-element-plus-admin/commit/835d76ae87b950106f957976ebc8f6f2e8842ddf)) + +### Build System + +- update plugins ([de34bb1](https://github.com/kailong321200875/vue-element-plus-admin/commit/de34bb193d6c844dbc1cec38db5f61b3f95e19f2)) + +### Styling + +- fix tabMenu z-index bug ([8b3be02](https://github.com/kailong321200875/vue-element-plus-admin/commit/8b3be02368a1bddb7dc78f18adbea7f4ebfe75d6)) +- fix tags-view style bug ([ebff817](https://github.com/kailong321200875/vue-element-plus-admin/commit/ebff81777b9c0b839256b83e321ecbdbff25fc73)) + +### [1.1.4](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.3...v1.1.4) (2022-03-01) + +### CI + +- update workflow ([0490d18](https://github.com/kailong321200875/vue-element-plus-admin/commit/0490d18145cb0d9c4b066ab01a2c10cb527e38ba)) +- update workflow ([51f7bca](https://github.com/kailong321200875/vue-element-plus-admin/commit/51f7bca6034902b251d081ee383b0d796782d434)) + +### [1.1.3](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.2...v1.1.3) (2022-03-01) + +### CI + +- update workflow ([91cc5c5](https://github.com/kailong321200875/vue-element-plus-admin/commit/91cc5c595cadc5695d8f54bdc4922d8f04439f24)) + +### [1.1.2](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.1...v1.1.2) (2022-03-01) + +### Workflows + +- update workflow ([d9708aa](https://github.com/kailong321200875/vue-element-plus-admin/commit/d9708aae5bc0cb795bb0fbf8d17df753cc88ba1d)) + +### [1.1.1](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.1.0...v1.1.1) (2022-03-01) + +### Workflows + +- update workflow ([085328a](https://github.com/kailong321200875/vue-element-plus-admin/commit/085328aba8c4f356bf7915a6bbdc1ec4f46ceeda)) + +## [1.1.0](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.0.3...v1.1.0) (2022-03-01) + +### Features + +- 🎸 layout 三种布局重构完成 ([429e428](https://github.com/kailong321200875/vue-element-plus-admin/commit/429e42809cef33a33662e41ad50297217d128b8c)) +- 🎸 layout 布局重构 �[bd24b92](https://github.com/kailong321200875/vue-element-plus-admin/commit/bd24b92acb279343dbaf83b74f1ed2a3f57f1003)) +- 🎸 Table 组件重构完成并给出相应示 �[35879f8](https://github.com/kailong321200875/vue-element-plus-admin/commit/35879f8ecc0ffa76122a336e2eaa93ecfb408c1d)) +- 🎸 v0.0.4 发布 ([a58dc1b](https://github.com/kailong321200875/vue-element-plus-admin/commit/a58dc1b1c2774974782ef6d116b8805975b82b1c)) +- 🎸 初始化项 �[26d4c7c](https://github.com/kailong321200875/vue-element-plus-admin/commit/26d4c7c56894cf2031b3a7cce08d53c37f4a49e3)) +- 🎸 初版完成 ([5bfe4d2](https://github.com/kailong321200875/vue-element-plus-admin/commit/5bfe4d236fd9c2841da100f34c980b4572b67b20)) +- 🎸 新增 Detail 详情组件并给出相应示 �[e77a931](https://github.com/kailong321200875/vue-element-plus-admin/commit/e77a931ef2d2967a9717e27b187d68512c01284f)) +- 🎸 新增二维码组 �[85555ee](https://github.com/kailong321200875/vue-element-plus-admin/commit/85555eef7dc7d72cb701bdd81044ba8fb8e72acc)) +- 🎸 新增全局配置 ([f8405a6](https://github.com/kailong321200875/vue-element-plus-admin/commit/f8405a63c9b1288fbe95bae235b65a08e8fae8d2)) +- 🎸 新增固定 � 级菜单配 �[4c4903e](https://github.com/kailong321200875/vue-element-plus-admin/commit/4c4903e806c8818e320108cc3e5279d728061c29)) +- 🎸 新增权限管理及相关示例文 �[32b6583](https://github.com/kailong321200875/vue-element-plus-admin/commit/32b6583099646b2ee622ac7b35388468769b91b8)) +- 🎸 显示更多组建 � 发中 ([fa9f24d](https://github.com/kailong321200875/vue-element-plus-admin/commit/fa9f24d5da8d2e40d7c3661eabacb8f0474a7bf2)) +- 🎸 权限管理 � 发中 ([38f5211](https://github.com/kailong321200875/vue-element-plus-admin/commit/38f521174ba9eba750fee4516141d7a267f1c4ce)) +- 🎸 权限管理 � 发中 ([6d7ea66](https://github.com/kailong321200875/vue-element-plus-admin/commit/6d7ea6694d8299332018a6689bcd82502a9a552c)) +- 🎸 综合实例重构 �[5142e6e](https://github.com/kailong321200875/vue-element-plus-admin/commit/5142e6e323cb20c89a97398bf41d32c93ce42cad)) +- 🎸 重构 layout ([7ede021](https://github.com/kailong321200875/vue-element-plus-admin/commit/7ede02141e258ab4c88e9b4daad966513d4dbe68)) +- 🎸 重构 layout-classic 布局 ([29d9c98](https://github.com/kailong321200875/vue-element-plus-admin/commit/29d9c988605b822195900268da6bc3f3b0b9c770)) +- 🎸 重构 sider 组件 �[51313d7](https://github.com/kailong321200875/vue-element-plus-admin/commit/51313d7116c7ab2ded7e3a65514ea9ac413edecd)) +- Add analysis api ([83327ea](https://github.com/kailong321200875/vue-element-plus-admin/commit/83327ea763ebb233bb540513276ffa288fbcb4a1)) +- Add analysis demo ([cd06934](https://github.com/kailong321200875/vue-element-plus-admin/commit/cd069340fc5157535fdc82e792c6b6dce7d7a97e)) +- Add count-to demo ([d3fbd3a](https://github.com/kailong321200875/vue-element-plus-admin/commit/d3fbd3a06c3b802fc863b4dc8013122c14bd16f2)) +- Add Descriptions component and add Descriptions demo ([7ad46f8](https://github.com/kailong321200875/vue-element-plus-admin/commit/7ad46f828d626a87699cd4d3a959a5634577d580)) +- Add Dialog component and add dailog demo ([a18ad8f](https://github.com/kailong321200875/vue-element-plus-admin/commit/a18ad8f4a89b78c73e57d8d2543494243f656d05)) +- add doucment link ([53201ae](https://github.com/kailong321200875/vue-element-plus-admin/commit/53201ae97a425714871d99e8847a3672ba0d389f)) +- Add dynamic route ([9d926b2](https://github.com/kailong321200875/vue-element-plus-admin/commit/9d926b2760b75e1d8e71a68dc7ff6c5026223a43)) +- Add Editor component and add editor demo ([3fb3e8d](https://github.com/kailong321200875/vue-element-plus-admin/commit/3fb3e8da39d816bcf4aedb65d40c7052bdb6d8bf)) +- Add Error component ([7411dbc](https://github.com/kailong321200875/vue-element-plus-admin/commit/7411dbc9fd8f122187c86a11523b49c88cc71a8c)) +- Add example-dialog demo ([262f421](https://github.com/kailong321200875/vue-element-plus-admin/commit/262f4211cf53aef30a32f4b88e88fb1b9246ffcb)) +- Add example-page demo ([1492f91](https://github.com/kailong321200875/vue-element-plus-admin/commit/1492f9119aa2960cc05956218e6d151c8b316875)) +- Add form demo ([472f574](https://github.com/kailong321200875/vue-element-plus-admin/commit/472f574f42f8f31c4e6047043ac755ba5fb35b7b)) +- Add form demo ([e6f9580](https://github.com/kailong321200875/vue-element-plus-admin/commit/e6f95803316bb5df2d1060285c1d591a79340721)) +- Add form demo ([543156f](https://github.com/kailong321200875/vue-element-plus-admin/commit/543156f328350bd12e71a41c872e547e41cda7fe)) +- Add form demo ([7795d2a](https://github.com/kailong321200875/vue-element-plus-admin/commit/7795d2a4fe3dbc9849ddc7c1d3e2d9215dc66f56)) +- Add guide demo ([0832194](https://github.com/kailong321200875/vue-element-plus-admin/commit/0832194e6131051416edff7c2eac6b0a016ffd80)) +- Add highlight demo ([eb206b0](https://github.com/kailong321200875/vue-element-plus-admin/commit/eb206b0cc31ac7da3dfd8b3d4b874061c5c91d53)) +- Add Icon demo ([e4b7a76](https://github.com/kailong321200875/vue-element-plus-admin/commit/e4b7a769126d6f0fca424007c294ff229eefcb35)) +- Add ImageViewer component and add ImageViewer demo ([af9fc0a](https://github.com/kailong321200875/vue-element-plus-admin/commit/af9fc0a4aded3ec08746ddeaeabac4c3cfa9463d)) +- Add Infotip component ([e4b7a76](https://github.com/kailong321200875/vue-element-plus-admin/commit/e4b7a769126d6f0fca424007c294ff229eefcb35)) +- Add infotip demo ([dbf3b0f](https://github.com/kailong321200875/vue-element-plus-admin/commit/dbf3b0f5a333ccef524bbac825035b0c6dc78ec9)) +- Add Qrcode component and add qrcode demo ([535a31b](https://github.com/kailong321200875/vue-element-plus-admin/commit/535a31b35eb6a76589f602fd96dcf91f46f349b0)) +- Add Search component and add search demo ([33eca8a](https://github.com/kailong321200875/vue-element-plus-admin/commit/33eca8a97d59f5cc453e1a60ee81b1519527d0f1)) +- Add Table component and add useTable hook ([17e8e7c](https://github.com/kailong321200875/vue-element-plus-admin/commit/17e8e7cda9a009818f11cfa0429ce0f9adc00be5)) +- Add useScrollTo hook ([7d7fd9e](https://github.com/kailong321200875/vue-element-plus-admin/commit/7d7fd9ed646d2b68cec0547ad8e65b0404bb95bb)) +- Add useWatermark hook and add useWatermark demo ([d3fbd3a](https://github.com/kailong321200875/vue-element-plus-admin/commit/d3fbd3a06c3b802fc863b4dc8013122c14bd16f2)) +- Add workplace api ([cb558f8](https://github.com/kailong321200875/vue-element-plus-admin/commit/cb558f8af9dfef2ba2879f021db395ee79e8c8d4)) +- **Animate:** Add animate.css ([1436543](https://github.com/kailong321200875/vue-element-plus-admin/commit/1436543a5c599f651ed7805165ea83b9ebcddef5)) +- **Breadcrumbe:** Add Breadcrumb component ([4612e55](https://github.com/kailong321200875/vue-element-plus-admin/commit/4612e5544bcd626d686972e5cb874d0aa4af08b3)) +- **component:** Add CountTo component and Echart component ([e20fa76](https://github.com/kailong321200875/vue-element-plus-admin/commit/e20fa76cad0894a69fd04c81c2108faabf392684)) +- **component:** Add Footer component ([dad7330](https://github.com/kailong321200875/vue-element-plus-admin/commit/dad733063413c79eca61c6cb5ff671b35933a85f)) +- **component:** Add Footer component ([f81e996](https://github.com/kailong321200875/vue-element-plus-admin/commit/f81e996a426538aeaa2aa37a540395dcf360a09c)) +- **Component:** Add Highlight component ([c53fa56](https://github.com/kailong321200875/vue-element-plus-admin/commit/c53fa562e540447df082e35c7f26e56f2426e430)) +- **component:** Add namespace of class ([d847ccb](https://github.com/kailong321200875/vue-element-plus-admin/commit/d847ccb098edc72fe55c1f8459bf149453a3b73d)) +- **Component:** Setting component add copy button ([e496096](https://github.com/kailong321200875/vue-element-plus-admin/commit/e496096539e6a56b0761a625c9d59210facc5432)) +- **ContextMenu:** Add ContextMenu component ([349ac9d](https://github.com/kailong321200875/vue-element-plus-admin/commit/349ac9d3989d77e5246cecf0006dd8d83c489990)) +- Detail 组件重构完成 ([7f5ef99](https://github.com/kailong321200875/vue-element-plus-admin/commit/7f5ef99ccc32b03f7be21f70c333bb8e679c7d93)) +- Highlight 组件重构 ([34221f3](https://github.com/kailong321200875/vue-element-plus-admin/commit/34221f387f5e15a08cdc21edd76ce8d8c5c20fbc)) +- **hooks:** Add useIntro hook ([0832194](https://github.com/kailong321200875/vue-element-plus-admin/commit/0832194e6131051416edff7c2eac6b0a016ffd80)) +- **hooks:** Add useTimeAgo hook ([c53fa56](https://github.com/kailong321200875/vue-element-plus-admin/commit/c53fa562e540447df082e35c7f26e56f2426e430)) +- **I18n:** Add Ii8n ([3810b8c](https://github.com/kailong321200875/vue-element-plus-admin/commit/3810b8c3b26f86c27aa7db479dfb7b0d283d970f)) +- **Layout:** Add classic layout ([839b601](https://github.com/kailong321200875/vue-element-plus-admin/commit/839b6015b8e31bf70e6f0bf0608fa729b028729b)) +- **Layout:** Add cutMenu layout ([ff4dd3a](https://github.com/kailong321200875/vue-element-plus-admin/commit/ff4dd3afbf5c0c7a439c71b0c494b81e0f2c70d4)) +- **Layout:** Add topLeft layout ([71b1c5e](https://github.com/kailong321200875/vue-element-plus-admin/commit/71b1c5e10cade8d1c018d0c5f63c98ba9357bab8)) +- **LocaleDropdown:** Add LocaleDropdown Component ([3810b8c](https://github.com/kailong321200875/vue-element-plus-admin/commit/3810b8c3b26f86c27aa7db479dfb7b0d283d970f)) +- **Logo:** Add Logo component ([958edef](https://github.com/kailong321200875/vue-element-plus-admin/commit/958edefe7bc2bf3ae77520a5d885a9d47e8a37b9)) +- **mock:** Add mock ([3fc7d4d](https://github.com/kailong321200875/vue-element-plus-admin/commit/3fc7d4d39a72056fcf419fe19a9d41d90f945bad)) +- **router:** Add dynamic routing ([b218ccc](https://github.com/kailong321200875/vue-element-plus-admin/commit/b218ccc9cce2ce1363c4a21d22b4d69c43c7b2dc)) +- Search component add expand attribute and expandField attribute ([9b4b317](https://github.com/kailong321200875/vue-element-plus-admin/commit/9b4b31781765d31dec50acc40e2eed91401502d4)) +- **store:** Add localeStore ([3810b8c](https://github.com/kailong321200875/vue-element-plus-admin/commit/3810b8c3b26f86c27aa7db479dfb7b0d283d970f)) +- **store:** Add tagsView store ([349ac9d](https://github.com/kailong321200875/vue-element-plus-admin/commit/349ac9d3989d77e5246cecf0006dd8d83c489990)) +- Table 组件重构 ([07adefb](https://github.com/kailong321200875/vue-element-plus-admin/commit/07adefb89b7555280e6217e09cf81ba7aa5b93c2)) +- **TagsView:** Add TagsView component ([349ac9d](https://github.com/kailong321200875/vue-element-plus-admin/commit/349ac9d3989d77e5246cecf0006dd8d83c489990)) +- **useForm:** Add useForm ([357fc44](https://github.com/kailong321200875/vue-element-plus-admin/commit/357fc44e519c5829567c17f611fcaadee3f9f933)) +- **useNProgress:** Add useNProgress ([c5ab359](https://github.com/kailong321200875/vue-element-plus-admin/commit/c5ab3599c8ea001ff7831b72fefc9e274163fbbb)) +- **useTitle:** Add useTitle ([c5ab359](https://github.com/kailong321200875/vue-element-plus-admin/commit/c5ab3599c8ea001ff7831b72fefc9e274163fbbb)) +- **utils:** Add color utils ([71dfba2](https://github.com/kailong321200875/vue-element-plus-admin/commit/71dfba21c5bc0276689b5aecf0d75e53efdda09f)) +- **VForm:** Add VForm component ([448ac52](https://github.com/kailong321200875/vue-element-plus-admin/commit/448ac5293e48a03840df2bb0b399a8f02aae666e)) +- **VInputPassword:** Add VInputPassword Component ([a1bf7e9](https://github.com/kailong321200875/vue-element-plus-admin/commit/a1bf7e9b552f75d3b87c64904ac9e7c99fc936a5)) +- **Workplace:** Add wrokplace demo ([c53fa56](https://github.com/kailong321200875/vue-element-plus-admin/commit/c53fa562e540447df082e35c7f26e56f2426e430)) +- 综合实例、权限管理重 �[a4bd206](https://github.com/kailong321200875/vue-element-plus-admin/commit/a4bd2068a5d40d146b5b45cb3727ced990147b68)) +- 部分组件重构完成 ([3d96229](https://github.com/kailong321200875/vue-element-plus-admin/commit/3d9622978dc234ef12dbce63e18caf3440563aa0)) + +### Bug Fixes + +- 🐛 删除 Editor 双向绑定,改 �props 传参 ([c395e27](https://github.com/kailong321200875/vue-element-plus-admin/commit/c395e27f67af9f60b151a5484ab5a3c90c4c1d1e)) +- fix Form component setProps not work bug ([48ffc52](https://github.com/kailong321200875/vue-element-plus-admin/commit/48ffc52ca8fa26d8e6a5fa4b8b3001701a1f0732)) +- fix useScrollTo not work bug ([53201ae](https://github.com/kailong321200875/vue-element-plus-admin/commit/53201ae97a425714871d99e8847a3672ba0d389f)) +- 修复 tagsView 无动画效 �[0e3eb4b](https://github.com/kailong321200875/vue-element-plus-admin/commit/0e3eb4ba8b1503e1d221dfda59a3a0001dbdcb56)) + +### Performance Improvements + +- update useForm hook ([8a958cd](https://github.com/kailong321200875/vue-element-plus-admin/commit/8a958cd71d9afbd32b243aac0814bfa3281477cd)) + +### Code Refactoring + +- 💡 综合实例查看详情重构 ([9c26edd](https://github.com/kailong321200875/vue-element-plus-admin/commit/9c26edd5d599b5fb5a832fb547e3d95b6bfa9a98)) + +### Build System + +- Add conventional-changelog-cli plugin ([384485f](https://github.com/kailong321200875/vue-element-plus-admin/commit/384485f6994c6ac33abee506508ab9d35fe658a9)) +- Add conventional-github-releaser plugin ([3cd5c71](https://github.com/kailong321200875/vue-element-plus-admin/commit/3cd5c71899dde3ac3910aef0180d8b39fad51f1b)) +- Add standard-version plugin ([110ce25](https://github.com/kailong321200875/vue-element-plus-admin/commit/110ce257841648e29b247a0338624a188694b6e9)) +- Add vite-plugin-html plugin ([d5b6e2a](https://github.com/kailong321200875/vue-element-plus-admin/commit/d5b6e2a7770eb59aa32839f69da5be37397e3538)) +- delete useless plugin ([c756761](https://github.com/kailong321200875/vue-element-plus-admin/commit/c756761dfc3200156acb228474d3539197ef413b)) +- **pinia:** Add pinia ([2040500](https://github.com/kailong321200875/vue-element-plus-admin/commit/2040500af14d277a79f01eba5eca2a440203cecf)) +- **types:** Add vue-types ([2c41826](https://github.com/kailong321200875/vue-element-plus-admin/commit/2c41826c572268b74a663a6966c548628ac7e280)) +- **unplugin-auto-import:** Delete unplugin-auto-import ([2040500](https://github.com/kailong321200875/vue-element-plus-admin/commit/2040500af14d277a79f01eba5eca2a440203cecf)) +- **unplugin-vue-components:** Delete unplugin-vue-components ([2040500](https://github.com/kailong321200875/vue-element-plus-admin/commit/2040500af14d277a79f01eba5eca2a440203cecf)) +- update plugin ([8d08bc6](https://github.com/kailong321200875/vue-element-plus-admin/commit/8d08bc6fc92258674abdd12834eaa4530ec276dd)) +- update plugins ([3c58042](https://github.com/kailong321200875/vue-element-plus-admin/commit/3c580420a20121845f02c0dd3caca5a74f06a89d)) +- **vite-plugin-style-import:** Add vite-plugin-style-import ([2040500](https://github.com/kailong321200875/vue-element-plus-admin/commit/2040500af14d277a79f01eba5eca2a440203cecf)) +- **vite-plugin-vue-setup-extend:** Delete vite-plugin-vue-setup-extend ([2040500](https://github.com/kailong321200875/vue-element-plus-admin/commit/2040500af14d277a79f01eba5eca2a440203cecf)) +- 修改 vite 配置 ([9991fb4](https://github.com/kailong321200875/vue-element-plus-admin/commit/9991fb4e5c46b9e4016beaade7232e28dc272797)) +- 设置多语 �([45e879e](https://github.com/kailong321200875/vue-element-plus-admin/commit/45e879edeef677b6aa1d2cfe4dd8dc5b76c83c59)) +- 配置代码格式 �[ffdb556](https://github.com/kailong321200875/vue-element-plus-admin/commit/ffdb556a096db247306eae8eecc1b85718314cdd)) +- 集成基础配置 ([ced99de](https://github.com/kailong321200875/vue-element-plus-admin/commit/ced99de9b113a01d9d0b190f6d2c6adc983a3102)) +- 集成基础配置 ([5dbcf23](https://github.com/kailong321200875/vue-element-plus-admin/commit/5dbcf2397ccdec80c695c113f49e8aa9bb6d012c)) + +### Styling + +- 💄 优化 layout 样式 ([37ec378](https://github.com/kailong321200875/vue-element-plus-admin/commit/37ec378f0b2bf83d73ddf0e472aada6aab248f09)) +- 💄 微调样式 ([612b486](https://github.com/kailong321200875/vue-element-plus-admin/commit/612b48673c3389779ccfdd161e2ca80b21d265b2)) +- Add elNamespace ([d847ccb](https://github.com/kailong321200875/vue-element-plus-admin/commit/d847ccb098edc72fe55c1f8459bf149453a3b73d)) +- **appStore:** code style ([641ed68](https://github.com/kailong321200875/vue-element-plus-admin/commit/641ed684fefeb52e2f91e8baab7b610fc74c8d88)) +- **Breadcrumb:** fix Breadcrumb style bug ([8755c86](https://github.com/kailong321200875/vue-element-plus-admin/commit/8755c862b837d90a25b27c01fabe64abf81fc4a2)) +- **breadcrumb:** update disabled text color ([1522e92](https://github.com/kailong321200875/vue-element-plus-admin/commit/1522e925bae37cb9df4de2252d81f717788f4537)) +- change function to arrow function ([4612e55](https://github.com/kailong321200875/vue-element-plus-admin/commit/4612e5544bcd626d686972e5cb874d0aa4af08b3)) +- delete console.log ([49a6bfe](https://github.com/kailong321200875/vue-element-plus-admin/commit/49a6bfe9d81a40e2f5f15b68d7289e1787e89b54)) +- **Icon:** delete default color ([95a2bd8](https://github.com/kailong321200875/vue-element-plus-admin/commit/95a2bd884dd9846a56cda7c4c3ee4a41ce631b7c)) +- level demo style beautification ([dbf3b0f](https://github.com/kailong321200875/vue-element-plus-admin/commit/dbf3b0f5a333ccef524bbac825035b0c6dc78ec9)) +- lint code style ([b292419](https://github.com/kailong321200875/vue-element-plus-admin/commit/b2924190b8996e8208f951e3fadbcb09baddb8df)) +- **Login:** update login styles ([eb68f1d](https://github.com/kailong321200875/vue-element-plus-admin/commit/eb68f1d919e13c07b7d200e9aec53804b2a6dc7b)) +- modify menu z-index attribute ([0d7a778](https://github.com/kailong321200875/vue-element-plus-admin/commit/0d7a7781ce0b5e39f01355d3acdb3f364cabf76d)) +- **TagView:** Vertical center tag ([41281c4](https://github.com/kailong321200875/vue-element-plus-admin/commit/41281c4d541a2744e5df5dff2764cc85465b6a4c)) + +### Types + +- add ImportMetaEnv ([38e0257](https://github.com/kailong321200875/vue-element-plus-admin/commit/38e0257487e4138a74ad1bb4ee4ba004abcfaa12)) +- Adding BfFrom Component types ([8e036f5](https://github.com/kailong321200875/vue-element-plus-admin/commit/8e036f54b56ce8521eb8ec4b7ca21aa9c24f43f2)) +- **BfForm:** Adding BfForm types ([bc9195b](https://github.com/kailong321200875/vue-element-plus-admin/commit/bc9195b21eeb79629a82a04d90e2ac5aa6592928)) +- **BfForm:** Adding BfForm types ([184b468](https://github.com/kailong321200875/vue-element-plus-admin/commit/184b468cd41dcd1cdae11477b9ee2d6e17de1481)) +- **BfForm:** Adding BfForm types ([58cb24d](https://github.com/kailong321200875/vue-element-plus-admin/commit/58cb24d9f8a50be80b5ea793387d582a77a59137)) +- delete useless types ([3fc79c0](https://github.com/kailong321200875/vue-element-plus-admin/commit/3fc79c0ae7acd0929f47e33f96c8d45a90d8f762)) +- **VForm:** Adding VForm types ([7528fe6](https://github.com/kailong321200875/vue-element-plus-admin/commit/7528fe6da60368213d28d9f1b6310d02d3d53282)) + +### Docs + +- ✏️ 修改 readme ([8edb2a3](https://github.com/kailong321200875/vue-element-plus-admin/commit/8edb2a3493dca975036859b5d2c52afaa91f5dbb)) +- ✏️ 更新 readme ([62eeb55](https://github.com/kailong321200875/vue-element-plus-admin/commit/62eeb55330dd4af2a46801c7a19f38a3ef312bbf)) +- Add README.md ([21dcf88](https://github.com/kailong321200875/vue-element-plus-admin/commit/21dcf88ba31957bbdb50c6207d010650daab70fc)) +- Error modifying readme name ([25d5c84](https://github.com/kailong321200875/vue-element-plus-admin/commit/25d5c84e92e68aa72362a14f55aacd946fa5b1b2)) +- update description ([be6ff98](https://github.com/kailong321200875/vue-element-plus-admin/commit/be6ff9899b25cc00519210950d27ee56ac5112e6)) +- update description ([c15aa87](https://github.com/kailong321200875/vue-element-plus-admin/commit/c15aa8755c9c937512c7380a6d03c4d877ef4d87)) +- update README ([27979dc](https://github.com/kailong321200875/vue-element-plus-admin/commit/27979dc6def7d9d8cea62a08d49a6c828be2258b)) +- update README.md ([53201ae](https://github.com/kailong321200875/vue-element-plus-admin/commit/53201ae97a425714871d99e8847a3672ba0d389f)) +- update README.md ([c11823a](https://github.com/kailong321200875/vue-element-plus-admin/commit/c11823abd8a033e14b4c20d17ac941195d39bcfe)) +- 修改 README ([b79a567](https://github.com/kailong321200875/vue-element-plus-admin/commit/b79a56753df55976e749c4494266df052d315416)) diff --git a/promo-ui2/Dockerfile.dev b/promo-ui2/Dockerfile.dev new file mode 100644 index 0000000..106259c --- /dev/null +++ b/promo-ui2/Dockerfile.dev @@ -0,0 +1,13 @@ +FROM node:18.0.0 + +WORKDIR /app + +RUN npm install -g pnpm@8.1.0 + +COPY package.json . + +RUN pnpm install + +COPY . . + +CMD [ "pnpm", "run", "dev" ] \ No newline at end of file diff --git a/promo-ui2/LICENSE b/promo-ui2/LICENSE new file mode 100644 index 0000000..9861118 --- /dev/null +++ b/promo-ui2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-present Archer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/promo-ui2/README.md b/promo-ui2/README.md new file mode 100644 index 0000000..ab94ab3 --- /dev/null +++ b/promo-ui2/README.md @@ -0,0 +1,148 @@ +


+ +[![license](https://img.shields.io/github/license/kailong321200875/vue-element-plus-admin.svg)](LICENSE) [![repo-size](https://img.shields.io/github/repo-size/kailong321200875/vue-element-plus-admin.svg)](repo-size) [![last-commit](https://img.shields.io/github/last-commit/kailong321200875/vue-element-plus-admin.svg)](last-commit) [![stars](https://img.shields.io/github/stars/kailong321200875/vue-element-plus-admin.svg)](stars) [![forks](https://img.shields.io/github/forks/kailong321200875/vue-element-plus-admin.svg)](forks) [![release](https://img.shields.io/github/release/kailong321200875/vue-element-plus-admin.svg)](release) [![watchers](https://img.shields.io/github/watchers/kailong321200875/vue-element-plus-admin.svg)](watchers) + +

vue-element-plus-admin

+
+ +**English** | [中文](./README.zh-CN.md) + +## Introduction + +vue-element-plus-admin is a free and open source middle and background template based on `element-plus`. Developed using the latest mainstream technologies such as `vue3`, `vite` and `typescript`, the out of the box middle and background front-end solution can be used as the starting template of the project and learning reference. And always pay attention to the latest technological trends and update them as soon as possible. + +vue-element-plus-admin is positioned as a background integration scheme, which is not suitable for secondary development as a basic template. Because it integrates many functions that you may not use, it will cause a lot of code redundancy. If your project doesn't pay attention to this problem, you can also directly carry out secondary development based on it. + +If you need a basic template, please switch to the `mini` branch. `mini` simply integrates some common layout functions such as layout and dynamic menu, which is more suitable for developers to carry out secondary development. + +## Feature + +- **State of The Art Development**:Use front-end front-end technology development such as Vue3/vite4 +- **TypeScript**: Application-level JavaScript language +- **Theming**: Configurable themes +- **International**:Built-in complete internationalization program +- **Mock Server** Built-in mock data scheme +- **Authority** Built-in complete dynamic routing permission generation scheme. +- **Component** Multiple commonly used components are encapsulated twice +- **Examples** Built-in rich examples + +## Preview + +- [vue-element-plus-admin](https://element-plus-admin.cn/) - Full version of the github site +- [vue-element-plus-admin](https://kailong110120130.gitee.io/vue-element-plus-admin) - Full version of the gitee site + +account: **admin/admin** + +Online examples do not apply to menu filtering by default, but directly use Static routing + +## Documentation + +[Document Github](https://element-plus-admin-doc.cn/) + +[Document Gitee](https://kailong110120130.gitee.io/vue-element-plus-admin-doc) + +## Preparation + +- [node](http://nodejs.org/) and [git](https://git-scm.com/) - Project development environment +- [Vite](https://vitejs.dev/) - Familiar with vite features +- [Vue3](https://v3.vuejs.org/) - Familiar with Vue basic syntax +- [TypeScript](https://www.typescriptlang.org/) - Familiar with the basic syntax of `TypeScript` +- [Es6+](http://es6.ruanyifeng.com/) - Familiar with es6 basic syntax +- [Vue-Router-Next](https://next.router.vuejs.org/) - Familiar with the basic use of vue-router +- [Element-Plus](https://element-plus.org/) - Familiar with the basic use of element-plus +- [Mock.js](https://github.com/nuysoft/Mock) - mockjs basic syntax + +## Install and use + +- Get the project code + +```bash +git clone https://github.com/kailong321200875/vue-element-plus-admin.git +``` + +- Installation dependencies + +```bash +cd vue-element-plus-admin + +pnpm install + +``` + +- run + +```bash +pnpm run dev +``` + +- build + +```bash +pnpm run build:pro +``` + +## Change Log + +[CHANGELOG](./CHANGELOG.md) + +## How to contribute + + + + + +You can [Raise an issue](https://github.com/kailong321200875/vue-element-plus-admin/issues/new) Or submit a Pull Request. + +**Pull Request:** + +1. Fork code +2. Create your own branch: `git checkout -b feat/xxxx` +3. Submit your changes: `git commit -am 'feat(function): add xxxxx'` +4. Push your branch: `git push origin feat/xxxx` +5. submit `pull request` + +## Git Contribution submission specification + +- `feat` New features +- `fix` Fix bugs +- `docs` document +- `style` Format and style (changes that do not affect code operation) +- `refactor` Refactor +- `perf` Optimize related, such as improving performance and experience +- `test` Add test +- `build` Compilation related modifications, changes to project construction or dependencies +- `ci` Continuous integration modification +- `chore` Changes in the construction process or auxiliary tools +- `revert` Rollback to previous version +- `workflow` Workflow improvement +- `mod` Uncertain modification classification +- `wip` Under development +- `types` type + +## Browser support + +The `Chrome 80+` browser is recommended for local development + +Support modern browsers, not IE + +| [ Edge](http://godban.github.io/browsers-support-badges/)
IE | [ Edge](http://godban.github.io/browsers-support-badges/)
Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | +| :-: | :-: | :-: | :-: | :-: | +| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions | + +## Donate + +If you find this project helpful, welcome sponsorship to show your support~ + +[Paypal Me](https://www.paypal.com/paypalme/ckl94) + + + +### My QR code + +If you have any project cooperation or outsourcing, please scan the code to add me as a friend and leave a note of your purpose. + + + +## License + +[MIT](./LICENSE) diff --git a/promo-ui2/README.zh-CN.md b/promo-ui2/README.zh-CN.md new file mode 100644 index 0000000..2b153a6 --- /dev/null +++ b/promo-ui2/README.zh-CN.md @@ -0,0 +1,148 @@ +


+ +[![license](https://img.shields.io/github/license/kailong321200875/vue-element-plus-admin.svg)](LICENSE) [![repo-size](https://img.shields.io/github/repo-size/kailong321200875/vue-element-plus-admin.svg)](repo-size) [![last-commit](https://img.shields.io/github/last-commit/kailong321200875/vue-element-plus-admin.svg)](last-commit) [![stars](https://img.shields.io/github/stars/kailong321200875/vue-element-plus-admin.svg)](stars) [![forks](https://img.shields.io/github/forks/kailong321200875/vue-element-plus-admin.svg)](forks) [![release](https://img.shields.io/github/release/kailong321200875/vue-element-plus-admin.svg)](release) [![watchers](https://img.shields.io/github/watchers/kailong321200875/vue-element-plus-admin.svg)](watchers) + +

vue-element-plus-admin

+
+ +[English](./README.md) | **中文** + +## 介绍 + +vue-element-plus-admin 是一个基于 `element-plus` 免费开源的中后台模版。使用了最新的`vue3`,`vite`,`TypeScript`等主流技术开发,开箱即用的中后台前端解决方案,可以用来作为项目的启动模版,也可用于学习参考。并且时刻关注着最新技术动向,尽可能的第一时间更新。 + +vue-element-plus-admin 的定位是后台集成方案,不太适合当基础模板来进行二次开发。因为集成了很多你可能用不到的功能,会造成不少的代码冗余。如果你的项目不关注这方面的问题,也可以直接基于它进行二次开发。 + +如需要基础模版,请切换到 `mini` 分支,`mini` 只简单集成了一些如:布局、动态菜单等常用布局功能,更适合开发者进行二次开发。 + +## 特性 + +- **最新技术栈**:使用 Vue3/vite4 等前端前沿技术开发 +- **TypeScript**: 应用程序级 JavaScript 的语言 +- **主题**: 可配置的主题 +- **国际化**:内置完善的国际化方案 +- **自定义数据** 内置 Mock 数据方案 +- **权限** 内置完善的动态路由权限生成方案 +- **组件** 二次封装了多个常用的组件 +- **示例** 内置丰富的示例 + +## 预览 + +- [vue-element-plus-admin](https://element-plus-admin.cn/) - 完整版 github 站点 +- [vue-element-plus-admin](https://kailong110120130.gitee.io/vue-element-plus-admin) - 完整版 gitee 站点 + +帐号:**admin/admin** + +在线例子默认不适用菜单过滤,而是直接使用静态路由表 + +## 文档 + +[文档地址 Github](https://element-plus-admin-doc.cn/) + +[文档地址 Gitee](https://kailong110120130.gitee.io/vue-element-plus-admin-doc) + +## 前序准备 + +- [node](http://nodejs.org/) 和 [git](https://git-scm.com/) - 项目开发环境 +- [Vite](https://vitejs.dev/) - 熟悉 vite 特性 +- [Vue3](https://v3.vuejs.org/) - 熟悉 Vue 基础语法 +- [TypeScript](https://www.typescriptlang.org/) - 熟悉 `TypeScript` 基本语法 +- [Es6+](http://es6.ruanyifeng.com/) - 熟悉 es6 基本语法 +- [Vue-Router-Next](https://next.router.vuejs.org/) - 熟悉 vue-router 基本使用 +- [Element-Plus](https://element-plus.org/) - element-plus 基本使用 +- [Mock.js](https://github.com/nuysoft/Mock) - mockjs 基本语法 + +## 安装和使用 + +- 获取代码 + +```bash +git clone https://github.com/kailong321200875/vue-element-plus-admin.git +``` + +- 安装依赖 + +```bash +cd vue-element-plus-admin + +pnpm install + +``` + +- 运行 + +```bash +pnpm run dev +``` + +- 打包 + +```bash +pnpm run build:pro +``` + +## 更新日志 + +[更新日志](./CHANGELOG.md) + +## 如何贡献 + + + + + +你可以[提一个 issue](https://github.com/kailong321200875/vue-element-plus-admin/issues/new) 或者提交一个 Pull Request。 + +**Pull Request:** + +1. Fork 代码 +2. 创建自己的分支: `git checkout -b feat/xxxx` +3. 提交你的修改: `git commit -am 'feat(function): add xxxxx'` +4. 推送您的分支: `git push origin feat/xxxx` +5. 提交 `pull request` + +## Git 贡献提交规范 + +- `feat` 新功能 +- `fix` 修补 bug +- `docs` 文档 +- `style` 格式、样式(不影响代码运行的变动) +- `refactor` 重构(即不是新增功能,也不是修改 BUG 的代码) +- `perf` 优化相关,比如提升性能、体验 +- `test` 添加测试 +- `build` 编译相关的修改,对项目构建或者依赖的改动 +- `ci` 持续集成修改 +- `chore` 构建过程或辅助工具的变动 +- `revert` 回滚到上一个版本 +- `workflow` 工作流改进 +- `mod` 不确定分类的修改 +- `wip` 开发中 +- `types` 类型 + +## 浏览器支持 + +本地开发推荐使用 `Chrome 80+` 浏览器 + +支持现代浏览器, 不支持 IE + +| [ Edge](http://godban.github.io/browsers-support-badges/)
IE | [ Edge](http://godban.github.io/browsers-support-badges/)
Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | +| :-: | :-: | :-: | :-: | :-: | +| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions | + +## Donate + +如果你觉得这个项目有帮助,欢迎赞助以示支持~ + +[Paypal Me](https://www.paypal.com/paypalme/ckl94) + + + +### 我的二维码 + +如有项目合作或项目外包,扫码加我好友,请备注来意。 + + + +## 许可证 + +[MIT](./LICENSE) diff --git a/promo-ui2/apidoc/redpack.http b/promo-ui2/apidoc/redpack.http new file mode 100644 index 0000000..c0e5d64 --- /dev/null +++ b/promo-ui2/apidoc/redpack.http @@ -0,0 +1,17 @@ +POST http://localhost:8081/promo/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + +{"username":"15375298377","password":"123456"} + + +### 获取卡密列表 +GET http://localhost:8081/promo/card/list +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImQ4MWI5OTM5LTUwZWItNGFiNC05NGIxLTEwOWFiYzY5NzI5MCJ9.RHXzv6Ao6hUl0fwY-Zhszz1-Jda6ceZybjmFddKFLZQbjufYkZd167ZCmlU9SbcrZ3Ec8TIt4XqSS7zb7fVHtQ + + +### 生成卡密 + +POST http://localhost:8081/promo/generateCard/6/1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImQ4MWI5OTM5LTUwZWItNGFiNC05NGIxLTEwOWFiYzY5NzI5MCJ9.RHXzv6Ao6hUl0fwY-Zhszz1-Jda6ceZybjmFddKFLZQbjufYkZd167ZCmlU9SbcrZ3Ec8TIt4XqSS7zb7fVHtQ +Content-Type: application/json; charset=UTF-8 + diff --git a/promo-ui2/apidoc/user.http b/promo-ui2/apidoc/user.http new file mode 100644 index 0000000..f6a15b2 --- /dev/null +++ b/promo-ui2/apidoc/user.http @@ -0,0 +1,4 @@ +POST http://localhost:8081/promo/register HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + +{"username":"17688554522","password":"123456", "contactInformation": "hello"} \ No newline at end of file diff --git a/promo-ui2/apidoc/分享.http b/promo-ui2/apidoc/分享.http new file mode 100644 index 0000000..7ddada7 --- /dev/null +++ b/promo-ui2/apidoc/分享.http @@ -0,0 +1,11 @@ +POST http://localhost:8081/promo/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + +{"username":"15375298377","password":"123456"} + + +### 分享域名 + +GET http://localhost:8081/promo/getsharedomain HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImEwMmU0Y2MyLWM5MDctNGU4OS1iNTkxLTk1Mjk1MGI2NjRmNCJ9._imhNdbqREBrYv-Kx73VN0S5U7Pr1zMvvMLEMXHf77uKOLO1Y0NnWRuTvSQlYp1T1KMVcNxEChSLj7I-Xex2wA + diff --git a/promo-ui2/apidoc/普通用户.http b/promo-ui2/apidoc/普通用户.http new file mode 100644 index 0000000..0f12f2f --- /dev/null +++ b/promo-ui2/apidoc/普通用户.http @@ -0,0 +1,13 @@ +POST http://localhost:8081/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"username":"13545419205","password":"123456"} + + +### 兑换红包卡密 + +GET http://localhost:8081/api/bonus/receiveRedPacket/A628920554234AFDB539398823835378 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjY1MjA4OTYwLTA3NDQtNDQxZS05YzA1LWJjNTc2ZTQyN2RiMyJ9.Ogqmm8iTgiPhc8b7iDxz_a2atBtAgbmwqzi1M5ogLEpAVFAv91N0Ygo-1_yUVioPVn0WTWVJvJnIW42-4CgIJg + + diff --git a/promo-ui2/commitlint.config.cjs b/promo-ui2/commitlint.config.cjs new file mode 100644 index 0000000..78d145d --- /dev/null +++ b/promo-ui2/commitlint.config.cjs @@ -0,0 +1,28 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: { + 'type-enum': [ + 2, + 'always', + [ + 'feat', // 新功能(feature) + 'fix', // 修补bug + 'docs', // 文档(documentation) + 'style', // 格式、样式(不影响代码运行的变动) + 'refactor', // 重构(即不是新增功能,也不是修改BUG的代码) + 'perf', // 优化相关,比如提升性能、体验 + 'test', // 添加测试 + 'ci', // 持续集成修改 + 'chore', // 构建过程或辅助工具的变动 + 'revert', // 回滚到上一个版本 + 'workflow', // 工作流改进 + 'mod', // 不确定分类的修改 + 'wip', // 开发中 + 'types', // 类型修改 + 'release' // 版本发布 + ] + ], + 'subject-full-stop': [0, 'never'], + 'subject-case': [0, 'never'] + } +} diff --git a/promo-ui2/docker-compose.dev.yaml b/promo-ui2/docker-compose.dev.yaml new file mode 100644 index 0000000..b6fd3e1 --- /dev/null +++ b/promo-ui2/docker-compose.dev.yaml @@ -0,0 +1,10 @@ +services: + vue-element-plus-admin: + build: + context: . + dockerfile: Dockerfile.dev + ports: + - "4000:4000" + volumes: + - /app/node_modules + - .:/app \ No newline at end of file diff --git a/promo-ui2/eslint.config.mjs b/promo-ui2/eslint.config.mjs new file mode 100644 index 0000000..67bc096 --- /dev/null +++ b/promo-ui2/eslint.config.mjs @@ -0,0 +1,82 @@ +// 引入vue模版的eslint +import pluginVue from 'eslint-plugin-vue' +import eslint from '@eslint/js' +// ts-eslint解析器,使 eslint 可以解析 ts 语法 +import tseslint from 'typescript-eslint' +// vue文件解析器 +import vueParser from 'vue-eslint-parser' +import prettier from 'eslint-plugin-prettier' + +export default tseslint.config({ + // ignores: ['node_modules', 'prettier.config.cjs', 'dist*'], + files: ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.vue'], + // tseslint.config添加了extends扁平函数,直接用。否则是eslint9.0版本是没有extends的 + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommended, + ...pluginVue.configs['flat/essential'] + ], + plugins: { + prettier + }, + languageOptions: { + parser: vueParser, // 使用vue解析器,这个可以识别vue文件 + parserOptions: { + parser: tseslint.parser, // 在vue文件上使用ts解析器 + sourceType: 'module', + ecmaVersion: 2020, + ecmaFeatures: { + jsx: true + } + } + }, + rules: { + 'prettier/prettier': 'error', + 'no-useless-escape': 0, + 'no-undef': 0, + '@typescript-eslint/no-unused-expressions': 0, + '@typescript-eslint/no-unsafe-function-type': 0, + 'vue/no-setup-props-destructure': 0, + 'vue/script-setup-uses-vars': 1, + 'vue/no-reserved-component-names': 0, + '@typescript-eslint/ban-ts-ignore': 0, + '@typescript-eslint/explicit-function-return-type': 0, + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/no-var-requires': 0, + '@typescript-eslint/no-empty-function': 0, + 'vue/custom-event-name-casing': 0, + 'no-use-before-define': 0, + '@typescript-eslint/no-use-before-define': 0, + '@typescript-eslint/ban-ts-comment': 0, + '@typescript-eslint/ban-types': 0, + '@typescript-eslint/no-non-null-assertion': 0, + '@typescript-eslint/explicit-module-boundary-types': 0, + '@typescript-eslint/no-unused-vars': 0, + 'no-unused-vars': 0, + 'space-before-function-paren': 0, + 'vue/attributes-order': 0, + 'vue/one-component-per-file': 0, + 'vue/html-closing-bracket-newline': 0, + 'vue/max-attributes-per-line': 0, + 'vue/multiline-html-element-content-newline': 0, + 'vue/singleline-html-element-content-newline': 0, + 'vue/attribute-hyphenation': 0, + 'vue/require-default-prop': 0, + 'vue/require-explicit-emits': 0, + 'vue/html-self-closing': [ + 1, + { + html: { + void: 'always', + normal: 'never', + component: 'always' + }, + svg: 'always', + math: 'always' + } + ], + 'vue/multi-word-component-names': 0, + 'vue/no-v-html': 0, + 'vue/require-toggle-inside-transition': 0 + } +}) diff --git a/promo-ui2/index.html b/promo-ui2/index.html new file mode 100644 index 0000000..507d616 --- /dev/null +++ b/promo-ui2/index.html @@ -0,0 +1,142 @@ + + + + + + + <%= title %> + + +
+ +
+
+
+ +
<%= title %>
+
+
+
+
+
+
+
+
+ + + diff --git a/promo-ui2/mock/_createProductionServer.ts b/promo-ui2/mock/_createProductionServer.ts new file mode 100644 index 0000000..598ac64 --- /dev/null +++ b/promo-ui2/mock/_createProductionServer.ts @@ -0,0 +1,18 @@ +import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer' + +const modules = import.meta.glob('./**/*.mock.ts', { + import: 'default', + eager: true +}) + +const mockModules: any[] = [] +Object.keys(modules).forEach(async (key) => { + if (key.includes('_')) { + return + } + mockModules.push(...(modules[key] as any)) +}) + +export function setupProdMockServer() { + createProdMockServer(mockModules) +} diff --git a/promo-ui2/mock/analysis/index.mock.ts b/promo-ui2/mock/analysis/index.mock.ts new file mode 100644 index 0000000..195f367 --- /dev/null +++ b/promo-ui2/mock/analysis/index.mock.ts @@ -0,0 +1,87 @@ +import { SUCCESS_CODE } from '@/constants' +import { MockMethod } from 'vite-plugin-mock' + +const timeout = 1000 + +export default [ + // 分析页统计接口 + { + url: '/mock/analysis/total', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: { + users: 102400, + messages: 81212, + moneys: 9280, + shoppings: 13600 + } + } + } + }, + // 用户来源 + { + url: '/mock/analysis/userAccessSource', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: [ + { value: 1000, name: 'analysis.directAccess' }, + { value: 310, name: 'analysis.mailMarketing' }, + { value: 234, name: 'analysis.allianceAdvertising' }, + { value: 135, name: 'analysis.videoAdvertising' }, + { value: 1548, name: 'analysis.searchEngines' } + ] + } + } + }, + // 每周用户活跃量 + { + url: '/mock/analysis/weeklyUserActivity', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: [ + { value: 13253, name: 'analysis.monday' }, + { value: 34235, name: 'analysis.tuesday' }, + { value: 26321, name: 'analysis.wednesday' }, + { value: 12340, name: 'analysis.thursday' }, + { value: 24643, name: 'analysis.friday' }, + { value: 1322, name: 'analysis.saturday' }, + { value: 1324, name: 'analysis.sunday' } + ] + } + } + }, + // 每月销售额 + { + url: '/mock/analysis/monthlySales', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: [ + { estimate: 100, actual: 120, name: 'analysis.january' }, + { estimate: 120, actual: 82, name: 'analysis.february' }, + { estimate: 161, actual: 91, name: 'analysis.march' }, + { estimate: 134, actual: 154, name: 'analysis.april' }, + { estimate: 105, actual: 162, name: 'analysis.may' }, + { estimate: 160, actual: 140, name: 'analysis.june' }, + { estimate: 165, actual: 145, name: 'analysis.july' }, + { estimate: 114, actual: 250, name: 'analysis.august' }, + { estimate: 163, actual: 134, name: 'analysis.september' }, + { estimate: 185, actual: 56, name: 'analysis.october' }, + { estimate: 118, actual: 99, name: 'analysis.november' }, + { estimate: 123, actual: 123, name: 'analysis.december' } + ] + } + } + } +] as MockMethod[] diff --git a/promo-ui2/mock/department/index.mock.ts b/promo-ui2/mock/department/index.mock.ts new file mode 100644 index 0000000..443907e --- /dev/null +++ b/promo-ui2/mock/department/index.mock.ts @@ -0,0 +1,206 @@ +import { toAnyString } from '@/utils' +import Mock from 'mockjs' +import { SUCCESS_CODE } from '@/constants' + +const departmentList: any = [] + +const citys = ['厦门总公司', '北京分公司', '上海分公司', '福州分公司', '深圳分公司', '杭州分公司'] + +for (let i = 0; i < 5; i++) { + departmentList.push({ + // 部门名称 + departmentName: citys[i], + id: toAnyString(), + createTime: '@datetime', + // 状态 + status: Mock.Random.integer(0, 1), + // 备注 + remark: '@cword(10, 15)', + children: [ + { + // 部门名称 + departmentName: '研发部', + id: toAnyString(), + createTime: '@datetime', + // 状态 + status: Mock.Random.integer(0, 1), + // 备注 + remark: '@cword(10, 15)' + }, + { + // 部门名称 + departmentName: '产品部', + id: toAnyString(), + createTime: '@datetime', + // 状态 + status: Mock.Random.integer(0, 1), + // 备注 + remark: '@cword(10, 15)' + }, + { + // 部门名称 + departmentName: '运营部', + id: toAnyString(), + createTime: '@datetime', + // 状态 + status: Mock.Random.integer(0, 1), + // 备注 + remark: '@cword(10, 15)' + }, + { + // 部门名称 + departmentName: '市场部', + id: toAnyString(), + createTime: '@datetime', + // 状态 + status: Mock.Random.integer(0, 1), + // 备注 + remark: '@cword(10, 15)' + }, + { + // 部门名称 + departmentName: '销售部', + id: toAnyString(), + createTime: '@datetime', + // 状态 + status: Mock.Random.integer(0, 1), + // 备注 + remark: '@cword(10, 15)' + }, + { + // 部门名称 + departmentName: '客服部', + id: toAnyString(), + createTime: '@datetime', + // 状态 + status: Mock.Random.integer(0, 1), + // 备注 + remark: '@cword(10, 15)' + } + ] + }) +} + +export default [ + // 列表接口 + { + url: '/mock/department/list', + method: 'get', + response: () => { + return { + code: SUCCESS_CODE, + data: { + list: departmentList + } + } + } + }, + { + url: '/mock/department/table/list', + method: 'get', + response: () => { + return { + code: SUCCESS_CODE, + data: { + list: departmentList, + total: 5 + } + } + } + }, + { + url: '/mock/department/users', + method: 'get', + timeout: 1000, + response: ({ query }) => { + const { pageSize } = query + // 根据pageSize来创建数据 + const mockList: any = [] + for (let i = 0; i < pageSize; i++) { + mockList.push( + Mock.mock({ + // 用户名 + username: '@cname', + // 账号 + account: '@first', + // 邮箱 + email: '@EMAIL', + // 创建时间 + createTime: '@datetime', + // 用户id + id: toAnyString() + }) + ) + } + return { + code: SUCCESS_CODE, + data: { + total: 100, + list: mockList + } + } + } + }, + // 保存接口 + { + url: '/mock/department/user/save', + method: 'post', + timeout: 1000, + response: () => { + return { + code: SUCCESS_CODE, + data: 'success' + } + } + }, + // 删除接口 + { + url: '/mock/department/user/delete', + method: 'post', + response: ({ body }) => { + const ids = body.ids + if (!ids) { + return { + code: 500, + message: '请选择需要删除的数据' + } + } else { + return { + code: SUCCESS_CODE, + data: 'success' + } + } + } + }, + // 保存接口 + { + url: '/mock/department/save', + method: 'post', + timeout: 1000, + response: () => { + return { + code: SUCCESS_CODE, + data: 'success' + } + } + }, + // 删除接口 + { + url: '/mock/department/delete', + method: 'post', + response: ({ body }) => { + const ids = body.ids + if (!ids) { + return { + code: 500, + message: '请选择需要删除的数据' + } + } else { + return { + code: SUCCESS_CODE, + data: 'success' + } + } + } + } +] diff --git a/promo-ui2/mock/dict/index.mock.ts b/promo-ui2/mock/dict/index.mock.ts new file mode 100644 index 0000000..29042c6 --- /dev/null +++ b/promo-ui2/mock/dict/index.mock.ts @@ -0,0 +1,60 @@ +import { SUCCESS_CODE } from '@/constants' + +const timeout = 1000 + +const dictObj: Recordable = { + importance: [ + { + value: 0, + label: 'tableDemo.commonly' + }, + { + value: 1, + label: 'tableDemo.good' + }, + { + value: 2, + label: 'tableDemo.important' + } + ] +} + +export default [ + // 字典接口 + { + url: '/mock/dict/list', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: dictObj + } + } + }, + // 获取某个字典 + { + url: '/mock/dict/one', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: [ + { + label: 'test1', + value: 0 + }, + { + label: 'test2', + value: 1 + }, + { + label: 'test3', + value: 2 + } + ] + } + } + } +] diff --git a/promo-ui2/mock/menu/index.mock.ts b/promo-ui2/mock/menu/index.mock.ts new file mode 100644 index 0000000..2823692 --- /dev/null +++ b/promo-ui2/mock/menu/index.mock.ts @@ -0,0 +1,357 @@ +import Mock from 'mockjs' +import { SUCCESS_CODE } from '@/constants' + +const timeout = 1000 + +export default [ + // 列表接口 + { + url: '/mock/menu/list', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: { + list: [ + { + path: '/dashboard', + component: '#', + redirect: '/dashboard/analysis', + name: 'Dashboard', + status: Mock.Random.integer(0, 1), + id: 1, + type: 0, + parentId: undefined, + title: '首页', + meta: { + title: '首页', + icon: 'vi-ant-design:dashboard-filled', + alwaysShow: true + }, + children: [ + { + path: 'analysis', + component: 'views/Dashboard/Analysis', + name: 'Analysis', + status: Mock.Random.integer(0, 1), + id: 2, + type: 1, + parentId: 1, + title: '分析页', + permissionList: [ + { + id: 1, + label: '新增', + value: 'add' + }, + { + id: 2, + label: '编辑', + value: 'edit' + } + ], + meta: { + title: '分析页', + noCache: true, + permission: ['add', 'edit'] + } + }, + { + path: 'workplace', + component: 'views/Dashboard/Workplace', + name: 'Workplace', + status: Mock.Random.integer(0, 1), + id: 3, + type: 1, + parentId: 1, + title: '工作台', + permissionList: [ + { + id: 1, + label: '新增', + value: 'add' + }, + { + id: 2, + label: '编辑', + value: 'edit' + }, + { + id: 3, + label: '删除', + value: 'delete' + } + ], + meta: { + title: '工作台', + noCache: true + } + } + ] + }, + { + path: '/external-link', + component: '#', + meta: { + title: '文档', + icon: 'vi-clarity:document-solid' + }, + name: 'ExternalLink', + status: Mock.Random.integer(0, 1), + id: 4, + type: 0, + parentId: undefined, + title: '文档', + children: [ + { + path: 'https://element-plus-admin-doc.cn/', + name: 'DocumentLink', + status: Mock.Random.integer(0, 1), + id: 5, + type: 1, + parentId: 4, + title: '文档', + meta: { + title: '文档' + } + } + ] + }, + { + path: '/level', + component: '#', + redirect: '/level/menu1/menu1-1/menu1-1-1', + name: 'Level', + status: Mock.Random.integer(0, 1), + id: 6, + type: 0, + parentId: undefined, + title: '菜单', + meta: { + title: '菜单', + icon: 'vi-carbon:skill-level-advanced' + }, + children: [ + { + path: 'menu1', + name: 'Menu1', + component: '##', + status: Mock.Random.integer(0, 1), + id: 7, + type: 0, + parentId: 6, + title: '菜单1', + redirect: '/level/menu1/menu1-1/menu1-1-1', + meta: { + title: '菜单1' + }, + children: [ + { + path: 'menu1-1', + name: 'Menu11', + component: '##', + status: Mock.Random.integer(0, 1), + id: 8, + type: 0, + parentId: 7, + title: '菜单1-1', + redirect: '/level/menu1/menu1-1/menu1-1-1', + meta: { + title: '菜单1-1', + alwaysShow: true + }, + children: [ + { + path: 'menu1-1-1', + name: 'Menu111', + component: 'views/Level/Menu111', + status: Mock.Random.integer(0, 1), + id: 9, + type: 1, + parentId: 8, + title: '菜单1-1-1', + meta: { + title: '菜单1-1-1' + } + } + ] + }, + { + path: 'menu1-2', + name: 'Menu12', + component: 'views/Level/Menu12', + status: Mock.Random.integer(0, 1), + id: 10, + type: 1, + parentId: 7, + title: '菜单1-2', + meta: { + title: '菜单1-2' + } + } + ] + }, + { + path: 'menu2', + name: 'Menu2Demo', + component: 'views/Level/Menu2', + status: Mock.Random.integer(0, 1), + id: 11, + type: 1, + parentId: 6, + title: '菜单2', + meta: { + title: '菜单2' + } + } + ] + }, + { + path: '/example', + component: '#', + redirect: '/example/example-dialog', + name: 'Example', + status: Mock.Random.integer(0, 1), + id: 12, + type: 0, + parentId: undefined, + title: '综合示例', + meta: { + title: '综合示例', + icon: 'vi-ep:management', + alwaysShow: true + }, + children: [ + { + path: 'example-dialog', + component: 'views/Example/Dialog/ExampleDialog', + name: 'ExampleDialog', + status: Mock.Random.integer(0, 1), + id: 13, + type: 1, + parentId: 12, + title: '综合示例-弹窗', + permissionList: [ + { + id: 1, + label: '新增', + value: 'add' + }, + { + id: 2, + label: '编辑', + value: 'edit' + }, + { + id: 3, + label: '删除', + value: 'delete' + }, + { + id: 4, + label: '查看', + value: 'view' + } + ], + meta: { + title: '综合示例-弹窗' + } + }, + { + path: 'example-page', + component: 'views/Example/Page/ExamplePage', + name: 'ExamplePage', + status: Mock.Random.integer(0, 1), + id: 14, + type: 1, + parentId: 12, + title: '综合示例-页面', + permissionList: [ + { + id: 1, + label: '新增', + value: 'add' + }, + { + id: 2, + label: '编辑', + value: 'edit' + }, + { + id: 3, + label: '删除', + value: 'delete' + }, + { + id: 4, + label: '查看', + value: 'view' + } + ], + meta: { + title: '综合示例-页面' + } + }, + { + path: 'example-add', + component: 'views/Example/Page/ExampleAdd', + name: 'ExampleAdd', + status: Mock.Random.integer(0, 1), + id: 15, + type: 1, + parentId: 12, + title: '综合示例-新增', + meta: { + title: '综合示例-新增', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page' + } + }, + { + path: 'example-edit', + component: 'views/Example/Page/ExampleEdit', + name: 'ExampleEdit', + status: Mock.Random.integer(0, 1), + id: 16, + type: 1, + parentId: 12, + title: '综合示例-编辑', + meta: { + title: '综合示例-编辑', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page' + } + }, + { + path: 'example-detail', + component: 'views/Example/Page/ExampleDetail', + name: 'ExampleDetail', + status: Mock.Random.integer(0, 1), + id: 17, + type: 1, + parentId: 12, + title: '综合示例-详情', + meta: { + title: '综合示例-详情', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page' + } + } + ] + } + ] + } + } + } + } +] diff --git a/promo-ui2/mock/request/index.mock.ts b/promo-ui2/mock/request/index.mock.ts new file mode 100644 index 0000000..bfaa266 --- /dev/null +++ b/promo-ui2/mock/request/index.mock.ts @@ -0,0 +1,72 @@ +import { SUCCESS_CODE } from '@/constants' + +const timeout = 600000 + +export default [ + { + url: '/mock/request/1', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: 'request-1' + } + } + }, + { + url: '/mock/request/2', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: 'request-2' + } + } + }, + { + url: '/mock/request/3', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: 'request-3' + } + } + }, + { + url: '/mock/request/4', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: 'request-4' + } + } + }, + { + url: '/mock/request/5', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: 'request-5' + } + } + }, + { + url: '/mock/request/expired', + method: 'get', + timeout: 0, + response: () => { + return { + code: 401, + message: 'token expired' + } + } + } +] diff --git a/promo-ui2/mock/role/index.mock.ts b/promo-ui2/mock/role/index.mock.ts new file mode 100644 index 0000000..910b2cc --- /dev/null +++ b/promo-ui2/mock/role/index.mock.ts @@ -0,0 +1,1235 @@ +import Mock from 'mockjs' +import { SUCCESS_CODE } from '@/constants' +import { toAnyString } from '@/utils' + +const timeout = 1000 + +const adminList = [ + { + path: '/dashboard', + component: '#', + redirect: '/dashboard/analysis', + name: 'Dashboard', + meta: { + title: 'router.dashboard', + icon: 'vi-ant-design:dashboard-filled', + alwaysShow: true + }, + children: [ + { + path: 'analysis', + component: 'views/Dashboard/Analysis', + name: 'Analysis', + meta: { + title: 'router.analysis', + noCache: true, + affix: true + } + }, + { + path: 'workplace', + component: 'views/Dashboard/Workplace', + name: 'Workplace', + meta: { + title: 'router.workplace', + noCache: true, + affix: true + } + } + ] + }, + { + path: '/external-link', + component: '#', + meta: {}, + name: 'ExternalLink', + children: [ + { + path: 'https://element-plus-admin-doc.cn/', + name: 'DocumentLink', + meta: { + title: 'router.document', + icon: 'vi-clarity:document-solid' + } + } + ] + }, + { + path: '/guide', + component: '#', + name: 'Guide', + meta: {}, + children: [ + { + path: 'index', + component: 'views/Guide/Guide', + name: 'GuideDemo', + meta: { + title: 'router.guide', + icon: 'vi-cib:telegram-plane' + } + } + ] + }, + { + path: '/components', + component: '#', + redirect: '/components/form/default-form', + name: 'ComponentsDemo', + meta: { + title: 'router.component', + icon: 'vi-bx:bxs-component', + alwaysShow: true + }, + children: [ + { + path: 'form', + component: '##', + name: 'Form', + meta: { + title: 'router.form', + alwaysShow: true + }, + children: [ + { + path: 'default-form', + component: 'views/Components/Form/DefaultForm', + name: 'DefaultForm', + meta: { + title: 'router.defaultForm' + } + }, + { + path: 'use-form', + component: 'views/Components/Form/UseFormDemo', + name: 'UseForm', + meta: { + title: 'UseForm' + } + } + ] + }, + { + path: 'table', + component: '##', + redirect: '/components/table/default-table', + name: 'TableDemo', + meta: { + title: 'router.table', + alwaysShow: true + }, + children: [ + { + path: 'default-table', + component: 'views/Components/Table/DefaultTable', + name: 'DefaultTable', + meta: { + title: 'router.defaultTable' + } + }, + { + path: 'use-table', + component: 'views/Components/Table/UseTableDemo', + name: 'UseTable', + meta: { + title: 'UseTable' + } + }, + { + path: 'tree-table', + component: 'views/Components/Table/TreeTable', + name: 'TreeTable', + meta: { + title: 'TreeTable' + } + }, + { + path: 'table-image-preview', + component: 'views/Components/Table/TableImagePreview', + name: 'TableImagePreview', + meta: { + title: 'router.PicturePreview' + } + }, + { + path: 'table-video-preview', + component: 'views/Components/Table/TableVideoPreview', + name: 'TableVideoPreview', + meta: { + title: 'router.tableVideoPreview' + } + }, + { + path: 'card-table', + component: 'views/Components/Table/CardTable', + name: 'CardTable', + meta: { + title: 'router.cardTable' + } + } + // { + // path: 'ref-table', + // component: 'views/Components/Table/RefTable', + // name: 'RefTable', + // meta: { + // title: 'RefTable' + // } + // } + ] + }, + { + path: 'editor-demo', + component: '##', + redirect: '/components/editor-demo/editor', + name: 'EditorDemo', + meta: { + title: 'router.editor', + alwaysShow: true + }, + children: [ + { + path: 'editor', + component: 'views/Components/Editor/Editor', + name: 'Editor', + meta: { + title: 'router.richText' + } + }, + { + path: 'json-editor', + component: 'views/Components/Editor/JsonEditor', + name: 'JsonEditor', + meta: { + title: 'router.jsonEditor' + } + }, + { + path: 'code-editor', + component: 'views/Components/Editor/CodeEditor', + name: 'CodeEditor', + meta: { + title: 'router.codeEditor' + } + } + ] + }, + { + path: 'search', + component: 'views/Components/Search', + name: 'Search', + meta: { + title: 'router.search' + } + }, + { + path: 'descriptions', + component: 'views/Components/Descriptions', + name: 'Descriptions', + meta: { + title: 'router.descriptions' + } + }, + { + path: 'image-viewer', + component: 'views/Components/ImageViewer', + name: 'ImageViewer', + meta: { + title: 'router.imageViewer' + } + }, + { + path: 'dialog', + component: 'views/Components/Dialog', + name: 'Dialog', + meta: { + title: 'router.dialog' + } + }, + { + path: 'icon', + component: 'views/Components/Icon', + name: 'Icon', + meta: { + title: 'router.icon' + } + }, + { + path: 'icon-picker', + component: 'views/Components/IconPicker', + name: 'IconPicker', + meta: { + title: 'router.iconPicker' + } + }, + { + path: 'echart', + component: 'views/Components/Echart', + name: 'Echart', + meta: { + title: 'router.echart' + } + }, + { + path: 'count-to', + component: 'views/Components/CountTo', + name: 'CountTo', + meta: { + title: 'router.countTo' + } + }, + { + path: 'qrcode', + component: 'views/Components/Qrcode', + name: 'Qrcode', + meta: { + title: 'router.qrcode' + } + }, + { + path: 'highlight', + component: 'views/Components/Highlight', + name: 'Highlight', + meta: { + title: 'router.highlight' + } + }, + { + path: 'infotip', + component: 'views/Components/Infotip', + name: 'Infotip', + meta: { + title: 'router.infotip' + } + }, + { + path: 'input-password', + component: 'views/Components/InputPassword', + name: 'InputPassword', + meta: { + title: 'router.inputPassword' + } + }, + { + path: 'waterfall', + component: 'views/Components/Waterfall', + name: 'Waterfall', + meta: { + title: 'router.waterfall' + } + }, + { + path: 'image-cropping', + component: 'views/Components/ImageCropping', + name: 'ImageCropping', + meta: { + title: 'router.imageCropping' + } + }, + { + path: 'video-player', + component: 'views/Components/VideoPlayer', + name: 'VideoPlayer', + meta: { + title: 'router.videoPlayer' + } + }, + { + path: 'avatars', + component: 'views/Components/Avatars', + name: 'Avatars', + meta: { + title: 'router.avatars' + } + }, + { + path: 'i-agree', + component: 'views/Components/IAgree', + name: 'IAgree', + meta: { + title: 'router.iAgree' + } + }, + { + path: 'tree', + component: 'views/Components/Tree', + name: 'Tree', + meta: { + title: 'router.tree' + } + } + ] + }, + { + path: '/function', + component: '#', + redirect: '/function/multipleTabs', + name: 'Function', + meta: { + title: 'router.function', + icon: 'vi-ri:function-fill', + alwaysShow: true + }, + children: [ + { + path: 'multipleTabs', + component: 'views/Function/MultipleTabs', + name: 'MultipleTabs', + meta: { + title: 'router.multipleTabs' + } + }, + { + path: 'multiple-tabs-demo/:id', + component: 'views/Function/MultipleTabsDemo', + name: 'MultipleTabsDemo', + meta: { + hidden: true, + title: 'router.details', + canTo: true + } + }, + { + path: 'request', + component: 'views/Function/Request', + name: 'Request', + meta: { + title: 'router.request' + } + }, + { + path: 'test', + component: 'views/Function/Test', + name: 'Test', + meta: { + title: 'router.permission', + permission: ['add', 'edit', 'delete'] + } + } + ] + }, + { + path: '/hooks', + component: '#', + redirect: '/hooks/useWatermark', + name: 'Hooks', + meta: { + title: 'hooks', + icon: 'vi-ic:outline-webhook', + alwaysShow: true + }, + children: [ + { + path: 'useWatermark', + component: 'views/hooks/useWatermark', + name: 'UseWatermark', + meta: { + title: 'useWatermark' + } + }, + { + path: 'useTagsView', + component: 'views/hooks/useTagsView', + name: 'UseTagsView', + meta: { + title: 'useTagsView' + } + }, + { + path: 'useValidator', + component: 'views/hooks/useValidator', + name: 'UseValidator', + meta: { + title: 'useValidator' + } + }, + { + path: 'useCrudSchemas', + component: 'views/hooks/useCrudSchemas', + name: 'UseCrudSchemas', + meta: { + title: 'useCrudSchemas' + } + }, + { + path: 'useClipboard', + component: 'views/hooks/useClipboard', + name: 'UseClipboard', + meta: { + title: 'useClipboard' + } + }, + { + path: 'useNetwork', + component: 'views/hooks/useNetwork', + name: 'UseNetwork', + meta: { + title: 'useNetwork' + } + } + ] + }, + { + path: '/level', + component: '#', + redirect: '/level/menu1/menu1-1/menu1-1-1', + name: 'Level', + meta: { + title: 'router.level', + icon: 'vi-carbon:skill-level-advanced' + }, + children: [ + { + path: 'menu1', + name: 'Menu1', + component: '##', + redirect: '/level/menu1/menu1-1/menu1-1-1', + meta: { + title: 'router.menu1' + }, + children: [ + { + path: 'menu1-1', + name: 'Menu11', + component: '##', + redirect: '/level/menu1/menu1-1/menu1-1-1', + meta: { + title: 'router.menu11', + alwaysShow: true + }, + children: [ + { + path: 'menu1-1-1', + name: 'Menu111', + component: 'views/Level/Menu111', + meta: { + title: 'router.menu111' + } + } + ] + }, + { + path: 'menu1-2', + name: 'Menu12', + component: 'views/Level/Menu12', + meta: { + title: 'router.menu12' + } + } + ] + }, + { + path: 'menu2', + name: 'Menu2Demo', + component: 'views/Level/Menu2', + meta: { + title: 'router.menu2' + } + } + ] + }, + { + path: '/example', + component: '#', + redirect: '/example/example-dialog', + name: 'Example', + meta: { + title: 'router.example', + icon: 'vi-ep:management', + alwaysShow: true + }, + children: [ + { + path: 'example-dialog', + component: 'views/Example/Dialog/ExampleDialog', + name: 'ExampleDialog', + meta: { + title: 'router.exampleDialog' + } + }, + { + path: 'example-page', + component: 'views/Example/Page/ExamplePage', + name: 'ExamplePage', + meta: { + title: 'router.examplePage' + } + }, + { + path: 'example-add', + component: 'views/Example/Page/ExampleAdd', + name: 'ExampleAdd', + meta: { + title: 'router.exampleAdd', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page' + } + }, + { + path: 'example-edit', + component: 'views/Example/Page/ExampleEdit', + name: 'ExampleEdit', + meta: { + title: 'router.exampleEdit', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page' + } + }, + { + path: 'example-detail', + component: 'views/Example/Page/ExampleDetail', + name: 'ExampleDetail', + meta: { + title: 'router.exampleDetail', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page' + } + } + ] + }, + { + path: '/error', + component: '#', + redirect: '/error/404', + name: 'Error', + meta: { + title: 'router.errorPage', + icon: 'vi-ci:error', + alwaysShow: true + }, + children: [ + { + path: '404-demo', + component: 'views/Error/404', + name: '404Demo', + meta: { + title: '404' + } + }, + { + path: '403-demo', + component: 'views/Error/403', + name: '403Demo', + meta: { + title: '403' + } + }, + { + path: '500-demo', + component: 'views/Error/500', + name: '500Demo', + meta: { + title: '500' + } + } + ] + }, + { + path: '/authorization', + component: '#', + redirect: '/authorization/user', + name: 'Authorization', + meta: { + title: 'router.authorization', + icon: 'vi-eos-icons:role-binding', + alwaysShow: true + }, + children: [ + { + path: 'department', + component: 'views/Authorization/Department/Department', + name: 'Department', + meta: { + title: 'router.department' + } + }, + { + path: 'user', + component: 'views/Authorization/User/User', + name: 'User', + meta: { + title: 'router.user' + } + }, + { + path: 'menu', + component: 'views/Authorization/Menu/Menu', + name: 'Menu', + meta: { + title: 'router.menuManagement' + } + }, + { + path: 'role', + component: 'views/Authorization/Role/Role', + name: 'Role', + meta: { + title: 'router.role' + } + } + ] + } +] + +const testList: string[] = [ + '/dashboard', + '/dashboard/analysis', + '/dashboard/workplace', + 'external-link', + 'https://element-plus-admin-doc.cn/', + '/guide', + '/guide/index', + '/components', + '/components/form', + '/components/form/default-form', + '/components/form/use-form', + '/components/form/ref-form', + '/components/table', + '/components/table/default-table', + '/components/table/use-table', + '/components/table/tree-table', + '/components/table/table-image-preview', + '/components/table/table-video-preview', + '/components/table/ref-table', + '/components/table/card-table', + '/components/editor-demo', + '/components/editor-demo/editor', + '/components/editor-demo/json-editor', + '/components/editor-demo/code-editor', + '/components/search', + '/components/descriptions', + '/components/image-viewer', + '/components/dialog', + '/components/icon', + '/components/iconPicker', + '/components/echart', + '/components/count-to', + '/components/qrcode', + '/components/highlight', + '/components/infotip', + '/components/input-password', + '/components/waterfall', + '/components/image-cropping', + '/components/video-player', + '/components/avatars', + '/components/i-agree', + 'function', + '/function/multiple-tabs', + '/function/multiple-tabs-demo/:id', + '/function/request', + '/function/test', + '/hooks', + '/hooks/useWatermark', + '/hooks/useTagsView', + '/hooks/useValidator', + '/hooks/useCrudSchemas', + '/hooks/useClipboard', + '/hooks/useNetwork', + '/level', + '/level/menu1', + '/level/menu1/menu1-1', + '/level/menu1/menu1-1/menu1-1-1', + '/level/menu1/menu1-2', + '/level/menu2', + '/example', + '/example/example-dialog', + '/example/example-page', + '/example/example-add', + '/example/example-edit', + '/example/example-detail', + '/authorization', + '/authorization/department', + '/authorization/user', + '/authorization/role', + '/authorization/menu', + '/error', + '/error/404-demo', + '/error/403-demo', + '/error/500-demo' +] + +const List: any[] = [] + +const roleNames = ['超级管理员', '管理员', '普通用户', '游客'] +const menus = [ + [ + { + path: '/dashboard', + component: '#', + redirect: '/dashboard/analysis', + name: 'Dashboard', + status: Mock.Random.integer(0, 1), + id: 1, + meta: { + title: '首页', + icon: 'vi-ant-design:dashboard-filled', + alwaysShow: true + }, + children: [ + { + path: 'analysis', + component: 'views/Dashboard/Analysis', + name: 'Analysis', + status: Mock.Random.integer(0, 1), + id: 2, + meta: { + title: '分析页', + noCache: true + } + }, + { + path: 'workplace', + component: 'views/Dashboard/Workplace', + name: 'Workplace', + status: Mock.Random.integer(0, 1), + id: 3, + meta: { + title: '工作台', + noCache: true + } + } + ] + }, + { + path: '/external-link', + component: '#', + meta: { + title: '文档', + icon: 'vi-clarity:document-solid' + }, + name: 'ExternalLink', + status: Mock.Random.integer(0, 1), + id: 4, + children: [ + { + path: 'https://element-plus-admin-doc.cn/', + name: 'DocumentLink', + status: Mock.Random.integer(0, 1), + id: 5, + meta: { + title: '文档' + } + } + ] + }, + { + path: '/level', + component: '#', + redirect: '/level/menu1/menu1-1/menu1-1-1', + name: 'Level', + status: Mock.Random.integer(0, 1), + id: 6, + meta: { + title: '菜单', + icon: 'vi-carbon:skill-level-advanced' + }, + children: [ + { + path: 'menu1', + name: 'Menu1', + component: '##', + status: Mock.Random.integer(0, 1), + id: 7, + redirect: '/level/menu1/menu1-1/menu1-1-1', + meta: { + title: '菜单1' + }, + children: [ + { + path: 'menu1-1', + name: 'Menu11', + component: '##', + status: Mock.Random.integer(0, 1), + id: 8, + redirect: '/level/menu1/menu1-1/menu1-1-1', + meta: { + title: '菜单1-1', + alwaysShow: true + }, + children: [ + { + path: 'menu1-1-1', + name: 'Menu111', + component: 'views/Level/Menu111', + status: Mock.Random.integer(0, 1), + id: 9, + permission: ['edit', 'add', 'delete'], + meta: { + title: '菜单1-1-1', + permission: ['edit', 'add', 'delete'] + } + } + ] + }, + { + path: 'menu1-2', + name: 'Menu12', + component: 'views/Level/Menu12', + status: Mock.Random.integer(0, 1), + id: 10, + permission: ['edit', 'add', 'delete'], + meta: { + title: '菜单1-2', + permission: ['edit', 'add', 'delete'] + } + } + ] + }, + { + path: 'menu2', + name: 'Menu2Demo', + component: 'views/Level/Menu2', + status: Mock.Random.integer(0, 1), + id: 11, + permission: ['edit', 'add', 'delete'], + meta: { + title: '菜单2', + permission: ['edit', 'add', 'delete'] + } + } + ] + }, + { + path: '/example', + component: '#', + redirect: '/example/example-dialog', + name: 'Example', + status: Mock.Random.integer(0, 1), + id: 12, + meta: { + title: '综合示例', + icon: 'vi-ep:management', + alwaysShow: true + }, + children: [ + { + path: 'example-dialog', + component: 'views/Example/Dialog/ExampleDialog', + name: 'ExampleDialog', + status: Mock.Random.integer(0, 1), + id: 13, + permission: ['edit', 'add', 'delete'], + meta: { + title: '综合示例-弹窗', + permission: ['edit', 'add', 'delete'] + } + }, + { + path: 'example-page', + component: 'views/Example/Page/ExamplePage', + name: 'ExamplePage', + status: Mock.Random.integer(0, 1), + id: 14, + permission: ['edit', 'add', 'delete'], + meta: { + title: '综合示例-页面', + permission: ['edit', 'add', 'delete'] + } + }, + { + path: 'example-add', + component: 'views/Example/Page/ExampleAdd', + name: 'ExampleAdd', + status: Mock.Random.integer(0, 1), + id: 15, + permission: ['edit', 'add', 'delete'], + meta: { + title: '综合示例-新增', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page', + permission: ['edit', 'add', 'delete'] + } + }, + { + path: 'example-edit', + component: 'views/Example/Page/ExampleEdit', + name: 'ExampleEdit', + status: Mock.Random.integer(0, 1), + id: 16, + permission: ['edit', 'add', 'delete'], + meta: { + title: '综合示例-编辑', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page', + permission: ['edit', 'add', 'delete'] + } + }, + { + path: 'example-detail', + component: 'views/Example/Page/ExampleDetail', + name: 'ExampleDetail', + status: Mock.Random.integer(0, 1), + id: 17, + permission: ['edit', 'add', 'delete'], + meta: { + title: '综合示例-详情', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page', + permission: ['edit', 'add', 'delete'] + } + } + ] + } + ], + [ + { + path: '/dashboard', + component: '#', + redirect: '/dashboard/analysis', + name: 'Dashboard', + status: Mock.Random.integer(0, 1), + id: 1, + meta: { + title: '首页', + icon: 'vi-ant-design:dashboard-filled', + alwaysShow: true + }, + children: [ + { + path: 'analysis', + component: 'views/Dashboard/Analysis', + name: 'Analysis', + status: Mock.Random.integer(0, 1), + id: 2, + meta: { + title: '分析页', + noCache: true + } + }, + { + path: 'workplace', + component: 'views/Dashboard/Workplace', + name: 'Workplace', + status: Mock.Random.integer(0, 1), + id: 3, + meta: { + title: '工作台', + noCache: true + } + } + ] + } + ], + [ + { + path: '/external-link', + component: '#', + meta: { + title: '文档', + icon: 'vi-clarity:document-solid' + }, + name: 'ExternalLink', + status: Mock.Random.integer(0, 1), + id: 4, + children: [ + { + path: 'https://element-plus-admin-doc.cn/', + name: 'DocumentLink', + status: Mock.Random.integer(0, 1), + id: 5, + meta: { + title: '文档' + } + } + ] + }, + { + path: '/level', + component: '#', + redirect: '/level/menu1/menu1-1/menu1-1-1', + name: 'Level', + status: Mock.Random.integer(0, 1), + id: 6, + meta: { + title: '菜单', + icon: 'vi-carbon:skill-level-advanced' + }, + children: [ + { + path: 'menu1', + name: 'Menu1', + component: '##', + status: Mock.Random.integer(0, 1), + id: 7, + redirect: '/level/menu1/menu1-1/menu1-1-1', + meta: { + title: '菜单1' + }, + children: [ + { + path: 'menu1-1', + name: 'Menu11', + component: '##', + status: Mock.Random.integer(0, 1), + id: 8, + redirect: '/level/menu1/menu1-1/menu1-1-1', + meta: { + title: '菜单1-1', + alwaysShow: true + }, + children: [ + { + path: 'menu1-1-1', + name: 'Menu111', + component: 'views/Level/Menu111', + status: Mock.Random.integer(0, 1), + id: 9, + permission: ['edit', 'add', 'delete'], + meta: { + title: '菜单1-1-1', + permission: ['edit', 'add', 'delete'] + } + } + ] + }, + { + path: 'menu1-2', + name: 'Menu12', + component: 'views/Level/Menu12', + status: Mock.Random.integer(0, 1), + id: 10, + permission: ['edit', 'add', 'delete'], + meta: { + title: '菜单1-2', + permission: ['edit', 'add', 'delete'] + } + } + ] + }, + { + path: 'menu2', + name: 'Menu2Demo', + component: 'views/Level/Menu2', + status: Mock.Random.integer(0, 1), + id: 11, + permission: ['edit', 'add', 'delete'], + meta: { + title: '菜单2', + permission: ['edit', 'add', 'delete'] + } + } + ] + } + ], + [ + { + path: '/example', + component: '#', + redirect: '/example/example-dialog', + name: 'Example', + status: Mock.Random.integer(0, 1), + id: 12, + meta: { + title: '综合示例', + icon: 'vi-ep:management', + alwaysShow: true + }, + children: [ + { + path: 'example-detail', + component: 'views/Example/Page/ExampleDetail', + name: 'ExampleDetail', + status: Mock.Random.integer(0, 1), + id: 17, + permission: ['edit', 'add', 'delete'], + meta: { + title: '综合示例-详情', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page', + permission: ['edit', 'add', 'delete'] + } + } + ] + } + ] +] + +for (let i = 0; i < 4; i++) { + List.push( + Mock.mock({ + id: toAnyString(), + // timestamp: +Mock.Random.date('T'), + roleName: roleNames[i], + role: '@first', + status: Mock.Random.integer(0, 1), + createTime: '@datetime', + remark: '@cword(10, 15)', + menu: menus[i] + }) + ) +} + +export default [ + // 列表接口 + { + url: '/mock/role/list', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: adminList + } + } + }, + { + url: '/mock/role/table', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: { + list: List, + total: 4 + } + } + } + }, + // 列表接口 + { + url: '/mock/role/list2', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: testList + } + } + }, + { + url: '/mock/role/table', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: { + list: List, + total: 4 + } + } + } + } +] diff --git a/promo-ui2/mock/table/index.mock.ts b/promo-ui2/mock/table/index.mock.ts new file mode 100644 index 0000000..a83ed14 --- /dev/null +++ b/promo-ui2/mock/table/index.mock.ts @@ -0,0 +1,319 @@ +import Mock from 'mockjs' +import { SUCCESS_CODE } from '@/constants' +import { toAnyString } from '@/utils' + +const timeout = 1000 +const count = 100 + +const baseContent = + '

I am testing data, I am testing data.

' + +interface ListProps { + id: string + author: string + title: string + content: string + importance: number + display_time: any + pageviews: number + image_uri: string + video_uri?: string +} + +interface TreeListProps { + id: string + author: string + title: string + content: string + importance: number + display_time: any + image_uri: string + pageviews: number + video_uri?: string + children?: TreeListProps[] +} + +let List: ListProps[] = [] + +for (let i = 0; i < count; i++) { + List.push( + Mock.mock({ + id: toAnyString(), + // timestamp: +Mock.Random.date('T'), + author: '@first', + title: '@title(5, 10)', + content: baseContent, + importance: '@integer(1, 3)', + display_time: '@datetime', + pageviews: '@integer(100, 500)', + image_uri: Mock.Random.image('@integer(100, 500)x@integer(100, 500)'), + video_uri: + '//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4' + }) + ) +} + +const treeList: TreeListProps[] = [] + +for (let i = 0; i < count; i++) { + treeList.push( + Mock.mock({ + id: toAnyString(), + // timestamp: +Mock.Random.date('T'), + author: '@first', + title: '@title(5, 10)', + content: baseContent, + importance: '@integer(1, 3)', + display_time: '@datetime', + pageviews: '@integer(300, 5000)', + image_uri: Mock.Random.image('@integer(100, 500)x@integer(100, 500)'), + children: [ + { + id: toAnyString(), + // timestamp: +Mock.Random.date('T'), + author: '@first', + title: '@title(5, 10)', + content: baseContent, + importance: '@integer(1, 3)', + display_time: '@datetime', + pageviews: '@integer(300, 5000)', + image_uri: Mock.Random.image('@integer(100, 500)x@integer(100, 500)'), + children: [ + { + id: toAnyString(), + // timestamp: +Mock.Random.date('T'), + author: '@first', + title: '@title(5, 10)', + content: baseContent, + importance: '@integer(1, 3)', + display_time: '@datetime', + pageviews: '@integer(300, 5000)', + image_uri: Mock.Random.image('@integer(100, 500)x@integer(100, 500)') + }, + { + id: toAnyString(), + // timestamp: +Mock.Random.date('T'), + author: '@first', + title: '@title(5, 10)', + content: baseContent, + importance: '@integer(1, 3)', + display_time: '@datetime', + pageviews: '@integer(300, 5000)', + image_uri: Mock.Random.image('@integer(100, 500)x@integer(100, 500)') + } + ] + }, + { + id: toAnyString(), + // timestamp: +Mock.Random.date('T'), + author: '@first', + title: '@title(5, 10)', + content: baseContent, + importance: '@integer(1, 3)', + display_time: '@datetime', + pageviews: '@integer(300, 5000)', + image_uri: Mock.Random.image('@integer(100, 500)x@integer(100, 500)') + }, + { + id: toAnyString(), + // timestamp: +Mock.Random.date('T'), + author: '@first', + title: '@title(5, 10)', + content: baseContent, + importance: '@integer(1, 3)', + display_time: '@datetime', + pageviews: '@integer(300, 5000)', + image_uri: Mock.Random.image('@integer(100, 500)x@integer(100, 500)') + }, + { + id: toAnyString(), + // timestamp: +Mock.Random.date('T'), + author: '@first', + title: '@title(5, 10)', + content: baseContent, + importance: '@integer(1, 3)', + display_time: '@datetime', + pageviews: '@integer(300, 5000)', + image_uri: Mock.Random.image('@integer(100, 500)x@integer(100, 500)') + } + ] + // image_uri + }) + ) +} + +const cardList = [ + { + logo: 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png', + name: 'Alipay', + desc: '在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。' + }, + { + logo: 'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png', + name: 'Angular', + desc: '在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。' + }, + { + logo: 'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png', + name: 'Bootstrap', + desc: '在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。' + }, + { + logo: 'https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png', + name: 'React', + desc: '在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。' + }, + { + logo: 'https://gw.alipayobjects.com/zos/rmsportal/ComBAopevLwENQdKWiIn.png', + name: 'Vue', + desc: '在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。' + }, + { + logo: 'https://gw.alipayobjects.com/zos/rmsportal/nxkuOJlFJuAUhzlMTCEe.png', + name: 'Webpack', + desc: '在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。' + } +] + +export default [ + // 树形列表接口 + { + url: '/mock/example/treeList', + method: 'get', + timeout, + response: ({ query }) => { + const { title, pageIndex, pageSize } = query + const mockList = treeList.filter((item) => { + if (title && item.title.indexOf(title) < 0) return false + return true + }) + const pageList = mockList.filter( + (_, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1) + ) + return { + code: SUCCESS_CODE, + data: { + total: mockList.length, + list: pageList + } + } + } + }, + // 列表接口 + { + url: '/mock/example/list', + method: 'get', + timeout, + response: ({ query }) => { + const { title, pageIndex, pageSize } = query + const mockList = List.filter((item) => { + if (title && item.title.indexOf(title) < 0) return false + return true + }) + const pageList = mockList.filter( + (_, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1) + ) + return { + code: SUCCESS_CODE, + data: { + total: mockList.length, + list: pageList + } + } + } + }, + // 保存接口 + { + url: '/mock/example/save', + method: 'post', + timeout, + response: ({ body }) => { + if (!body.id) { + List = [ + Object.assign(body, { + id: toAnyString() + }) + ].concat(List) + return { + code: SUCCESS_CODE, + data: 'success' + } + } else { + List.map((item) => { + if (item.id === body.id) { + for (const key in item) { + item[key] = body[key] + } + } + }) + return { + code: SUCCESS_CODE, + data: 'success' + } + } + } + }, + // 详情接口 + { + url: '/mock/example/detail', + method: 'get', + response: ({ query }) => { + const { id } = query + for (const example of List) { + if (example.id === id) { + return { + code: SUCCESS_CODE, + data: example + } + } + } + } + }, + // 删除接口 + { + url: '/mock/example/delete', + method: 'post', + response: ({ body }) => { + const ids = body.ids + if (!ids) { + return { + code: 500, + message: '请选择需要删除的数据' + } + } else { + let i = List.length + while (i--) { + if (ids.indexOf(List[i].id) !== -1) { + List.splice(i, 1) + } + } + return { + code: SUCCESS_CODE, + data: 'success' + } + } + } + }, + { + url: '/mock/card/list', + method: 'get', + timeout, + response: ({ query }) => { + const { name, pageIndex, pageSize } = query + const mockList = cardList.filter((item) => { + if (name && item.name.indexOf(name) < 0) return false + return true + }) + const pageList = mockList.filter( + (_, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1) + ) + return { + code: SUCCESS_CODE, + data: { + total: mockList.length, + list: pageList + } + } + } + } +] diff --git a/promo-ui2/mock/user/index.mock.ts b/promo-ui2/mock/user/index.mock.ts new file mode 100644 index 0000000..4e5918c --- /dev/null +++ b/promo-ui2/mock/user/index.mock.ts @@ -0,0 +1,90 @@ +import { SUCCESS_CODE } from '@/constants' + +const timeout = 1000 + +const List: { + username: string + password: string + role: string + roleId: string + permissions: string | string[] +}[] = [ + { + username: 'admin', + password: 'admin', + role: 'admin', + roleId: '1', + permissions: ['*.*.*'] + }, + { + username: 'test', + password: 'test', + role: 'test', + roleId: '2', + permissions: ['example:dialog:create', 'example:dialog:delete'] + } +] + +export default [ + // 列表接口 + { + url: '/mock/user/list', + method: 'get', + response: ({ query }) => { + const { username, pageIndex, pageSize } = query + + const mockList = List.filter((item) => { + if (username && item.username.indexOf(username) < 0) return false + return true + }) + const pageList = mockList.filter( + (_, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1) + ) + + return { + code: SUCCESS_CODE, + data: { + total: mockList.length, + list: pageList + } + } + } + }, + // 登录接口 + { + url: '/mock/user/login', + method: 'post', + timeout, + response: ({ body }) => { + const data = body + let hasUser = false + for (const user of List) { + if (user.username === data.username && user.password === data.password) { + hasUser = true + return { + code: SUCCESS_CODE, + data: user + } + } + } + if (!hasUser) { + return { + code: 500, + message: '账号或密码错误' + } + } + } + }, + // 退出接口 + { + url: '/mock/user/loginOut', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: null + } + } + } +] diff --git a/promo-ui2/mock/workplace/index.mock.ts b/promo-ui2/mock/workplace/index.mock.ts new file mode 100644 index 0000000..593ba27 --- /dev/null +++ b/promo-ui2/mock/workplace/index.mock.ts @@ -0,0 +1,169 @@ +import { SUCCESS_CODE } from '@/constants' + +const timeout = 1000 + +export default [ + // 获取统计 + { + url: '/mock/workplace/total', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: { + project: 40, + access: 2340, + todo: 10 + } + } + } + }, + // 获取项目 + { + url: '/mock/workplace/project', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: [ + { + name: 'Github', + icon: 'akar-icons:github-fill', + message: 'workplace.introduction', + personal: 'Archer', + time: new Date() + }, + { + name: 'Vue', + icon: 'logos:vue', + message: 'workplace.introduction', + personal: 'Archer', + time: new Date() + }, + { + name: 'Angular', + icon: 'logos:angular-icon', + message: 'workplace.introduction', + personal: 'Archer', + time: new Date() + }, + { + name: 'React', + icon: 'logos:react', + message: 'workplace.introduction', + personal: 'Archer', + time: new Date() + }, + { + name: 'Webpack', + icon: 'logos:webpack', + message: 'workplace.introduction', + personal: 'Archer', + time: new Date() + }, + { + name: 'Vite', + icon: 'vscode-icons:file-type-vite', + message: 'workplace.introduction', + personal: 'Archer', + time: new Date() + } + ] + } + } + }, + // 获取动态 + { + url: '/mock/workplace/dynamic', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: [ + { + keys: ['workplace.push', 'Github'], + time: new Date() + }, + { + keys: ['workplace.push', 'Github'], + time: new Date() + }, + { + keys: ['workplace.push', 'Github'], + time: new Date() + }, + { + keys: ['workplace.push', 'Github'], + time: new Date() + }, + { + keys: ['workplace.push', 'Github'], + time: new Date() + }, + { + keys: ['workplace.push', 'Github'], + time: new Date() + } + ] + } + } + }, + // 获取团队信息 + { + url: '/mock/workplace/team', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: [ + { + name: 'Github', + icon: 'akar-icons:github-fill' + }, + { + name: 'Vue', + icon: 'logos:vue' + }, + { + name: 'Angular', + icon: 'logos:angular-icon' + }, + { + name: 'React', + icon: 'logos:react' + }, + { + name: 'Webpack', + icon: 'logos:webpack' + }, + { + name: 'Vite', + icon: 'vscode-icons:file-type-vite' + } + ] + } + } + }, + // 获取指数 + { + url: '/mock/workplace/radar', + method: 'get', + timeout, + response: () => { + return { + code: SUCCESS_CODE, + data: [ + { name: 'workplace.quote', max: 65, personal: 42, team: 50 }, + { name: 'workplace.contribution', max: 160, personal: 30, team: 140 }, + { name: 'workplace.hot', max: 300, personal: 20, team: 28 }, + { name: 'workplace.yield', max: 130, personal: 35, team: 35 }, + { name: 'workplace.follow', max: 100, personal: 80, team: 90 } + ] + } + } + } +] diff --git a/promo-ui2/package.json b/promo-ui2/package.json new file mode 100644 index 0000000..d452fa9 --- /dev/null +++ b/promo-ui2/package.json @@ -0,0 +1,139 @@ +{ + "name": "vue-element-plus-admin", + "version": "2.9.0", + "description": "一套基于vue3、element-plus、typesScript、vite4的后台集成方案。", + "author": "Archer <502431556@qq.com>", + "private": false, + "type": "module", + "scripts": { + "i": "pnpm install", + "dev": "pnpm vite --mode base", + "ts:check": "pnpm vue-tsc --noEmit --skipLibCheck", + "build:pro": "pnpm vite build --mode pro", + "build:gitee": "pnpm vite build --mode gitee", + "build:dev": "pnpm vite build --mode dev", + "build:test": "pnpm vite build --mode test", + "serve:pro": "pnpm vite preview --mode pro", + "serve:dev": "pnpm vite preview --mode dev", + "serve:test": "pnpm vite preview --mode test", + "npm:check": "pnpx npm-check-updates -u", + "clean": "pnpx rimraf node_modules", + "clean:cache": "pnpx rimraf node_modules/.cache", + "lint:eslint": "eslint . --fix \"src/**/*.{js,ts,tsx,vue,html}\"", + "lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,vue,html,md}\"", + "lint:style": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", + "lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.cjs", + "prepare": "husky install", + "p": "plop", + "icon": "esno ./scripts/icon.ts" + }, + "dependencies": { + "@iconify/iconify": "^3.1.1", + "@iconify/vue": "^4.3.0", + "@vueuse/core": "^12.3.0", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.10", + "@zxcvbn-ts/core": "^3.0.4", + "animate.css": "^4.1.1", + "axios": "^1.7.9", + "cropperjs": "^1.6.2", + "dayjs": "^1.11.13", + "driver.js": "^1.3.1", + "echarts": "^5.6.0", + "echarts-wordcloud": "^2.1.0", + "element-plus": "2.9.2", + "lodash-es": "^4.17.21", + "mitt": "^3.0.1", + "monaco-editor": "^0.52.2", + "nprogress": "^0.2.0", + "pinia": "^2.3.0", + "pinia-plugin-persistedstate": "^4.2.0", + "qrcode": "^1.5.4", + "qs": "^6.13.1", + "url": "^0.11.4", + "vue": "3.5.13", + "vue-draggable-plus": "^0.6.0", + "vue-i18n": "11.0.1", + "vue-json-pretty": "^2.4.0", + "vue-router": "^4.5.0", + "vue-types": "^5.1.3", + "xgplayer": "^3.0.20" + }, + "devDependencies": { + "@commitlint/cli": "^19.6.1", + "@commitlint/config-conventional": "^19.6.0", + "@iconify/json": "^2.2.293", + "@intlify/unplugin-vue-i18n": "^6.0.3", + "@types/fs-extra": "^11.0.4", + "@types/inquirer": "^9.0.7", + "@types/lodash-es": "^4.17.12", + "@types/mockjs": "^1.0.10", + "@types/node": "^22.10.5", + "@types/nprogress": "^0.2.3", + "@types/qrcode": "^1.5.5", + "@types/qs": "^6.9.17", + "@types/sortablejs": "^1.15.8", + "@typescript-eslint/eslint-plugin": "^8.19.1", + "@typescript-eslint/parser": "^8.19.1", + "@unocss/transformer-variant-group": "^0.65.4", + "@vitejs/plugin-legacy": "^6.0.0", + "@vitejs/plugin-vue": "^5.2.1", + "@vitejs/plugin-vue-jsx": "^4.1.1", + "autoprefixer": "^10.4.20", + "chalk": "^5.4.1", + "consola": "^3.3.3", + "eslint": "^9.17.0", + "eslint-config-prettier": "^9.1.0", + "eslint-define-config": "^2.1.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-vue": "^9.32.0", + "esno": "^4.8.0", + "fs-extra": "^11.2.0", + "husky": "^9.1.7", + "inquirer": "^12.3.0", + "less": "^4.2.1", + "lint-staged": "^15.3.0", + "mockjs": "^1.1.0", + "plop": "^4.0.1", + "postcss": "^8.4.49", + "postcss-html": "^1.7.0", + "postcss-less": "^6.0.0", + "prettier": "^3.4.2", + "rimraf": "^6.0.1", + "rollup": "^4.30.1", + "rollup-plugin-visualizer": "^5.14.0", + "stylelint": "^16.12.0", + "stylelint-config-html": "^1.1.0", + "stylelint-config-recommended": "^14.0.1", + "stylelint-config-standard": "^36.0.1", + "stylelint-order": "^6.0.4", + "terser": "^5.37.0", + "typescript": "5.7.3", + "typescript-eslint": "^8.19.1", + "unocss": "^0.65.4", + "vite": "6.0.7", + "vite-plugin-ejs": "^1.7.0", + "vite-plugin-eslint": "^1.8.1", + "vite-plugin-mock": "2.9.6", + "vite-plugin-progress": "^0.0.7", + "vite-plugin-purge-icons": "^0.10.0", + "vite-plugin-style-import": "2.0.0", + "vite-plugin-svg-icons": "^2.0.1", + "vite-plugin-url-copy": "^1.1.4", + "vue-tsc": "^2.2.0" + }, + "packageManager": "pnpm@9.15.3", + "engines": { + "node": ">=18.0.0", + "pnpm": ">=8.1.0" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/kailong321200875/vue-element-plus-admin.git" + }, + "bugs": { + "url": "https://github.com/kailong321200875/vue-element-plus-admin/issues" + }, + "homepage": "https://github.com/kailong321200875/vue-element-plus-admin" +} diff --git a/promo-ui2/plop/component/component.hbs b/promo-ui2/plop/component/component.hbs new file mode 100644 index 0000000..21e5653 --- /dev/null +++ b/promo-ui2/plop/component/component.hbs @@ -0,0 +1,11 @@ + + + diff --git a/promo-ui2/plop/component/index.hbs b/promo-ui2/plop/component/index.hbs new file mode 100644 index 0000000..aca6f0a --- /dev/null +++ b/promo-ui2/plop/component/index.hbs @@ -0,0 +1,3 @@ +import {{ upperFirstName }} from './src/{{ upperFirstName }}.vue' + +export { {{ upperFirstName }} } diff --git a/promo-ui2/plop/component/prompt.cjs b/promo-ui2/plop/component/prompt.cjs new file mode 100644 index 0000000..98a852f --- /dev/null +++ b/promo-ui2/plop/component/prompt.cjs @@ -0,0 +1,38 @@ +const toUpperCase = (str) => str.charAt(0).toUpperCase() + str.slice(1) + +module.exports = { + description: 'Create vue component', + prompts: [ + { + type: 'input', + name: 'name', + message: '请输入组件名称(Please enter the component name)' + } + ], + actions: (data) => { + const { name } = data + const upperFirstName = toUpperCase(name) + + const actions = [] + if (name) { + actions.push({ + type: 'add', + path: `./src/components/${upperFirstName}/src/${upperFirstName}.vue`, + templateFile: './plop/component/component.hbs', + data: { + name, + upperFirstName + } + }, { + type: 'add', + path: `./src/components/${upperFirstName}/index.ts`, + templateFile: './plop/component/index.hbs', + data: { + upperFirstName + } + }) + } + + return actions + } +} diff --git a/promo-ui2/plop/view/prompt.cjs b/promo-ui2/plop/view/prompt.cjs new file mode 100644 index 0000000..dbf324f --- /dev/null +++ b/promo-ui2/plop/view/prompt.cjs @@ -0,0 +1,37 @@ +const toUpperCase = (str) => str.charAt(0).toUpperCase() + str.slice(1) + +module.exports = { + description: 'Create vue view', + prompts: [ + { + type: 'input', + name: 'path', + message: '请输入路径(Please enter a path)', + default: 'views' + }, + { + type: 'input', + name: 'name', + message: '请输入模块名称(Please enter module name)' + } + ], + actions: (data) => { + const { name, path } = data + const upperFirstName = toUpperCase(name) + + const actions = [] + if (name) { + actions.push({ + type: 'add', + path: `./src/${path}/${upperFirstName}.vue`, + templateFile: './plop/view/view.hbs', + data: { + name, + upperFirstName + } + }) + } + + return actions + } +} diff --git a/promo-ui2/plop/view/view.hbs b/promo-ui2/plop/view/view.hbs new file mode 100644 index 0000000..0ae9c15 --- /dev/null +++ b/promo-ui2/plop/view/view.hbs @@ -0,0 +1,7 @@ + + + diff --git a/promo-ui2/plopfile.cjs b/promo-ui2/plopfile.cjs new file mode 100644 index 0000000..ad90fa1 --- /dev/null +++ b/promo-ui2/plopfile.cjs @@ -0,0 +1,7 @@ +const viewGenerator = require('./plop/view/prompt.cjs') +const componentGenerator = require('./plop/component/prompt.cjs') + +module.exports = function (plop) { + plop.setGenerator('view', viewGenerator) + plop.setGenerator('component', componentGenerator) +} diff --git a/promo-ui2/pnpm-lock.yaml b/promo-ui2/pnpm-lock.yaml new file mode 100644 index 0000000..f0a6f4e --- /dev/null +++ b/promo-ui2/pnpm-lock.yaml @@ -0,0 +1,11876 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@iconify/iconify': + specifier: ^3.1.1 + version: 3.1.1 + '@iconify/vue': + specifier: ^4.3.0 + version: 4.3.0(vue@3.5.13(typescript@5.7.3)) + '@vueuse/core': + specifier: ^12.3.0 + version: 12.3.0(typescript@5.7.3) + '@wangeditor/editor': + specifier: ^5.1.23 + version: 5.1.23 + '@wangeditor/editor-for-vue': + specifier: ^5.1.10 + version: 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.13(typescript@5.7.3)) + '@zxcvbn-ts/core': + specifier: ^3.0.4 + version: 3.0.4 + animate.css: + specifier: ^4.1.1 + version: 4.1.1 + axios: + specifier: ^1.7.9 + version: 1.7.9 + cropperjs: + specifier: ^1.6.2 + version: 1.6.2 + dayjs: + specifier: ^1.11.13 + version: 1.11.13 + driver.js: + specifier: ^1.3.1 + version: 1.3.1 + echarts: + specifier: ^5.6.0 + version: 5.6.0 + echarts-wordcloud: + specifier: ^2.1.0 + version: 2.1.0(echarts@5.6.0) + element-plus: + specifier: 2.9.2 + version: 2.9.2(vue@3.5.13(typescript@5.7.3)) + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 + mitt: + specifier: ^3.0.1 + version: 3.0.1 + monaco-editor: + specifier: ^0.52.2 + version: 0.52.2 + nprogress: + specifier: ^0.2.0 + version: 0.2.0 + pinia: + specifier: ^2.3.0 + version: 2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)) + pinia-plugin-persistedstate: + specifier: ^4.2.0 + version: 4.2.0(pinia@2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)))(rollup@4.30.1) + qrcode: + specifier: ^1.5.4 + version: 1.5.4 + qs: + specifier: ^6.13.1 + version: 6.13.1 + url: + specifier: ^0.11.4 + version: 0.11.4 + vue: + specifier: 3.5.13 + version: 3.5.13(typescript@5.7.3) + vue-draggable-plus: + specifier: ^0.6.0 + version: 0.6.0(@types/sortablejs@1.15.8) + vue-i18n: + specifier: 11.0.1 + version: 11.0.1(vue@3.5.13(typescript@5.7.3)) + vue-json-pretty: + specifier: ^2.4.0 + version: 2.4.0(vue@3.5.13(typescript@5.7.3)) + vue-router: + specifier: ^4.5.0 + version: 4.5.0(vue@3.5.13(typescript@5.7.3)) + vue-types: + specifier: ^5.1.3 + version: 5.1.3(vue@3.5.13(typescript@5.7.3)) + xgplayer: + specifier: ^3.0.20 + version: 3.0.20(core-js@3.40.0) + devDependencies: + '@commitlint/cli': + specifier: ^19.6.1 + version: 19.6.1(@types/node@22.10.5)(typescript@5.7.3) + '@commitlint/config-conventional': + specifier: ^19.6.0 + version: 19.6.0 + '@iconify/json': + specifier: ^2.2.293 + version: 2.2.293 + '@intlify/unplugin-vue-i18n': + specifier: ^6.0.3 + version: 6.0.3(@vue/compiler-dom@3.5.13)(eslint@9.17.0(jiti@2.4.2))(rollup@4.30.1)(typescript@5.7.3)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3)) + '@types/fs-extra': + specifier: ^11.0.4 + version: 11.0.4 + '@types/inquirer': + specifier: ^9.0.7 + version: 9.0.7 + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 + '@types/mockjs': + specifier: ^1.0.10 + version: 1.0.10 + '@types/node': + specifier: ^22.10.5 + version: 22.10.5 + '@types/nprogress': + specifier: ^0.2.3 + version: 0.2.3 + '@types/qrcode': + specifier: ^1.5.5 + version: 1.5.5 + '@types/qs': + specifier: ^6.9.17 + version: 6.9.17 + '@types/sortablejs': + specifier: ^1.15.8 + version: 1.15.8 + '@typescript-eslint/eslint-plugin': + specifier: ^8.19.1 + version: 8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/parser': + specifier: ^8.19.1 + version: 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + '@unocss/transformer-variant-group': + specifier: ^0.65.4 + version: 0.65.4 + '@vitejs/plugin-legacy': + specifier: ^6.0.0 + version: 6.0.0(terser@5.37.0)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + '@vitejs/plugin-vue': + specifier: ^5.2.1 + version: 5.2.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3)) + '@vitejs/plugin-vue-jsx': + specifier: ^4.1.1 + version: 4.1.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3)) + autoprefixer: + specifier: ^10.4.20 + version: 10.4.20(postcss@8.4.49) + chalk: + specifier: ^5.4.1 + version: 5.4.1 + consola: + specifier: ^3.3.3 + version: 3.3.3 + eslint: + specifier: ^9.17.0 + version: 9.17.0(jiti@2.4.2) + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@9.17.0(jiti@2.4.2)) + eslint-define-config: + specifier: ^2.1.0 + version: 2.1.0 + eslint-plugin-prettier: + specifier: ^5.2.1 + version: 5.2.1(@types/eslint@8.56.12)(eslint-config-prettier@9.1.0(eslint@9.17.0(jiti@2.4.2)))(eslint@9.17.0(jiti@2.4.2))(prettier@3.4.2) + eslint-plugin-vue: + specifier: ^9.32.0 + version: 9.32.0(eslint@9.17.0(jiti@2.4.2)) + esno: + specifier: ^4.8.0 + version: 4.8.0 + fs-extra: + specifier: ^11.2.0 + version: 11.2.0 + husky: + specifier: ^9.1.7 + version: 9.1.7 + inquirer: + specifier: ^12.3.0 + version: 12.3.0(@types/node@22.10.5) + less: + specifier: ^4.2.1 + version: 4.2.1 + lint-staged: + specifier: ^15.3.0 + version: 15.3.0 + mockjs: + specifier: ^1.1.0 + version: 1.1.0 + plop: + specifier: ^4.0.1 + version: 4.0.1 + postcss: + specifier: ^8.4.49 + version: 8.4.49 + postcss-html: + specifier: ^1.7.0 + version: 1.7.0 + postcss-less: + specifier: ^6.0.0 + version: 6.0.0(postcss@8.4.49) + prettier: + specifier: ^3.4.2 + version: 3.4.2 + rimraf: + specifier: ^6.0.1 + version: 6.0.1 + rollup: + specifier: ^4.30.1 + version: 4.30.1 + rollup-plugin-visualizer: + specifier: ^5.14.0 + version: 5.14.0(rollup@4.30.1) + stylelint: + specifier: ^16.12.0 + version: 16.12.0(typescript@5.7.3) + stylelint-config-html: + specifier: ^1.1.0 + version: 1.1.0(postcss-html@1.7.0)(stylelint@16.12.0(typescript@5.7.3)) + stylelint-config-recommended: + specifier: ^14.0.1 + version: 14.0.1(stylelint@16.12.0(typescript@5.7.3)) + stylelint-config-standard: + specifier: ^36.0.1 + version: 36.0.1(stylelint@16.12.0(typescript@5.7.3)) + stylelint-order: + specifier: ^6.0.4 + version: 6.0.4(stylelint@16.12.0(typescript@5.7.3)) + terser: + specifier: ^5.37.0 + version: 5.37.0 + typescript: + specifier: 5.7.3 + version: 5.7.3 + typescript-eslint: + specifier: ^8.19.1 + version: 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + unocss: + specifier: ^0.65.4 + version: 0.65.4(postcss@8.4.49)(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3)) + vite: + specifier: 6.0.7 + version: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + vite-plugin-ejs: + specifier: ^1.7.0 + version: 1.7.0(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + vite-plugin-eslint: + specifier: ^1.8.1 + version: 1.8.1(eslint@9.17.0(jiti@2.4.2))(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + vite-plugin-mock: + specifier: 2.9.6 + version: 2.9.6(mockjs@1.1.0)(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + vite-plugin-progress: + specifier: ^0.0.7 + version: 0.0.7(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + vite-plugin-purge-icons: + specifier: ^0.10.0 + version: 0.10.0(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + vite-plugin-style-import: + specifier: 2.0.0 + version: 2.0.0(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + vite-plugin-svg-icons: + specifier: ^2.0.1 + version: 2.0.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + vite-plugin-url-copy: + specifier: ^1.1.4 + version: 1.1.4(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + vue-tsc: + specifier: ^2.2.0 + version: 2.2.0(typescript@5.7.3) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@antfu/install-pkg@0.4.1': + resolution: {integrity: sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==} + + '@antfu/utils@0.7.10': + resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} + + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.26.3': + resolution: {integrity: sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.26.0': + resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.26.3': + resolution: {integrity: sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.25.9': + resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.25.9': + resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.25.9': + resolution: {integrity: sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.26.3': + resolution: {integrity: sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.3': + resolution: {integrity: sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-member-expression-to-functions@7.25.9': + resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.25.9': + resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.25.9': + resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.25.9': + resolution: {integrity: sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.25.9': + resolution: {integrity: sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.25.9': + resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.25.9': + resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.26.0': + resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.26.3': + resolution: {integrity: sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9': + resolution: {integrity: sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9': + resolution: {integrity: sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9': + resolution: {integrity: sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9': + resolution: {integrity: sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9': + resolution: {integrity: sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.26.0': + resolution: {integrity: sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.26.0': + resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.25.9': + resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.25.9': + resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.25.9': + resolution: {integrity: sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.25.9': + resolution: {integrity: sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.25.9': + resolution: {integrity: sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.25.9': + resolution: {integrity: sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.25.9': + resolution: {integrity: sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.25.9': + resolution: {integrity: sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.26.0': + resolution: {integrity: sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.25.9': + resolution: {integrity: sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.25.9': + resolution: {integrity: sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.25.9': + resolution: {integrity: sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.25.9': + resolution: {integrity: sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.25.9': + resolution: {integrity: sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9': + resolution: {integrity: sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.25.9': + resolution: {integrity: sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.26.3': + resolution: {integrity: sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.25.9': + resolution: {integrity: sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.25.9': + resolution: {integrity: sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.25.9': + resolution: {integrity: sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.25.9': + resolution: {integrity: sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.25.9': + resolution: {integrity: sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.25.9': + resolution: {integrity: sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.25.9': + resolution: {integrity: sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.25.9': + resolution: {integrity: sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.26.3': + resolution: {integrity: sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.25.9': + resolution: {integrity: sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.25.9': + resolution: {integrity: sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.25.9': + resolution: {integrity: sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.25.9': + resolution: {integrity: sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.25.9': + resolution: {integrity: sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.25.9': + resolution: {integrity: sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.25.9': + resolution: {integrity: sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.25.9': + resolution: {integrity: sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.25.9': + resolution: {integrity: sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.25.9': + resolution: {integrity: sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.25.9': + resolution: {integrity: sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.25.9': + resolution: {integrity: sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.25.9': + resolution: {integrity: sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.25.9': + resolution: {integrity: sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.25.9': + resolution: {integrity: sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.26.0': + resolution: {integrity: sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.25.9': + resolution: {integrity: sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.25.9': + resolution: {integrity: sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.25.9': + resolution: {integrity: sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.25.9': + resolution: {integrity: sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.25.9': + resolution: {integrity: sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.25.9': + resolution: {integrity: sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.26.3': + resolution: {integrity: sha512-6+5hpdr6mETwSKjmJUdYw0EIkATiQhnELWlE3kJFBwSg/BGIVwVaVbX+gOXBCdc7Ln1RXZxyWGecIXhUfnl7oA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.25.9': + resolution: {integrity: sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.25.9': + resolution: {integrity: sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.25.9': + resolution: {integrity: sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.25.9': + resolution: {integrity: sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.26.0': + resolution: {integrity: sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} + engines: {node: '>=6.9.0'} + + '@babel/standalone@7.26.4': + resolution: {integrity: sha512-SF+g7S2mhTT1b7CHyfNjDkPU1corxg4LPYsyP0x5KuCl+EbtBQHRLqr9N3q7e7+x7NQ5LYxQf8mJ2PmzebLr0A==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.26.4': + resolution: {integrity: sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.26.3': + resolution: {integrity: sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==} + engines: {node: '>=6.9.0'} + + '@commitlint/cli@19.6.1': + resolution: {integrity: sha512-8hcyA6ZoHwWXC76BoC8qVOSr8xHy00LZhZpauiD0iO0VYbVhMnED0da85lTfIULxl7Lj4c6vZgF0Wu/ed1+jlQ==} + engines: {node: '>=v18'} + hasBin: true + + '@commitlint/config-conventional@19.6.0': + resolution: {integrity: sha512-DJT40iMnTYtBtUfw9ApbsLZFke1zKh6llITVJ+x9mtpHD08gsNXaIRqHTmwTZL3dNX5+WoyK7pCN/5zswvkBCQ==} + engines: {node: '>=v18'} + + '@commitlint/config-validator@19.5.0': + resolution: {integrity: sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==} + engines: {node: '>=v18'} + + '@commitlint/ensure@19.5.0': + resolution: {integrity: sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==} + engines: {node: '>=v18'} + + '@commitlint/execute-rule@19.5.0': + resolution: {integrity: sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==} + engines: {node: '>=v18'} + + '@commitlint/format@19.5.0': + resolution: {integrity: sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==} + engines: {node: '>=v18'} + + '@commitlint/is-ignored@19.6.0': + resolution: {integrity: sha512-Ov6iBgxJQFR9koOupDPHvcHU9keFupDgtB3lObdEZDroiG4jj1rzky60fbQozFKVYRTUdrBGICHG0YVmRuAJmw==} + engines: {node: '>=v18'} + + '@commitlint/lint@19.6.0': + resolution: {integrity: sha512-LRo7zDkXtcIrpco9RnfhOKeg8PAnE3oDDoalnrVU/EVaKHYBWYL1DlRR7+3AWn0JiBqD8yKOfetVxJGdEtZ0tg==} + engines: {node: '>=v18'} + + '@commitlint/load@19.6.1': + resolution: {integrity: sha512-kE4mRKWWNju2QpsCWt428XBvUH55OET2N4QKQ0bF85qS/XbsRGG1MiTByDNlEVpEPceMkDr46LNH95DtRwcsfA==} + engines: {node: '>=v18'} + + '@commitlint/message@19.5.0': + resolution: {integrity: sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==} + engines: {node: '>=v18'} + + '@commitlint/parse@19.5.0': + resolution: {integrity: sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==} + engines: {node: '>=v18'} + + '@commitlint/read@19.5.0': + resolution: {integrity: sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==} + engines: {node: '>=v18'} + + '@commitlint/resolve-extends@19.5.0': + resolution: {integrity: sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==} + engines: {node: '>=v18'} + + '@commitlint/rules@19.6.0': + resolution: {integrity: sha512-1f2reW7lbrI0X0ozZMesS/WZxgPa4/wi56vFuJENBmed6mWq5KsheN/nxqnl/C23ioxpPO/PL6tXpiiFy5Bhjw==} + engines: {node: '>=v18'} + + '@commitlint/to-lines@19.5.0': + resolution: {integrity: sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==} + engines: {node: '>=v18'} + + '@commitlint/top-level@19.5.0': + resolution: {integrity: sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==} + engines: {node: '>=v18'} + + '@commitlint/types@19.5.0': + resolution: {integrity: sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==} + engines: {node: '>=v18'} + + '@csstools/css-parser-algorithms@3.0.4': + resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/css-tokenizer@3.0.3': + resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} + engines: {node: '>=18'} + + '@csstools/media-query-list-parser@4.0.2': + resolution: {integrity: sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 + + '@csstools/selector-specificity@5.0.0': + resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==} + engines: {node: '>=18'} + peerDependencies: + postcss-selector-parser: ^7.0.0 + + '@ctrl/tinycolor@3.6.1': + resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} + engines: {node: '>=10'} + + '@dual-bundle/import-meta-resolve@4.1.0': + resolution: {integrity: sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==} + + '@element-plus/icons-vue@2.3.1': + resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==} + peerDependencies: + vue: ^3.2.0 + + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.24.2': + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.24.2': + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.1': + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.19.1': + resolution: {integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.9.1': + resolution: {integrity: sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.2.0': + resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.17.0': + resolution: {integrity: sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.5': + resolution: {integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.4': + resolution: {integrity: sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@floating-ui/core@1.6.9': + resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==} + + '@floating-ui/dom@1.6.13': + resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==} + + '@floating-ui/utils@0.2.9': + resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.1': + resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} + engines: {node: '>=18.18'} + + '@iconify/iconify@2.1.2': + resolution: {integrity: sha512-QcUzFeEWkE/mW+BVtEGmcWATClcCOIJFiYUD/PiCWuTcdEA297o8D4oN6Ra44WrNOHu1wqNW4J0ioaDIiqaFOQ==} + + '@iconify/iconify@3.1.1': + resolution: {integrity: sha512-1nemfyD/OJzh9ALepH7YfuuP8BdEB24Skhd8DXWh0hzcOxImbb1ZizSZkpCzAwSZSGcJFmscIBaBQu+yLyWaxQ==} + + '@iconify/json@2.2.293': + resolution: {integrity: sha512-eMRJNfQa+MXmE7I9noABNSVPxwsCkgmfgzeh84IKS2Su1cdQhi5FtsZa6YjEXK8y1cpbfv6pYk7KKJ6fV3mvOw==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@2.2.1': + resolution: {integrity: sha512-0/7J7hk4PqXmxo5PDBDxmnecw5PxklZJfNjIVG9FM0mEfVrvfudS22rYWsqVk6gR3UJ/mSYS90X4R3znXnqfNA==} + + '@iconify/vue@4.3.0': + resolution: {integrity: sha512-Xq0h6zMrHBbrW8jXJ9fISi+x8oDQllg5hTDkDuxnWiskJ63rpJu9CvJshj8VniHVTbsxCg9fVoPAaNp3RQI5OQ==} + peerDependencies: + vue: '>=3' + + '@inquirer/checkbox@4.0.4': + resolution: {integrity: sha512-fYAKCAcGNMdfjL6hZTRUwkIByQ8EIZCXKrIQZH7XjADnN/xvRUhj8UdBbpC4zoUzvChhkSC/zRKaP/tDs3dZpg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/confirm@5.1.1': + resolution: {integrity: sha512-vVLSbGci+IKQvDOtzpPTCOiEJCNidHcAq9JYVoWTW0svb5FiwSLotkM+JXNXejfjnzVYV9n0DTBythl9+XgTxg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/core@10.1.2': + resolution: {integrity: sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==} + engines: {node: '>=18'} + + '@inquirer/editor@4.2.1': + resolution: {integrity: sha512-xn9aDaiP6nFa432i68JCaL302FyL6y/6EG97nAtfIPnWZ+mWPgCMLGc4XZ2QQMsZtu9q3Jd5AzBPjXh10aX9kA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/expand@4.0.4': + resolution: {integrity: sha512-GYocr+BPyxKPxQ4UZyNMqZFSGKScSUc0Vk17II3J+0bDcgGsQm0KYQNooN1Q5iBfXsy3x/VWmHGh20QnzsaHwg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/figures@1.0.9': + resolution: {integrity: sha512-BXvGj0ehzrngHTPTDqUoDT3NXL8U0RxUk2zJm2A66RhCEIWdtU1v6GuUqNAgArW4PQ9CinqIWyHdQgdwOj06zQ==} + engines: {node: '>=18'} + + '@inquirer/input@4.1.1': + resolution: {integrity: sha512-nAXAHQndZcXB+7CyjIW3XuQZZHbQQ0q8LX6miY6bqAWwDzNa9JUioDBYrFmOUNIsuF08o1WT/m2gbBXvBhYVxg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/number@3.0.4': + resolution: {integrity: sha512-DX7a6IXRPU0j8kr2ovf+QaaDiIf+zEKaZVzCWdLOTk7XigqSXvoh4cul7x68xp54WTQrgSnW7P1WBJDbyY3GhA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/password@4.0.4': + resolution: {integrity: sha512-wiliQOWdjM8FnBmdIHtQV2Ca3S1+tMBUerhyjkRCv1g+4jSvEweGu9GCcvVEgKDhTBT15nrxvk5/bVrGUqSs1w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/prompts@7.2.1': + resolution: {integrity: sha512-v2JSGri6/HXSfoGIwuKEn8sNCQK6nsB2BNpy2lSX6QH9bsECrMv93QHnj5+f+1ZWpF/VNioIV2B/PDox8EvGuQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/rawlist@4.0.4': + resolution: {integrity: sha512-IsVN2EZdNHsmFdKWx9HaXb8T/s3FlR/U1QPt9dwbSyPtjFbMTlW9CRFvnn0bm/QIsrMRD2oMZqrQpSWPQVbXXg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/search@3.0.4': + resolution: {integrity: sha512-tSkJk2SDmC2MEdTIjknXWmCnmPr5owTs9/xjfa14ol1Oh95n6xW7SYn5fiPk4/vrJPys0ggSWiISdPze4LTa7A==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/select@4.0.4': + resolution: {integrity: sha512-ZzYLuLoUzTIW9EJm++jBpRiTshGqS3Q1o5qOEQqgzaBlmdsjQr6pA4TUNkwu6OBYgM2mIRbCz6mUhFDfl/GF+w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/type@3.0.2': + resolution: {integrity: sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@intlify/bundle-utils@10.0.0': + resolution: {integrity: sha512-BR5yLOkF2dzrARTbAg7RGAIPcx9Aark7p1K/0O285F7rfzso9j2dsa+S4dA67clZ0rToZ10NSSTfbyUptVu7Bg==} + engines: {node: '>= 18'} + peerDependencies: + petite-vue-i18n: '*' + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + + '@intlify/core-base@11.0.1': + resolution: {integrity: sha512-NAmhw1l/llM0HZRpagR/ChJTNymW4ll6/4EDSJML5c8L5Hl/+k6UyF8EIgE6DeHpfheQujkSRngauViHqq6jJQ==} + engines: {node: '>= 16'} + + '@intlify/message-compiler@11.0.0-rc.1': + resolution: {integrity: sha512-TGw2uBfuTFTegZf/BHtUQBEKxl7Q/dVGLoqRIdw8lFsp9g/53sYn5iD+0HxIzdYjbWL6BTJMXCPUHp9PxDTRPw==} + engines: {node: '>= 16'} + + '@intlify/message-compiler@11.0.1': + resolution: {integrity: sha512-5RFH8x+Mn3mbjcHXnb6KCXGiczBdiQkWkv99iiA0JpKrNuTAQeW59Pjq/uObMB0eR0shnKYGTkIJxum+DbL3sw==} + engines: {node: '>= 16'} + + '@intlify/shared@11.0.0-rc.1': + resolution: {integrity: sha512-8tR1xe7ZEbkabTuE/tNhzpolygUn9OaYp9yuYAF4MgDNZg06C3Qny80bes2/e9/Wm3aVkPUlCw6WgU7mQd0yEg==} + engines: {node: '>= 16'} + + '@intlify/shared@11.0.1': + resolution: {integrity: sha512-lH164+aDDptHZ3dBDbIhRa1dOPQUp+83iugpc+1upTOWCnwyC1PVis6rSWNMMJ8VQxvtHQB9JMib48K55y0PvQ==} + engines: {node: '>= 16'} + + '@intlify/unplugin-vue-i18n@6.0.3': + resolution: {integrity: sha512-9ZDjBlhUHtgjRl23TVcgfJttgu8cNepwVhWvOv3mUMRDAhjW0pur1mWKEUKr1I8PNwE4Gvv2IQ1xcl4RL0nG0g==} + engines: {node: '>= 18'} + peerDependencies: + petite-vue-i18n: '*' + vue: ^3.2.25 + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + + '@intlify/vue-i18n-extensions@8.0.0': + resolution: {integrity: sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==} + engines: {node: '>= 18'} + peerDependencies: + '@intlify/shared': ^9.0.0 || ^10.0.0 || ^11.0.0 + '@vue/compiler-dom': ^3.0.0 + vue: ^3.0.0 + vue-i18n: ^9.0.0 || ^10.0.0 || ^11.0.0 + peerDependenciesMeta: + '@intlify/shared': + optional: true + '@vue/compiler-dom': + optional: true + vue: + optional: true + vue-i18n: + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha1-dhnC6yGyVIP20WdUi0z9WnSIw9U=} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha1-W9Jir5Tp0lvR5xsF3u1Eh2oiLos=} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha1-6Vc36LtnRt3t9pxVaVNJTxlv5po=} + engines: {node: '>= 8'} + + '@nuxt/kit@3.15.1': + resolution: {integrity: sha512-7cVWjzfz3L6CsZrg6ppDZa7zGrZxCSfZjEQDIvVFn4mFKtJlK9k2izf5EewL6luzWwIQojkZAC3iq/1wtgI0Xw==} + engines: {node: '>=18.20.5'} + + '@nuxt/schema@3.15.1': + resolution: {integrity: sha512-n5kOHt8uUyUM9z4Wu/8tIZkBYh3KTCGvyruG6oD9bfeT4OaS21+X3M7XsTXFMe+eYBZA70IFFlWn1JJZIPsKeA==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@pkgr/core@0.1.1': + resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@polka/url@1.0.0-next.28': + resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} + + '@purge-icons/core@0.10.0': + resolution: {integrity: sha512-AtJbZv5Yy+vWX5v32DPTr+CW7AkSK8HJx52orDbrYt/9s4lGM2t4KKAmwaTQEH2HYr2HVh1mlqs54/S1s3WT1g==} + + '@purge-icons/generated@0.10.0': + resolution: {integrity: sha512-I+1yN7/yDy/eZzfhAZqKF8Z6FM8D/O1vempbPrHJ0m9HlZwvf8sWXOArPJ2qRQGB6mJUVSpaXkoGBuoz1GQX5A==} + + '@rollup/plugin-node-resolve@13.3.0': + resolution: {integrity: sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==} + engines: {node: '>= 10.0.0'} + peerDependencies: + rollup: ^2.42.0 + + '@rollup/pluginutils@3.1.0': + resolution: {integrity: sha1-cGtFJO5tyLEDs8mVUz5a1oDAK5s=} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + + '@rollup/pluginutils@4.2.1': + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} + + '@rollup/pluginutils@5.1.4': + resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.30.1': + resolution: {integrity: sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.30.1': + resolution: {integrity: sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.30.1': + resolution: {integrity: sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.30.1': + resolution: {integrity: sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.30.1': + resolution: {integrity: sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.30.1': + resolution: {integrity: sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.30.1': + resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.30.1': + resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.30.1': + resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.30.1': + resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loongarch64-gnu@4.30.1': + resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': + resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-gnu@4.30.1': + resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-s390x-gnu@4.30.1': + resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.30.1': + resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.30.1': + resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-win32-arm64-msvc@4.30.1': + resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.30.1': + resolution: {integrity: sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.30.1': + resolution: {integrity: sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==} + cpu: [x64] + os: [win32] + + '@sindresorhus/merge-streams@2.3.0': + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + + '@sxzz/popperjs-es@2.11.7': + resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==} + + '@transloadit/prettier-bytes@0.0.7': + resolution: {integrity: sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==} + + '@trysound/sax@0.2.0': + resolution: {integrity: sha1-zMqrdYr1Z2Hre/N69vA/Mm3XmK0=} + engines: {node: '>=10.13.0'} + + '@types/conventional-commits-parser@5.0.1': + resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==} + + '@types/eslint@8.56.12': + resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==} + + '@types/estree@0.0.39': + resolution: {integrity: sha1-4Xfmme4bjCLSMXTKqnQiZEOJUJ8=} + + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + + '@types/event-emitter@0.3.5': + resolution: {integrity: sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==} + + '@types/fined@1.1.5': + resolution: {integrity: sha512-2N93vadEGDFhASTIRbizbl4bNqpMOId5zZfj6hHqYZfEzEfO9onnU4Im8xvzo8uudySDveDHBOOSlTWf38ErfQ==} + + '@types/fs-extra@11.0.4': + resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} + + '@types/inquirer@9.0.7': + resolution: {integrity: sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/jsonfile@6.1.4': + resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + + '@types/liftoff@4.0.3': + resolution: {integrity: sha512-UgbL2kR5pLrWICvr8+fuSg0u43LY250q7ZMkC+XKC3E+rs/YBDEnQIzsnhU5dYsLlwMi3R75UvCL87pObP1sxw==} + + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + + '@types/lodash@4.17.14': + resolution: {integrity: sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==} + + '@types/mockjs@1.0.10': + resolution: {integrity: sha512-SXgrhajHG7boLv6oU93CcmdDm0HYRiceuz6b+7z+/2lCJPTWDv0V5YiwFHT2ejE4bQqgSXQiVPQYPWv7LGsK1g==} + + '@types/node@10.17.60': + resolution: {integrity: sha1-NfPWIT2u2V2n8Pc+dbzGmA6QWXs=} + + '@types/node@22.10.5': + resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} + + '@types/nprogress@0.2.3': + resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==} + + '@types/qrcode@1.5.5': + resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==} + + '@types/qs@6.9.17': + resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==} + + '@types/resolve@1.17.1': + resolution: {integrity: sha1-Ov1q2JZ8d+Q3bFmKgt3Vj0bsRdY=} + + '@types/sortablejs@1.15.8': + resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==} + + '@types/svgo@2.6.4': + resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==} + + '@types/through@0.0.33': + resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} + + '@types/web-bluetooth@0.0.16': + resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} + + '@types/web-bluetooth@0.0.20': + resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + + '@typescript-eslint/eslint-plugin@8.19.1': + resolution: {integrity: sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/parser@8.19.1': + resolution: {integrity: sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/scope-manager@8.19.1': + resolution: {integrity: sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@8.19.1': + resolution: {integrity: sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/types@8.19.1': + resolution: {integrity: sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.19.1': + resolution: {integrity: sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/utils@8.19.1': + resolution: {integrity: sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/visitor-keys@8.19.1': + resolution: {integrity: sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unocss/astro@0.65.4': + resolution: {integrity: sha512-ex1CJOQ6yeftBEPcbA9/W47/YoV+mhQnrAoc8MA1VVrvvFKDitICFU62+nSt3NWRe53XL/fXnQbcbCb8AAgKlA==} + peerDependencies: + vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 + peerDependenciesMeta: + vite: + optional: true + + '@unocss/cli@0.65.4': + resolution: {integrity: sha512-D/4hY5Hezh3QETscl4i+ojb+q8YU9Cl9AYJ8v3gsjc/GjTmEuIOD5V4x+/aN25vY5wjqgoApOgaIDGCV3b+2Ig==} + engines: {node: '>=14'} + hasBin: true + + '@unocss/config@0.65.4': + resolution: {integrity: sha512-/vCt4AXnJ4p4Ow6xqsYwdrelF9533yhZjzkg3SQmL3rKeSkicPayKpeq8nkYECdhDI03VTCVD+6oh5Y/26Hg7A==} + engines: {node: '>=14'} + + '@unocss/core@0.65.4': + resolution: {integrity: sha512-a2JOoFutrhqd5RgPhIR5FIXrDoHDU3gwCbPrpT6KYTjsqlSc/fv02yZ+JGOZFN3MCFhCmaPTs+idDFtwb3xU8g==} + + '@unocss/extractor-arbitrary-variants@0.65.4': + resolution: {integrity: sha512-GbvTgsDaHplfWfsQtOY8RrvEZvptmvR9k9NwQ5NsZBNIG1JepYVel93CVQvsxT5KioKcoWngXxTYLNOGyxLs0g==} + + '@unocss/inspector@0.65.4': + resolution: {integrity: sha512-byg9x549Ul17U4Ety7ufDwC0UOygypoq4QnLEPzhlZ0KJG1f7WmXKYanOhupeg3h4qCj6Nc/xdZYMGbHl9QRIg==} + + '@unocss/postcss@0.65.4': + resolution: {integrity: sha512-8peDRo0+rNQsnKh/H2uZEVy67sV2cC16rAeSLpgbVJUMNfZlmF0rC2DNGsOV17uconUXSwz7+mGcHKNiv+8YlQ==} + engines: {node: '>=14'} + peerDependencies: + postcss: ^8.4.21 + + '@unocss/preset-attributify@0.65.4': + resolution: {integrity: sha512-zxE9hJJ5b37phjdzDdZsxX559ZlmH9rFlY5LVEcQySTnsfY0znviHxPbD2iRpCBCRd+YC5HfFd2jb3XlnTKMJQ==} + + '@unocss/preset-icons@0.65.4': + resolution: {integrity: sha512-5sSzTN72X2Ag3VH48xY1pYudeWnql9jqdMiwgZuLJcmvETBNGelXy2wGxm7tsUUEx/l40Yr04Ck8XRPGT9jLBw==} + + '@unocss/preset-mini@0.65.4': + resolution: {integrity: sha512-dcO2PzSl87qN1KdQWcfZDIKEhpdFeImWbYfiXtE7k6pi1393FJkdHEopgI/1ZciIQN1CkTvQJ5c7EpEVWftYRA==} + + '@unocss/preset-tagify@0.65.4': + resolution: {integrity: sha512-qll6koqdFEkvmz594vKnxj9+3nfM3ugkJxYHrTkqtwx7DAnTgtM8fInFFGZelvjwUzR3o3+Zw6uMhFkLTVTfvg==} + + '@unocss/preset-typography@0.65.4': + resolution: {integrity: sha512-Dl940ATrviWD9Vh+4fcN0QZXb6wA7al+c7QkdVAzW7I+NtdN2ELvLcN0cY22KnLRpwztzmg52Qp2J/1QnqrLTw==} + + '@unocss/preset-uno@0.65.4': + resolution: {integrity: sha512-56bdBtf476i+soQCQmT36uGzcF2z+7DGCnG1hwWiw6XAbL6gmRMQsubwi1c8z8TcTQNBsOFUnOziFil0gbWufw==} + + '@unocss/preset-web-fonts@0.65.4': + resolution: {integrity: sha512-UB/MvXHUTqMNVH1bbiKZ/ZtZUI5tsYlTYAvBrnXPO1Cztuwr8hJKSi4RCfI9g+YYtKHX4uYuxUbW5bcN85gmBQ==} + + '@unocss/preset-wind@0.65.4': + resolution: {integrity: sha512-0rbNbw5E8Lvh2yf4R1Mq+lxI/wL5Tm6+r+crE0uAAhCPe9kxPHW4k+x1cWKDIwq6Vudlm3cNX85N49wN5tYgdA==} + + '@unocss/reset@0.65.4': + resolution: {integrity: sha512-m685H0KFvVMz6R2i5GDIFv4RS9Z7y2G8hJK7xg2OWli+7w8l2ZMihYvXKofPsst4q/ms8EgKXpWc/qqUOTucvA==} + + '@unocss/rule-utils@0.65.4': + resolution: {integrity: sha512-+EzdJEWcqGcO6HwbBTe7vEdBRpuKkBiz4MycQeLD6GEio04T45y6VHHO7/WTqxltbO4YwwW9/s2TKRMxKtoG8g==} + engines: {node: '>=14'} + + '@unocss/transformer-attributify-jsx@0.65.4': + resolution: {integrity: sha512-n438EzWdTKlLCOlAUSpFjmH6FflctqzIReMzMZSJDkmkorymc+C5GpjN3Nty2cKRJXIl6Vwq0oxPuB59RT+FIw==} + + '@unocss/transformer-compile-class@0.65.4': + resolution: {integrity: sha512-n1yHDC/iIbcj/9fBUTXkSoASKfLBuRoCN7P1a0ecPc8Gu+uOGfoxafOhrlqC+tpD3hlQGoL+0h74BHSKh+L23Q==} + + '@unocss/transformer-directives@0.65.4': + resolution: {integrity: sha512-zkoDEwzPkgXi6ohW7P11gbArwfTRMZ9knYSUYoPEltQz+UZYzeRQ85exiAmdz5MsbCAuhQEr577Kd/CWfhjEuA==} + + '@unocss/transformer-variant-group@0.65.4': + resolution: {integrity: sha512-ggO6xMGeOeoD5GHS2xXBJrYFuzqyiZ25tM0zHAMJn9QU9GIu1NwWvcXluvLCF/MRIygBJGPpAE98aEICI6ifEA==} + + '@unocss/vite@0.65.4': + resolution: {integrity: sha512-02pRcVLfb5UUxMJwudnjS/0ZQdSlskjuXVHdpZpLBZCA8hhoru2uEOsPbUOBRNNMjDj6ld00pmgk/+im07M35Q==} + peerDependencies: + vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 + + '@uppy/companion-client@2.2.2': + resolution: {integrity: sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==} + + '@uppy/core@2.3.4': + resolution: {integrity: sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==} + + '@uppy/store-default@2.1.1': + resolution: {integrity: sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==} + + '@uppy/utils@4.1.3': + resolution: {integrity: sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==} + + '@uppy/xhr-upload@2.1.3': + resolution: {integrity: sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==} + peerDependencies: + '@uppy/core': ^2.3.3 + + '@vitejs/plugin-legacy@6.0.0': + resolution: {integrity: sha512-pWt9cWaGJAKYw+67VLpN8hSP+G+yAQnrf5Pqh/NzSDKFl/4KpxTtwb5OLQezHoZOxghahO/ha3IpvblBbX/t6A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + peerDependencies: + terser: ^5.16.0 + vite: ^6.0.0 + + '@vitejs/plugin-vue-jsx@4.1.1': + resolution: {integrity: sha512-uMJqv/7u1zz/9NbWAD3XdjaY20tKTf17XVfQ9zq4wY1BjsB/PjpJPMe2xiG39QpP4ZdhYNhm4Hvo66uJrykNLA==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.0.0 + + '@vitejs/plugin-vue@5.2.1': + resolution: {integrity: sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + + '@volar/language-core@2.4.11': + resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==} + + '@volar/source-map@2.4.11': + resolution: {integrity: sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==} + + '@volar/typescript@2.4.11': + resolution: {integrity: sha512-2DT+Tdh88Spp5PyPbqhyoYavYCPDsqbHLFwcUI9K1NlY1YgUJvujGdrqUp0zWxnW7KWNTr3xSpMuv2WnaTKDAw==} + + '@vue/babel-helper-vue-transform-on@1.2.5': + resolution: {integrity: sha512-lOz4t39ZdmU4DJAa2hwPYmKc8EsuGa2U0L9KaZaOJUt0UwQNjNA3AZTq6uEivhOKhhG1Wvy96SvYBoFmCg3uuw==} + + '@vue/babel-plugin-jsx@1.2.5': + resolution: {integrity: sha512-zTrNmOd4939H9KsRIGmmzn3q2zvv1mjxkYZHgqHZgDrXz5B1Q3WyGEjO2f+JrmKghvl1JIRcvo63LgM1kH5zFg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + peerDependenciesMeta: + '@babel/core': + optional: true + + '@vue/babel-plugin-resolve-type@1.2.5': + resolution: {integrity: sha512-U/ibkQrf5sx0XXRnUZD1mo5F7PkpKyTbfXM3a3rC4YnUz6crHEz9Jg09jzzL6QYlXNto/9CePdOg/c87O4Nlfg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/compiler-core@3.5.13': + resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} + + '@vue/compiler-dom@3.5.13': + resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} + + '@vue/compiler-sfc@3.5.13': + resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==} + + '@vue/compiler-ssr@3.5.13': + resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + + '@vue/language-core@2.2.0': + resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.5.13': + resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} + + '@vue/runtime-core@3.5.13': + resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==} + + '@vue/runtime-dom@3.5.13': + resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==} + + '@vue/server-renderer@3.5.13': + resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==} + peerDependencies: + vue: 3.5.13 + + '@vue/shared@3.5.13': + resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} + + '@vueuse/core@12.3.0': + resolution: {integrity: sha512-cnV8QDKZrsyKC7tWjPbeEUz2cD9sa9faxF2YkR8QqNwfofgbOhmfIgvSYmkp+ttSvfOw4E6hLcQx15mRPr0yBA==} + + '@vueuse/core@9.13.0': + resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} + + '@vueuse/metadata@12.3.0': + resolution: {integrity: sha512-M/iQHHjMffOv2npsw2ihlUx1CTiBwPEgb7DzByLq7zpg1+Ke8r7s9p5ybUWc5OIeGewtpY4Xy0R2cKqFqM8hFg==} + + '@vueuse/metadata@9.13.0': + resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} + + '@vueuse/shared@12.3.0': + resolution: {integrity: sha512-X3YD35GUeW0d5Gajcwv9jdLAJTV2Jdb/Ll6Ii2JIYcKLYZqv5wxyLeKtiQkqWmHg3v0J0ZWjDUMVOw2E7RCXfA==} + + '@vueuse/shared@9.13.0': + resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} + + '@wangeditor/basic-modules@1.1.7': + resolution: {integrity: sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.throttle: ^4.1.1 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/code-highlight@1.0.3': + resolution: {integrity: sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/core@1.1.19': + resolution: {integrity: sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==} + peerDependencies: + '@uppy/core': ^2.1.1 + '@uppy/xhr-upload': ^2.0.3 + dom7: ^3.0.0 + is-hotkey: ^0.2.0 + lodash.camelcase: ^4.3.0 + lodash.clonedeep: ^4.5.0 + lodash.debounce: ^4.0.8 + lodash.foreach: ^4.5.0 + lodash.isequal: ^4.5.0 + lodash.throttle: ^4.1.1 + lodash.toarray: ^4.4.0 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/editor-for-vue@5.1.12': + resolution: {integrity: sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==} + peerDependencies: + '@wangeditor/editor': '>=5.1.0' + vue: ^3.0.5 + + '@wangeditor/editor@5.1.23': + resolution: {integrity: sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==} + + '@wangeditor/list-module@1.0.5': + resolution: {integrity: sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/table-module@1.1.4': + resolution: {integrity: sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.isequal: ^4.5.0 + lodash.throttle: ^4.1.1 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/upload-image-module@1.0.2': + resolution: {integrity: sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==} + peerDependencies: + '@uppy/core': ^2.0.3 + '@uppy/xhr-upload': ^2.0.3 + '@wangeditor/basic-modules': 1.x + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.foreach: ^4.5.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/video-module@1.1.4': + resolution: {integrity: sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==} + peerDependencies: + '@uppy/core': ^2.1.4 + '@uppy/xhr-upload': ^2.0.7 + '@wangeditor/core': 1.x + dom7: ^3.0.0 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@zxcvbn-ts/core@3.0.4': + resolution: {integrity: sha512-aQeiT0F09FuJaAqNrxynlAwZ2mW/1MdXakKWNmGM1Qp/VaY6CnB/GfnMS2T8gB2231Esp1/maCWd8vTG4OuShw==} + + JSONStream@1.3.5: + resolution: {integrity: sha1-MgjB8I06TZkmGrZPkjArwV4RHKA=} + hasBin: true + + acorn-jsx@5.3.2: + resolution: {integrity: sha1-ftW7VZCLOy8bxVxq8WU7rafweTc=} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + + aggregate-error@4.0.1: + resolution: {integrity: sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==} + engines: {node: '>=12'} + + ajv@6.12.6: + resolution: {integrity: sha1-uvWmLoArB9l3A0WG+MO69a3ybfQ=} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + alien-signals@0.4.14: + resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==} + + animate.css@4.1.1: + resolution: {integrity: sha1-YU7FqBEx1+TcNipYFD90BqvWgHU=} + + ansi-escapes@4.3.2: + resolution: {integrity: sha1-ayKR0dt9mLZSHV8e+kLQ86n+tl4=} + engines: {node: '>=8'} + + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + + ansi-regex@2.1.1: + resolution: {integrity: sha1-w7M6te42DYbg5ijwRorn7yfWVN8=} + engines: {node: '>=0.10.0'} + + ansi-regex@5.0.1: + resolution: {integrity: sha1-CCyyyJyf6GWaMRpTvWpNxTAdswQ=} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@2.2.1: + resolution: {integrity: sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=} + engines: {node: '>=0.10.0'} + + ansi-styles@4.3.0: + resolution: {integrity: sha1-7dgDYornHATIWuegkG7a00tkiTc=} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@2.0.1: + resolution: {integrity: sha1-JG9Q88p4oyQPbJl+ipvR6sSeSzg=} + + arr-diff@4.0.0: + resolution: {integrity: sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=} + engines: {node: '>=0.10.0'} + + arr-flatten@1.1.0: + resolution: {integrity: sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=} + engines: {node: '>=0.10.0'} + + arr-union@3.1.0: + resolution: {integrity: sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=} + engines: {node: '>=0.10.0'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-each@1.0.1: + resolution: {integrity: sha1-p5SvDAWrF1KEbudTofIRoFugxE8=} + engines: {node: '>=0.10.0'} + + array-ify@1.0.0: + resolution: {integrity: sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=} + + array-slice@1.1.0: + resolution: {integrity: sha1-42jqFfibxwaff/uJrsOmx9SsItQ=} + engines: {node: '>=0.10.0'} + + array-union@2.1.0: + resolution: {integrity: sha1-t5hCCtvrHego2ErNii4j0+/oXo0=} + engines: {node: '>=8'} + + array-unique@0.3.2: + resolution: {integrity: sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=} + engines: {node: '>=0.10.0'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + assign-symbols@1.0.0: + resolution: {integrity: sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=} + engines: {node: '>=0.10.0'} + + astral-regex@2.0.0: + resolution: {integrity: sha1-SDFDxWeu7UeFdZwIZXhtx319LjE=} + engines: {node: '>=8'} + + async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha1-x57Zf380y48robyXkLzDZkdLS3k=} + + atob@2.1.2: + resolution: {integrity: sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=} + engines: {node: '>= 4.5.0'} + hasBin: true + + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axios@0.26.1: + resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} + + axios@1.7.9: + resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==} + + babel-plugin-polyfill-corejs2@0.4.12: + resolution: {integrity: sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.10.6: + resolution: {integrity: sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.3: + resolution: {integrity: sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + balanced-match@1.0.2: + resolution: {integrity: sha1-6D46fj8wCzTLnYf2FfoMvzV2kO4=} + + balanced-match@2.0.0: + resolution: {integrity: sha1-3HD5INeNuLhYU1eVhnv0j4IGM9k=} + + base64-js@1.5.1: + resolution: {integrity: sha1-GxtEAWClv3rUC2UPCVljSBkDkwo=} + + base@0.11.2: + resolution: {integrity: sha1-e95c7RRbbVUakNuH+DxVi060io8=} + engines: {node: '>=0.10.0'} + + big.js@5.2.2: + resolution: {integrity: sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg=} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha1-RRU1JkGCvsL7vIOmKrmM8R2fezo=} + + bluebird@3.7.2: + resolution: {integrity: sha1-nyKcFb4nJFT/qXOs4NvueaGww28=} + + boolbase@1.0.0: + resolution: {integrity: sha1-aN/1++YMUes3cl6p4+0xDcwed24=} + + brace-expansion@1.1.11: + resolution: {integrity: sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=} + + brace-expansion@2.0.1: + resolution: {integrity: sha1-HtxFng8MVISG7Pn8mfIiE2S5oK4=} + + braces@2.3.2: + resolution: {integrity: sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=} + engines: {node: '>=0.10.0'} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist-to-esbuild@2.1.1: + resolution: {integrity: sha512-KN+mty6C3e9AN8Z5dI1xeN15ExcRNeISoC3g7V0Kax/MMF9MSoYA2G7lkTTcVUFntiEjkpI0HNgqJC1NjdyNUw==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + browserslist: '*' + + browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha1-KxRqb9cugLT1XSVfNe1Zo6mkG9U=} + + buffer@5.7.1: + resolution: {integrity: sha1-umLnwTEzBTWCGXFghRqPZI6Z7tA=} + + builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.18' + + c12@2.0.1: + resolution: {integrity: sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A==} + peerDependencies: + magicast: ^0.3.5 + peerDependenciesMeta: + magicast: + optional: true + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cache-base@1.0.1: + resolution: {integrity: sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=} + engines: {node: '>=0.10.0'} + + call-bind-apply-helpers@1.0.1: + resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.3: + resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha1-s2MKvYlDQy9Us/BRkjjjPNffL3M=} + engines: {node: '>=6'} + + camel-case@4.1.2: + resolution: {integrity: sha1-lygHKpVPgFIoIlpt7qazhGHhvVo=} + + camelcase@5.3.1: + resolution: {integrity: sha1-48mzFWnhBoEd8kL3FXJaH0xJQyA=} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001690: + resolution: {integrity: sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==} + + capital-case@1.0.4: + resolution: {integrity: sha1-nRMCkjU8kkn2sA+lhSvuOKcX5mk=} + + chalk@1.1.3: + resolution: {integrity: sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=} + engines: {node: '>=0.10.0'} + + chalk@4.1.2: + resolution: {integrity: sha1-qsTit3NKdAhnrrFr8CqtVWoeegE=} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + change-case@4.1.2: + resolution: {integrity: sha1-/t/F8TYEXiOYwEEO5EH5VwRkHhI=} + + chardet@0.7.0: + resolution: {integrity: sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chownr@2.0.0: + resolution: {integrity: sha1-Fb++U9LqtM9w8YqM1o6+Wzyx3s4=} + engines: {node: '>=10'} + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + + class-utils@0.3.6: + resolution: {integrity: sha1-+TNprouafOAv1B+q0MqDAzGQxGM=} + engines: {node: '>=0.10.0'} + + clean-stack@4.2.0: + resolution: {integrity: sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==} + engines: {node: '>=12'} + + cli-cursor@3.1.0: + resolution: {integrity: sha1-JkMFp65JDR0Dvwybp8kl0XU68wc=} + engines: {node: '>=8'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + clipboardy@4.0.0: + resolution: {integrity: sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==} + engines: {node: '>=18'} + + cliui@6.0.0: + resolution: {integrity: sha1-UR1wLAxOQcoVbX0OlgIfI+EyJbE=} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@1.0.4: + resolution: {integrity: sha1-2jCcwmPfFZlMaIypAheco8fNfH4=} + engines: {node: '>=0.8'} + + clone@2.1.2: + resolution: {integrity: sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=} + engines: {node: '>=0.8'} + + collection-visit@1.0.0: + resolution: {integrity: sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=} + engines: {node: '>=0.10.0'} + + color-convert@2.0.1: + resolution: {integrity: sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=} + engines: {node: '>= 0.8'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@13.0.0: + resolution: {integrity: sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha1-/UhehMA+tIgcIHIrpIA16FMa6zM=} + + commander@7.2.0: + resolution: {integrity: sha1-o2y1fQtQHOEI5NIFWaFQo5HZerc=} + engines: {node: '>= 10'} + + compare-func@2.0.0: + resolution: {integrity: sha1-+2XnXtvd/S5WhVTotbBf/3pR/LM=} + + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + + compute-scroll-into-view@1.0.20: + resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + + concat-map@0.0.1: + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + connect@3.7.0: + resolution: {integrity: sha1-XUk0iRDKpeB6AYALAw0MNfIEhPg=} + engines: {node: '>= 0.10.0'} + + consola@3.3.3: + resolution: {integrity: sha512-Qil5KwghMzlqd51UXM0b6fyaGHtOC22scxrwrz4A2882LyUMwQjnvaedN1HAeXzphspQ6CpHkzMAWxBTUruDLg==} + engines: {node: ^14.18.0 || >=16.10.0} + + console@0.7.2: + resolution: {integrity: sha1-+aQzEkkpFZG3v5v/qOIFNW8gqfA=} + + constant-case@3.0.4: + resolution: {integrity: sha1-O4Sprq9M8x7EXmv13pG9+wWJ+vE=} + + conventional-changelog-angular@7.0.0: + resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} + engines: {node: '>=16'} + + conventional-changelog-conventionalcommits@7.0.2: + resolution: {integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==} + engines: {node: '>=16'} + + conventional-commits-parser@5.0.0: + resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} + engines: {node: '>=16'} + hasBin: true + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + + copy-descriptor@0.1.1: + resolution: {integrity: sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=} + engines: {node: '>=0.10.0'} + + core-js-compat@3.40.0: + resolution: {integrity: sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==} + + core-js@3.40.0: + resolution: {integrity: sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==} + + cors@2.8.5: + resolution: {integrity: sha1-6sEdpRWS3Ya58G9uesKTs9+HXSk=} + engines: {node: '>= 0.10'} + + cosmiconfig-typescript-loader@6.1.0: + resolution: {integrity: sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==} + engines: {node: '>=v18'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=9' + typescript: '>=5' + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cropperjs@1.6.2: + resolution: {integrity: sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==} + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-functions-list@3.2.3: + resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==} + engines: {node: '>=12 || >=16'} + + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-tree@1.1.3: + resolution: {integrity: sha1-60hw+2/XcHMn7JXC/yqwm16NuR0=} + engines: {node: '>=8.0.0'} + + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha1-N3QZGZA7hoVl4cCep0dEXNGJg+4=} + engines: {node: '>=4'} + hasBin: true + + csso@4.2.0: + resolution: {integrity: sha1-6jpWE0bo3J9UbW/r7dUBh884lSk=} + engines: {node: '>=8.0.0'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + d@1.0.2: + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} + + danmu.js@1.1.13: + resolution: {integrity: sha512-knFd0/cB2HA4FFWiA7eB2suc5vCvoHdqio33FyyCSfP7C+1A+zQcTvnvwfxaZhrxsGj4qaQI2I8XiTqedRaVmg==} + + dargs@8.1.0: + resolution: {integrity: sha1-o0hZ6lCcvORUheWqNW/vcL/McnI=} + engines: {node: '>=12'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + + de-indent@1.0.2: + resolution: {integrity: sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=} + + debug@2.6.9: + resolution: {integrity: sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@1.2.0: + resolution: {integrity: sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=} + engines: {node: '>=0.10.0'} + + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + + deep-is@0.1.4: + resolution: {integrity: sha1-pvLc5hL63S7x9Rm3NVHxfoUZmDE=} + + deep-pick-omit@1.2.1: + resolution: {integrity: sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha1-P3rkIRKbyqrJvHSQXJigAJ7J7n8=} + engines: {node: '>=8'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + define-property@0.2.5: + resolution: {integrity: sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=} + engines: {node: '>=0.10.0'} + + define-property@1.0.0: + resolution: {integrity: sha1-dp66rz9KY6rTr56NMEybvnm/sOY=} + engines: {node: '>=0.10.0'} + + define-property@2.0.2: + resolution: {integrity: sha1-1Flono1lS6d+AqgX+HENcCyxbp0=} + engines: {node: '>=0.10.0'} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + del@7.1.0: + resolution: {integrity: sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg==} + engines: {node: '>=14.16'} + + delayed-stream@1.0.0: + resolution: {integrity: sha1-3zrhmayt+31ECqrgsp4icrJOxhk=} + engines: {node: '>=0.4.0'} + + delegate@3.2.0: + resolution: {integrity: sha1-tmtxwxWFIuirV0T3INjKDCr1kWY=} + + destr@2.0.3: + resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==} + + detect-file@1.0.0: + resolution: {integrity: sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=} + engines: {node: '>=0.10.0'} + + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + + dir-glob@3.0.1: + resolution: {integrity: sha1-Vtv3PZkqSpO6FYT0U0Bj/S5BcX8=} + engines: {node: '>=8'} + + dom-serializer@0.2.2: + resolution: {integrity: sha1-GvuB9TNxcXXUeGVd68XjMtn5u1E=} + + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + dom7@3.0.0: + resolution: {integrity: sha1-uGHOXWemvs16qjrQKUL/FLEkAzE=} + + domelementtype@1.3.1: + resolution: {integrity: sha1-0EjESzew0Qp/Kj1f7j9DM9eQSB8=} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@2.4.2: + resolution: {integrity: sha1-iAUJfpM9ZehVRvcm1g9euItE+AM=} + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@1.7.0: + resolution: {integrity: sha1-Vuo0HoNOBuZ0ivehyyXaZ+qfjCo=} + + domutils@2.8.0: + resolution: {integrity: sha1-RDfe9dtuLR9dbuhZvZXKfQIEgTU=} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dot-case@3.0.4: + resolution: {integrity: sha1-mytnDQCkMWZ6inW6Kc0bmICc51E=} + + dot-prop@5.3.0: + resolution: {integrity: sha1-kMzOcIzZzYLMTcjD3dmr3VWyDog=} + engines: {node: '>=8'} + + dotenv@16.4.7: + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} + engines: {node: '>=12'} + + downloadjs@1.4.7: + resolution: {integrity: sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=} + + driver.js@1.3.1: + resolution: {integrity: sha512-MvUdXbqSgEsgS/H9KyWb5Rxy0aE6BhOVT4cssi2x2XjmXea6qQfgdx32XKVLLSqTaIw7q/uxU5Xl3NV7+cN6FQ==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexer@0.1.2: + resolution: {integrity: sha1-Or5DrvODX4rgd9E23c4PJ2sEAOY=} + + eastasianwidth@0.2.0: + resolution: {integrity: sha1-aWzi7Aqg5uqTo5f/zySqeEDIJ8s=} + + echarts-wordcloud@2.1.0: + resolution: {integrity: sha512-Kt1JmbcROgb+3IMI48KZECK2AP5lG6bSsOEs+AsuwaWJxQom31RTNd6NFYI01E/YaI1PFZeueaupjlmzSQasjQ==} + peerDependencies: + echarts: ^5.0.1 + + echarts@5.6.0: + resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==} + + ee-first@1.1.1: + resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.79: + resolution: {integrity: sha512-nYOxJNxQ9Om4EC88BE4pPoNI8xwSFf8pU/BAeOl4Hh/b/i6V4biTAzwV7pXi3ARKeoYO5JZKMIXTryXSVer5RA==} + + element-plus@2.9.2: + resolution: {integrity: sha512-HS+Cc5mmy70DixJuoN3cMxPPoNWXkjHzUw2PcGmysk6NHQzzUtwi2Vc+dlmbmRxj3eNqgC1xpPQV5Nf9uDtQRg==} + peerDependencies: + vue: ^3.2.0 + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha1-6Bj9ac5cz8tARZT4QpY79TFkzDc=} + + emoji-regex@9.2.2: + resolution: {integrity: sha1-hAyIA7DYBH9P8M+WMXazLU7z7XI=} + + emojis-list@3.0.0: + resolution: {integrity: sha1-VXBmIEatKeLpFucariYKvf9Pang=} + engines: {node: '>= 4'} + + encodeurl@1.0.2: + resolution: {integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=} + engines: {node: '>= 0.8'} + + entities@1.1.2: + resolution: {integrity: sha1-vfpzUplmTfr9NFKe1PhSKidf6lY=} + + entities@2.2.0: + resolution: {integrity: sha1-CY3JDruD2N/6CJ1VJWs1HTTE2lU=} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha1-QgOZ1BbOH76bwKB8Yvpo1n/Q+PI=} + engines: {node: '>=6'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + errno@0.1.8: + resolution: {integrity: sha1-i7Ppx9Rjvkl2/4iPdrSAnrwugR8=} + hasBin: true + + error-ex@1.3.2: + resolution: {integrity: sha1-tKxAZIEH/c3PriQvQovqihTU8b8=} + + es-abstract@1.23.9: + resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@0.9.3: + resolution: {integrity: sha1-bxPbAMw4QXE32vdDZvU1yOtDjxk=} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + es5-ext@0.10.64: + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} + engines: {node: '>=0.10'} + + es6-iterator@2.0.3: + resolution: {integrity: sha1-p96IkUGgWpSwhUQDstCg+/qY87c=} + + es6-symbol@3.1.4: + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} + engines: {node: '>=0.12'} + + esbuild@0.11.3: + resolution: {integrity: sha1-tXFluQe+T/umUfZFBTjOjYwdXrA=} + hasBin: true + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha1-FLqDpdNz49MR5a/KKc9b+tllvzQ=} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha1-RoMSa1ALYXYvLb66zhgG6L4xscg=} + engines: {node: '>=12'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-config-prettier@9.1.0: + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-define-config@2.1.0: + resolution: {integrity: sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ==} + engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>=8.6.0'} + + eslint-plugin-prettier@5.2.1: + resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-vue@9.32.0: + resolution: {integrity: sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-scope@8.2.0: + resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.17.0: + resolution: {integrity: sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + + esno@4.8.0: + resolution: {integrity: sha512-acMtooReAQGzLU0zcuEDHa8S62meh5aIyi8jboYxyvAePdmuWx2Mpwmt0xjwO0bs9/SXf+dvXJ0QJoDWw814Iw==} + hasBin: true + + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha1-eteWTWeauyi+5yzsY3WLHF0smSE=} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha1-LupSkHAvJquP5TcDcP+GyWXSESM=} + engines: {node: '>=4.0'} + + estree-walker@1.0.1: + resolution: {integrity: sha1-MbxdYSyWtwQQa0d+bdXYqhOMtwA=} + + estree-walker@2.0.2: + resolution: {integrity: sha1-UvAQF4wqTBF6d1fP6UKtt9LaTKw=} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=} + engines: {node: '>= 0.6'} + + event-emitter@0.3.5: + resolution: {integrity: sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=} + + eventemitter3@4.0.7: + resolution: {integrity: sha1-Lem2j2Uo1WRO9cWVJqG0oHMGFp8=} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + expand-brackets@2.1.4: + resolution: {integrity: sha1-t3c14xXOMPa27/D4OwQVGiJEliI=} + engines: {node: '>=0.10.0'} + + expand-tilde@2.0.2: + resolution: {integrity: sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=} + engines: {node: '>=0.10.0'} + + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + + extend-shallow@2.0.1: + resolution: {integrity: sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=} + engines: {node: '>=0.10.0'} + + extend-shallow@3.0.2: + resolution: {integrity: sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=} + engines: {node: '>=0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=} + + external-editor@3.1.0: + resolution: {integrity: sha1-ywP3QL764D6k0oPK7SdBqD8zVJU=} + engines: {node: '>=4'} + + extglob@2.0.4: + resolution: {integrity: sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=} + + fast-uri@3.0.5: + resolution: {integrity: sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.18.0: + resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==} + + fdir@6.4.2: + resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + file-entry-cache@9.1.0: + resolution: {integrity: sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==} + engines: {node: '>=18'} + + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + + fill-range@4.0.0: + resolution: {integrity: sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=} + engines: {node: '>=0.10.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.1.2: + resolution: {integrity: sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=} + engines: {node: '>= 0.8'} + + find-up@4.1.0: + resolution: {integrity: sha1-l6/n1s3AvFkoWEt8jXsW6KmqXRk=} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha1-TJKBnstwg1YeT0okCoa+UZj1Nvw=} + engines: {node: '>=10'} + + find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + + findup-sync@5.0.0: + resolution: {integrity: sha1-VDgK2WWn7coAzI9jETVZqtxUG9I=} + engines: {node: '>= 10.13.0'} + + fined@2.0.0: + resolution: {integrity: sha512-OFRzsL6ZMHz5s0JrsEr+TpdGNCtrVtnuG3x1yzGNiQHT0yaDnXAj8V/lWcpJVrnoDpcwXcASxAZYbuXda2Y82A==} + engines: {node: '>= 10.13.0'} + + flagged-respawn@2.0.0: + resolution: {integrity: sha1-q/OXGdz+GsBshslGYIHFQcaCmHs=} + engines: {node: '>= 10.13.0'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flat-cache@5.0.0: + resolution: {integrity: sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==} + engines: {node: '>=18'} + + flatted@3.3.2: + resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.3: + resolution: {integrity: sha1-abRH6IoKXTLD5whPPxcQA0shN24=} + + for-in@1.0.2: + resolution: {integrity: sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=} + engines: {node: '>=0.10.0'} + + for-own@1.0.0: + resolution: {integrity: sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=} + engines: {node: '>=0.10.0'} + + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + + form-data@4.0.1: + resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} + engines: {node: '>= 6'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fragment-cache@0.2.1: + resolution: {integrity: sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=} + engines: {node: '>=0.10.0'} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + + fs-minipass@2.1.0: + resolution: {integrity: sha1-f1A2/b8SxjwWkZDL5BmchSJx+fs=} + engines: {node: '>= 8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha1-MqbudsPX9S1GsrGuXZP+qFgKJeA=} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha1-T5RBKoLbMvNuOwuXQfipf+sDH34=} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + get-intrinsic@1.2.7: + resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + + get-value@2.0.6: + resolution: {integrity: sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=} + engines: {node: '>=0.10.0'} + + giget@1.2.3: + resolution: {integrity: sha512-8EHPljDvs7qKykr6uw8b+lqLiUc/vUg+KVTI0uND4s63TdsZM2Xus3mflvF0DDG9SiM4RlCkFGL+7aAjRmV7KA==} + hasBin: true + + git-raw-commits@4.0.0: + resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} + engines: {node: '>=16'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha1-hpgyxYA0/mikCTwX3BXoNA2EAcQ=} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha1-bSN9mQg5UMeSkPJMdkKj3poo+eM=} + engines: {node: '>=10.13.0'} + + glob@11.0.0: + resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} + engines: {node: 20 || >=22} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + + global-modules@1.0.0: + resolution: {integrity: sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=} + engines: {node: '>=0.10.0'} + + global-modules@2.0.0: + resolution: {integrity: sha1-mXYFrSNF8n9RU5vqJldEISFcd4A=} + engines: {node: '>=6'} + + global-prefix@1.0.2: + resolution: {integrity: sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=} + engines: {node: '>=0.10.0'} + + global-prefix@3.0.0: + resolution: {integrity: sha1-/IX3MGTfafUEIfR/iD/luRO6m5c=} + engines: {node: '>=6'} + + globals@11.12.0: + resolution: {integrity: sha1-q4eVM4hooLq9hSV1gBjCp+uVxC4=} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.14.0: + resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + globby@14.0.2: + resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} + engines: {node: '>=18'} + + globjoin@0.1.4: + resolution: {integrity: sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + gzip-size@6.0.0: + resolution: {integrity: sha1-BlNn/VDCOcBnHLy61b4+LusQ5GI=} + engines: {node: '>=10'} + + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + has-ansi@2.0.0: + resolution: {integrity: sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=} + engines: {node: '>=0.10.0'} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@1.0.0: + resolution: {integrity: sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=} + engines: {node: '>=0.10.0'} + + has-flag@4.0.0: + resolution: {integrity: sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + has-value@0.3.1: + resolution: {integrity: sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=} + engines: {node: '>=0.10.0'} + + has-value@1.0.0: + resolution: {integrity: sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=} + engines: {node: '>=0.10.0'} + + has-values@0.1.4: + resolution: {integrity: sha1-bWHeldkd/Km5oCCJrThL/49it3E=} + engines: {node: '>=0.10.0'} + + has-values@1.0.0: + resolution: {integrity: sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=} + engines: {node: '>=0.10.0'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha1-hK5l+n6vsWX922FWauFLrwVmTw8=} + hasBin: true + + header-case@2.0.4: + resolution: {integrity: sha1-WkLmO1UXc0nPQFvrjXdayruSwGM=} + + homedir-polyfill@1.0.3: + resolution: {integrity: sha1-dDKYzvTlrz4ZQWH7rcwhUdOgWOg=} + engines: {node: '>=0.10.0'} + + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + + html-void-elements@2.0.1: + resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==} + + htmlparser2@3.10.1: + resolution: {integrity: sha1-vWedw/WYl7ajS7EHSchVu1OpOS8=} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + i18next@20.6.1: + resolution: {integrity: sha1-U15fbluutoXH0l33DbY788wKo0U=} + + iconv-lite@0.4.24: + resolution: {integrity: sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha1-pS+AvzjaGVLrXGgXkHGYcaGnJQE=} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha1-jrehCmP/8l0VpXsAFYbRd9Gw01I=} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@6.0.2: + resolution: {integrity: sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==} + engines: {node: '>= 4'} + + ignore@7.0.0: + resolution: {integrity: sha512-lcX8PNQygAa22u/0BysEY8VhaFRzlOkvdlKczDPnJvrkJD1EuqzEky5VYYKM2iySIuaVIDv9N190DfSreSLw2A==} + engines: {node: '>= 4'} + + image-size@0.5.5: + resolution: {integrity: sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=} + engines: {node: '>=0.10.0'} + hasBin: true + + immer@9.0.21: + resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + + import-fresh@3.3.0: + resolution: {integrity: sha1-NxYsJfy566oublPVtNiM4X2eDCs=} + engines: {node: '>=6'} + + import-meta-resolve@4.1.0: + resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + + importx@0.5.1: + resolution: {integrity: sha512-YrRaigAec1sC2CdIJjf/hCH1Wp9Ii8Cq5ROw4k5nJ19FVl2FcJUHZ5gGIb1vs8+JNYIyOJpc2fcufS2330bxDw==} + + imurmurhash@0.1.4: + resolution: {integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o=} + engines: {node: '>=0.8.19'} + + indent-string@5.0.0: + resolution: {integrity: sha1-T9KYD8yvhiLRTGTWlPTPM8gZUaU=} + engines: {node: '>=12'} + + inflight@1.0.6: + resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} + + inherits@2.0.4: + resolution: {integrity: sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=} + + ini@1.3.8: + resolution: {integrity: sha1-op2kJbSIBvNHZ6Tvzjlyaa8oQyw=} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + inquirer@12.3.0: + resolution: {integrity: sha512-3NixUXq+hM8ezj2wc7wC37b32/rHq1MwNZDYdvx+d6jokOD+r+i8Q4Pkylh9tISYP114A128LCX8RKhopC5RfQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + inquirer@9.3.7: + resolution: {integrity: sha512-LJKFHCSeIRq9hanN14IlOtPSTe3lNES7TYDTE2xxdAy1LS5rYphajK1qtwvj3YmQXvvk0U2Vbmcni8P9EIQW9w==} + engines: {node: '>=18'} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + interpret@3.1.1: + resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} + engines: {node: '>=10.13.0'} + + is-absolute@1.0.0: + resolution: {integrity: sha1-OV4a6EsR8mrReV5zwXN45IowFXY=} + engines: {node: '>=0.10.0'} + + is-accessor-descriptor@1.0.1: + resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==} + engines: {node: '>= 0.10'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=} + + is-async-function@2.1.0: + resolution: {integrity: sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=} + engines: {node: '>=8'} + + is-boolean-object@1.2.1: + resolution: {integrity: sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==} + engines: {node: '>= 0.4'} + + is-buffer@1.1.6: + resolution: {integrity: sha1-76ouqdqg16suoTqXsritUf776L4=} + + is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-descriptor@1.0.1: + resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-descriptor@0.1.7: + resolution: {integrity: sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==} + engines: {node: '>= 0.4'} + + is-descriptor@1.0.3: + resolution: {integrity: sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha1-M+6r4jz+hvFL3kQIoCwM+4U6zao=} + engines: {node: '>=8'} + hasBin: true + + is-docker@3.0.0: + resolution: {integrity: sha1-kAk6oxBid9inelkQ265xdH4VogA=} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extendable@0.1.1: + resolution: {integrity: sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=} + engines: {node: '>=0.10.0'} + + is-extendable@1.0.1: + resolution: {integrity: sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=} + engines: {node: '>=0.10.0'} + + is-extglob@2.1.1: + resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha1-8Rb4Bk/pCz94RKOJl8C3UFEmnx0=} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha1-+uMWfHKedGP4RhzlErCApJJoqog=} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-generator-function@1.1.0: + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha1-ZPYeQsu7LuwgcanawLKLoeZdUIQ=} + engines: {node: '>=0.10.0'} + + is-hotkey@0.2.0: + resolution: {integrity: sha1-GDWmgXGpHlyUYIadljNpR8g0DO8=} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-interactive@1.0.0: + resolution: {integrity: sha1-zqbmrlyHCnsKAAQHC3tYfgJSkS4=} + engines: {node: '>=8'} + + is-interactive@2.0.0: + resolution: {integrity: sha1-QMV2FFk4JtoRAK3mBZd41ZfxbpA=} + engines: {node: '>=12'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-module@1.0.0: + resolution: {integrity: sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@3.0.0: + resolution: {integrity: sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha1-Rz+wXZc3BeP9liBUUBjKjiLvSYI=} + engines: {node: '>=8'} + + is-path-cwd@3.0.0: + resolution: {integrity: sha1-iJtB5VyFiLHrKpamHQV0CmdFIcc=} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-path-inside@4.0.0: + resolution: {integrity: sha1-gFrrYsR8GxL8P9E7+z7R50MAcds=} + engines: {node: '>=12'} + + is-plain-obj@1.1.0: + resolution: {integrity: sha1-caUMhCnfync8kqOQpKA7OfzVHT4=} + engines: {node: '>=0.10.0'} + + is-plain-object@2.0.4: + resolution: {integrity: sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=} + engines: {node: '>=0.10.0'} + + is-plain-object@5.0.0: + resolution: {integrity: sha1-RCf1CrNCnpAl6n1S6QQ6nvQVk0Q=} + engines: {node: '>=0.10.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-relative@1.0.0: + resolution: {integrity: sha1-obtpNc6MXboei5dUubLcwCDiJg0=} + engines: {node: '>=0.10.0'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@3.0.0: + resolution: {integrity: sha1-5r/XqmvvafT0cs6btoHj5XtDGaw=} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-text-path@2.0.0: + resolution: {integrity: sha1-skhOK3IKYz/rLoW2fcGT/3LHVjY=} + engines: {node: '>=8'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-unc-path@1.0.0: + resolution: {integrity: sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=} + engines: {node: '>=0.10.0'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha1-PybHaoCVk7Ur+i7LVxDtJ3m1Iqc=} + engines: {node: '>=10'} + + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-url@1.2.4: + resolution: {integrity: sha1-BKTfRtKMTP89c9Af8Gq+sxihqlI=} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.0: + resolution: {integrity: sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-what@3.14.1: + resolution: {integrity: sha1-4SIvRt3ahd6tD9HJ3xMXYOd3VcE=} + + is-windows@1.0.2: + resolution: {integrity: sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=} + engines: {node: '>=0.10.0'} + + is-wsl@2.2.0: + resolution: {integrity: sha1-dKTHbnfKn9P5MvKQwX6jJs0VcnE=} + engines: {node: '>=8'} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + is64bit@2.0.0: + resolution: {integrity: sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==} + engines: {node: '>=18'} + + isarray@1.0.0: + resolution: {integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=} + + isarray@2.0.5: + resolution: {integrity: sha1-ivHkwSISRMxiRZ+vOJQNTmRKVyM=} + + isbinaryfile@5.0.4: + resolution: {integrity: sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==} + engines: {node: '>= 18.0.0'} + + isexe@2.0.0: + resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=} + + isobject@2.1.0: + resolution: {integrity: sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=} + engines: {node: '>=0.10.0'} + + isobject@3.0.1: + resolution: {integrity: sha1-TkMekrEalzFjaqH5yNHMvP2reN8=} + engines: {node: '>=0.10.0'} + + jackspeak@4.0.2: + resolution: {integrity: sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==} + engines: {node: 20 || >=22} + + jake@10.9.2: + resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} + engines: {node: '>=10'} + hasBin: true + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + js-base64@2.6.4: + resolution: {integrity: sha1-9OaGxd4eofhn28rT1G2WlCjfmMQ=} + + js-tokens@4.0.0: + resolution: {integrity: sha1-GSA/tZmR35jjoocFDUZHzerzJJk=} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha1-wftl+PUBeQHN0slRhkuhhFihBgI=} + hasBin: true + + jsesc@3.0.2: + resolution: {integrity: sha1-u4sJpll7pCZCXy5KByRcPQC5ND4=} + engines: {node: '>=6'} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha1-kziAKjDTtmBfvgYT4JQAjKjAWhM=} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha1-fEeAWpQxmSjgV3dAXcEuH3pO4C0=} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha1-afaofZUTq4u4/mO9sJecRI5oRmA=} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha1-rnvLNlard6c7pcSb9lTzjmtoYOI=} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-eslint-parser@2.4.0: + resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + jsonfile@6.1.0: + resolution: {integrity: sha1-vFWyY0eTxnnsZAMJTrE2mKbsCq4=} + + jsonparse@1.3.1: + resolution: {integrity: sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=} + engines: {'0': node >= 0.2.0} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kind-of@3.2.2: + resolution: {integrity: sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=} + engines: {node: '>=0.10.0'} + + kind-of@4.0.0: + resolution: {integrity: sha1-IIE989cSkosgc3hpGkUGb65y3Vc=} + engines: {node: '>=0.10.0'} + + kind-of@5.1.0: + resolution: {integrity: sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=} + engines: {node: '>=0.10.0'} + + kind-of@6.0.3: + resolution: {integrity: sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=} + engines: {node: '>=0.10.0'} + + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + + knitwork@1.2.0: + resolution: {integrity: sha512-xYSH7AvuQ6nXkq42x0v5S8/Iry+cfulBz/DJQzhIyESdLD7425jXsPy4vn5cCXU+HhRN2kVw51Vd1K6/By4BQg==} + + known-css-properties@0.35.0: + resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + less@4.2.1: + resolution: {integrity: sha512-CasaJidTIhWmjcqv0Uj5vccMI7pJgfD9lMkKtlnTHAdJdYK/7l8pM9tumLyJ0zhbD4KJLo/YvTj+xznQd5NBhg==} + engines: {node: '>=6'} + hasBin: true + + levn@0.4.1: + resolution: {integrity: sha1-rkViwAdHO5MqYgDUAyaN0v/8at4=} + engines: {node: '>= 0.8.0'} + + liftoff@4.0.0: + resolution: {integrity: sha512-rMGwYF8q7g2XhG2ulBmmJgWv25qBsqRbDn5gH0+wnuyeFt7QBJlHJmtg5qEdn4pN6WVAUMgXnIxytMFRX9c1aA==} + engines: {node: '>=10.13.0'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha1-7KKE910pZQeTCdwK2SVauy68FjI=} + + lint-staged@15.3.0: + resolution: {integrity: sha512-vHFahytLoF2enJklgtOtCtIjZrKD/LoxlaUusd5nh7dWv/dkKQJY74ndFSzxCdv7g0ueGg1ORgTSt4Y9LPZn9A==} + engines: {node: '>=18.12.0'} + hasBin: true + + listr2@8.2.5: + resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==} + engines: {node: '>=18.0.0'} + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + loader-utils@1.4.2: + resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==} + engines: {node: '>=4.0.0'} + + local-pkg@0.5.1: + resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} + engines: {node: '>=14'} + + locate-path@5.0.0: + resolution: {integrity: sha1-Gvujlq/WdqbUJQTQpno6frn2KqA=} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha1-VTIeswn+u8WcSAHZMackUqaB0oY=} + engines: {node: '>=10'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash-es@4.17.21: + resolution: {integrity: sha1-Q+YmxG5lkbd1C+srUBFzkMYJ4+4=} + + lodash-unified@1.0.3: + resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==} + peerDependencies: + '@types/lodash-es': '*' + lodash: '*' + lodash-es: '*' + + lodash.camelcase@4.3.0: + resolution: {integrity: sha1-soqmKIorn8ZRA1x3EfZathkDMaY=} + + lodash.clonedeep@4.5.0: + resolution: {integrity: sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=} + + lodash.debounce@4.0.8: + resolution: {integrity: sha1-gteb/zCmfEAF/9XiUVMArZyk168=} + + lodash.foreach@4.5.0: + resolution: {integrity: sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=} + + lodash.get@4.4.2: + resolution: {integrity: sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=} + + lodash.isequal@4.5.0: + resolution: {integrity: sha1-QVxEePK8wwEgwizhDtMib30+GOA=} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=} + + lodash.kebabcase@4.1.1: + resolution: {integrity: sha1-hImxyw0p/4gZXM7KRI/21swpXDY=} + + lodash.merge@4.6.2: + resolution: {integrity: sha1-VYqlO0O2YeGSWgr9+japoQhf5Xo=} + + lodash.mergewith@4.6.2: + resolution: {integrity: sha1-YXEh+JrFX1kEfHrsHM1mVMZZD1U=} + + lodash.snakecase@4.1.1: + resolution: {integrity: sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40=} + + lodash.startcase@4.4.0: + resolution: {integrity: sha1-lDbjTtJgk+1/+uGTYUQ1CRXZrdg=} + + lodash.throttle@4.1.1: + resolution: {integrity: sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=} + + lodash.toarray@4.4.0: + resolution: {integrity: sha1-JMS/zWsvuji/0FlNsRedjptlZWE=} + + lodash.truncate@4.4.2: + resolution: {integrity: sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=} + + lodash.uniq@4.5.0: + resolution: {integrity: sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=} + + lodash.upperfirst@4.3.1: + resolution: {integrity: sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=} + + lodash@4.17.21: + resolution: {integrity: sha1-Z5WRxWTDv/quhFTPCz3zcMPWkRw=} + + log-symbols@4.1.0: + resolution: {integrity: sha1-P727lbRoOsn8eFER55LlWNSr1QM=} + engines: {node: '>=10'} + + log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + lower-case@2.0.2: + resolution: {integrity: sha1-b6I3xj29xKgsoP2ILkci3F5jTig=} + + lru-cache@11.0.2: + resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha1-HaJ+ZxAnGUdpXa9oSOhH8B2EuSA=} + + magic-string@0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + make-dir@2.1.0: + resolution: {integrity: sha1-XwMQ4YuL6JjMBwCSlaMK5B6R5vU=} + engines: {node: '>=6'} + + make-iterator@1.0.1: + resolution: {integrity: sha1-KbM/MSqo9UfEpeSQ9Wr87JkTOtY=} + engines: {node: '>=0.10.0'} + + map-cache@0.2.2: + resolution: {integrity: sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=} + engines: {node: '>=0.10.0'} + + map-visit@1.0.0: + resolution: {integrity: sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=} + engines: {node: '>=0.10.0'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mathml-tag-names@2.1.3: + resolution: {integrity: sha1-TdrdZzCOeAzxakdoWHjuJ7c2oKM=} + + mdn-data@2.0.14: + resolution: {integrity: sha1-cRP8QoGRfWPOKbQ0RvcB5owlulA=} + + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + + memoize-one@6.0.0: + resolution: {integrity: sha1-slkbhx7YKUiu5HJ9xqvO7qyMEEU=} + + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + + merge-options@1.0.1: + resolution: {integrity: sha1-KmSyRFe+zU5NxggoMkfpTOWJqjI=} + engines: {node: '>=4'} + + merge-stream@2.0.0: + resolution: {integrity: sha1-UoI2KaFN0AyXcPtq1H3GMQ8sH2A=} + + merge2@1.4.1: + resolution: {integrity: sha1-Q2iJL4hekHRVpv19xVwMnUBJkK4=} + engines: {node: '>= 8'} + + micromatch@3.1.0: + resolution: {integrity: sha1-UQLU6vILaZfWAI46z+HESj+oFeI=} + engines: {node: '>=0.10.0'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-match@1.0.2: + resolution: {integrity: sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=} + engines: {node: '>=4'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha1-ftLCzMyvhNP/y3pptXcR/CCDQBs=} + engines: {node: '>=6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha1-YKkFUNXLCyOcymXYk7GlOymHHsw=} + engines: {node: '>=12'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@10.0.1: + resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} + engines: {node: 20 || >=22} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha1-6Q00Zrogm5MkUVCKEc49NjIUWTE=} + engines: {node: '>= 8'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mixin-deep@1.3.2: + resolution: {integrity: sha1-ESC0PcNZp4Xc5ltVuC4lfM9HlWY=} + engines: {node: '>=0.10.0'} + + mkdirp@1.0.4: + resolution: {integrity: sha1-PrXtYmInVteaXw4qIh3+utdcL34=} + engines: {node: '>=10'} + hasBin: true + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mlly@1.7.3: + resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==} + + mockjs@1.1.0: + resolution: {integrity: sha1-5qDDeOkZBtuv8gkRzAJzs8fXWwY=} + hasBin: true + + monaco-editor@0.52.2: + resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==} + + mrmime@2.0.0: + resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + engines: {node: '>=10'} + + ms@2.0.0: + resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} + + ms@2.1.3: + resolution: {integrity: sha1-V0yBOM4dK1hh8LRFedut1gxmFbI=} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + + namespace-emitter@2.0.1: + resolution: {integrity: sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==} + + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanomatch@1.2.13: + resolution: {integrity: sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=} + engines: {node: '>=0.10.0'} + + natural-compare@1.4.0: + resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=} + + needle@3.3.1: + resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} + engines: {node: '>= 4.4.x'} + hasBin: true + + neo-async@2.6.2: + resolution: {integrity: sha1-tKr7k+OustgXTKU88WOrfXMIMF8=} + + next-tick@1.1.0: + resolution: {integrity: sha1-GDbuMK1W1n7ygbIr0Zn3CUSbNes=} + + no-case@3.0.4: + resolution: {integrity: sha1-02H9XJgA9VhVGoNp/A3NRmK2Ek0=} + + node-fetch-native@1.6.4: + resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-plop@0.32.0: + resolution: {integrity: sha512-lKFSRSRuDHhwDKMUobdsvaWCbbDRbV3jMUSMiajQSQux1aNUevAZVxUHc2JERI//W8ABPRbi3ebYuSuIzkNIpQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + normalize-path@3.0.0: + resolution: {integrity: sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=} + engines: {node: '>=0.10.0'} + + normalize-wheel-es@1.2.0: + resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + nprogress@0.2.0: + resolution: {integrity: sha1-y480xTIT2JVyP8urkH6UIq28r7E=} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nypm@0.3.12: + resolution: {integrity: sha512-D3pzNDWIvgA+7IORhD/IuWzEk4uXv6GsgOxiid4UU3h9oq5IqV1KtPDi63n4sZJ/xcWlr88c0QM2RgN5VbOhFA==} + engines: {node: ^14.16.0 || >=16.10.0} + hasBin: true + + object-assign@4.1.1: + resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=} + engines: {node: '>=0.10.0'} + + object-copy@0.1.0: + resolution: {integrity: sha1-fn2Fi3gb18mRpBupde04EnVOmYw=} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.3: + resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha1-HEfyct8nfzsdrwYWd9nILiMixg4=} + engines: {node: '>= 0.4'} + + object-visit@1.0.1: + resolution: {integrity: sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=} + engines: {node: '>=0.10.0'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.defaults@1.1.0: + resolution: {integrity: sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=} + engines: {node: '>=0.10.0'} + + object.map@1.0.1: + resolution: {integrity: sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=} + engines: {node: '>=0.10.0'} + + object.pick@1.3.0: + resolution: {integrity: sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=} + engines: {node: '>=0.10.0'} + + ofetch@1.4.1: + resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} + + ohash@1.1.4: + resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==} + + on-finished@2.3.0: + resolution: {integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} + + onetime@5.1.2: + resolution: {integrity: sha1-0Oluu1awdHbfHdnEgG5SN5hcpF4=} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha1-fCTBjtH9LpvKS9JoBqM2E8d9NLQ=} + engines: {node: '>=12'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@5.4.1: + resolution: {integrity: sha1-GyZ4Qmr0rEpQkAjl5KyemVnbnhg=} + engines: {node: '>=10'} + + ora@8.1.1: + resolution: {integrity: sha512-YWielGi1XzG1UTvOaCFaNgEnuhZVMSHYkW/FQ7UX8O26PtlpdM84c0f7wLPlkvx2RfiQmnzd61d/MGxmpQeJPw==} + engines: {node: '>=18'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=} + engines: {node: '>=0.10.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@2.3.0: + resolution: {integrity: sha1-PdM8ZHohT9//2DWTPrCG2g3CHbE=} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha1-4drMvnjQ0TiMoYxk/qOOPlfjcGs=} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha1-kUr2VE7TK/pUZwsGHK/L0EmEtkQ=} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@4.1.0: + resolution: {integrity: sha1-o0KLtwiLOmApL2aRkni3wpetTwc=} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha1-g8gxXGeFAF470CGDlBHJ4RDm2DQ=} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha1-PamknUk0uQEIncozAvpl3FoFwE8=} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@5.5.0: + resolution: {integrity: sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==} + engines: {node: '>=12'} + + p-try@2.2.0: + resolution: {integrity: sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + package-manager-detector@0.2.8: + resolution: {integrity: sha512-ts9KSdroZisdvKMWVAVCXiKqnqNfXz4+IbrBG8/BWx/TR5le+jfenvoBuIZ6UWM9nz47W7AbD9qYfAwfWMIwzA==} + + param-case@3.0.4: + resolution: {integrity: sha1-fRf+SqEr3jTUp32RrPtiGcqtAcU=} + + parent-module@1.0.1: + resolution: {integrity: sha1-aR0nCeeMefrjoVZiJFLQB2LKqqI=} + engines: {node: '>=6'} + + parse-filepath@1.0.2: + resolution: {integrity: sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=} + engines: {node: '>=0.8'} + + parse-json@5.2.0: + resolution: {integrity: sha1-x2/Gbe5UIxyWKyK8yKcs8vmXU80=} + engines: {node: '>=8'} + + parse-node-version@1.0.1: + resolution: {integrity: sha1-4rXb7eAOf6m8NjYH9TMn6LBzGJs=} + engines: {node: '>= 0.10'} + + parse-passwd@1.0.0: + resolution: {integrity: sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=} + engines: {node: '>=0.10.0'} + + parseurl@1.3.3: + resolution: {integrity: sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=} + engines: {node: '>= 0.8'} + + pascal-case@3.1.2: + resolution: {integrity: sha1-tI4O8rmOIF58Ha50fQsVCCN2YOs=} + + pascalcase@0.1.1: + resolution: {integrity: sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=} + engines: {node: '>=0.10.0'} + + path-browserify@1.0.1: + resolution: {integrity: sha1-2YRUqcN1PVeQhg8W9ohnueRr4f0=} + + path-case@3.0.4: + resolution: {integrity: sha1-kWhkUzTrlCZYN1xW+AtMDLX4LG8=} + + path-exists@4.0.0: + resolution: {integrity: sha1-UTvb4tO5XXdi6METfvoZXGxhtbM=} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha1-pqrZSJIAsh+rMeSc8JJ35RFvuec=} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-is-absolute@1.0.1: + resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha1-WB9q3mWMu6ZaDTOA3ndTKVBU83U=} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha1-KVWI3DruZBVPh3rbnXgLgcVUvxg=} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha1-+8EUtgykKzDZ2vWFjkvWi77bZzU=} + + path-root-regex@0.1.2: + resolution: {integrity: sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=} + engines: {node: '>=0.10.0'} + + path-root@0.1.1: + resolution: {integrity: sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=} + engines: {node: '>=0.10.0'} + + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + path-type@4.0.0: + resolution: {integrity: sha1-hO0BwKe6OAr+CdkKjBgNzZ0DBDs=} + engines: {node: '>=8'} + + path-type@5.0.0: + resolution: {integrity: sha1-FLAe166n3fnHw/RhgdTQT5x4W7g=} + engines: {node: '>=12'} + + pathe@0.2.0: + resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathe@2.0.0: + resolution: {integrity: sha512-G7n4uhtk9qJt2hlD+UFfsIGY854wpF+zs2bUbQ3CQEUTcn7v25LRsrmurOxTo4bJgjE4qkyshd9ldsEuY9M6xg==} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pify@4.0.1: + resolution: {integrity: sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=} + engines: {node: '>=6'} + + pinia-plugin-persistedstate@4.2.0: + resolution: {integrity: sha512-3buhA7ac+ssbOIx3VRCC8oHkoFwhDM9oHRCjo7nj+O8WUqnW+jRqh7eYT5eS/DNa3H28zp3dYf/nd/Vc8zj8eQ==} + peerDependencies: + '@pinia/nuxt': '>=0.9.0' + pinia: '>=2.3.0' + peerDependenciesMeta: + '@pinia/nuxt': + optional: true + pinia: + optional: true + + pinia@2.3.0: + resolution: {integrity: sha512-ohZj3jla0LL0OH5PlLTDMzqKiVw2XARmC1XYLdLWIPBMdhDW/123ZWr4zVAhtJm+aoSkFa13pYXskAvAscIkhQ==} + peerDependencies: + typescript: '>=4.4.4' + vue: ^2.7.0 || ^3.5.11 + peerDependenciesMeta: + typescript: + optional: true + + pkg-types@1.3.0: + resolution: {integrity: sha512-kS7yWjVFCkIw9hqdJBoMxDdzEngmkr5FXeWZZfQ6GoYacjVnsW6l2CcYW/0ThD0vF4LPJgVYnrg4d0uuhwYQbg==} + + plop@4.0.1: + resolution: {integrity: sha512-5n8QU93kvL/ObOzBcPAB1siVFtAH1TZM6TntJ3JK5kXT0jIgnQV+j+uaOWWFJlg1cNkzLYm8klgASF65K36q9w==} + engines: {node: '>=18'} + hasBin: true + + pngjs@5.0.0: + resolution: {integrity: sha1-553SshV2f9nARWHAEjbflgvOf7s=} + engines: {node: '>=10.13.0'} + + posix-character-classes@0.1.1: + resolution: {integrity: sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=} + engines: {node: '>=0.10.0'} + + possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + + postcss-html@1.7.0: + resolution: {integrity: sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw==} + engines: {node: ^12 || >=14} + + postcss-less@6.0.0: + resolution: {integrity: sha512-FPX16mQLyEjLzEuuJtxA8X3ejDLNGGEG503d2YGZR5Ask1SpDN8KmZUMpzCvyalWRywAn1n1VOA5dcqfCLo5rg==} + engines: {node: '>=12'} + peerDependencies: + postcss: ^8.3.5 + + postcss-prefix-selector@1.16.1: + resolution: {integrity: sha512-Umxu+FvKMwlY6TyDzGFoSUnzW+NOfMBLyC1tAkIjgX+Z/qGspJeRjVC903D7mx7TuBpJlwti2ibXtWuA7fKMeQ==} + peerDependencies: + postcss: '>4 <9' + + postcss-resolve-nested-selector@0.1.6: + resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} + + postcss-safe-parser@6.0.0: + resolution: {integrity: sha1-u0wpiUFxqUvFyZa5owMX70Aq2qE=} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-selector-parser@7.0.0: + resolution: {integrity: sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==} + engines: {node: '>=4'} + + postcss-sorting@8.0.2: + resolution: {integrity: sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==} + peerDependencies: + postcss: ^8.4.20 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@5.2.18: + resolution: {integrity: sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=} + engines: {node: '>=0.12'} + + postcss@8.4.49: + resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} + engines: {node: ^10 || ^12 || >=14} + + posthtml-parser@0.2.1: + resolution: {integrity: sha1-NdUw3jhnQMK6JP8usvrznM3ycd0=} + + posthtml-rename-id@1.0.12: + resolution: {integrity: sha1-z39us3FGvxr6wx5o8YxswZrmFDM=} + + posthtml-render@1.4.0: + resolution: {integrity: sha1-QBFAcMRYgcrLkzR9rj7/U6+8/xM=} + engines: {node: '>=10'} + + posthtml-svg-mode@1.0.3: + resolution: {integrity: sha1-q9VU+s6BIjyrDLNn4Y5O/SpOdLA=} + + posthtml@0.9.2: + resolution: {integrity: sha1-9MBtufZ7Yf0XxOJW5+PZUVv3Jv0=} + engines: {node: '>=0.10.0'} + + preact@10.25.4: + resolution: {integrity: sha512-jLdZDb+Q+odkHJ+MpW/9U5cODzqnB+fy2EiHSZES7ldV5LK7yjlVzTp7R8Xy6W6y75kfK8iWYtFVH7lvjwrCMA==} + + prelude-ls@1.2.1: + resolution: {integrity: sha1-3rxkidem5rDnYRiIzsiAM30xY5Y=} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha1-0j1B/hN1ZG3i0BBNNFSjAIgCz3s=} + engines: {node: '>=6.0.0'} + + prettier@3.4.2: + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} + engines: {node: '>=14'} + hasBin: true + + prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + + progress@2.0.3: + resolution: {integrity: sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=} + engines: {node: '>=0.4.0'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha1-4QLxbKNVQkhldV0sno6k8k1Yw+I=} + + prr@1.0.1: + resolution: {integrity: sha1-0/wRS6BplaRexok/SEzrHXj19HY=} + + punycode@1.4.1: + resolution: {integrity: sha1-wNWmOycYgArY4esPpSachN1BhF4=} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + + qs@6.13.1: + resolution: {integrity: sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==} + engines: {node: '>=0.6'} + + query-string@4.3.4: + resolution: {integrity: sha1-u7aTucqRXCMlFbIosaArYJBD2+s=} + engines: {node: '>=0.10.0'} + + queue-microtask@1.2.3: + resolution: {integrity: sha1-SSkii7xyTfrEPg77BYyve2z7YkM=} + + rc9@2.1.2: + resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} + + rd@2.0.1: + resolution: {integrity: sha1-4YqK9bL3RAwNsVI8oExuD5ZgAD8=} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha1-dKNwvYVxFuJFspzJc0DNQxoCpsc=} + engines: {node: '>=8.10.0'} + + readdirp@4.0.2: + resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} + engines: {node: '>= 14.16.0'} + + rechoir@0.8.0: + resolution: {integrity: sha1-Sfhm4NMhRhQto62PDv81KzIV/yI=} + engines: {node: '>= 10.13.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regenerate-unicode-properties@10.2.0: + resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha1-uTRtiCfo9aMve6KWN9OYtpAUhIo=} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + regenerator-transform@0.15.2: + resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + + regex-not@1.0.2: + resolution: {integrity: sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=} + engines: {node: '>=0.10.0'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + regexpu-core@6.2.0: + resolution: {integrity: sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.12.0: + resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} + hasBin: true + + repeat-element@1.1.4: + resolution: {integrity: sha1-vmgVIIR6tYx1aKx1+/rSjtQtOek=} + engines: {node: '>=0.10.0'} + + repeat-string@1.6.1: + resolution: {integrity: sha1-jcrkcOHIirwtYA//Sndihtp15jc=} + engines: {node: '>=0.10'} + + require-directory@2.1.1: + resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha1-iaf92TgmEmcxjq/hT5wy5ZjDaQk=} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha1-0LMp7MfMD2Fkn2IhW+aa9UqomJs=} + + resolve-dir@1.0.1: + resolution: {integrity: sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY=} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha1-w1IlhD3493bfIcV1V7wIfp39/Gk=} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve-url@0.2.1: + resolution: {integrity: sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha1-OfZ8VLOnpYzqUjbZXPADQjljH34=} + engines: {node: '>=8'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + ret@0.1.15: + resolution: {integrity: sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=} + engines: {node: '>=0.12'} + + reusify@1.0.4: + resolution: {integrity: sha1-kNo4Kx4SbvwCFG6QhFqI2xKSXXY=} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@3.0.2: + resolution: {integrity: sha1-8aVAK6YiCtUswSgrrBrjqkn9Bho=} + hasBin: true + + rimraf@6.0.1: + resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} + engines: {node: 20 || >=22} + hasBin: true + + rollup-plugin-purge-icons@0.10.0: + resolution: {integrity: sha512-GD2ftg4L9G/sagIhtCmBn5vdyzePOisniythubpbywP0Q3ix9rZuDeFvgXTPemOsc22pvH7t22ryYQIl0rwGog==} + engines: {node: '>= 12'} + + rollup-plugin-visualizer@5.14.0: + resolution: {integrity: sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + rolldown: 1.x + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + + rollup@2.79.2: + resolution: {integrity: sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==} + engines: {node: '>=10.0.0'} + hasBin: true + + rollup@4.30.1: + resolution: {integrity: sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha1-ZtE2jae9+SHrnZW9GpIp5/IaQ+4=} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-regex@1.1.0: + resolution: {integrity: sha1-QKNmnzsHfR6UPURinhV91IAjvy4=} + + safer-buffer@2.1.2: + resolution: {integrity: sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=} + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + + scroll-into-view-if-needed@2.2.31: + resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} + + scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + sentence-case@3.0.4: + resolution: {integrity: sha1-NkWnuMEXx4f96HAgViJbtipFEx8=} + + set-blocking@2.0.0: + resolution: {integrity: sha1-BF+XgtARrppoA93TgrJDkrPYkPc=} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + set-value@2.0.1: + resolution: {integrity: sha1-oY1AUw5vB95CKMfe/kInr4ytAFs=} + engines: {node: '>=0.10.0'} + + shebang-command@2.0.0: + resolution: {integrity: sha1-zNCvT4g1+9wmW4JGGq8MNmY/NOo=} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha1-rhbxZE2HPsrYQ7AwexQzYtTEIXI=} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sirv@3.0.0: + resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} + engines: {node: '>=18'} + + slash@3.0.0: + resolution: {integrity: sha1-ZTm+hwwWWtvVJAIg2+Nh8bxNRjQ=} + engines: {node: '>=8'} + + slash@4.0.0: + resolution: {integrity: sha1-JCI3IXbExsWt214q2oha+YSzlqc=} + engines: {node: '>=12'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slate-history@0.66.0: + resolution: {integrity: sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==} + peerDependencies: + slate: '>=0.65.3' + + slate@0.72.8: + resolution: {integrity: sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==} + + slice-ansi@4.0.0: + resolution: {integrity: sha1-UA6N0P1VsFgVCGJVsxla3ypF/ms=} + engines: {node: '>=10'} + + slice-ansi@5.0.0: + resolution: {integrity: sha1-tzBjxXqpb5zYgWVLFSlNldKFxCo=} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + snabbdom@3.6.2: + resolution: {integrity: sha512-ig5qOnCDbugFntKi6c7Xlib8bA6xiJVk8O+WdFrV3wxbMqeHO0hXFQC4nAhPVWfZfi8255lcZkNhtIBINCc4+Q==} + engines: {node: '>=12.17.0'} + + snake-case@3.0.4: + resolution: {integrity: sha1-Tyu9Vo6ZNavf1ZPzTGkdrbScRSw=} + + snapdragon-node@2.1.1: + resolution: {integrity: sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=} + engines: {node: '>=0.10.0'} + + snapdragon-util@3.0.1: + resolution: {integrity: sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=} + engines: {node: '>=0.10.0'} + + snapdragon@0.8.2: + resolution: {integrity: sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=} + engines: {node: '>=0.10.0'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-resolve@0.5.3: + resolution: {integrity: sha1-GQhmvs51U+H48mei7oLGBrVQmho=} + + source-map-support@0.5.21: + resolution: {integrity: sha1-BP58f54e0tZiIzwoyys1ufY/bk8=} + + source-map-url@0.4.1: + resolution: {integrity: sha1-CvZmBadFpaL5HPG7+KevvCg97FY=} + + source-map@0.5.7: + resolution: {integrity: sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha1-dHIq8y6WFOnCh6jQu95IteLxomM=} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + sourcemap-codec@1.4.8: + resolution: {integrity: sha1-6oBL2UhXQC5pktBaOO8a41qatMQ=} + + split-string@3.1.0: + resolution: {integrity: sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=} + engines: {node: '>=0.10.0'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + ssr-window@3.0.0: + resolution: {integrity: sha1-/VuCgBY4lD4MxwTEaRgBQ1r3rDc=} + + stable@0.1.8: + resolution: {integrity: sha1-g26zyDgv4pNv6vVEYxAXzn1Ho88=} + + static-extend@0.1.2: + resolution: {integrity: sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=} + engines: {node: '>=0.10.0'} + + statuses@1.5.0: + resolution: {integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=} + engines: {node: '>= 0.6'} + + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + + stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + + strict-uri-encode@1.1.0: + resolution: {integrity: sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=} + engines: {node: '>=0.10.0'} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha1-JpxxF9J7Ba0uU2gwqOyJXvnG0BA=} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.3.0: + resolution: {integrity: sha1-QvEUWUpGzxqOMLCoT1bHjD7awh4=} + + strip-ansi@3.0.1: + resolution: {integrity: sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=} + engines: {node: '>=0.10.0'} + + strip-ansi@6.0.1: + resolution: {integrity: sha1-nibGPTD1NEPpSJSVshBdN7Z6hdk=} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha1-UolMMT+/8xiDUoCu1g/3Hr8SuP0=} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha1-MfEoGzgyYwQ0gxwxDAHMzajL4AY=} + engines: {node: '>=8'} + + strip-literal@2.1.1: + resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==} + + stylelint-config-html@1.1.0: + resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==} + engines: {node: ^12 || >=14} + peerDependencies: + postcss-html: ^1.0.0 + stylelint: '>=14.0.0' + + stylelint-config-recommended@14.0.1: + resolution: {integrity: sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.1.0 + + stylelint-config-standard@36.0.1: + resolution: {integrity: sha512-8aX8mTzJ6cuO8mmD5yon61CWuIM4UD8Q5aBcWKGSf6kg+EC3uhB+iOywpTK4ca6ZL7B49en8yanOFtUW0qNzyw==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.1.0 + + stylelint-order@6.0.4: + resolution: {integrity: sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==} + peerDependencies: + stylelint: ^14.0.0 || ^15.0.0 || ^16.0.1 + + stylelint@16.12.0: + resolution: {integrity: sha512-F8zZ3L/rBpuoBZRvI4JVT20ZanPLXfQLzMOZg1tzPflRVh9mKpOZ8qcSIhh1my3FjAjZWG4T2POwGnmn6a6hbg==} + engines: {node: '>=18.12.0'} + hasBin: true + + supports-color@2.0.0: + resolution: {integrity: sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=} + engines: {node: '>=0.8.0'} + + supports-color@3.2.3: + resolution: {integrity: sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=} + engines: {node: '>=0.8.0'} + + supports-color@7.2.0: + resolution: {integrity: sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=} + engines: {node: '>=8'} + + supports-hyperlinks@3.1.0: + resolution: {integrity: sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==} + engines: {node: '>=14.18'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-baker@1.7.0: + resolution: {integrity: sha1-g2f3jYdVUMUv5HVvcwPVxdfC6ac=} + + svg-tags@1.0.0: + resolution: {integrity: sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=} + + svgo@2.8.0: + resolution: {integrity: sha1-T/gMzmcQ3CeV8MfHQQHmdkz8zSQ=} + engines: {node: '>=10.13.0'} + hasBin: true + + synckit@0.9.2: + resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} + engines: {node: ^14.18.0 || >=16.0.0} + + system-architecture@0.1.0: + resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} + engines: {node: '>=18'} + + systemjs@6.15.1: + resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==} + + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + terser@5.37.0: + resolution: {integrity: sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==} + engines: {node: '>=10'} + hasBin: true + + text-extensions@2.4.0: + resolution: {integrity: sha1-oc/MUM802kG/0EfMdE+ATRaA6jQ=} + engines: {node: '>=8'} + + through@2.3.8: + resolution: {integrity: sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=} + + tiny-warning@1.0.3: + resolution: {integrity: sha1-lKMNtFPfTGQ9D9VmBg1gqHXYR1Q=} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.10: + resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} + engines: {node: '>=12.0.0'} + + title-case@3.0.3: + resolution: {integrity: sha1-vGibRvAuQR8dHh0IH3w97KBImYI=} + + tmp@0.0.33: + resolution: {integrity: sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=} + engines: {node: '>=0.6.0'} + + to-object-path@0.3.0: + resolution: {integrity: sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=} + engines: {node: '>=0.10.0'} + + to-regex-range@2.1.1: + resolution: {integrity: sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=} + engines: {node: '>=0.10.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=} + engines: {node: '>=8.0'} + + to-regex@3.0.2: + resolution: {integrity: sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=} + engines: {node: '>=0.10.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=} + + traverse@0.6.10: + resolution: {integrity: sha512-hN4uFRxbK+PX56DxYiGHsTn2dME3TVr9vbNqlQGcGcPhJAn+tdP126iA+TArMpI4YSgnTkMWyoLl5bf81Hi5TA==} + engines: {node: '>= 0.4'} + + ts-api-utils@2.0.0: + resolution: {integrity: sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.3.0: + resolution: {integrity: sha1-gDuM2rPhK6WBpMpByIObuw2ssJ4=} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.19.2: + resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha1-B7ggO/pwVsBlcFDjzNLDdzC6uPE=} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha1-G/IH9LKPkVg2ZstfvTJ4hzAc1fQ=} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha1-0mCiSwGYQ24TP6JqUkptZfo7Ljc=} + engines: {node: '>=10'} + + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typedarray.prototype.slice@1.0.5: + resolution: {integrity: sha512-q7QNVDGTdl702bVFiI5eY4l/HkgCM6at9KhcFbgUAzezHFbOVy4+0O/lCjsABEQwbZPravVfBIiBVGo89yzHFg==} + engines: {node: '>= 0.4'} + + typescript-eslint@8.19.1: + resolution: {integrity: sha512-LKPUQpdEMVOeKluHi8md7rwLcoXHhwvWp3x+sJkMuq3gGm9yaYJtPo8sRZSblMFJ5pcOGCAak/scKf1mvZDlQw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + unc-path-regex@0.1.2: + resolution: {integrity: sha1-5z3T17DXxe2G+6xrCufYxqadUPo=} + engines: {node: '>=0.10.0'} + + unconfig@0.6.0: + resolution: {integrity: sha512-4C67J0nIF2QwSXty2kW3zZx1pMZ3iXabylvJWWgHybWVUcMf9pxwsngoQt0gC+AVstRywFqrRBp3qOXJayhpOw==} + + unctx@2.4.1: + resolution: {integrity: sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==} + + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha1-VP0W4OyxZ88Ezx91a9zJLrp5dsM=} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.0: + resolution: {integrity: sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.1.0: + resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} + engines: {node: '>=4'} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + unimport@3.14.5: + resolution: {integrity: sha512-tn890SwFFZxqaJSKQPPd+yygfKSATbM8BZWW1aCR2TJBTs1SDrmLamBueaFtYsGjHtQaRgqEbQflOjN2iW12gA==} + + union-value@1.0.1: + resolution: {integrity: sha1-C2/nuDWuzaYcbqTU8CwUIh4QmEc=} + engines: {node: '>=0.10.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unocss@0.65.4: + resolution: {integrity: sha512-KUCW5OzI20Ik6j1zXkkrpWhxZ59TwSKl6+DvmYHEzMfaEcrHlBZaFSApAoSt2CYSvo6SluGiKyr+Im1UTkd4KA==} + engines: {node: '>=14'} + peerDependencies: + '@unocss/webpack': 0.65.4 + vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 + peerDependenciesMeta: + '@unocss/webpack': + optional: true + vite: + optional: true + + unpipe@1.0.0: + resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=} + engines: {node: '>= 0.8'} + + unplugin@1.16.1: + resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==} + engines: {node: '>=14.0.0'} + + unplugin@2.1.2: + resolution: {integrity: sha512-Q3LU0e4zxKfRko1wMV2HmP8lB9KWislY7hxXpxd+lGx0PRInE4vhMBVEZwpdVYHvtqzhSrzuIfErsob6bQfCzw==} + engines: {node: '>=18.12.0'} + + unset-value@1.0.0: + resolution: {integrity: sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=} + engines: {node: '>=0.10.0'} + + untyped@1.5.2: + resolution: {integrity: sha512-eL/8PlhLcMmlMDtNPKhyyz9kEBDS3Uk4yMu/ewlkT2WFbtzScjHWPJLdQLmaGPUKjXzwe9MumOtOgc4Fro96Kg==} + hasBin: true + + update-browserslist-db@1.1.2: + resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + upper-case-first@2.0.2: + resolution: {integrity: sha1-mSwyc/iCq9GdHgKJTMFHEX+EQyQ=} + + upper-case@2.0.2: + resolution: {integrity: sha1-2JgQgj+qsd8VSbfZenb4Ziuub3o=} + + uqr@0.1.2: + resolution: {integrity: sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==} + + uri-js@4.4.1: + resolution: {integrity: sha1-mxpSWVIlhZ5V9mnZKPiMbFfyp34=} + + urix@0.1.0: + resolution: {integrity: sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=} + + url@0.11.4: + resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} + engines: {node: '>= 0.4'} + + use@3.1.1: + resolution: {integrity: sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=} + engines: {node: '>=0.10.0'} + + util-deprecate@1.0.2: + resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} + + utils-merge@1.0.1: + resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=} + engines: {node: '>= 0.4.0'} + + v8flags@4.0.1: + resolution: {integrity: sha512-fcRLaS4H/hrZk9hYwbdRM35D0U8IYMfEClhXxCivOojl+yTRAZH3Zy2sSy6qVCiGbV9YAtPssP6jaChqC9vPCg==} + engines: {node: '>= 10.13.0'} + + vary@1.1.2: + resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=} + engines: {node: '>= 0.8'} + + vite-plugin-ejs@1.7.0: + resolution: {integrity: sha512-JNP3zQDC4mSbfoJ3G73s5mmZITD8NGjUmLkq4swxyahy/W0xuokK9U9IJGXw7KCggq6UucT6hJ0p+tQrNtqTZw==} + peerDependencies: + vite: '>=5.0.0' + + vite-plugin-eslint@1.8.1: + resolution: {integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==} + peerDependencies: + eslint: '>=7' + vite: '>=2' + + vite-plugin-mock@2.9.6: + resolution: {integrity: sha512-/Rm59oPppe/ncbkSrUuAxIQihlI2YcBmnbR4ST1RA2VzM1C0tEQc1KlbQvnUGhXECAGTaQN2JyasiwXP6EtKgg==} + engines: {node: '>=12.0.0'} + peerDependencies: + mockjs: '>=1.1.0' + vite: '>=2.0.0' + + vite-plugin-progress@0.0.7: + resolution: {integrity: sha512-zyvKdcc/X+6hnw3J1HVV1TKrlFKC4Rh8GnDnWG/2qhRXjqytTcM++xZ+SAPnoDsSyWl8O93ymK0wZRgHAoglEQ==} + engines: {node: '>=14', pnpm: '>=7.0.0'} + peerDependencies: + vite: '>2.0.0-0' + + vite-plugin-purge-icons@0.10.0: + resolution: {integrity: sha512-4fMJKQuBu9lAPJWjqGEytRaxty1pP9bWgQLA68dwbbaCXu6NBrOUb/3kMaUc7TP09kerEk+qTriCk05OZXpjwA==} + engines: {node: '>= 12'} + peerDependencies: + vite: '>=2' + + vite-plugin-style-import@2.0.0: + resolution: {integrity: sha512-qtoHQae5dSUQPo/rYz/8p190VU5y19rtBaeV7ryLa/AYAU/e9CG89NrN/3+k7MR8mJy/GPIu91iJ3zk9foUOSA==} + peerDependencies: + vite: '>=2.0.0' + + vite-plugin-svg-icons@2.0.1: + resolution: {integrity: sha512-6ktD+DhV6Rz3VtedYvBKKVA2eXF+sAQVaKkKLDSqGUfnhqXl3bj5PPkVTl3VexfTuZy66PmINi8Q6eFnVfRUmA==} + peerDependencies: + vite: '>=2.0.0' + + vite-plugin-url-copy@1.1.4: + resolution: {integrity: sha512-LuNI0RHdg3gfbyIiY4yzJaIxhq2fAapUTV31eskHIV56CxioddReH+H+bZtRkEdcgr3XkFBdetUVRzBSJjNEyg==} + peerDependencies: + vite: '>=4.0.0' + + vite@6.0.7: + resolution: {integrity: sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vscode-uri@3.0.8: + resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + vue-draggable-plus@0.6.0: + resolution: {integrity: sha512-G5TSfHrt9tX9EjdG49InoFJbt2NYk0h3kgjgKxkFWr3ulIUays0oFObr5KZ8qzD4+QnhtALiRwIqY6qul4egqw==} + peerDependencies: + '@types/sortablejs': ^1.15.0 + '@vue/composition-api': '*' + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + vue-eslint-parser@9.4.3: + resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + vue-flow-layout@0.1.1: + resolution: {integrity: sha512-JdgRRUVrN0Y2GosA0M68DEbKlXMqJ7FQgsK8CjQD2vxvNSqAU6PZEpi4cfcTVtfM2GVOMjHo7GKKLbXxOBqDqA==} + peerDependencies: + vue: ^3.4.37 + + vue-i18n@11.0.1: + resolution: {integrity: sha512-pWAT8CusK8q9/EpN7V3oxwHwxWm6+Kp2PeTZmRGvdZTkUzMQDpbbmHp0TwQ8xw04XKm23cr6B4GL72y3W8Yekg==} + engines: {node: '>= 16'} + peerDependencies: + vue: ^3.0.0 + + vue-json-pretty@2.4.0: + resolution: {integrity: sha512-e9bP41DYYIc2tWaB6KuwqFJq5odZ8/GkE6vHQuGcbPn37kGk4a3n1RNw3ZYeDrl66NWXgTlOfS+M6NKkowmkWw==} + engines: {node: '>= 10.0.0', npm: '>= 5.0.0'} + peerDependencies: + vue: '>=3.0.0' + + vue-router@4.5.0: + resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==} + peerDependencies: + vue: ^3.2.0 + + vue-tsc@2.2.0: + resolution: {integrity: sha512-gtmM1sUuJ8aSb0KoAFmK9yMxb8TxjewmxqTJ1aKphD5Cbu0rULFY6+UQT51zW7SpUcenfPUuflKyVwyx9Qdnxg==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue-types@5.1.3: + resolution: {integrity: sha512-3Wy6QcZl0VusCCHX3vYrWSILFlrOB2EQDoySnuYmASM5cUp1FivJGfkS5lp1CutDgyRb41g32r/1QCmiBj5i1Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + vue: ^2.0.0 || ^3.0.0 + peerDependenciesMeta: + vue: + optional: true + + vue@3.5.13: + resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + wcwidth@1.0.1: + resolution: {integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=} + + webidl-conversions@3.0.1: + resolution: {integrity: sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha1-lmRU6HZUYuN2RNNib2dCzotwll0=} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + which-typed-array@1.1.18: + resolution: {integrity: sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==} + engines: {node: '>= 0.4'} + + which@1.3.1: + resolution: {integrity: sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha1-fGqN0KY2oDJ+ELWckobu6T8/UbE=} + engines: {node: '>= 8'} + hasBin: true + + wildcard@1.1.2: + resolution: {integrity: sha1-pwIEUwhNjNLv5wup02liY94XEKU=} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wordwrap@1.0.0: + resolution: {integrity: sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=} + + wrap-ansi@6.2.0: + resolution: {integrity: sha1-6Tk7oHEC5skaOyIUePAlfNKFblM=} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha1-Z+FFz/UQpqaYS98RUpEdadLrnkM=} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + xgplayer-subtitles@3.0.20: + resolution: {integrity: sha512-I1bjsIY+aKOrhYQspLdneOkYg+Vf4cJVGPnDSFnNebnxXl9Mhz5SEpWGzYizMYxL9UvsQ9pgjeEY0o4hkwM+kQ==} + peerDependencies: + core-js: '>=3.12.1' + + xgplayer@3.0.20: + resolution: {integrity: sha512-UNKZJRyODOZGdka83ao8fI18xdhzOV8qG4aNEOOkuOQbXFXfXsJMr/dazRHFP+uXmTqiCXr568euee3ch7CS7g==} + peerDependencies: + core-js: '>=3.12.1' + + xml-name-validator@4.0.0: + resolution: {integrity: sha1-eaAG4uYxSahgDxVDDwpHJdFSSDU=} + engines: {node: '>=12'} + + y18n@4.0.3: + resolution: {integrity: sha1-tfJZyCzW4zaSHv17/Yv1YN6e7t8=} + + y18n@5.0.8: + resolution: {integrity: sha1-f0k00PfKjFb5UxSTndzS3ZHOHVU=} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha1-27fa+b/YusmrRev2ArjLrQ1dCP0=} + + yallist@4.0.0: + resolution: {integrity: sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=} + + yaml-eslint-parser@1.2.3: + resolution: {integrity: sha512-4wZWvE398hCP7O8n3nXKu/vdq1HcH01ixYlCREaJL5NUMwQ0g3MaGFUBNSlmBtKmhbtVG/Cm6lyYmSVTEVil8A==} + engines: {node: ^14.17.0 || >=16.0.0} + + yaml@2.6.1: + resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} + engines: {node: '>= 14'} + hasBin: true + + yaml@2.7.0: + resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@18.1.3: + resolution: {integrity: sha1-vmjEl1xrKr9GkjawyHA2L6sJp7A=} + engines: {node: '>=6'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@15.4.1: + resolution: {integrity: sha1-DYehbeAa7p2L7Cv7909nhRcw9Pg=} + engines: {node: '>=8'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha1-ApTrPe4FAo0x7hpfosVWpqrxChs=} + engines: {node: '>=10'} + + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + + zrender@5.6.1: + resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@antfu/install-pkg@0.4.1': + dependencies: + package-manager-detector: 0.2.8 + tinyexec: 0.3.2 + + '@antfu/utils@0.7.10': {} + + '@babel/code-frame@7.26.2': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.26.3': {} + + '@babel/core@7.26.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.3 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helpers': 7.26.0 + '@babel/parser': 7.26.3 + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + convert-source-map: 2.0.0 + debug: 4.4.0 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.26.3': + dependencies: + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.25.9': + dependencies: + '@babel/types': 7.26.3 + + '@babel/helper-compilation-targets@7.25.9': + dependencies: + '@babel/compat-data': 7.26.3 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.4 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-member-expression-to-functions': 7.25.9 + '@babel/helper-optimise-call-expression': 7.25.9 + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/traverse': 7.26.4 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.26.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + regexpu-core: 6.2.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + debug: 4.4.0 + lodash.debounce: 4.0.8 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + + '@babel/helper-member-expression-to-functions@7.25.9': + dependencies: + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.25.9': + dependencies: + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.26.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.25.9': + dependencies: + '@babel/types': 7.26.3 + + '@babel/helper-plugin-utils@7.25.9': {} + + '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-wrap-function': 7.25.9 + '@babel/traverse': 7.26.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-member-expression-to-functions': 7.25.9 + '@babel/helper-optimise-call-expression': 7.25.9 + '@babel/traverse': 7.26.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.25.9': + dependencies: + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.25.9': {} + + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/helper-validator-option@7.25.9': {} + + '@babel/helper-wrap-function@7.25.9': + dependencies: + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.26.0': + dependencies: + '@babel/template': 7.25.9 + '@babel/types': 7.26.3 + + '@babel/parser@7.26.3': + dependencies: + '@babel/types': 7.26.3 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/traverse': 7.26.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/traverse': 7.26.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + + '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-async-generator-functions@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) + '@babel/traverse': 7.26.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + '@babel/traverse': 7.26.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/template': 7.25.9 + + '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-exponentiation-operator@7.26.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/traverse': 7.26.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.26.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.26.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-nullish-coalescing-operator@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) + + '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + regenerator-transform: 0.15.2 + + '@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-template-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-typeof-symbol@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-typescript@7.26.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/preset-env@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/compat-data': 7.26.3 + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-validator-option': 7.25.9 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0) + '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.26.0) + '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-async-generator-functions': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-block-scoped-functions': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-class-static-block': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-dotall-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-duplicate-keys': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-dynamic-import': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-exponentiation-operator': 7.26.3(@babel/core@7.26.0) + '@babel/plugin-transform-export-namespace-from': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-for-of': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-json-strings': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-member-expression-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-amd': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.0) + '@babel/plugin-transform-modules-systemjs': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-umd': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-new-target': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-object-super': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-property-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-regexp-modifiers': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-transform-reserved-words': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-template-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-typeof-symbol': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-unicode-escapes': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-unicode-property-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-unicode-sets-regex': 7.25.9(@babel/core@7.26.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.26.0) + babel-plugin-polyfill-corejs2: 0.4.12(@babel/core@7.26.0) + babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.0) + babel-plugin-polyfill-regenerator: 0.6.3(@babel/core@7.26.0) + core-js-compat: 3.40.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/types': 7.26.3 + esutils: 2.0.3 + + '@babel/runtime@7.26.0': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/standalone@7.26.4': {} + + '@babel/template@7.25.9': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 + + '@babel/traverse@7.26.4': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.3 + '@babel/parser': 7.26.3 + '@babel/template': 7.25.9 + '@babel/types': 7.26.3 + debug: 4.4.0 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.26.3': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + + '@commitlint/cli@19.6.1(@types/node@22.10.5)(typescript@5.7.3)': + dependencies: + '@commitlint/format': 19.5.0 + '@commitlint/lint': 19.6.0 + '@commitlint/load': 19.6.1(@types/node@22.10.5)(typescript@5.7.3) + '@commitlint/read': 19.5.0 + '@commitlint/types': 19.5.0 + tinyexec: 0.3.2 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/config-conventional@19.6.0': + dependencies: + '@commitlint/types': 19.5.0 + conventional-changelog-conventionalcommits: 7.0.2 + + '@commitlint/config-validator@19.5.0': + dependencies: + '@commitlint/types': 19.5.0 + ajv: 8.17.1 + + '@commitlint/ensure@19.5.0': + dependencies: + '@commitlint/types': 19.5.0 + lodash.camelcase: 4.3.0 + lodash.kebabcase: 4.1.1 + lodash.snakecase: 4.1.1 + lodash.startcase: 4.4.0 + lodash.upperfirst: 4.3.1 + + '@commitlint/execute-rule@19.5.0': {} + + '@commitlint/format@19.5.0': + dependencies: + '@commitlint/types': 19.5.0 + chalk: 5.4.1 + + '@commitlint/is-ignored@19.6.0': + dependencies: + '@commitlint/types': 19.5.0 + semver: 7.6.3 + + '@commitlint/lint@19.6.0': + dependencies: + '@commitlint/is-ignored': 19.6.0 + '@commitlint/parse': 19.5.0 + '@commitlint/rules': 19.6.0 + '@commitlint/types': 19.5.0 + + '@commitlint/load@19.6.1(@types/node@22.10.5)(typescript@5.7.3)': + dependencies: + '@commitlint/config-validator': 19.5.0 + '@commitlint/execute-rule': 19.5.0 + '@commitlint/resolve-extends': 19.5.0 + '@commitlint/types': 19.5.0 + chalk: 5.4.1 + cosmiconfig: 9.0.0(typescript@5.7.3) + cosmiconfig-typescript-loader: 6.1.0(@types/node@22.10.5)(cosmiconfig@9.0.0(typescript@5.7.3))(typescript@5.7.3) + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + lodash.uniq: 4.5.0 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/message@19.5.0': {} + + '@commitlint/parse@19.5.0': + dependencies: + '@commitlint/types': 19.5.0 + conventional-changelog-angular: 7.0.0 + conventional-commits-parser: 5.0.0 + + '@commitlint/read@19.5.0': + dependencies: + '@commitlint/top-level': 19.5.0 + '@commitlint/types': 19.5.0 + git-raw-commits: 4.0.0 + minimist: 1.2.8 + tinyexec: 0.3.2 + + '@commitlint/resolve-extends@19.5.0': + dependencies: + '@commitlint/config-validator': 19.5.0 + '@commitlint/types': 19.5.0 + global-directory: 4.0.1 + import-meta-resolve: 4.1.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + + '@commitlint/rules@19.6.0': + dependencies: + '@commitlint/ensure': 19.5.0 + '@commitlint/message': 19.5.0 + '@commitlint/to-lines': 19.5.0 + '@commitlint/types': 19.5.0 + + '@commitlint/to-lines@19.5.0': {} + + '@commitlint/top-level@19.5.0': + dependencies: + find-up: 7.0.0 + + '@commitlint/types@19.5.0': + dependencies: + '@types/conventional-commits-parser': 5.0.1 + chalk: 5.4.1 + + '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/css-tokenizer@3.0.3': {} + + '@csstools/media-query-list-parser@4.0.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + + '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.0.0)': + dependencies: + postcss-selector-parser: 7.0.0 + + '@ctrl/tinycolor@3.6.1': {} + + '@dual-bundle/import-meta-resolve@4.1.0': {} + + '@element-plus/icons-vue@2.3.1(vue@3.5.13(typescript@5.7.3))': + dependencies: + vue: 3.5.13(typescript@5.7.3) + + '@esbuild/aix-ppc64@0.23.1': + optional: true + + '@esbuild/aix-ppc64@0.24.2': + optional: true + + '@esbuild/android-arm64@0.23.1': + optional: true + + '@esbuild/android-arm64@0.24.2': + optional: true + + '@esbuild/android-arm@0.23.1': + optional: true + + '@esbuild/android-arm@0.24.2': + optional: true + + '@esbuild/android-x64@0.23.1': + optional: true + + '@esbuild/android-x64@0.24.2': + optional: true + + '@esbuild/darwin-arm64@0.23.1': + optional: true + + '@esbuild/darwin-arm64@0.24.2': + optional: true + + '@esbuild/darwin-x64@0.23.1': + optional: true + + '@esbuild/darwin-x64@0.24.2': + optional: true + + '@esbuild/freebsd-arm64@0.23.1': + optional: true + + '@esbuild/freebsd-arm64@0.24.2': + optional: true + + '@esbuild/freebsd-x64@0.23.1': + optional: true + + '@esbuild/freebsd-x64@0.24.2': + optional: true + + '@esbuild/linux-arm64@0.23.1': + optional: true + + '@esbuild/linux-arm64@0.24.2': + optional: true + + '@esbuild/linux-arm@0.23.1': + optional: true + + '@esbuild/linux-arm@0.24.2': + optional: true + + '@esbuild/linux-ia32@0.23.1': + optional: true + + '@esbuild/linux-ia32@0.24.2': + optional: true + + '@esbuild/linux-loong64@0.23.1': + optional: true + + '@esbuild/linux-loong64@0.24.2': + optional: true + + '@esbuild/linux-mips64el@0.23.1': + optional: true + + '@esbuild/linux-mips64el@0.24.2': + optional: true + + '@esbuild/linux-ppc64@0.23.1': + optional: true + + '@esbuild/linux-ppc64@0.24.2': + optional: true + + '@esbuild/linux-riscv64@0.23.1': + optional: true + + '@esbuild/linux-riscv64@0.24.2': + optional: true + + '@esbuild/linux-s390x@0.23.1': + optional: true + + '@esbuild/linux-s390x@0.24.2': + optional: true + + '@esbuild/linux-x64@0.23.1': + optional: true + + '@esbuild/linux-x64@0.24.2': + optional: true + + '@esbuild/netbsd-arm64@0.24.2': + optional: true + + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/netbsd-x64@0.24.2': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.24.2': + optional: true + + '@esbuild/openbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-x64@0.24.2': + optional: true + + '@esbuild/sunos-x64@0.23.1': + optional: true + + '@esbuild/sunos-x64@0.24.2': + optional: true + + '@esbuild/win32-arm64@0.23.1': + optional: true + + '@esbuild/win32-arm64@0.24.2': + optional: true + + '@esbuild/win32-ia32@0.23.1': + optional: true + + '@esbuild/win32-ia32@0.24.2': + optional: true + + '@esbuild/win32-x64@0.23.1': + optional: true + + '@esbuild/win32-x64@0.24.2': + optional: true + + '@eslint-community/eslint-utils@4.4.1(eslint@9.17.0(jiti@2.4.2))': + dependencies: + eslint: 9.17.0(jiti@2.4.2) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.19.1': + dependencies: + '@eslint/object-schema': 2.1.5 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/core@0.9.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.2.0': + dependencies: + ajv: 6.12.6 + debug: 4.4.0 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.17.0': {} + + '@eslint/object-schema@2.1.5': {} + + '@eslint/plugin-kit@0.2.4': + dependencies: + levn: 0.4.1 + + '@floating-ui/core@1.6.9': + dependencies: + '@floating-ui/utils': 0.2.9 + + '@floating-ui/dom@1.6.13': + dependencies: + '@floating-ui/core': 1.6.9 + '@floating-ui/utils': 0.2.9 + + '@floating-ui/utils@0.2.9': {} + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.1': {} + + '@iconify/iconify@2.1.2': + dependencies: + cross-fetch: 3.2.0 + transitivePeerDependencies: + - encoding + + '@iconify/iconify@3.1.1': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/json@2.2.293': + dependencies: + '@iconify/types': 2.0.0 + pathe: 1.1.2 + + '@iconify/types@2.0.0': {} + + '@iconify/utils@2.2.1': + dependencies: + '@antfu/install-pkg': 0.4.1 + '@antfu/utils': 0.7.10 + '@iconify/types': 2.0.0 + debug: 4.4.0 + globals: 15.14.0 + kolorist: 1.8.0 + local-pkg: 0.5.1 + mlly: 1.7.3 + transitivePeerDependencies: + - supports-color + + '@iconify/vue@4.3.0(vue@3.5.13(typescript@5.7.3))': + dependencies: + '@iconify/types': 2.0.0 + vue: 3.5.13(typescript@5.7.3) + + '@inquirer/checkbox@4.0.4(@types/node@22.10.5)': + dependencies: + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/figures': 1.0.9 + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + + '@inquirer/confirm@5.1.1(@types/node@22.10.5)': + dependencies: + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 + + '@inquirer/core@10.1.2(@types/node@22.10.5)': + dependencies: + '@inquirer/figures': 1.0.9 + '@inquirer/type': 3.0.2(@types/node@22.10.5) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + transitivePeerDependencies: + - '@types/node' + + '@inquirer/editor@4.2.1(@types/node@22.10.5)': + dependencies: + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 + external-editor: 3.1.0 + + '@inquirer/expand@4.0.4(@types/node@22.10.5)': + dependencies: + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 + yoctocolors-cjs: 2.1.2 + + '@inquirer/figures@1.0.9': {} + + '@inquirer/input@4.1.1(@types/node@22.10.5)': + dependencies: + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 + + '@inquirer/number@3.0.4(@types/node@22.10.5)': + dependencies: + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 + + '@inquirer/password@4.0.4(@types/node@22.10.5)': + dependencies: + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 + ansi-escapes: 4.3.2 + + '@inquirer/prompts@7.2.1(@types/node@22.10.5)': + dependencies: + '@inquirer/checkbox': 4.0.4(@types/node@22.10.5) + '@inquirer/confirm': 5.1.1(@types/node@22.10.5) + '@inquirer/editor': 4.2.1(@types/node@22.10.5) + '@inquirer/expand': 4.0.4(@types/node@22.10.5) + '@inquirer/input': 4.1.1(@types/node@22.10.5) + '@inquirer/number': 3.0.4(@types/node@22.10.5) + '@inquirer/password': 4.0.4(@types/node@22.10.5) + '@inquirer/rawlist': 4.0.4(@types/node@22.10.5) + '@inquirer/search': 3.0.4(@types/node@22.10.5) + '@inquirer/select': 4.0.4(@types/node@22.10.5) + '@types/node': 22.10.5 + + '@inquirer/rawlist@4.0.4(@types/node@22.10.5)': + dependencies: + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 + yoctocolors-cjs: 2.1.2 + + '@inquirer/search@3.0.4(@types/node@22.10.5)': + dependencies: + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/figures': 1.0.9 + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 + yoctocolors-cjs: 2.1.2 + + '@inquirer/select@4.0.4(@types/node@22.10.5)': + dependencies: + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/figures': 1.0.9 + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + + '@inquirer/type@3.0.2(@types/node@22.10.5)': + dependencies: + '@types/node': 22.10.5 + + '@intlify/bundle-utils@10.0.0(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))': + dependencies: + '@intlify/message-compiler': 11.0.0-rc.1 + '@intlify/shared': 11.0.0-rc.1 + acorn: 8.14.0 + escodegen: 2.1.0 + estree-walker: 2.0.2 + jsonc-eslint-parser: 2.4.0 + mlly: 1.7.3 + source-map-js: 1.2.1 + yaml-eslint-parser: 1.2.3 + optionalDependencies: + vue-i18n: 11.0.1(vue@3.5.13(typescript@5.7.3)) + + '@intlify/core-base@11.0.1': + dependencies: + '@intlify/message-compiler': 11.0.1 + '@intlify/shared': 11.0.1 + + '@intlify/message-compiler@11.0.0-rc.1': + dependencies: + '@intlify/shared': 11.0.0-rc.1 + source-map-js: 1.2.1 + + '@intlify/message-compiler@11.0.1': + dependencies: + '@intlify/shared': 11.0.1 + source-map-js: 1.2.1 + + '@intlify/shared@11.0.0-rc.1': {} + + '@intlify/shared@11.0.1': {} + + '@intlify/unplugin-vue-i18n@6.0.3(@vue/compiler-dom@3.5.13)(eslint@9.17.0(jiti@2.4.2))(rollup@4.30.1)(typescript@5.7.3)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))': + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@2.4.2)) + '@intlify/bundle-utils': 10.0.0(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3))) + '@intlify/shared': 11.0.1 + '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.0.1)(@vue/compiler-dom@3.5.13)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3)) + '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@typescript-eslint/scope-manager': 8.19.1 + '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) + debug: 4.4.0 + fast-glob: 3.3.3 + js-yaml: 4.1.0 + json5: 2.2.3 + pathe: 1.1.2 + picocolors: 1.1.1 + source-map-js: 1.2.1 + unplugin: 1.16.1 + vue: 3.5.13(typescript@5.7.3) + optionalDependencies: + vue-i18n: 11.0.1(vue@3.5.13(typescript@5.7.3)) + transitivePeerDependencies: + - '@vue/compiler-dom' + - eslint + - rollup + - supports-color + - typescript + + '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.0.1)(@vue/compiler-dom@3.5.13)(vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))': + dependencies: + '@babel/parser': 7.26.3 + optionalDependencies: + '@intlify/shared': 11.0.1 + '@vue/compiler-dom': 3.5.13 + vue: 3.5.13(typescript@5.7.3) + vue-i18n: 11.0.1(vue@3.5.13(typescript@5.7.3)) + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.18.0 + + '@nuxt/kit@3.15.1(rollup@4.30.1)': + dependencies: + '@nuxt/schema': 3.15.1 + c12: 2.0.1 + consola: 3.3.3 + defu: 6.1.4 + destr: 2.0.3 + globby: 14.0.2 + ignore: 7.0.0 + jiti: 2.4.2 + klona: 2.0.6 + knitwork: 1.2.0 + mlly: 1.7.3 + ohash: 1.1.4 + pathe: 2.0.0 + pkg-types: 1.3.0 + scule: 1.3.0 + semver: 7.6.3 + ufo: 1.5.4 + unctx: 2.4.1 + unimport: 3.14.5(rollup@4.30.1) + untyped: 1.5.2 + transitivePeerDependencies: + - magicast + - rollup + - supports-color + + '@nuxt/schema@3.15.1': + dependencies: + consola: 3.3.3 + defu: 6.1.4 + pathe: 2.0.0 + std-env: 3.8.0 + + '@pkgr/core@0.1.1': {} + + '@polka/url@1.0.0-next.28': {} + + '@purge-icons/core@0.10.0': + dependencies: + '@iconify/iconify': 2.1.2 + axios: 0.26.1(debug@4.4.0) + debug: 4.4.0 + fast-glob: 3.3.3 + fs-extra: 10.1.0 + transitivePeerDependencies: + - encoding + - supports-color + + '@purge-icons/generated@0.10.0': + dependencies: + '@iconify/iconify': 3.1.1 + + '@rollup/plugin-node-resolve@13.3.0(rollup@4.30.1)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@4.30.1) + '@types/resolve': 1.17.1 + deepmerge: 4.3.1 + is-builtin-module: 3.2.1 + is-module: 1.0.0 + resolve: 1.22.10 + rollup: 4.30.1 + + '@rollup/pluginutils@3.1.0(rollup@4.30.1)': + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: 4.30.1 + + '@rollup/pluginutils@4.2.1': + dependencies: + estree-walker: 2.0.2 + picomatch: 2.3.1 + + '@rollup/pluginutils@5.1.4(rollup@4.30.1)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.30.1 + + '@rollup/rollup-android-arm-eabi@4.30.1': + optional: true + + '@rollup/rollup-android-arm64@4.30.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.30.1': + optional: true + + '@rollup/rollup-darwin-x64@4.30.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.30.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.30.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.30.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.30.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.30.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.30.1': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.30.1': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.30.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.30.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.30.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.30.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.30.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.30.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.30.1': + optional: true + + '@sindresorhus/merge-streams@2.3.0': {} + + '@sxzz/popperjs-es@2.11.7': {} + + '@transloadit/prettier-bytes@0.0.7': {} + + '@trysound/sax@0.2.0': {} + + '@types/conventional-commits-parser@5.0.1': + dependencies: + '@types/node': 22.10.5 + + '@types/eslint@8.56.12': + dependencies: + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + + '@types/estree@0.0.39': {} + + '@types/estree@1.0.6': {} + + '@types/event-emitter@0.3.5': {} + + '@types/fined@1.1.5': {} + + '@types/fs-extra@11.0.4': + dependencies: + '@types/jsonfile': 6.1.4 + '@types/node': 22.10.5 + + '@types/inquirer@9.0.7': + dependencies: + '@types/through': 0.0.33 + rxjs: 7.8.1 + + '@types/json-schema@7.0.15': {} + + '@types/jsonfile@6.1.4': + dependencies: + '@types/node': 22.10.5 + + '@types/liftoff@4.0.3': + dependencies: + '@types/fined': 1.1.5 + '@types/node': 22.10.5 + + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.14 + + '@types/lodash@4.17.14': {} + + '@types/mockjs@1.0.10': {} + + '@types/node@10.17.60': {} + + '@types/node@22.10.5': + dependencies: + undici-types: 6.20.0 + + '@types/nprogress@0.2.3': {} + + '@types/qrcode@1.5.5': + dependencies: + '@types/node': 22.10.5 + + '@types/qs@6.9.17': {} + + '@types/resolve@1.17.1': + dependencies: + '@types/node': 22.10.5 + + '@types/sortablejs@1.15.8': {} + + '@types/svgo@2.6.4': + dependencies: + '@types/node': 22.10.5 + + '@types/through@0.0.33': + dependencies: + '@types/node': 22.10.5 + + '@types/web-bluetooth@0.0.16': {} + + '@types/web-bluetooth@0.0.20': {} + + '@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.19.1 + '@typescript-eslint/type-utils': 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/utils': 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.19.1 + eslint: 9.17.0(jiti@2.4.2) + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 2.0.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.19.1 + '@typescript-eslint/types': 8.19.1 + '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.19.1 + debug: 4.4.0 + eslint: 9.17.0(jiti@2.4.2) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.19.1': + dependencies: + '@typescript-eslint/types': 8.19.1 + '@typescript-eslint/visitor-keys': 8.19.1 + + '@typescript-eslint/type-utils@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) + '@typescript-eslint/utils': 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + debug: 4.4.0 + eslint: 9.17.0(jiti@2.4.2) + ts-api-utils: 2.0.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.19.1': {} + + '@typescript-eslint/typescript-estree@8.19.1(typescript@5.7.3)': + dependencies: + '@typescript-eslint/types': 8.19.1 + '@typescript-eslint/visitor-keys': 8.19.1 + debug: 4.4.0 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 2.0.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3)': + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.19.1 + '@typescript-eslint/types': 8.19.1 + '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) + eslint: 9.17.0(jiti@2.4.2) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.19.1': + dependencies: + '@typescript-eslint/types': 8.19.1 + eslint-visitor-keys: 4.2.0 + + '@unocss/astro@0.65.4(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3))': + dependencies: + '@unocss/core': 0.65.4 + '@unocss/reset': 0.65.4 + '@unocss/vite': 0.65.4(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3)) + optionalDependencies: + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + transitivePeerDependencies: + - rollup + - supports-color + - vue + + '@unocss/cli@0.65.4(rollup@4.30.1)': + dependencies: + '@ampproject/remapping': 2.3.0 + '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@unocss/config': 0.65.4 + '@unocss/core': 0.65.4 + '@unocss/preset-uno': 0.65.4 + cac: 6.7.14 + chokidar: 3.6.0 + colorette: 2.0.20 + consola: 3.3.3 + magic-string: 0.30.17 + pathe: 1.1.2 + perfect-debounce: 1.0.0 + tinyglobby: 0.2.10 + transitivePeerDependencies: + - rollup + - supports-color + + '@unocss/config@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + unconfig: 0.6.0 + transitivePeerDependencies: + - supports-color + + '@unocss/core@0.65.4': {} + + '@unocss/extractor-arbitrary-variants@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + + '@unocss/inspector@0.65.4(vue@3.5.13(typescript@5.7.3))': + dependencies: + '@unocss/core': 0.65.4 + '@unocss/rule-utils': 0.65.4 + colorette: 2.0.20 + gzip-size: 6.0.0 + sirv: 3.0.0 + vue-flow-layout: 0.1.1(vue@3.5.13(typescript@5.7.3)) + transitivePeerDependencies: + - vue + + '@unocss/postcss@0.65.4(postcss@8.4.49)': + dependencies: + '@unocss/config': 0.65.4 + '@unocss/core': 0.65.4 + '@unocss/rule-utils': 0.65.4 + css-tree: 3.1.0 + postcss: 8.4.49 + tinyglobby: 0.2.10 + transitivePeerDependencies: + - supports-color + + '@unocss/preset-attributify@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + + '@unocss/preset-icons@0.65.4': + dependencies: + '@iconify/utils': 2.2.1 + '@unocss/core': 0.65.4 + ofetch: 1.4.1 + transitivePeerDependencies: + - supports-color + + '@unocss/preset-mini@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + '@unocss/extractor-arbitrary-variants': 0.65.4 + '@unocss/rule-utils': 0.65.4 + + '@unocss/preset-tagify@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + + '@unocss/preset-typography@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + '@unocss/preset-mini': 0.65.4 + + '@unocss/preset-uno@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + '@unocss/preset-mini': 0.65.4 + '@unocss/preset-wind': 0.65.4 + '@unocss/rule-utils': 0.65.4 + + '@unocss/preset-web-fonts@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + ofetch: 1.4.1 + + '@unocss/preset-wind@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + '@unocss/preset-mini': 0.65.4 + '@unocss/rule-utils': 0.65.4 + + '@unocss/reset@0.65.4': {} + + '@unocss/rule-utils@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + magic-string: 0.30.17 + + '@unocss/transformer-attributify-jsx@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + + '@unocss/transformer-compile-class@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + + '@unocss/transformer-directives@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + '@unocss/rule-utils': 0.65.4 + css-tree: 3.1.0 + + '@unocss/transformer-variant-group@0.65.4': + dependencies: + '@unocss/core': 0.65.4 + + '@unocss/vite@0.65.4(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@unocss/config': 0.65.4 + '@unocss/core': 0.65.4 + '@unocss/inspector': 0.65.4(vue@3.5.13(typescript@5.7.3)) + chokidar: 3.6.0 + magic-string: 0.30.17 + tinyglobby: 0.2.10 + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + transitivePeerDependencies: + - rollup + - supports-color + - vue + + '@uppy/companion-client@2.2.2': + dependencies: + '@uppy/utils': 4.1.3 + namespace-emitter: 2.0.1 + + '@uppy/core@2.3.4': + dependencies: + '@transloadit/prettier-bytes': 0.0.7 + '@uppy/store-default': 2.1.1 + '@uppy/utils': 4.1.3 + lodash.throttle: 4.1.1 + mime-match: 1.0.2 + namespace-emitter: 2.0.1 + nanoid: 3.3.8 + preact: 10.25.4 + + '@uppy/store-default@2.1.1': {} + + '@uppy/utils@4.1.3': + dependencies: + lodash.throttle: 4.1.1 + + '@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4)': + dependencies: + '@uppy/companion-client': 2.2.2 + '@uppy/core': 2.3.4 + '@uppy/utils': 4.1.3 + nanoid: 3.3.8 + + '@vitejs/plugin-legacy@6.0.0(terser@5.37.0)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))': + dependencies: + '@babel/core': 7.26.0 + '@babel/preset-env': 7.26.0(@babel/core@7.26.0) + browserslist: 4.24.4 + browserslist-to-esbuild: 2.1.1(browserslist@4.24.4) + core-js: 3.40.0 + magic-string: 0.30.17 + regenerator-runtime: 0.14.1 + systemjs: 6.15.1 + terser: 5.37.0 + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue-jsx@4.1.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3))': + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-transform-typescript': 7.26.3(@babel/core@7.26.0) + '@vue/babel-plugin-jsx': 1.2.5(@babel/core@7.26.0) + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + vue: 3.5.13(typescript@5.7.3) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue@5.2.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3))': + dependencies: + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + vue: 3.5.13(typescript@5.7.3) + + '@volar/language-core@2.4.11': + dependencies: + '@volar/source-map': 2.4.11 + + '@volar/source-map@2.4.11': {} + + '@volar/typescript@2.4.11': + dependencies: + '@volar/language-core': 2.4.11 + path-browserify: 1.0.1 + vscode-uri: 3.0.8 + + '@vue/babel-helper-vue-transform-on@1.2.5': {} + + '@vue/babel-plugin-jsx@1.2.5(@babel/core@7.26.0)': + dependencies: + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.4 + '@babel/types': 7.26.3 + '@vue/babel-helper-vue-transform-on': 1.2.5 + '@vue/babel-plugin-resolve-type': 1.2.5(@babel/core@7.26.0) + html-tags: 3.3.1 + svg-tags: 1.0.0 + optionalDependencies: + '@babel/core': 7.26.0 + transitivePeerDependencies: + - supports-color + + '@vue/babel-plugin-resolve-type@1.2.5(@babel/core@7.26.0)': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/parser': 7.26.3 + '@vue/compiler-sfc': 3.5.13 + transitivePeerDependencies: + - supports-color + + '@vue/compiler-core@3.5.13': + dependencies: + '@babel/parser': 7.26.3 + '@vue/shared': 3.5.13 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.13': + dependencies: + '@vue/compiler-core': 3.5.13 + '@vue/shared': 3.5.13 + + '@vue/compiler-sfc@3.5.13': + dependencies: + '@babel/parser': 7.26.3 + '@vue/compiler-core': 3.5.13 + '@vue/compiler-dom': 3.5.13 + '@vue/compiler-ssr': 3.5.13 + '@vue/shared': 3.5.13 + estree-walker: 2.0.2 + magic-string: 0.30.17 + postcss: 8.4.49 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.13': + dependencies: + '@vue/compiler-dom': 3.5.13 + '@vue/shared': 3.5.13 + + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/devtools-api@6.6.4': {} + + '@vue/language-core@2.2.0(typescript@5.7.3)': + dependencies: + '@volar/language-core': 2.4.11 + '@vue/compiler-dom': 3.5.13 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.13 + alien-signals: 0.4.14 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.7.3 + + '@vue/reactivity@3.5.13': + dependencies: + '@vue/shared': 3.5.13 + + '@vue/runtime-core@3.5.13': + dependencies: + '@vue/reactivity': 3.5.13 + '@vue/shared': 3.5.13 + + '@vue/runtime-dom@3.5.13': + dependencies: + '@vue/reactivity': 3.5.13 + '@vue/runtime-core': 3.5.13 + '@vue/shared': 3.5.13 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.7.3))': + dependencies: + '@vue/compiler-ssr': 3.5.13 + '@vue/shared': 3.5.13 + vue: 3.5.13(typescript@5.7.3) + + '@vue/shared@3.5.13': {} + + '@vueuse/core@12.3.0(typescript@5.7.3)': + dependencies: + '@types/web-bluetooth': 0.0.20 + '@vueuse/metadata': 12.3.0 + '@vueuse/shared': 12.3.0(typescript@5.7.3) + vue: 3.5.13(typescript@5.7.3) + transitivePeerDependencies: + - typescript + + '@vueuse/core@9.13.0(vue@3.5.13(typescript@5.7.3))': + dependencies: + '@types/web-bluetooth': 0.0.16 + '@vueuse/metadata': 9.13.0 + '@vueuse/shared': 9.13.0(vue@3.5.13(typescript@5.7.3)) + vue-demi: 0.14.10(vue@3.5.13(typescript@5.7.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/metadata@12.3.0': {} + + '@vueuse/metadata@9.13.0': {} + + '@vueuse/shared@12.3.0(typescript@5.7.3)': + dependencies: + vue: 3.5.13(typescript@5.7.3) + transitivePeerDependencies: + - typescript + + '@vueuse/shared@9.13.0(vue@3.5.13(typescript@5.7.3))': + dependencies: + vue-demi: 0.14.10(vue@3.5.13(typescript@5.7.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + is-url: 1.2.4 + lodash.throttle: 4.1.1 + nanoid: 3.3.8 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@wangeditor/code-highlight@1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + prismjs: 1.29.0 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@types/event-emitter': 0.3.5 + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + dom7: 3.0.0 + event-emitter: 0.3.5 + html-void-elements: 2.0.1 + i18next: 20.6.1 + is-hotkey: 0.2.0 + lodash.camelcase: 4.3.0 + lodash.clonedeep: 4.5.0 + lodash.debounce: 4.0.8 + lodash.foreach: 4.5.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + lodash.toarray: 4.4.0 + nanoid: 3.3.8 + scroll-into-view-if-needed: 2.2.31 + slate: 0.72.8 + slate-history: 0.66.0(slate@0.72.8) + snabbdom: 3.6.2 + + '@wangeditor/editor-for-vue@5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.13(typescript@5.7.3))': + dependencies: + '@wangeditor/editor': 5.1.23 + vue: 3.5.13(typescript@5.7.3) + + '@wangeditor/editor@5.1.23': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/code-highlight': 1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/list-module': 1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/table-module': 1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/upload-image-module': 1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/video-module': 1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + is-hotkey: 0.2.0 + lodash.camelcase: 4.3.0 + lodash.clonedeep: 4.5.0 + lodash.debounce: 4.0.8 + lodash.foreach: 4.5.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + lodash.toarray: 4.4.0 + nanoid: 3.3.8 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@wangeditor/list-module@1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@wangeditor/table-module@1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + nanoid: 3.3.8 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@wangeditor/upload-image-module@1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + lodash.foreach: 4.5.0 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@wangeditor/video-module@1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.8)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + nanoid: 3.3.8 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@zxcvbn-ts/core@3.0.4': + dependencies: + fastest-levenshtein: 1.0.16 + + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + + acorn-jsx@5.3.2(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + + acorn@8.14.0: {} + + aggregate-error@4.0.1: + dependencies: + clean-stack: 4.2.0 + indent-string: 5.0.0 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.5 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + alien-signals@0.4.14: {} + + animate.css@4.1.1: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-regex@2.1.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@2.2.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@2.0.1: {} + + arr-diff@4.0.0: {} + + arr-flatten@1.1.0: {} + + arr-union@3.1.0: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.3 + is-array-buffer: 3.0.5 + + array-each@1.0.1: {} + + array-ify@1.0.0: {} + + array-slice@1.1.0: {} + + array-union@2.1.0: {} + + array-unique@0.3.2: {} + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + get-intrinsic: 1.2.7 + is-array-buffer: 3.0.5 + + assign-symbols@1.0.0: {} + + astral-regex@2.0.0: {} + + async-validator@4.2.5: {} + + async@3.2.6: {} + + asynckit@0.4.0: {} + + atob@2.1.2: {} + + autoprefixer@10.4.20(postcss@8.4.49): + dependencies: + browserslist: 4.24.4 + caniuse-lite: 1.0.30001690 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.4.49 + postcss-value-parser: 4.2.0 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.0.0 + + axios@0.26.1(debug@4.4.0): + dependencies: + follow-redirects: 1.15.9(debug@4.4.0) + transitivePeerDependencies: + - debug + + axios@1.7.9: + dependencies: + follow-redirects: 1.15.9(debug@4.4.0) + form-data: 4.0.1 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + babel-plugin-polyfill-corejs2@0.4.12(@babel/core@7.26.0): + dependencies: + '@babel/compat-data': 7.26.3 + '@babel/core': 7.26.0 + '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) + core-js-compat: 3.40.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.3(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + balanced-match@1.0.2: {} + + balanced-match@2.0.0: {} + + base64-js@1.5.1: {} + + base@0.11.2: + dependencies: + cache-base: 1.0.1 + class-utils: 0.3.6 + component-emitter: 1.3.1 + define-property: 1.0.0 + isobject: 3.0.1 + mixin-deep: 1.3.2 + pascalcase: 0.1.1 + + big.js@5.2.2: {} + + binary-extensions@2.3.0: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + bluebird@3.7.2: {} + + boolbase@1.0.0: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@2.3.2: + dependencies: + arr-flatten: 1.1.0 + array-unique: 0.3.2 + extend-shallow: 2.0.1 + fill-range: 4.0.0 + isobject: 3.0.1 + repeat-element: 1.1.4 + snapdragon: 0.8.2 + snapdragon-node: 2.1.1 + split-string: 3.1.0 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist-to-esbuild@2.1.1(browserslist@4.24.4): + dependencies: + browserslist: 4.24.4 + meow: 13.2.0 + + browserslist@4.24.4: + dependencies: + caniuse-lite: 1.0.30001690 + electron-to-chromium: 1.5.79 + node-releases: 2.0.19 + update-browserslist-db: 1.1.2(browserslist@4.24.4) + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + builtin-modules@3.3.0: {} + + bundle-require@5.1.0(esbuild@0.24.2): + dependencies: + esbuild: 0.24.2 + load-tsconfig: 0.2.5 + + c12@2.0.1: + dependencies: + chokidar: 4.0.3 + confbox: 0.1.8 + defu: 6.1.4 + dotenv: 16.4.7 + giget: 1.2.3 + jiti: 2.4.2 + mlly: 1.7.3 + ohash: 1.1.4 + pathe: 1.1.2 + perfect-debounce: 1.0.0 + pkg-types: 1.3.0 + rc9: 2.1.2 + + cac@6.7.14: {} + + cache-base@1.0.1: + dependencies: + collection-visit: 1.0.0 + component-emitter: 1.3.1 + get-value: 2.0.6 + has-value: 1.0.0 + isobject: 3.0.1 + set-value: 2.0.1 + to-object-path: 0.3.0 + union-value: 1.0.1 + unset-value: 1.0.0 + + call-bind-apply-helpers@1.0.1: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.1 + es-define-property: 1.0.1 + get-intrinsic: 1.2.7 + set-function-length: 1.2.2 + + call-bound@1.0.3: + dependencies: + call-bind-apply-helpers: 1.0.1 + get-intrinsic: 1.2.7 + + callsites@3.1.0: {} + + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.1 + + camelcase@5.3.1: {} + + caniuse-lite@1.0.30001690: {} + + capital-case@1.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + upper-case-first: 2.0.2 + + chalk@1.1.3: + dependencies: + ansi-styles: 2.2.1 + escape-string-regexp: 1.0.5 + has-ansi: 2.0.0 + strip-ansi: 3.0.1 + supports-color: 2.0.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + change-case@4.1.2: + dependencies: + camel-case: 4.1.2 + capital-case: 1.0.4 + constant-case: 3.0.4 + dot-case: 3.0.4 + header-case: 2.0.4 + no-case: 3.0.4 + param-case: 3.0.4 + pascal-case: 3.1.2 + path-case: 3.0.4 + sentence-case: 3.0.4 + snake-case: 3.0.4 + tslib: 2.8.1 + + chardet@0.7.0: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.0.2 + + chownr@2.0.0: {} + + citty@0.1.6: + dependencies: + consola: 3.3.3 + + class-utils@0.3.6: + dependencies: + arr-union: 3.1.0 + define-property: 0.2.5 + isobject: 3.0.1 + static-extend: 0.1.2 + + clean-stack@4.2.0: + dependencies: + escape-string-regexp: 5.0.0 + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@2.9.2: {} + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + cli-width@4.1.0: {} + + clipboardy@4.0.0: + dependencies: + execa: 8.0.1 + is-wsl: 3.1.0 + is64bit: 2.0.0 + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone@1.0.4: {} + + clone@2.1.2: {} + + collection-visit@1.0.0: + dependencies: + map-visit: 1.0.0 + object-visit: 1.0.1 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colord@2.9.3: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@12.1.0: {} + + commander@13.0.0: {} + + commander@2.20.3: {} + + commander@7.2.0: {} + + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + + component-emitter@1.3.1: {} + + compute-scroll-into-view@1.0.20: {} + + concat-map@0.0.1: {} + + confbox@0.1.8: {} + + connect@3.7.0: + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + + consola@3.3.3: {} + + console@0.7.2: {} + + constant-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + upper-case: 2.0.2 + + conventional-changelog-angular@7.0.0: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-conventionalcommits@7.0.2: + dependencies: + compare-func: 2.0.0 + + conventional-commits-parser@5.0.0: + dependencies: + JSONStream: 1.3.5 + is-text-path: 2.0.0 + meow: 12.1.1 + split2: 4.2.0 + + convert-source-map@2.0.0: {} + + copy-anything@2.0.6: + dependencies: + is-what: 3.14.1 + + copy-descriptor@0.1.1: {} + + core-js-compat@3.40.0: + dependencies: + browserslist: 4.24.4 + + core-js@3.40.0: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig-typescript-loader@6.1.0(@types/node@22.10.5)(cosmiconfig@9.0.0(typescript@5.7.3))(typescript@5.7.3): + dependencies: + '@types/node': 22.10.5 + cosmiconfig: 9.0.0(typescript@5.7.3) + jiti: 2.4.2 + typescript: 5.7.3 + + cosmiconfig@9.0.0(typescript@5.7.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.7.3 + + cropperjs@1.6.2: {} + + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-functions-list@3.2.3: {} + + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + + css-what@6.1.0: {} + + cssesc@3.0.0: {} + + csso@4.2.0: + dependencies: + css-tree: 1.1.3 + + csstype@3.1.3: {} + + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 + + danmu.js@1.1.13: + dependencies: + event-emitter: 0.3.5 + + dargs@8.1.0: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + dayjs@1.11.13: {} + + de-indent@1.0.2: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + decamelize@1.2.0: {} + + decode-uri-component@0.2.2: {} + + deep-is@0.1.4: {} + + deep-pick-omit@1.2.1: {} + + deepmerge@4.3.1: {} + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-lazy-prop@2.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + define-property@0.2.5: + dependencies: + is-descriptor: 0.1.7 + + define-property@1.0.0: + dependencies: + is-descriptor: 1.0.3 + + define-property@2.0.2: + dependencies: + is-descriptor: 1.0.3 + isobject: 3.0.1 + + defu@6.1.4: {} + + del@7.1.0: + dependencies: + globby: 13.2.2 + graceful-fs: 4.2.11 + is-glob: 4.0.3 + is-path-cwd: 3.0.0 + is-path-inside: 4.0.0 + p-map: 5.5.0 + rimraf: 3.0.2 + slash: 4.0.0 + + delayed-stream@1.0.0: {} + + delegate@3.2.0: {} + + destr@2.0.3: {} + + detect-file@1.0.0: {} + + dijkstrajs@1.0.3: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dom-serializer@0.2.2: + dependencies: + domelementtype: 2.3.0 + entities: 2.2.0 + + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + dom7@3.0.0: + dependencies: + ssr-window: 3.0.0 + + domelementtype@1.3.1: {} + + domelementtype@2.3.0: {} + + domhandler@2.4.2: + dependencies: + domelementtype: 1.3.1 + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@1.7.0: + dependencies: + dom-serializer: 0.2.2 + domelementtype: 1.3.1 + + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + + dotenv@16.4.7: {} + + downloadjs@1.4.7: {} + + driver.js@1.3.1: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexer@0.1.2: {} + + eastasianwidth@0.2.0: {} + + echarts-wordcloud@2.1.0(echarts@5.6.0): + dependencies: + echarts: 5.6.0 + + echarts@5.6.0: + dependencies: + tslib: 2.3.0 + zrender: 5.6.1 + + ee-first@1.1.1: {} + + ejs@3.1.10: + dependencies: + jake: 10.9.2 + + electron-to-chromium@1.5.79: {} + + element-plus@2.9.2(vue@3.5.13(typescript@5.7.3)): + dependencies: + '@ctrl/tinycolor': 3.6.1 + '@element-plus/icons-vue': 2.3.1(vue@3.5.13(typescript@5.7.3)) + '@floating-ui/dom': 1.6.13 + '@popperjs/core': '@sxzz/popperjs-es@2.11.7' + '@types/lodash': 4.17.14 + '@types/lodash-es': 4.17.12 + '@vueuse/core': 9.13.0(vue@3.5.13(typescript@5.7.3)) + async-validator: 4.2.5 + dayjs: 1.11.13 + escape-html: 1.0.3 + lodash: 4.17.21 + lodash-es: 4.17.21 + lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21) + memoize-one: 6.0.0 + normalize-wheel-es: 1.2.0 + vue: 3.5.13(typescript@5.7.3) + transitivePeerDependencies: + - '@vue/composition-api' + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + emojis-list@3.0.0: {} + + encodeurl@1.0.2: {} + + entities@1.1.2: {} + + entities@2.2.0: {} + + entities@4.5.0: {} + + env-paths@2.2.1: {} + + environment@1.1.0: {} + + errno@0.1.8: + dependencies: + prr: 1.0.1 + optional: true + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-abstract@1.23.9: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.2.7 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.0 + math-intrinsics: 1.1.0 + object-inspect: 1.13.3 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.18 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@0.9.3: {} + + es-object-atoms@1.0.0: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.2.7 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + es5-ext@0.10.64: + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esniff: 2.0.1 + next-tick: 1.1.0 + + es6-iterator@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-symbol: 3.1.4 + + es6-symbol@3.1.4: + dependencies: + d: 1.0.2 + ext: 1.7.0 + + esbuild@0.11.3: {} + + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + + esbuild@0.24.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-config-prettier@9.1.0(eslint@9.17.0(jiti@2.4.2)): + dependencies: + eslint: 9.17.0(jiti@2.4.2) + + eslint-define-config@2.1.0: {} + + eslint-plugin-prettier@5.2.1(@types/eslint@8.56.12)(eslint-config-prettier@9.1.0(eslint@9.17.0(jiti@2.4.2)))(eslint@9.17.0(jiti@2.4.2))(prettier@3.4.2): + dependencies: + eslint: 9.17.0(jiti@2.4.2) + prettier: 3.4.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.9.2 + optionalDependencies: + '@types/eslint': 8.56.12 + eslint-config-prettier: 9.1.0(eslint@9.17.0(jiti@2.4.2)) + + eslint-plugin-vue@9.32.0(eslint@9.17.0(jiti@2.4.2)): + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@2.4.2)) + eslint: 9.17.0(jiti@2.4.2) + globals: 13.24.0 + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 6.1.2 + semver: 7.6.3 + vue-eslint-parser: 9.4.3(eslint@9.17.0(jiti@2.4.2)) + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - supports-color + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-scope@8.2.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.0: {} + + eslint@9.17.0(jiti@2.4.2): + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@2.4.2)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.19.1 + '@eslint/core': 0.9.1 + '@eslint/eslintrc': 3.2.0 + '@eslint/js': 9.17.0 + '@eslint/plugin-kit': 0.2.4 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.1 + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0 + escape-string-regexp: 4.0.0 + eslint-scope: 8.2.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.4.2 + transitivePeerDependencies: + - supports-color + + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.3 + + esno@4.8.0: + dependencies: + tsx: 4.19.2 + + espree@10.3.0: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 4.2.0 + + espree@9.6.1: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@1.0.1: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.6 + + esutils@2.0.3: {} + + etag@1.8.1: {} + + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + + eventemitter3@4.0.7: {} + + eventemitter3@5.0.1: {} + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + expand-brackets@2.1.4: + dependencies: + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + posix-character-classes: 0.1.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + + expand-tilde@2.0.2: + dependencies: + homedir-polyfill: 1.0.3 + + ext@1.7.0: + dependencies: + type: 2.7.3 + + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + extend-shallow@3.0.2: + dependencies: + assign-symbols: 1.0.0 + is-extendable: 1.0.1 + + extend@3.0.2: {} + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + extglob@2.0.4: + dependencies: + array-unique: 0.3.2 + define-property: 1.0.0 + expand-brackets: 2.1.4 + extend-shallow: 2.0.1 + fragment-cache: 0.2.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.0.5: {} + + fastest-levenshtein@1.0.16: {} + + fastq@1.18.0: + dependencies: + reusify: 1.0.4 + + fdir@6.4.2(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-entry-cache@9.1.0: + dependencies: + flat-cache: 5.0.0 + + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + + fill-range@4.0.0: + dependencies: + extend-shallow: 2.0.1 + is-number: 3.0.0 + repeat-string: 1.6.1 + to-regex-range: 2.1.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@1.1.2: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-up@7.0.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + + findup-sync@5.0.0: + dependencies: + detect-file: 1.0.0 + is-glob: 4.0.3 + micromatch: 4.0.8 + resolve-dir: 1.0.1 + + fined@2.0.0: + dependencies: + expand-tilde: 2.0.2 + is-plain-object: 5.0.0 + object.defaults: 1.1.0 + object.pick: 1.3.0 + parse-filepath: 1.0.2 + + flagged-respawn@2.0.0: {} + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.2 + keyv: 4.5.4 + + flat-cache@5.0.0: + dependencies: + flatted: 3.3.2 + keyv: 4.5.4 + + flatted@3.3.2: {} + + follow-redirects@1.15.9(debug@4.4.0): + optionalDependencies: + debug: 4.4.0 + + for-each@0.3.3: + dependencies: + is-callable: 1.2.7 + + for-in@1.0.2: {} + + for-own@1.0.0: + dependencies: + for-in: 1.0.2 + + foreground-child@3.3.0: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.1: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + fraction.js@4.3.7: {} + + fragment-cache@0.2.1: + dependencies: + map-cache: 0.2.2 + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.3 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.3.0: {} + + get-intrinsic@1.2.7: + dependencies: + call-bind-apply-helpers: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.0.0 + + get-stream@8.0.1: {} + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.7 + + get-tsconfig@4.8.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + get-value@2.0.6: {} + + giget@1.2.3: + dependencies: + citty: 0.1.6 + consola: 3.3.3 + defu: 6.1.4 + node-fetch-native: 1.6.4 + nypm: 0.3.12 + ohash: 1.1.4 + pathe: 1.1.2 + tar: 6.2.1 + + git-raw-commits@4.0.0: + dependencies: + dargs: 8.1.0 + meow: 12.1.1 + split2: 4.2.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@11.0.0: + dependencies: + foreground-child: 3.3.0 + jackspeak: 4.0.2 + minimatch: 10.0.1 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + + global-modules@1.0.0: + dependencies: + global-prefix: 1.0.2 + is-windows: 1.0.2 + resolve-dir: 1.0.1 + + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@1.0.2: + dependencies: + expand-tilde: 2.0.2 + homedir-polyfill: 1.0.3 + ini: 1.3.8 + is-windows: 1.0.2 + which: 1.3.1 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + + globals@11.12.0: {} + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globals@14.0.0: {} + + globals@15.14.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globby@13.2.2: + dependencies: + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 4.0.0 + + globby@14.0.2: + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.3 + ignore: 5.3.2 + path-type: 5.0.0 + slash: 5.1.0 + unicorn-magic: 0.1.0 + + globjoin@0.1.4: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + gzip-size@6.0.0: + dependencies: + duplexer: 0.1.2 + + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + has-ansi@2.0.0: + dependencies: + ansi-regex: 2.1.1 + + has-bigints@1.1.0: {} + + has-flag@1.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + has-value@0.3.1: + dependencies: + get-value: 2.0.6 + has-values: 0.1.4 + isobject: 2.1.0 + + has-value@1.0.0: + dependencies: + get-value: 2.0.6 + has-values: 1.0.0 + isobject: 3.0.1 + + has-values@0.1.4: {} + + has-values@1.0.0: + dependencies: + is-number: 3.0.0 + kind-of: 4.0.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + header-case@2.0.4: + dependencies: + capital-case: 1.0.4 + tslib: 2.8.1 + + homedir-polyfill@1.0.3: + dependencies: + parse-passwd: 1.0.0 + + html-tags@3.3.1: {} + + html-void-elements@2.0.1: {} + + htmlparser2@3.10.1: + dependencies: + domelementtype: 1.3.1 + domhandler: 2.4.2 + domutils: 1.7.0 + entities: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.2 + + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + + human-signals@5.0.0: {} + + husky@9.1.7: {} + + i18next@20.6.1: + dependencies: + '@babel/runtime': 7.26.0 + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@6.0.2: {} + + ignore@7.0.0: {} + + image-size@0.5.5: {} + + immer@9.0.21: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-meta-resolve@4.1.0: {} + + importx@0.5.1: + dependencies: + bundle-require: 5.1.0(esbuild@0.24.2) + debug: 4.4.0 + esbuild: 0.24.2 + jiti: 2.4.2 + pathe: 1.1.2 + tsx: 4.19.2 + transitivePeerDependencies: + - supports-color + + imurmurhash@0.1.4: {} + + indent-string@5.0.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ini@4.1.1: {} + + inquirer@12.3.0(@types/node@22.10.5): + dependencies: + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/prompts': 7.2.1(@types/node@22.10.5) + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 + ansi-escapes: 4.3.2 + mute-stream: 2.0.0 + run-async: 3.0.0 + rxjs: 7.8.1 + + inquirer@9.3.7: + dependencies: + '@inquirer/figures': 1.0.9 + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + external-editor: 3.1.0 + mute-stream: 1.0.0 + ora: 5.4.1 + run-async: 3.0.0 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + interpret@3.1.1: {} + + is-absolute@1.0.0: + dependencies: + is-relative: 1.0.0 + is-windows: 1.0.2 + + is-accessor-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.3 + get-intrinsic: 1.2.7 + + is-arrayish@0.2.1: {} + + is-async-function@2.1.0: + dependencies: + call-bound: 1.0.3 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-boolean-object@1.2.1: + dependencies: + call-bound: 1.0.3 + has-tostringtag: 1.0.2 + + is-buffer@1.1.6: {} + + is-builtin-module@3.2.1: + dependencies: + builtin-modules: 3.3.0 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.3 + get-intrinsic: 1.2.7 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.3 + has-tostringtag: 1.0.2 + + is-descriptor@0.1.7: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + + is-descriptor@1.0.3: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + + is-docker@2.2.1: {} + + is-docker@3.0.0: {} + + is-extendable@0.1.1: {} + + is-extendable@1.0.1: + dependencies: + is-plain-object: 2.0.4 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.3 + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.3.0 + + is-generator-function@1.1.0: + dependencies: + call-bound: 1.0.3 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hotkey@0.2.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-interactive@1.0.0: {} + + is-interactive@2.0.0: {} + + is-map@2.0.3: {} + + is-module@1.0.0: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.3 + has-tostringtag: 1.0.2 + + is-number@3.0.0: + dependencies: + kind-of: 3.2.2 + + is-number@7.0.0: {} + + is-obj@2.0.0: {} + + is-path-cwd@3.0.0: {} + + is-path-inside@4.0.0: {} + + is-plain-obj@1.1.0: {} + + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + + is-plain-object@5.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.3 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-relative@1.0.0: + dependencies: + is-unc-path: 1.0.0 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.3 + + is-stream@3.0.0: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.3 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.3 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-text-path@2.0.0: + dependencies: + text-extensions: 2.4.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.18 + + is-unc-path@1.0.0: + dependencies: + unc-path-regex: 0.1.2 + + is-unicode-supported@0.1.0: {} + + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.1.0: {} + + is-url@1.2.4: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.0: + dependencies: + call-bound: 1.0.3 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.3 + get-intrinsic: 1.2.7 + + is-what@3.14.1: {} + + is-windows@1.0.2: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + is64bit@2.0.0: + dependencies: + system-architecture: 0.1.0 + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isbinaryfile@5.0.4: {} + + isexe@2.0.0: {} + + isobject@2.1.0: + dependencies: + isarray: 1.0.0 + + isobject@3.0.1: {} + + jackspeak@4.0.2: + dependencies: + '@isaacs/cliui': 8.0.2 + + jake@10.9.2: + dependencies: + async: 3.2.6 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + + jiti@2.4.2: {} + + js-base64@2.6.4: {} + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.0.2: {} + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsonc-eslint-parser@2.4.0: + dependencies: + acorn: 8.14.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.6.3 + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonparse@1.3.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kind-of@3.2.2: + dependencies: + is-buffer: 1.1.6 + + kind-of@4.0.0: + dependencies: + is-buffer: 1.1.6 + + kind-of@5.1.0: {} + + kind-of@6.0.3: {} + + klona@2.0.6: {} + + knitwork@1.2.0: {} + + known-css-properties@0.35.0: {} + + kolorist@1.8.0: {} + + less@4.2.1: + dependencies: + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.8.1 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.3.1 + source-map: 0.6.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + liftoff@4.0.0: + dependencies: + extend: 3.0.2 + findup-sync: 5.0.0 + fined: 2.0.0 + flagged-respawn: 2.0.0 + is-plain-object: 5.0.0 + object.map: 1.0.1 + rechoir: 0.8.0 + resolve: 1.22.10 + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lint-staged@15.3.0: + dependencies: + chalk: 5.4.1 + commander: 12.1.0 + debug: 4.4.0 + execa: 8.0.1 + lilconfig: 3.1.3 + listr2: 8.2.5 + micromatch: 4.0.8 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.6.1 + transitivePeerDependencies: + - supports-color + + listr2@8.2.5: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + load-tsconfig@0.2.5: {} + + loader-utils@1.4.2: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 1.0.2 + + local-pkg@0.5.1: + dependencies: + mlly: 1.7.3 + pkg-types: 1.3.0 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash-es@4.17.21: {} + + lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21): + dependencies: + '@types/lodash-es': 4.17.12 + lodash: 4.17.21 + lodash-es: 4.17.21 + + lodash.camelcase@4.3.0: {} + + lodash.clonedeep@4.5.0: {} + + lodash.debounce@4.0.8: {} + + lodash.foreach@4.5.0: {} + + lodash.get@4.4.2: {} + + lodash.isequal@4.5.0: {} + + lodash.isplainobject@4.0.6: {} + + lodash.kebabcase@4.1.1: {} + + lodash.merge@4.6.2: {} + + lodash.mergewith@4.6.2: {} + + lodash.snakecase@4.1.1: {} + + lodash.startcase@4.4.0: {} + + lodash.throttle@4.1.1: {} + + lodash.toarray@4.4.0: {} + + lodash.truncate@4.4.2: {} + + lodash.uniq@4.5.0: {} + + lodash.upperfirst@4.3.1: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-symbols@6.0.0: + dependencies: + chalk: 5.4.1 + is-unicode-supported: 1.3.0 + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lru-cache@11.0.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.25.9: + dependencies: + sourcemap-codec: 1.4.8 + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + optional: true + + make-iterator@1.0.1: + dependencies: + kind-of: 6.0.3 + + map-cache@0.2.2: {} + + map-visit@1.0.0: + dependencies: + object-visit: 1.0.1 + + math-intrinsics@1.1.0: {} + + mathml-tag-names@2.1.3: {} + + mdn-data@2.0.14: {} + + mdn-data@2.12.2: {} + + memoize-one@6.0.0: {} + + meow@12.1.1: {} + + meow@13.2.0: {} + + merge-options@1.0.1: + dependencies: + is-plain-obj: 1.1.0 + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@3.1.0: + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + braces: 2.3.2 + define-property: 1.0.0 + extend-shallow: 2.0.1 + extglob: 2.0.4 + fragment-cache: 0.2.1 + kind-of: 5.1.0 + nanomatch: 1.2.13 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-match@1.0.2: + dependencies: + wildcard: 1.1.2 + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: + optional: true + + mimic-fn@2.1.0: {} + + mimic-fn@4.0.0: {} + + mimic-function@5.0.1: {} + + minimatch@10.0.1: + dependencies: + brace-expansion: 2.0.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minimist@1.2.8: {} + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minipass@7.1.2: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mitt@3.0.1: {} + + mixin-deep@1.3.2: + dependencies: + for-in: 1.0.2 + is-extendable: 1.0.1 + + mkdirp@1.0.4: {} + + mkdirp@3.0.1: {} + + mlly@1.7.3: + dependencies: + acorn: 8.14.0 + pathe: 1.1.2 + pkg-types: 1.3.0 + ufo: 1.5.4 + + mockjs@1.1.0: + dependencies: + commander: 13.0.0 + + monaco-editor@0.52.2: {} + + mrmime@2.0.0: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + mute-stream@1.0.0: {} + + mute-stream@2.0.0: {} + + namespace-emitter@2.0.1: {} + + nanoid@3.3.8: {} + + nanomatch@1.2.13: + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + define-property: 2.0.2 + extend-shallow: 3.0.2 + fragment-cache: 0.2.1 + is-windows: 1.0.2 + kind-of: 6.0.3 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + + natural-compare@1.4.0: {} + + needle@3.3.1: + dependencies: + iconv-lite: 0.6.3 + sax: 1.4.1 + optional: true + + neo-async@2.6.2: {} + + next-tick@1.1.0: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-fetch-native@1.6.4: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-plop@0.32.0: + dependencies: + '@types/inquirer': 9.0.7 + change-case: 4.1.2 + del: 7.1.0 + globby: 13.2.2 + handlebars: 4.7.8 + inquirer: 9.3.7 + isbinaryfile: 5.0.4 + lodash.get: 4.4.2 + lower-case: 2.0.2 + mkdirp: 3.0.1 + resolve: 1.22.10 + title-case: 3.0.3 + upper-case: 2.0.2 + + node-releases@2.0.19: {} + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + normalize-wheel-es@1.2.0: {} + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + nprogress@0.2.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nypm@0.3.12: + dependencies: + citty: 0.1.6 + consola: 3.3.3 + execa: 8.0.1 + pathe: 1.1.2 + pkg-types: 1.3.0 + ufo: 1.5.4 + + object-assign@4.1.1: {} + + object-copy@0.1.0: + dependencies: + copy-descriptor: 0.1.1 + define-property: 0.2.5 + kind-of: 3.2.2 + + object-inspect@1.13.3: {} + + object-keys@1.1.1: {} + + object-visit@1.0.1: + dependencies: + isobject: 3.0.1 + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.3 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.defaults@1.1.0: + dependencies: + array-each: 1.0.1 + array-slice: 1.1.0 + for-own: 1.0.0 + isobject: 3.0.1 + + object.map@1.0.1: + dependencies: + for-own: 1.0.0 + make-iterator: 1.0.1 + + object.pick@1.3.0: + dependencies: + isobject: 3.0.1 + + ofetch@1.4.1: + dependencies: + destr: 2.0.3 + node-fetch-native: 1.6.4 + ufo: 1.5.4 + + ohash@1.1.4: {} + + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + ora@8.1.1: + dependencies: + chalk: 5.4.1 + cli-cursor: 5.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + os-tmpdir@1.0.2: {} + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.2.7 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.1.1 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-map@5.5.0: + dependencies: + aggregate-error: 4.0.1 + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + package-manager-detector@0.2.8: {} + + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-filepath@1.0.2: + dependencies: + is-absolute: 1.0.0 + map-cache: 0.2.2 + path-root: 0.1.1 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.26.2 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-node-version@1.0.1: {} + + parse-passwd@1.0.0: {} + + parseurl@1.3.3: {} + + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + pascalcase@0.1.1: {} + + path-browserify@1.0.1: {} + + path-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + path-exists@4.0.0: {} + + path-exists@5.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-root-regex@0.1.2: {} + + path-root@0.1.1: + dependencies: + path-root-regex: 0.1.2 + + path-scurry@2.0.0: + dependencies: + lru-cache: 11.0.2 + minipass: 7.1.2 + + path-to-regexp@6.3.0: {} + + path-type@4.0.0: {} + + path-type@5.0.0: {} + + pathe@0.2.0: {} + + pathe@1.1.2: {} + + pathe@2.0.0: {} + + perfect-debounce@1.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + pidtree@0.6.0: {} + + pify@4.0.1: + optional: true + + pinia-plugin-persistedstate@4.2.0(pinia@2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)))(rollup@4.30.1): + dependencies: + '@nuxt/kit': 3.15.1(rollup@4.30.1) + deep-pick-omit: 1.2.1 + defu: 6.1.4 + destr: 2.0.3 + optionalDependencies: + pinia: 2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)) + transitivePeerDependencies: + - magicast + - rollup + - supports-color + + pinia@2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.13(typescript@5.7.3) + vue-demi: 0.14.10(vue@3.5.13(typescript@5.7.3)) + optionalDependencies: + typescript: 5.7.3 + transitivePeerDependencies: + - '@vue/composition-api' + + pkg-types@1.3.0: + dependencies: + confbox: 0.1.8 + mlly: 1.7.3 + pathe: 1.1.2 + + plop@4.0.1: + dependencies: + '@types/liftoff': 4.0.3 + chalk: 5.4.1 + interpret: 3.1.1 + liftoff: 4.0.0 + minimist: 1.2.8 + node-plop: 0.32.0 + ora: 8.1.1 + v8flags: 4.0.1 + + pngjs@5.0.0: {} + + posix-character-classes@0.1.1: {} + + possible-typed-array-names@1.0.0: {} + + postcss-html@1.7.0: + dependencies: + htmlparser2: 8.0.2 + js-tokens: 9.0.1 + postcss: 8.4.49 + postcss-safe-parser: 6.0.0(postcss@8.4.49) + + postcss-less@6.0.0(postcss@8.4.49): + dependencies: + postcss: 8.4.49 + + postcss-prefix-selector@1.16.1(postcss@5.2.18): + dependencies: + postcss: 5.2.18 + + postcss-resolve-nested-selector@0.1.6: {} + + postcss-safe-parser@6.0.0(postcss@8.4.49): + dependencies: + postcss: 8.4.49 + + postcss-safe-parser@7.0.1(postcss@8.4.49): + dependencies: + postcss: 8.4.49 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@7.0.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-sorting@8.0.2(postcss@8.4.49): + dependencies: + postcss: 8.4.49 + + postcss-value-parser@4.2.0: {} + + postcss@5.2.18: + dependencies: + chalk: 1.1.3 + js-base64: 2.6.4 + source-map: 0.5.7 + supports-color: 3.2.3 + + postcss@8.4.49: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + posthtml-parser@0.2.1: + dependencies: + htmlparser2: 3.10.1 + isobject: 2.1.0 + + posthtml-rename-id@1.0.12: + dependencies: + escape-string-regexp: 1.0.5 + + posthtml-render@1.4.0: {} + + posthtml-svg-mode@1.0.3: + dependencies: + merge-options: 1.0.1 + posthtml: 0.9.2 + posthtml-parser: 0.2.1 + posthtml-render: 1.4.0 + + posthtml@0.9.2: + dependencies: + posthtml-parser: 0.2.1 + posthtml-render: 1.4.0 + + preact@10.25.4: {} + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier@3.4.2: {} + + prismjs@1.29.0: {} + + progress@2.0.3: {} + + proxy-from-env@1.1.0: {} + + prr@1.0.1: + optional: true + + punycode@1.4.1: {} + + punycode@2.3.1: {} + + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + + qs@6.13.1: + dependencies: + side-channel: 1.1.0 + + query-string@4.3.4: + dependencies: + object-assign: 4.1.1 + strict-uri-encode: 1.1.0 + + queue-microtask@1.2.3: {} + + rc9@2.1.2: + dependencies: + defu: 6.1.4 + destr: 2.0.3 + + rd@2.0.1: + dependencies: + '@types/node': 10.17.60 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.0.2: {} + + rechoir@0.8.0: + dependencies: + resolve: 1.22.10 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.7 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regenerate-unicode-properties@10.2.0: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regenerator-runtime@0.14.1: {} + + regenerator-transform@0.15.2: + dependencies: + '@babel/runtime': 7.26.0 + + regex-not@1.0.2: + dependencies: + extend-shallow: 3.0.2 + safe-regex: 1.1.0 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + regexpu-core@6.2.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.0 + regjsgen: 0.8.0 + regjsparser: 0.12.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.0 + + regjsgen@0.8.0: {} + + regjsparser@0.12.0: + dependencies: + jsesc: 3.0.2 + + repeat-element@1.1.4: {} + + repeat-string@1.6.1: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + require-main-filename@2.0.0: {} + + resolve-dir@1.0.1: + dependencies: + expand-tilde: 2.0.2 + global-modules: 1.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve-url@0.2.1: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + ret@0.1.15: {} + + reusify@1.0.4: {} + + rfdc@1.4.1: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rimraf@6.0.1: + dependencies: + glob: 11.0.0 + package-json-from-dist: 1.0.1 + + rollup-plugin-purge-icons@0.10.0: + dependencies: + '@purge-icons/core': 0.10.0 + '@purge-icons/generated': 0.10.0 + transitivePeerDependencies: + - encoding + - supports-color + + rollup-plugin-visualizer@5.14.0(rollup@4.30.1): + dependencies: + open: 8.4.2 + picomatch: 4.0.2 + source-map: 0.7.4 + yargs: 17.7.2 + optionalDependencies: + rollup: 4.30.1 + + rollup@2.79.2: + optionalDependencies: + fsevents: 2.3.3 + + rollup@4.30.1: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.30.1 + '@rollup/rollup-android-arm64': 4.30.1 + '@rollup/rollup-darwin-arm64': 4.30.1 + '@rollup/rollup-darwin-x64': 4.30.1 + '@rollup/rollup-freebsd-arm64': 4.30.1 + '@rollup/rollup-freebsd-x64': 4.30.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.30.1 + '@rollup/rollup-linux-arm-musleabihf': 4.30.1 + '@rollup/rollup-linux-arm64-gnu': 4.30.1 + '@rollup/rollup-linux-arm64-musl': 4.30.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.30.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.30.1 + '@rollup/rollup-linux-riscv64-gnu': 4.30.1 + '@rollup/rollup-linux-s390x-gnu': 4.30.1 + '@rollup/rollup-linux-x64-gnu': 4.30.1 + '@rollup/rollup-linux-x64-musl': 4.30.1 + '@rollup/rollup-win32-arm64-msvc': 4.30.1 + '@rollup/rollup-win32-ia32-msvc': 4.30.1 + '@rollup/rollup-win32-x64-msvc': 4.30.1 + fsevents: 2.3.3 + + run-async@3.0.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@7.8.1: + dependencies: + tslib: 2.8.1 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.3 + get-intrinsic: 1.2.7 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-regex@1.1.0: + dependencies: + ret: 0.1.15 + + safer-buffer@2.1.2: {} + + sax@1.4.1: + optional: true + + scroll-into-view-if-needed@2.2.31: + dependencies: + compute-scroll-into-view: 1.0.20 + + scule@1.3.0: {} + + semver@5.7.2: + optional: true + + semver@6.3.1: {} + + semver@7.6.3: {} + + sentence-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + upper-case-first: 2.0.2 + + set-blocking@2.0.0: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.7 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + + set-value@2.0.1: + dependencies: + extend-shallow: 2.0.1 + is-extendable: 0.1.1 + is-plain-object: 2.0.4 + split-string: 3.1.0 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.3 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.7 + object-inspect: 1.13.3 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.7 + object-inspect: 1.13.3 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.3 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + sirv@3.0.0: + dependencies: + '@polka/url': 1.0.0-next.28 + mrmime: 2.0.0 + totalist: 3.0.1 + + slash@3.0.0: {} + + slash@4.0.0: {} + + slash@5.1.0: {} + + slate-history@0.66.0(slate@0.72.8): + dependencies: + is-plain-object: 5.0.0 + slate: 0.72.8 + + slate@0.72.8: + dependencies: + immer: 9.0.21 + is-plain-object: 5.0.0 + tiny-warning: 1.0.3 + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + snabbdom@3.6.2: {} + + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + snapdragon-node@2.1.1: + dependencies: + define-property: 1.0.0 + isobject: 3.0.1 + snapdragon-util: 3.0.1 + + snapdragon-util@3.0.1: + dependencies: + kind-of: 3.2.2 + + snapdragon@0.8.2: + dependencies: + base: 0.11.2 + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + map-cache: 0.2.2 + source-map: 0.5.7 + source-map-resolve: 0.5.3 + use: 3.1.1 + transitivePeerDependencies: + - supports-color + + source-map-js@1.2.1: {} + + source-map-resolve@0.5.3: + dependencies: + atob: 2.1.2 + decode-uri-component: 0.2.2 + resolve-url: 0.2.1 + source-map-url: 0.4.1 + urix: 0.1.0 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-url@0.4.1: {} + + source-map@0.5.7: {} + + source-map@0.6.1: {} + + source-map@0.7.4: {} + + sourcemap-codec@1.4.8: {} + + split-string@3.1.0: + dependencies: + extend-shallow: 3.0.2 + + split2@4.2.0: {} + + ssr-window@3.0.0: {} + + stable@0.1.8: {} + + static-extend@0.1.2: + dependencies: + define-property: 0.2.5 + object-copy: 0.1.0 + + statuses@1.5.0: {} + + std-env@3.8.0: {} + + stdin-discarder@0.2.2: {} + + strict-uri-encode@1.1.0: {} + + string-argv@0.3.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.3 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-object-atoms: 1.0.0 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.3 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@3.0.1: + dependencies: + ansi-regex: 2.1.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-final-newline@3.0.0: {} + + strip-json-comments@3.1.1: {} + + strip-literal@2.1.1: + dependencies: + js-tokens: 9.0.1 + + stylelint-config-html@1.1.0(postcss-html@1.7.0)(stylelint@16.12.0(typescript@5.7.3)): + dependencies: + postcss-html: 1.7.0 + stylelint: 16.12.0(typescript@5.7.3) + + stylelint-config-recommended@14.0.1(stylelint@16.12.0(typescript@5.7.3)): + dependencies: + stylelint: 16.12.0(typescript@5.7.3) + + stylelint-config-standard@36.0.1(stylelint@16.12.0(typescript@5.7.3)): + dependencies: + stylelint: 16.12.0(typescript@5.7.3) + stylelint-config-recommended: 14.0.1(stylelint@16.12.0(typescript@5.7.3)) + + stylelint-order@6.0.4(stylelint@16.12.0(typescript@5.7.3)): + dependencies: + postcss: 8.4.49 + postcss-sorting: 8.0.2(postcss@8.4.49) + stylelint: 16.12.0(typescript@5.7.3) + + stylelint@16.12.0(typescript@5.7.3): + dependencies: + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/media-query-list-parser': 4.0.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.0.0) + '@dual-bundle/import-meta-resolve': 4.1.0 + balanced-match: 2.0.0 + colord: 2.9.3 + cosmiconfig: 9.0.0(typescript@5.7.3) + css-functions-list: 3.2.3 + css-tree: 3.1.0 + debug: 4.4.0 + fast-glob: 3.3.3 + fastest-levenshtein: 1.0.16 + file-entry-cache: 9.1.0 + global-modules: 2.0.0 + globby: 11.1.0 + globjoin: 0.1.4 + html-tags: 3.3.1 + ignore: 6.0.2 + imurmurhash: 0.1.4 + is-plain-object: 5.0.0 + known-css-properties: 0.35.0 + mathml-tag-names: 2.1.3 + meow: 13.2.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + picocolors: 1.1.1 + postcss: 8.4.49 + postcss-resolve-nested-selector: 0.1.6 + postcss-safe-parser: 7.0.1(postcss@8.4.49) + postcss-selector-parser: 7.0.0 + postcss-value-parser: 4.2.0 + resolve-from: 5.0.0 + string-width: 4.2.3 + supports-hyperlinks: 3.1.0 + svg-tags: 1.0.0 + table: 6.9.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + - typescript + + supports-color@2.0.0: {} + + supports-color@3.2.3: + dependencies: + has-flag: 1.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-hyperlinks@3.1.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svg-baker@1.7.0: + dependencies: + bluebird: 3.7.2 + clone: 2.1.2 + he: 1.2.0 + image-size: 0.5.5 + loader-utils: 1.4.2 + merge-options: 1.0.1 + micromatch: 3.1.0 + postcss: 5.2.18 + postcss-prefix-selector: 1.16.1(postcss@5.2.18) + posthtml-rename-id: 1.0.12 + posthtml-svg-mode: 1.0.3 + query-string: 4.3.4 + traverse: 0.6.10 + transitivePeerDependencies: + - supports-color + + svg-tags@1.0.0: {} + + svgo@2.8.0: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 4.3.0 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.1.1 + stable: 0.1.8 + + synckit@0.9.2: + dependencies: + '@pkgr/core': 0.1.1 + tslib: 2.8.1 + + system-architecture@0.1.0: {} + + systemjs@6.15.1: {} + + table@6.9.0: + dependencies: + ajv: 8.17.1 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + terser@5.37.0: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.14.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + text-extensions@2.4.0: {} + + through@2.3.8: {} + + tiny-warning@1.0.3: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.10: + dependencies: + fdir: 6.4.2(picomatch@4.0.2) + picomatch: 4.0.2 + + title-case@3.0.3: + dependencies: + tslib: 2.8.1 + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + to-object-path@0.3.0: + dependencies: + kind-of: 3.2.2 + + to-regex-range@2.1.1: + dependencies: + is-number: 3.0.0 + repeat-string: 1.6.1 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + to-regex@3.0.2: + dependencies: + define-property: 2.0.2 + extend-shallow: 3.0.2 + regex-not: 1.0.2 + safe-regex: 1.1.0 + + totalist@3.0.1: {} + + tr46@0.0.3: {} + + traverse@0.6.10: + dependencies: + gopd: 1.2.0 + typedarray.prototype.slice: 1.0.5 + which-typed-array: 1.1.18 + + ts-api-utils@2.0.0(typescript@5.7.3): + dependencies: + typescript: 5.7.3 + + tslib@2.3.0: {} + + tslib@2.8.1: {} + + tsx@4.19.2: + dependencies: + esbuild: 0.23.1 + get-tsconfig: 4.8.1 + optionalDependencies: + fsevents: 2.3.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + type-fest@0.21.3: {} + + type@2.7.3: {} + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.3 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.0.0 + reflect.getprototypeof: 1.0.10 + + typedarray.prototype.slice@1.0.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + get-proto: 1.0.1 + math-intrinsics: 1.1.0 + typed-array-buffer: 1.0.3 + typed-array-byte-offset: 1.0.4 + + typescript-eslint@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/parser': 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/utils': 8.19.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.3) + eslint: 9.17.0(jiti@2.4.2) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + typescript@5.7.3: {} + + ufo@1.5.4: {} + + uglify-js@3.19.3: + optional: true + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.3 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + unc-path-regex@0.1.2: {} + + unconfig@0.6.0: + dependencies: + '@antfu/utils': 0.7.10 + defu: 6.1.4 + importx: 0.5.1 + transitivePeerDependencies: + - supports-color + + unctx@2.4.1: + dependencies: + acorn: 8.14.0 + estree-walker: 3.0.3 + magic-string: 0.30.17 + unplugin: 2.1.2 + + undici-types@6.20.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.1.0 + + unicode-match-property-value-ecmascript@2.2.0: {} + + unicode-property-aliases-ecmascript@2.1.0: {} + + unicorn-magic@0.1.0: {} + + unimport@3.14.5(rollup@4.30.1): + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + acorn: 8.14.0 + escape-string-regexp: 5.0.0 + estree-walker: 3.0.3 + fast-glob: 3.3.3 + local-pkg: 0.5.1 + magic-string: 0.30.17 + mlly: 1.7.3 + pathe: 1.1.2 + picomatch: 4.0.2 + pkg-types: 1.3.0 + scule: 1.3.0 + strip-literal: 2.1.1 + unplugin: 1.16.1 + transitivePeerDependencies: + - rollup + + union-value@1.0.1: + dependencies: + arr-union: 3.1.0 + get-value: 2.0.6 + is-extendable: 0.1.1 + set-value: 2.0.1 + + universalify@2.0.1: {} + + unocss@0.65.4(postcss@8.4.49)(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3)): + dependencies: + '@unocss/astro': 0.65.4(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3)) + '@unocss/cli': 0.65.4(rollup@4.30.1) + '@unocss/core': 0.65.4 + '@unocss/postcss': 0.65.4(postcss@8.4.49) + '@unocss/preset-attributify': 0.65.4 + '@unocss/preset-icons': 0.65.4 + '@unocss/preset-mini': 0.65.4 + '@unocss/preset-tagify': 0.65.4 + '@unocss/preset-typography': 0.65.4 + '@unocss/preset-uno': 0.65.4 + '@unocss/preset-web-fonts': 0.65.4 + '@unocss/preset-wind': 0.65.4 + '@unocss/transformer-attributify-jsx': 0.65.4 + '@unocss/transformer-compile-class': 0.65.4 + '@unocss/transformer-directives': 0.65.4 + '@unocss/transformer-variant-group': 0.65.4 + '@unocss/vite': 0.65.4(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3)) + optionalDependencies: + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + transitivePeerDependencies: + - postcss + - rollup + - supports-color + - vue + + unpipe@1.0.0: {} + + unplugin@1.16.1: + dependencies: + acorn: 8.14.0 + webpack-virtual-modules: 0.6.2 + + unplugin@2.1.2: + dependencies: + acorn: 8.14.0 + webpack-virtual-modules: 0.6.2 + + unset-value@1.0.0: + dependencies: + has-value: 0.3.1 + isobject: 3.0.1 + + untyped@1.5.2: + dependencies: + '@babel/core': 7.26.0 + '@babel/standalone': 7.26.4 + '@babel/types': 7.26.3 + citty: 0.1.6 + defu: 6.1.4 + jiti: 2.4.2 + knitwork: 1.2.0 + scule: 1.3.0 + transitivePeerDependencies: + - supports-color + + update-browserslist-db@1.1.2(browserslist@4.24.4): + dependencies: + browserslist: 4.24.4 + escalade: 3.2.0 + picocolors: 1.1.1 + + upper-case-first@2.0.2: + dependencies: + tslib: 2.8.1 + + upper-case@2.0.2: + dependencies: + tslib: 2.8.1 + + uqr@0.1.2: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + urix@0.1.0: {} + + url@0.11.4: + dependencies: + punycode: 1.4.1 + qs: 6.13.1 + + use@3.1.1: {} + + util-deprecate@1.0.2: {} + + utils-merge@1.0.1: {} + + v8flags@4.0.1: {} + + vary@1.1.2: {} + + vite-plugin-ejs@1.7.0(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): + dependencies: + ejs: 3.1.10 + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + + vite-plugin-eslint@1.8.1(eslint@9.17.0(jiti@2.4.2))(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): + dependencies: + '@rollup/pluginutils': 4.2.1 + '@types/eslint': 8.56.12 + eslint: 9.17.0(jiti@2.4.2) + rollup: 2.79.2 + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + + vite-plugin-mock@2.9.6(mockjs@1.1.0)(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): + dependencies: + '@rollup/plugin-node-resolve': 13.3.0(rollup@4.30.1) + '@types/mockjs': 1.0.10 + chalk: 4.1.2 + chokidar: 3.6.0 + connect: 3.7.0 + debug: 4.4.0 + esbuild: 0.11.3 + fast-glob: 3.3.3 + mockjs: 1.1.0 + path-to-regexp: 6.3.0 + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + transitivePeerDependencies: + - rollup + - supports-color + + vite-plugin-progress@0.0.7(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): + dependencies: + picocolors: 1.1.1 + progress: 2.0.3 + rd: 2.0.1 + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + + vite-plugin-purge-icons@0.10.0(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): + dependencies: + '@purge-icons/core': 0.10.0 + '@purge-icons/generated': 0.10.0 + rollup-plugin-purge-icons: 0.10.0 + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + transitivePeerDependencies: + - encoding + - supports-color + + vite-plugin-style-import@2.0.0(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): + dependencies: + '@rollup/pluginutils': 4.2.1 + change-case: 4.1.2 + console: 0.7.2 + es-module-lexer: 0.9.3 + fs-extra: 10.1.0 + magic-string: 0.25.9 + pathe: 0.2.0 + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + + vite-plugin-svg-icons@2.0.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): + dependencies: + '@types/svgo': 2.6.4 + cors: 2.8.5 + debug: 4.4.0 + etag: 1.8.1 + fs-extra: 10.1.0 + pathe: 0.2.0 + svg-baker: 1.7.0 + svgo: 2.8.0 + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + transitivePeerDependencies: + - supports-color + + vite-plugin-url-copy@1.1.4(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): + dependencies: + clipboardy: 4.0.0 + consola: 3.3.3 + uqr: 0.1.2 + vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + + vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0): + dependencies: + esbuild: 0.24.2 + postcss: 8.4.49 + rollup: 4.30.1 + optionalDependencies: + '@types/node': 22.10.5 + fsevents: 2.3.3 + jiti: 2.4.2 + less: 4.2.1 + terser: 5.37.0 + tsx: 4.19.2 + yaml: 2.7.0 + + vscode-uri@3.0.8: {} + + vue-demi@0.14.10(vue@3.5.13(typescript@5.7.3)): + dependencies: + vue: 3.5.13(typescript@5.7.3) + + vue-draggable-plus@0.6.0(@types/sortablejs@1.15.8): + dependencies: + '@types/sortablejs': 1.15.8 + + vue-eslint-parser@9.4.3(eslint@9.17.0(jiti@2.4.2)): + dependencies: + debug: 4.4.0 + eslint: 9.17.0(jiti@2.4.2) + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + lodash: 4.17.21 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + + vue-flow-layout@0.1.1(vue@3.5.13(typescript@5.7.3)): + dependencies: + vue: 3.5.13(typescript@5.7.3) + + vue-i18n@11.0.1(vue@3.5.13(typescript@5.7.3)): + dependencies: + '@intlify/core-base': 11.0.1 + '@intlify/shared': 11.0.1 + '@vue/devtools-api': 6.6.4 + vue: 3.5.13(typescript@5.7.3) + + vue-json-pretty@2.4.0(vue@3.5.13(typescript@5.7.3)): + dependencies: + vue: 3.5.13(typescript@5.7.3) + + vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.13(typescript@5.7.3) + + vue-tsc@2.2.0(typescript@5.7.3): + dependencies: + '@volar/typescript': 2.4.11 + '@vue/language-core': 2.2.0(typescript@5.7.3) + typescript: 5.7.3 + + vue-types@5.1.3(vue@3.5.13(typescript@5.7.3)): + dependencies: + is-plain-object: 5.0.0 + optionalDependencies: + vue: 3.5.13(typescript@5.7.3) + + vue@3.5.13(typescript@5.7.3): + dependencies: + '@vue/compiler-dom': 3.5.13 + '@vue/compiler-sfc': 3.5.13 + '@vue/runtime-dom': 3.5.13 + '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.7.3)) + '@vue/shared': 3.5.13 + optionalDependencies: + typescript: 5.7.3 + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + webidl-conversions@3.0.1: {} + + webpack-virtual-modules@0.6.2: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.1 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.3 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.0 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.0 + is-regex: 1.2.1 + is-weakref: 1.1.0 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.18 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-module@2.0.1: {} + + which-typed-array@1.1.18: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.3 + for-each: 0.3.3 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wildcard@1.1.2: {} + + word-wrap@1.2.5: {} + + wordwrap@1.0.0: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + xgplayer-subtitles@3.0.20(core-js@3.40.0): + dependencies: + core-js: 3.40.0 + eventemitter3: 4.0.7 + + xgplayer@3.0.20(core-js@3.40.0): + dependencies: + core-js: 3.40.0 + danmu.js: 1.1.13 + delegate: 3.2.0 + downloadjs: 1.4.7 + eventemitter3: 4.0.7 + xgplayer-subtitles: 3.0.20(core-js@3.40.0) + + xml-name-validator@4.0.0: {} + + y18n@4.0.3: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yaml-eslint-parser@1.2.3: + dependencies: + eslint-visitor-keys: 3.4.3 + lodash: 4.17.21 + yaml: 2.7.0 + + yaml@2.6.1: {} + + yaml@2.7.0: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs-parser@21.1.1: {} + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + yocto-queue@1.1.1: {} + + yoctocolors-cjs@2.1.2: {} + + zrender@5.6.1: + dependencies: + tslib: 2.3.0 diff --git a/promo-ui2/postcss.config.cjs b/promo-ui2/postcss.config.cjs new file mode 100644 index 0000000..961986e --- /dev/null +++ b/promo-ui2/postcss.config.cjs @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + autoprefixer: {} + } +} diff --git a/promo-ui2/prettier.config.cjs b/promo-ui2/prettier.config.cjs new file mode 100644 index 0000000..b46239f --- /dev/null +++ b/promo-ui2/prettier.config.cjs @@ -0,0 +1,19 @@ +module.exports = { + printWidth: 100, + tabWidth: 2, + useTabs: false, + semi: false, + vueIndentScriptAndStyle: false, + singleQuote: true, + quoteProps: 'as-needed', + bracketSpacing: true, + trailingComma: 'none', + jsxSingleQuote: false, + arrowParens: 'always', + insertPragma: false, + requirePragma: false, + proseWrap: 'never', + htmlWhitespaceSensitivity: 'strict', + endOfLine: 'auto', + rangeStart: 0 +} diff --git a/promo-ui2/public/favicon.ico b/promo-ui2/public/favicon.ico new file mode 100644 index 0000000..5a7de08 Binary files /dev/null and b/promo-ui2/public/favicon.ico differ diff --git a/promo-ui2/public/logo.png b/promo-ui2/public/logo.png new file mode 100644 index 0000000..b76c71b Binary files /dev/null and b/promo-ui2/public/logo.png differ diff --git a/promo-ui2/scripts/icon.ts b/promo-ui2/scripts/icon.ts new file mode 100644 index 0000000..724342e --- /dev/null +++ b/promo-ui2/scripts/icon.ts @@ -0,0 +1,73 @@ +import path from 'path' +import fs from 'fs-extra' +import inquirer from 'inquirer' +import chalk from 'chalk' +import pkg from '../package.json' +import { ICON_PREFIX } from '../src/constants' + +interface Icon { + name: string + prefix: string + icons: string[] +} + +async function generateIcon() { + const dir = path.resolve(process.cwd(), 'node_modules/@iconify/json') + + const raw = await fs.readJSON(path.join(dir, 'collections.json')) + + const collections = Object.entries(raw).map(([id, v]) => ({ + ...(v as any), + id + })) + + const choices = collections.map((item) => ({ key: item.id, value: item.id, name: item.name })) + + inquirer + .prompt([ + // { + // type: 'list', + // name: 'useType', + // choices: [ + // { key: 'local', value: 'local', name: 'Local' }, + // { key: 'onLine', value: 'onLine', name: 'OnLine' } + // ], + // message: 'How to use icons?' + // }, + { + type: 'list', + name: 'iconSet', + choices: choices, + message: 'Select the icon set that needs to be generated?' + } + ]) + // ↓命令行问答的答案 + .then(async (answers) => { + const { iconSet } = answers + // const isOnLine = useType === 'onLine' + const outputDir = path.resolve(process.cwd(), 'src/components/IconPicker/src/data') + fs.ensureDir(outputDir) + const genCollections = collections.filter((item) => [iconSet].includes(item.id)) + const prefixSet: string[] = [] + for (const info of genCollections) { + const data = await fs.readJSON(path.join(dir, 'json', `${info.id}.json`)) + if (data) { + const { prefix } = data + const prefixName = `${ICON_PREFIX}${prefix}` + const icons = Object.keys(data.icons).map((item) => `${prefixName}:${item}`) + + await fs.writeFileSync( + path.join('src/components/IconPicker/src/data', `icons.${prefix}.ts`), + `export default ${JSON.stringify({ name: info.name, prefix: prefixName, icons })}` + ) + // ↓分类处理完成,push类型名称 + prefixSet.push(prefix) + } + } + console.log( + `✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]` + ) + }) +} + +generateIcon() diff --git a/promo-ui2/src/App.vue b/promo-ui2/src/App.vue new file mode 100644 index 0000000..9ea48b6 --- /dev/null +++ b/promo-ui2/src/App.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/promo-ui2/src/api/common/index.ts b/promo-ui2/src/api/common/index.ts new file mode 100644 index 0000000..b8c986c --- /dev/null +++ b/promo-ui2/src/api/common/index.ts @@ -0,0 +1,11 @@ +import request from '@/axios' + +// 获取所有字典 +export const getDictApi = () => { + return request.get({ url: '/mock/dict/list' }) +} + +// 模拟获取某个字典 +export const getDictOneApi = async () => { + return request.get({ url: '/mock/dict/one' }) +} diff --git a/promo-ui2/src/api/dashboard/analysis/index.ts b/promo-ui2/src/api/dashboard/analysis/index.ts new file mode 100644 index 0000000..f559dc9 --- /dev/null +++ b/promo-ui2/src/api/dashboard/analysis/index.ts @@ -0,0 +1,23 @@ +import request from '@/axios' +import type { + AnalysisTotalTypes, + UserAccessSource, + WeeklyUserActivity, + MonthlySales +} from './types' + +export const getCountApi = (): Promise> => { + return request.get({ url: '/mock/analysis/total' }) +} + +export const getUserAccessSourceApi = (): Promise> => { + return request.get({ url: '/mock/analysis/userAccessSource' }) +} + +export const getWeeklyUserActivityApi = (): Promise> => { + return request.get({ url: '/mock/analysis/weeklyUserActivity' }) +} + +export const getMonthlySalesApi = (): Promise> => { + return request.get({ url: '/mock/analysis/monthlySales' }) +} diff --git a/promo-ui2/src/api/dashboard/analysis/types.ts b/promo-ui2/src/api/dashboard/analysis/types.ts new file mode 100644 index 0000000..4a239f5 --- /dev/null +++ b/promo-ui2/src/api/dashboard/analysis/types.ts @@ -0,0 +1,22 @@ +export type AnalysisTotalTypes = { + users: number + messages: number + moneys: number + shoppings: number +} + +export type UserAccessSource = { + value: number + name: string +} + +export type WeeklyUserActivity = { + value: number + name: string +} + +export type MonthlySales = { + name: string + estimate: number + actual: number +} diff --git a/promo-ui2/src/api/dashboard/workplace/index.ts b/promo-ui2/src/api/dashboard/workplace/index.ts new file mode 100644 index 0000000..062f351 --- /dev/null +++ b/promo-ui2/src/api/dashboard/workplace/index.ts @@ -0,0 +1,22 @@ +import request from '@/axios' +import type { WorkplaceTotal, Project, Dynamic, Team, RadarData } from './types' + +export const getCountApi = (): Promise> => { + return request.get({ url: '/mock/workplace/total' }) +} + +export const getProjectApi = (): Promise> => { + return request.get({ url: '/mock/workplace/project' }) +} + +export const getDynamicApi = (): Promise> => { + return request.get({ url: '/mock/workplace/dynamic' }) +} + +export const getTeamApi = (): Promise> => { + return request.get({ url: '/mock/workplace/team' }) +} + +export const getRadarApi = (): Promise> => { + return request.get({ url: '/mock/workplace/radar' }) +} diff --git a/promo-ui2/src/api/dashboard/workplace/types.ts b/promo-ui2/src/api/dashboard/workplace/types.ts new file mode 100644 index 0000000..da23e74 --- /dev/null +++ b/promo-ui2/src/api/dashboard/workplace/types.ts @@ -0,0 +1,30 @@ +export type WorkplaceTotal = { + project: number + access: number + todo: number +} + +export type Project = { + name: string + icon: string + message: string + personal: string + time: Date | number | string +} + +export type Dynamic = { + keys: string[] + time: Date | number | string +} + +export type Team = { + name: string + icon: string +} + +export type RadarData = { + personal: number + team: number + max: number + name: string +} diff --git a/promo-ui2/src/api/department/index.ts b/promo-ui2/src/api/department/index.ts new file mode 100644 index 0000000..eb583ac --- /dev/null +++ b/promo-ui2/src/api/department/index.ts @@ -0,0 +1,30 @@ +import request from '@/axios' +import { DepartmentListResponse, DepartmentUserParams, DepartmentUserResponse } from './types' + +export const getDepartmentApi = () => { + return request.get({ url: '/mock/department/list' }) +} + +export const getUserByIdApi = (params: DepartmentUserParams) => { + return request.get({ url: '/mock/department/users', params }) +} + +export const deleteUserByIdApi = (ids: string[] | number[]) => { + return request.post({ url: '/mock/department/user/delete', data: { ids } }) +} + +export const saveUserApi = (data: any) => { + return request.post({ url: '/mock/department/user/save', data }) +} + +export const saveDepartmentApi = (data: any) => { + return request.post({ url: '/mock/department/save', data }) +} + +export const deleteDepartmentApi = (ids: string[] | number[]) => { + return request.post({ url: '/mock/department/delete', data: { ids } }) +} + +export const getDepartmentTableApi = (params: any) => { + return request.get({ url: '/mock/department/table/list', params }) +} diff --git a/promo-ui2/src/api/department/types.ts b/promo-ui2/src/api/department/types.ts new file mode 100644 index 0000000..027a628 --- /dev/null +++ b/promo-ui2/src/api/department/types.ts @@ -0,0 +1,32 @@ +export interface DepartmentItem { + id: string + departmentName: string + children?: DepartmentItem[] +} + +export interface DepartmentListResponse { + list: DepartmentItem[] +} + +export interface DepartmentUserParams { + pageSize: number + pageIndex: number + id: string + username?: string + account?: string +} + +export interface DepartmentUserItem { + id: string + username: string + account: string + email: string + createTime: string + role: string + department: DepartmentItem +} + +export interface DepartmentUserResponse { + list: DepartmentUserItem[] + total: number +} diff --git a/promo-ui2/src/api/login/index.ts b/promo-ui2/src/api/login/index.ts new file mode 100644 index 0000000..c52b0a2 --- /dev/null +++ b/promo-ui2/src/api/login/index.ts @@ -0,0 +1,48 @@ +import request from '@/axios' +import type { ApiSmsBodyType, RegisterUserType, UserType } from './types' + +interface RoleParams { + roleName: string +} + +export const loginApi = (data: UserType): Promise> => { + return request.post({ url: '/promo/login', data }) +} + +export const registerApi = (data: RegisterUserType): Promise> => { + return request.post({ url: '/promo/register', data }) +} + +export const getCodeApi = (phoneNumber: string): Promise> => { + return request.post({ + url: '/api/sms/getVerifyCode', + data: { + phoneNumber: phoneNumber, + type: '1' + } + }) +} + +export const loginOutApi = (): Promise => { + return request.get({ url: '/mock/user/loginOut' }) +} + +export const getUserListApi = ({ params }: AxiosConfig) => { + return request.get<{ + code: string + data: { + list: UserType[] + total: number + } + }>({ url: '/mock/user/list', params }) +} + +export const getAdminRoleApi = ( + params: RoleParams +): Promise> => { + return request.get({ url: '/mock/role/list', params }) +} + +export const getTestRoleApi = (params: RoleParams): Promise> => { + return request.get({ url: '/mock/role/list2', params }) +} diff --git a/promo-ui2/src/api/login/types.ts b/promo-ui2/src/api/login/types.ts new file mode 100644 index 0000000..edaaf91 --- /dev/null +++ b/promo-ui2/src/api/login/types.ts @@ -0,0 +1,28 @@ +export interface UserLoginType { + username: string + password: string +} + +export interface UserType { + uid: number + username: string + password: string + role: string + roleId: string + token: string + shareLink: string + shareDomain: string +} + +export interface RegisterUserType { + username: string + password: string + check_password: string + contactInformation: string + parentInvitationCode: string +} + +export interface ApiSmsBodyType { + phoneNumber: string; + type: string; +} \ No newline at end of file diff --git a/promo-ui2/src/api/menu/index.ts b/promo-ui2/src/api/menu/index.ts new file mode 100644 index 0000000..a2b934c --- /dev/null +++ b/promo-ui2/src/api/menu/index.ts @@ -0,0 +1,5 @@ +import request from '@/axios' + +export const getMenuListApi = () => { + return request.get({ url: '/mock/menu/list' }) +} diff --git a/promo-ui2/src/api/request/index.ts b/promo-ui2/src/api/request/index.ts new file mode 100644 index 0000000..207f913 --- /dev/null +++ b/promo-ui2/src/api/request/index.ts @@ -0,0 +1,38 @@ +import request from '@/axios' +import { RequestResponse } from './types' + +export const request1 = () => { + return request.get>({ + url: '/mock/request/1' + }) +} + +export const request2 = () => { + return request.get>({ + url: '/mock/request/2' + }) +} + +export const request3 = () => { + return request.get>({ + url: '/mock/request/3' + }) +} + +export const request4 = () => { + return request.get>({ + url: '/mock/request/4' + }) +} + +export const request5 = () => { + return request.get>({ + url: '/mock/request/5' + }) +} + +export const expired = () => { + return request.get>({ + url: '/mock/request/expired' + }) +} diff --git a/promo-ui2/src/api/request/types.ts b/promo-ui2/src/api/request/types.ts new file mode 100644 index 0000000..9a2063f --- /dev/null +++ b/promo-ui2/src/api/request/types.ts @@ -0,0 +1,3 @@ +export interface RequestResponse { + data: string +} diff --git a/promo-ui2/src/api/role/index.ts b/promo-ui2/src/api/role/index.ts new file mode 100644 index 0000000..ecbf9d9 --- /dev/null +++ b/promo-ui2/src/api/role/index.ts @@ -0,0 +1,5 @@ +import request from '@/axios' + +export const getRoleListApi = () => { + return request.get({ url: '/mock/role/table' }) +} diff --git a/promo-ui2/src/api/subflow/index.ts b/promo-ui2/src/api/subflow/index.ts new file mode 100644 index 0000000..b2996b9 --- /dev/null +++ b/promo-ui2/src/api/subflow/index.ts @@ -0,0 +1,29 @@ +import request from '@/axios' +import { getPurchaseByUserIdRequest, getSubBranchesRequest } from './types' + +export function getSubBranches(req: getSubBranchesRequest) { + return request.get({ + url: '/promo/turnover/getSubBranches', + params: req + }) +} + +export function getPurchaseByUserId(req: getPurchaseByUserIdRequest) { + return request.get({ + url: '/promo/turnover/getPurchaseByUserId/' + req.userId, + params: req + }) +} + +export function getCommissionRateByUserId(userId) { + return request.get({ + url: '/promo/turnover/getCommissionRateByUserId/' + userId + }) +} + +export function updateCommissionRate(data) { + return request.put({ + url: '/promo/turnover/updateCommissionRate', + data: data + }) +} diff --git a/promo-ui2/src/api/subflow/types.ts b/promo-ui2/src/api/subflow/types.ts new file mode 100644 index 0000000..8412662 --- /dev/null +++ b/promo-ui2/src/api/subflow/types.ts @@ -0,0 +1,14 @@ +export interface getPurchaseByUserIdRequest { + pageNum: number + pageSize: number + beginTime: undefined | string + endTime: string | undefined + userId: undefined | string | number +} + +export interface getSubBranchesRequest { + pageNum: number + pageSize: number + userId: undefined | string | number + playerId: undefined | string | number +} diff --git a/promo-ui2/src/api/table/index.ts b/promo-ui2/src/api/table/index.ts new file mode 100644 index 0000000..c870f25 --- /dev/null +++ b/promo-ui2/src/api/table/index.ts @@ -0,0 +1,26 @@ +import request from '@/axios' +import type { TableData } from './types' + +export const getTableListApi = (params: any) => { + return request.get({ url: '/mock/example/list', params }) +} + +export const getCardTableListApi = (params: any) => { + return request.get({ url: '/mock/card/list', params }) +} + +export const getTreeTableListApi = (params: any) => { + return request.get({ url: '/mock/example/treeList', params }) +} + +export const saveTableApi = (data: Partial): Promise => { + return request.post({ url: '/mock/example/save', data }) +} + +export const getTableDetApi = (id: string): Promise> => { + return request.get({ url: '/mock/example/detail', params: { id } }) +} + +export const delTableListApi = (ids: string[] | number[]): Promise => { + return request.post({ url: '/mock/example/delete', data: { ids } }) +} diff --git a/promo-ui2/src/api/table/types.ts b/promo-ui2/src/api/table/types.ts new file mode 100644 index 0000000..876c111 --- /dev/null +++ b/promo-ui2/src/api/table/types.ts @@ -0,0 +1,9 @@ +export type TableData = { + id: string + author: string + title: string + content: string + importance: number + display_time: string + pageviews: number +} diff --git a/promo-ui2/src/api/turnover/index.ts b/promo-ui2/src/api/turnover/index.ts new file mode 100644 index 0000000..241955a --- /dev/null +++ b/promo-ui2/src/api/turnover/index.ts @@ -0,0 +1,27 @@ +import request from '@/axios' + +export function getRealTimeData() { + return request.get({ + url: '/promo/turnover/getRealTimeData' + }) +} + +export function getLast10DaysPromotionData() { + return request.get({ + url: '/promo/turnover/getLast10DaysPromotionData' + }) +} + +export function getAnchorPromotionData(query) { + return request.get({ + url: '/promo/turnover/getAnchorPromotionData', + params: query + }) +} + +export function queryPromoteWelfare(query) { + return request.get({ + url: '/promo/turnover/queryPromoteWelfare', + params: query + }) +} diff --git a/promo-ui2/src/api/user/index.ts b/promo-ui2/src/api/user/index.ts new file mode 100644 index 0000000..111b59d --- /dev/null +++ b/promo-ui2/src/api/user/index.ts @@ -0,0 +1,33 @@ +import request from '@/axios' + +export function getInfo() { + return request.get({ + url: '/api/getInfo', + method: 'get' + }) +} + +export function updateInvitationCode(data: any) { + return request.post({ + url: '/promo/updateInvitationCode', + data + }) +} + +export function getShareDomain() { + return request.get({ + url: '/promo/getsharedomain' + }) +} + +export function getRechargeListApi() { + return request.get({ + url: '/promo/card/list' + }) +} + +export function generateCardListApi(id:number, num:number) { + return request.post({ + url: `/promo/generateCard/${id}/${num}` + }) +} \ No newline at end of file diff --git a/promo-ui2/src/assets/imgs/avatar.jpg b/promo-ui2/src/assets/imgs/avatar.jpg new file mode 100644 index 0000000..d46a70a Binary files /dev/null and b/promo-ui2/src/assets/imgs/avatar.jpg differ diff --git a/promo-ui2/src/assets/imgs/logo.png b/promo-ui2/src/assets/imgs/logo.png new file mode 100644 index 0000000..b76c71b Binary files /dev/null and b/promo-ui2/src/assets/imgs/logo.png differ diff --git a/promo-ui2/src/assets/imgs/personal-center-bg.jpg b/promo-ui2/src/assets/imgs/personal-center-bg.jpg new file mode 100644 index 0000000..b8ce80b Binary files /dev/null and b/promo-ui2/src/assets/imgs/personal-center-bg.jpg differ diff --git a/promo-ui2/src/assets/svgs/403.svg b/promo-ui2/src/assets/svgs/403.svg new file mode 100644 index 0000000..4500596 --- /dev/null +++ b/promo-ui2/src/assets/svgs/403.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/promo-ui2/src/assets/svgs/404.svg b/promo-ui2/src/assets/svgs/404.svg new file mode 100644 index 0000000..5244d8d --- /dev/null +++ b/promo-ui2/src/assets/svgs/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/promo-ui2/src/assets/svgs/500.svg b/promo-ui2/src/assets/svgs/500.svg new file mode 100644 index 0000000..9c02092 --- /dev/null +++ b/promo-ui2/src/assets/svgs/500.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/promo-ui2/src/assets/svgs/icon.svg b/promo-ui2/src/assets/svgs/icon.svg new file mode 100644 index 0000000..7024bec --- /dev/null +++ b/promo-ui2/src/assets/svgs/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/promo-ui2/src/assets/svgs/login-bg.svg b/promo-ui2/src/assets/svgs/login-bg.svg new file mode 100644 index 0000000..bbe06c1 --- /dev/null +++ b/promo-ui2/src/assets/svgs/login-bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/promo-ui2/src/assets/svgs/login-box-bg.svg b/promo-ui2/src/assets/svgs/login-box-bg.svg new file mode 100644 index 0000000..ab10040 --- /dev/null +++ b/promo-ui2/src/assets/svgs/login-box-bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/promo-ui2/src/assets/svgs/message.svg b/promo-ui2/src/assets/svgs/message.svg new file mode 100644 index 0000000..14ca817 --- /dev/null +++ b/promo-ui2/src/assets/svgs/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/promo-ui2/src/assets/svgs/money.svg b/promo-ui2/src/assets/svgs/money.svg new file mode 100644 index 0000000..c1580de --- /dev/null +++ b/promo-ui2/src/assets/svgs/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/promo-ui2/src/assets/svgs/peoples.svg b/promo-ui2/src/assets/svgs/peoples.svg new file mode 100644 index 0000000..aab852e --- /dev/null +++ b/promo-ui2/src/assets/svgs/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/promo-ui2/src/assets/svgs/shopping.svg b/promo-ui2/src/assets/svgs/shopping.svg new file mode 100644 index 0000000..f395bc7 --- /dev/null +++ b/promo-ui2/src/assets/svgs/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/promo-ui2/src/axios/config.ts b/promo-ui2/src/axios/config.ts new file mode 100644 index 0000000..d94529f --- /dev/null +++ b/promo-ui2/src/axios/config.ts @@ -0,0 +1,58 @@ +import { AxiosResponse, InternalAxiosRequestConfig } from './types' +import { ElMessage } from 'element-plus' +import qs from 'qs' +import { SUCCESS_CODE, TRANSFORM_REQUEST_DATA } from '@/constants' +import { useUserStoreWithOut } from '@/store/modules/user' +import { objToFormData } from '@/utils' + +const defaultRequestInterceptors = (config: InternalAxiosRequestConfig) => { + if ( + config.method === 'post' && + config.headers['Content-Type'] === 'application/x-www-form-urlencoded' + ) { + config.data = qs.stringify(config.data) + } else if ( + TRANSFORM_REQUEST_DATA && + config.method === 'post' && + config.headers['Content-Type'] === 'multipart/form-data' && + !(config.data instanceof FormData) + ) { + config.data = objToFormData(config.data) + } + if (config.method === 'get' && config.params) { + let url = config.url as string + url += '?' + const keys = Object.keys(config.params) + for (const key of keys) { + if (config.params[key] !== void 0 && config.params[key] !== null) { + url += `${key}=${encodeURIComponent(config.params[key])}&` + } + } + url = url.substring(0, url.length - 1) + config.params = {} + config.url = url + } + return config +} + +const defaultResponseInterceptors = (response: AxiosResponse) => { + if (response?.config?.responseType === 'blob') { + // 如果是文件流,直接过 + return response + } else if (response.data.code === SUCCESS_CODE || response.data.code === 200) { + return response.data + } else { + if (response?.data?.msg) { + ElMessage.error(response?.data?.msg) + } else { + ElMessage.error(response?.data?.message) + } + + if (response?.data?.code === 401) { + const userStore = useUserStoreWithOut() + userStore.logout() + } + } +} + +export { defaultResponseInterceptors, defaultRequestInterceptors } diff --git a/promo-ui2/src/axios/index.ts b/promo-ui2/src/axios/index.ts new file mode 100644 index 0000000..60b46c0 --- /dev/null +++ b/promo-ui2/src/axios/index.ts @@ -0,0 +1,42 @@ +import service from './service' +import { CONTENT_TYPE } from '@/constants' +import { useUserStoreWithOut } from '@/store/modules/user' + +const request = (option: AxiosConfig) => { + const { url, method, params, data, headers, responseType } = option + + const userStore = useUserStoreWithOut() + return service.request({ + url: url, + method, + params, + data: data, + responseType: responseType, + headers: { + 'Content-Type': CONTENT_TYPE, + [userStore.getTokenKey ?? 'Authorization']: userStore.getToken ?? '', + ...headers + } + }) +} + +export default { + get: (option: AxiosConfig) => { + return request({ method: 'get', ...option }) as Promise> + }, + post: (option: AxiosConfig) => { + return request({ method: 'post', ...option }) as Promise> + }, + delete: (option: AxiosConfig) => { + return request({ method: 'delete', ...option }) as Promise> + }, + put: (option: AxiosConfig) => { + return request({ method: 'put', ...option }) as Promise> + }, + cancelRequest: (url: string | string[]) => { + return service.cancelRequest(url) + }, + cancelAllRequest: () => { + return service.cancelAllRequest() + } +} diff --git a/promo-ui2/src/axios/service.ts b/promo-ui2/src/axios/service.ts new file mode 100644 index 0000000..7b0edc5 --- /dev/null +++ b/promo-ui2/src/axios/service.ts @@ -0,0 +1,76 @@ +import axios, { AxiosError } from 'axios' +import { defaultRequestInterceptors, defaultResponseInterceptors } from './config' +import { AxiosInstance, InternalAxiosRequestConfig, RequestConfig, AxiosResponse } from './types' +import { ElMessage } from 'element-plus' +import { REQUEST_TIMEOUT } from '@/constants' + +export const PATH_URL = import.meta.env.VITE_API_BASE_PATH + +const abortControllerMap: Map = new Map() + +const axiosInstance: AxiosInstance = axios.create({ + timeout: REQUEST_TIMEOUT, + baseURL: PATH_URL +}) + +axiosInstance.interceptors.request.use((res: InternalAxiosRequestConfig) => { + const controller = new AbortController() + const url = res.url || '' + res.signal = controller.signal + abortControllerMap.set( + import.meta.env.VITE_USE_MOCK === 'true' ? url.replace('/mock', '') : url, + controller + ) + return res +}) + +axiosInstance.interceptors.response.use( + (res: AxiosResponse) => { + const url = res.config.url || '' + abortControllerMap.delete(url) + // 这里不能做任何处理,否则后面的 interceptors 拿不到完整的上下文了 + return res + }, + (error: AxiosError) => { + console.log('err: ' + error) // for debug + ElMessage.error(error.message) + return Promise.reject(error) + } +) + +axiosInstance.interceptors.request.use(defaultRequestInterceptors) +axiosInstance.interceptors.response.use(defaultResponseInterceptors) + +const service = { + request: (config: RequestConfig) => { + return new Promise((resolve, reject) => { + if (config.interceptors?.requestInterceptors) { + config = config.interceptors.requestInterceptors(config as any) + } + + axiosInstance + .request(config) + .then((res) => { + resolve(res) + }) + .catch((err: any) => { + reject(err) + }) + }) + }, + cancelRequest: (url: string | string[]) => { + const urlList = Array.isArray(url) ? url : [url] + for (const _url of urlList) { + abortControllerMap.get(_url)?.abort() + abortControllerMap.delete(_url) + } + }, + cancelAllRequest() { + for (const [_, controller] of abortControllerMap) { + controller.abort() + } + abortControllerMap.clear() + } +} + +export default service diff --git a/promo-ui2/src/axios/types/index.ts b/promo-ui2/src/axios/types/index.ts new file mode 100644 index 0000000..b70eb26 --- /dev/null +++ b/promo-ui2/src/axios/types/index.ts @@ -0,0 +1,31 @@ +import type { + InternalAxiosRequestConfig, + AxiosResponse, + AxiosRequestConfig, + AxiosInstance, + AxiosRequestHeaders, + AxiosError +} from 'axios' + +interface RequestInterceptors { + // 请求拦截 + requestInterceptors?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig + requestInterceptorsCatch?: (err: any) => any + // 响应拦截 + responseInterceptors?: (config: T) => T + responseInterceptorsCatch?: (err: any) => any +} + +interface RequestConfig extends AxiosRequestConfig { + interceptors?: RequestInterceptors +} + +export { + AxiosResponse, + RequestInterceptors, + RequestConfig, + AxiosInstance, + InternalAxiosRequestConfig, + AxiosRequestHeaders, + AxiosError +} diff --git a/promo-ui2/src/components/Avatars/index.ts b/promo-ui2/src/components/Avatars/index.ts new file mode 100644 index 0000000..5eaa329 --- /dev/null +++ b/promo-ui2/src/components/Avatars/index.ts @@ -0,0 +1,4 @@ +import Avatars from './src/Avatars.vue' + +export type { AvatarItem } from './src/types' +export { Avatars } diff --git a/promo-ui2/src/components/Avatars/src/Avatars.vue b/promo-ui2/src/components/Avatars/src/Avatars.vue new file mode 100644 index 0000000..1f3619c --- /dev/null +++ b/promo-ui2/src/components/Avatars/src/Avatars.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/promo-ui2/src/components/Avatars/src/types/index.ts b/promo-ui2/src/components/Avatars/src/types/index.ts new file mode 100644 index 0000000..c2554ee --- /dev/null +++ b/promo-ui2/src/components/Avatars/src/types/index.ts @@ -0,0 +1,4 @@ +export interface AvatarItem { + url: string + name?: string +} diff --git a/promo-ui2/src/components/Backtop/index.ts b/promo-ui2/src/components/Backtop/index.ts new file mode 100644 index 0000000..96de88d --- /dev/null +++ b/promo-ui2/src/components/Backtop/index.ts @@ -0,0 +1,3 @@ +import Backtop from './src/Backtop.vue' + +export { Backtop } diff --git a/promo-ui2/src/components/Backtop/src/Backtop.vue b/promo-ui2/src/components/Backtop/src/Backtop.vue new file mode 100644 index 0000000..4619017 --- /dev/null +++ b/promo-ui2/src/components/Backtop/src/Backtop.vue @@ -0,0 +1,15 @@ + + + diff --git a/promo-ui2/src/components/Breadcrumb/index.ts b/promo-ui2/src/components/Breadcrumb/index.ts new file mode 100644 index 0000000..93ffe70 --- /dev/null +++ b/promo-ui2/src/components/Breadcrumb/index.ts @@ -0,0 +1,3 @@ +import Breadcrumb from './src/Breadcrumb.vue' + +export { Breadcrumb } diff --git a/promo-ui2/src/components/Breadcrumb/src/Breadcrumb.vue b/promo-ui2/src/components/Breadcrumb/src/Breadcrumb.vue new file mode 100644 index 0000000..cc79f1b --- /dev/null +++ b/promo-ui2/src/components/Breadcrumb/src/Breadcrumb.vue @@ -0,0 +1,126 @@ + + + diff --git a/promo-ui2/src/components/Breadcrumb/src/helper.ts b/promo-ui2/src/components/Breadcrumb/src/helper.ts new file mode 100644 index 0000000..690cff9 --- /dev/null +++ b/promo-ui2/src/components/Breadcrumb/src/helper.ts @@ -0,0 +1,30 @@ +import { pathResolve } from '@/utils/routerHelper' + +export const filterBreadcrumb = ( + routes: AppRouteRecordRaw[], + parentPath = '' +): AppRouteRecordRaw[] => { + const res: AppRouteRecordRaw[] = [] + + for (const route of routes) { + const meta = route?.meta + if (meta.hidden && !meta.canTo) { + continue + } + + const data: AppRouteRecordRaw = + !meta.alwaysShow && route.children?.length === 1 + ? { ...route.children[0], path: pathResolve(route.path, route.children[0].path) } + : { ...route } + + data.path = pathResolve(parentPath, data.path) + + if (data.children) { + data.children = filterBreadcrumb(data.children, data.path) + } + if (data) { + res.push(data) + } + } + return res +} diff --git a/promo-ui2/src/components/Button/index.ts b/promo-ui2/src/components/Button/index.ts new file mode 100644 index 0000000..6ae43b1 --- /dev/null +++ b/promo-ui2/src/components/Button/index.ts @@ -0,0 +1,3 @@ +import BaseButton from './src/Button.vue' + +export { BaseButton } diff --git a/promo-ui2/src/components/Button/src/Button.vue b/promo-ui2/src/components/Button/src/Button.vue new file mode 100644 index 0000000..709f4a7 --- /dev/null +++ b/promo-ui2/src/components/Button/src/Button.vue @@ -0,0 +1,121 @@ + + + diff --git a/promo-ui2/src/components/CodeEditor/index.ts b/promo-ui2/src/components/CodeEditor/index.ts new file mode 100644 index 0000000..5bf3e9e --- /dev/null +++ b/promo-ui2/src/components/CodeEditor/index.ts @@ -0,0 +1,3 @@ +import CodeEditor from './src/CodeEditor.vue' + +export { CodeEditor } diff --git a/promo-ui2/src/components/CodeEditor/src/CodeEditor.vue b/promo-ui2/src/components/CodeEditor/src/CodeEditor.vue new file mode 100644 index 0000000..3ec2495 --- /dev/null +++ b/promo-ui2/src/components/CodeEditor/src/CodeEditor.vue @@ -0,0 +1,119 @@ + + + diff --git a/promo-ui2/src/components/CodeEditor/src/config/config.ts b/promo-ui2/src/components/CodeEditor/src/config/config.ts new file mode 100644 index 0000000..4fa7b30 --- /dev/null +++ b/promo-ui2/src/components/CodeEditor/src/config/config.ts @@ -0,0 +1,129 @@ +export const languageOptions = [ + { label: 'plaintext', value: 'plaintext' }, + { label: 'abap', value: 'abap' }, + { label: 'apex', value: 'apex' }, + { label: 'azcli', value: 'azcli' }, + { label: 'bat', value: 'bat' }, + { label: 'bicep', value: 'bicep' }, + { label: 'cameligo', value: 'cameligo' }, + { label: 'clojure', value: 'clojure' }, + { label: 'coffeescript', value: 'coffeescript' }, + { label: 'c', value: 'c' }, + { label: 'cpp', value: 'cpp' }, + { label: 'csharp', value: 'csharp' }, + { label: 'csp', value: 'csp' }, + { label: 'css', value: 'css' }, + { label: 'cypher', value: 'cypher' }, + { label: 'dart', value: 'dart' }, + { label: 'dockerfile', value: 'dockerfile' }, + { label: 'ecl', value: 'ecl' }, + { label: 'elixir', value: 'elixir' }, + { label: 'flow9', value: 'flow9' }, + { label: 'fsharp', value: 'fsharp' }, + { label: 'freemarker2', value: 'freemarker2' }, + { + label: 'freemarker2.tag-angle.interpolation-dollar', + value: 'freemarker2.tag-angle.interpolation-dollar' + }, + { + label: 'freemarker2.tag-bracket.interpolation-dollar', + value: 'freemarker2.tag-bracket.interpolation-dollar' + }, + { + label: 'freemarker2.tag-angle.interpolation-bracket', + value: 'freemarker2.tag-angle.interpolation-bracket' + }, + { + label: 'freemarker2.tag-bracket.interpolation-bracket', + value: 'freemarker2.tag-bracket.interpolation-bracket' + }, + { + label: 'freemarker2.tag-auto.interpolation-dollar', + value: 'freemarker2.tag-auto.interpolation-dollar' + }, + { + label: 'freemarker2.tag-auto.interpolation-bracket', + value: 'freemarker2.tag-auto.interpolation-bracket' + }, + { label: 'go', value: 'go' }, + { label: 'graphql', value: 'graphql' }, + { label: 'handlebars', value: 'handlebars' }, + { label: 'hcl', value: 'hcl' }, + { label: 'html', value: 'html' }, + { label: 'ini', value: 'ini' }, + { label: 'java', value: 'java' }, + { label: 'javascript', value: 'javascript' }, + { label: 'julia', value: 'julia' }, + { label: 'kotlin', value: 'kotlin' }, + { label: 'less', value: 'less' }, + { label: 'lexon', value: 'lexon' }, + { label: 'lua', value: 'lua' }, + { label: 'liquid', value: 'liquid' }, + { label: 'm3', value: 'm3' }, + { label: 'markdown', value: 'markdown' }, + { label: 'mdx', value: 'mdx' }, + { label: 'mips', value: 'mips' }, + { label: 'msdax', value: 'msdax' }, + { label: 'mysql', value: 'mysql' }, + { label: 'objective-c', value: 'objective-c' }, + { label: 'pascal', value: 'pascal' }, + { label: 'pascaligo', value: 'pascaligo' }, + { label: 'perl', value: 'perl' }, + { label: 'pgsql', value: 'pgsql' }, + { label: 'php', value: 'php' }, + { label: 'pla', value: 'pla' }, + { label: 'postiats', value: 'postiats' }, + { label: 'powerquery', value: 'powerquery' }, + { label: 'powershell', value: 'powershell' }, + { label: 'proto', value: 'proto' }, + { label: 'pug', value: 'pug' }, + { label: 'python', value: 'python' }, + { label: 'qsharp', value: 'qsharp' }, + { label: 'r', value: 'r' }, + { label: 'razor', value: 'razor' }, + { label: 'redis', value: 'redis' }, + { label: 'redshift', value: 'redshift' }, + { label: 'restructuredtext', value: 'restructuredtext' }, + { label: 'ruby', value: 'ruby' }, + { label: 'rust', value: 'rust' }, + { label: 'sb', value: 'sb' }, + { label: 'scala', value: 'scala' }, + { label: 'scheme', value: 'scheme' }, + { label: 'scss', value: 'scss' }, + { label: 'shell', value: 'shell' }, + { label: 'sol', value: 'sol' }, + { label: 'aes', value: 'aes' }, + { label: 'sparql', value: 'sparql' }, + { label: 'sql', value: 'sql' }, + { label: 'st', value: 'st' }, + { label: 'swift', value: 'swift' }, + { label: 'systemverilog', value: 'systemverilog' }, + { label: 'verilog', value: 'verilog' }, + { label: 'tcl', value: 'tcl' }, + { label: 'twig', value: 'twig' }, + { label: 'typescript', value: 'typescript' }, + { label: 'vb', value: 'vb' }, + { label: 'wgsl', value: 'wgsl' }, + { label: 'xml', value: 'xml' }, + { label: 'yaml', value: 'yaml' }, + { label: 'json', value: 'json' } +] + +export const themeOptions = [ + { + label: 'vs', + value: 'vs' + }, + { + label: 'vs-dark', + value: 'vs-dark' + }, + { + label: 'hc-black', + value: 'hc-black' + }, + { + label: 'hc-light', + value: 'hc-light' + } +] diff --git a/promo-ui2/src/components/Collapse/index.ts b/promo-ui2/src/components/Collapse/index.ts new file mode 100644 index 0000000..73f65a3 --- /dev/null +++ b/promo-ui2/src/components/Collapse/index.ts @@ -0,0 +1,3 @@ +import Collapse from './src/Collapse.vue' + +export { Collapse } diff --git a/promo-ui2/src/components/Collapse/src/Collapse.vue b/promo-ui2/src/components/Collapse/src/Collapse.vue new file mode 100644 index 0000000..542d9d3 --- /dev/null +++ b/promo-ui2/src/components/Collapse/src/Collapse.vue @@ -0,0 +1,34 @@ + + + diff --git a/promo-ui2/src/components/ConfigGlobal/index.ts b/promo-ui2/src/components/ConfigGlobal/index.ts new file mode 100644 index 0000000..eaeb7d0 --- /dev/null +++ b/promo-ui2/src/components/ConfigGlobal/index.ts @@ -0,0 +1,5 @@ +import ConfigGlobal from './src/ConfigGlobal.vue' + +export type { ConfigGlobalTypes } from './src/types' + +export { ConfigGlobal } diff --git a/promo-ui2/src/components/ConfigGlobal/src/ConfigGlobal.vue b/promo-ui2/src/components/ConfigGlobal/src/ConfigGlobal.vue new file mode 100644 index 0000000..5bd90cf --- /dev/null +++ b/promo-ui2/src/components/ConfigGlobal/src/ConfigGlobal.vue @@ -0,0 +1,62 @@ + + + diff --git a/promo-ui2/src/components/ConfigGlobal/src/types/index.ts b/promo-ui2/src/components/ConfigGlobal/src/types/index.ts new file mode 100644 index 0000000..d41e4d3 --- /dev/null +++ b/promo-ui2/src/components/ConfigGlobal/src/types/index.ts @@ -0,0 +1,5 @@ +import { ComponentSize } from 'element-plus' + +export interface ConfigGlobalTypes { + size?: ComponentSize +} diff --git a/promo-ui2/src/components/ContentDetailWrap/index.ts b/promo-ui2/src/components/ContentDetailWrap/index.ts new file mode 100644 index 0000000..1871cac --- /dev/null +++ b/promo-ui2/src/components/ContentDetailWrap/index.ts @@ -0,0 +1,3 @@ +import ContentDetailWrap from './src/ContentDetailWrap.vue' + +export { ContentDetailWrap } diff --git a/promo-ui2/src/components/ContentDetailWrap/src/ContentDetailWrap.vue b/promo-ui2/src/components/ContentDetailWrap/src/ContentDetailWrap.vue new file mode 100644 index 0000000..29b4ff7 --- /dev/null +++ b/promo-ui2/src/components/ContentDetailWrap/src/ContentDetailWrap.vue @@ -0,0 +1,25 @@ + + + diff --git a/promo-ui2/src/components/ContentWrap/index.ts b/promo-ui2/src/components/ContentWrap/index.ts new file mode 100644 index 0000000..8c22cc8 --- /dev/null +++ b/promo-ui2/src/components/ContentWrap/index.ts @@ -0,0 +1,3 @@ +import ContentWrap from './src/ContentWrap.vue' + +export { ContentWrap } diff --git a/promo-ui2/src/components/ContentWrap/src/ContentWrap.vue b/promo-ui2/src/components/ContentWrap/src/ContentWrap.vue new file mode 100644 index 0000000..8557e81 --- /dev/null +++ b/promo-ui2/src/components/ContentWrap/src/ContentWrap.vue @@ -0,0 +1,36 @@ + + + diff --git a/promo-ui2/src/components/ContextMenu/index.ts b/promo-ui2/src/components/ContextMenu/index.ts new file mode 100644 index 0000000..1b5442d --- /dev/null +++ b/promo-ui2/src/components/ContextMenu/index.ts @@ -0,0 +1,12 @@ +import ContextMenu from './src/ContextMenu.vue' +import { ElDropdown } from 'element-plus' +import type { RouteLocationNormalizedLoaded } from 'vue-router' + +export type { ContextMenuSchema } from './src/types' + +export interface ContextMenuExpose { + elDropdownMenuRef: ComponentRef + tagItem: RouteLocationNormalizedLoaded +} + +export { ContextMenu } diff --git a/promo-ui2/src/components/ContextMenu/src/ContextMenu.vue b/promo-ui2/src/components/ContextMenu/src/ContextMenu.vue new file mode 100644 index 0000000..9583037 --- /dev/null +++ b/promo-ui2/src/components/ContextMenu/src/ContextMenu.vue @@ -0,0 +1,72 @@ + + + diff --git a/promo-ui2/src/components/ContextMenu/src/types/index.ts b/promo-ui2/src/components/ContextMenu/src/types/index.ts new file mode 100644 index 0000000..b2be72b --- /dev/null +++ b/promo-ui2/src/components/ContextMenu/src/types/index.ts @@ -0,0 +1,7 @@ +export interface ContextMenuSchema { + disabled?: boolean + divided?: boolean + icon?: string + label: string + command?: (item: ContextMenuSchema) => void +} diff --git a/promo-ui2/src/components/CountTo/index.ts b/promo-ui2/src/components/CountTo/index.ts new file mode 100644 index 0000000..2119f02 --- /dev/null +++ b/promo-ui2/src/components/CountTo/index.ts @@ -0,0 +1,3 @@ +import CountTo from './src/CountTo.vue' + +export { CountTo } diff --git a/promo-ui2/src/components/CountTo/src/CountTo.vue b/promo-ui2/src/components/CountTo/src/CountTo.vue new file mode 100644 index 0000000..cab8ea9 --- /dev/null +++ b/promo-ui2/src/components/CountTo/src/CountTo.vue @@ -0,0 +1,180 @@ + + + diff --git a/promo-ui2/src/components/Descriptions/index.ts b/promo-ui2/src/components/Descriptions/index.ts new file mode 100644 index 0000000..8f9a0f0 --- /dev/null +++ b/promo-ui2/src/components/Descriptions/index.ts @@ -0,0 +1,5 @@ +import Descriptions from './src/Descriptions.vue' + +export type { DescriptionsSchema } from './src/types' + +export { Descriptions } diff --git a/promo-ui2/src/components/Descriptions/src/Descriptions.vue b/promo-ui2/src/components/Descriptions/src/Descriptions.vue new file mode 100644 index 0000000..a4b222b --- /dev/null +++ b/promo-ui2/src/components/Descriptions/src/Descriptions.vue @@ -0,0 +1,197 @@ + + + diff --git a/promo-ui2/src/components/Descriptions/src/types/index.ts b/promo-ui2/src/components/Descriptions/src/types/index.ts new file mode 100644 index 0000000..be55b1c --- /dev/null +++ b/promo-ui2/src/components/Descriptions/src/types/index.ts @@ -0,0 +1,15 @@ +export interface DescriptionsSchema { + span?: number // 占多少分 + field: string // 字段名 + label?: string // label名 + width?: string | number + minWidth?: string | number + align?: 'left' | 'center' | 'right' + labelAlign?: 'left' | 'center' | 'right' + className?: string + labelClassName?: string + slots?: { + default?: (...args: any[]) => JSX.Element | null + label?: (...args: any[]) => JSX.Element | null + } +} diff --git a/promo-ui2/src/components/Dialog/hooks/useResize.ts b/promo-ui2/src/components/Dialog/hooks/useResize.ts new file mode 100644 index 0000000..d52b279 --- /dev/null +++ b/promo-ui2/src/components/Dialog/hooks/useResize.ts @@ -0,0 +1,178 @@ +import { ref } from 'vue' + +export const useResize = (props?: { + minHeightPx?: number + minWidthPx?: number + initHeight?: number + initWidth?: number +}) => { + const { + minHeightPx = 400, + minWidthPx = window.innerWidth / 2, + initHeight = 400, + initWidth = window.innerWidth / 2 + } = props || {} + // 屏幕宽度的 50% 作为最小宽度 + // const minWidthPx = window.innerWidth / 2 + // 固定的最小高度 400px + // const minHeightPx = 400 + // 初始高度限制为 400px + const maxHeight = ref(initHeight + 'px') + // 初始宽度限制为 50% + const minWidth = ref(initWidth + 'px') + const setupDrag = (elDialog: any, el: any) => { + // 获取对话框元素 + // 是否正在调整大小的标志 + let isResizing = false + // 当前调整的方向 + let currentResizeDirection = '' + + // 鼠标移动时的事件处理器,用于检测鼠标位置并设置相应的光标样式 + const handleMouseMove = (e: any) => { + const rect = elDialog.getBoundingClientRect() + // 鼠标相对于对话框左侧的偏移量 + const offsetX = e.clientX - rect.left + // 鼠标相对于对话框顶部的偏移量 + const offsetY = e.clientY - rect.top + const width = elDialog.clientWidth + const height = elDialog.clientHeight + + // 获取对话框的内边距 + const computedStyle = window.getComputedStyle(elDialog) + const paddingLeft = parseFloat(computedStyle.paddingLeft) + const paddingRight = parseFloat(computedStyle.paddingRight) + const paddingBottom = parseFloat(computedStyle.paddingBottom) + const paddingTop = parseFloat(computedStyle.paddingTop) + + // 根据鼠标位置设置相应的光标样式和调整方向 + if (!isResizing) { + if (offsetX < paddingLeft && offsetY > paddingTop && offsetY < height - paddingBottom) { + elDialog.style.cursor = 'ew-resize' // 左右箭头 + currentResizeDirection = 'left' + } else if ( + offsetX > width - paddingRight && + offsetY > paddingTop && + offsetY < height - paddingBottom + ) { + elDialog.style.cursor = 'ew-resize' // 左右箭头 + currentResizeDirection = 'right' + } else if ( + offsetY < paddingTop && + offsetX > paddingLeft && + offsetX < width - paddingRight + ) { + elDialog.style.cursor = 'ns-resize' // 上下箭头 + currentResizeDirection = 'top' + } else if ( + offsetY > height - paddingBottom && + offsetX > paddingLeft && + offsetX < width - paddingRight + ) { + elDialog.style.cursor = 'ns-resize' // 上下箭头 + currentResizeDirection = 'bottom' + } else if (offsetX < paddingLeft && offsetY < paddingTop) { + elDialog.style.cursor = 'nwse-resize' // 左上右下箭头 + currentResizeDirection = 'top-left' + } else if (offsetX > width - paddingRight && offsetY < paddingTop) { + elDialog.style.cursor = 'nesw-resize' // 右上左下箭头 + currentResizeDirection = 'top-right' + } else if (offsetX < paddingLeft && offsetY > height - paddingBottom) { + elDialog.style.cursor = 'nesw-resize' // 右上左下箭头 + currentResizeDirection = 'bottom-left' + } else if (offsetX > width - paddingRight && offsetY > height - paddingBottom) { + elDialog.style.cursor = 'nwse-resize' // 左上右下箭头 + currentResizeDirection = 'bottom-right' + } else { + elDialog.style.cursor = 'default' + currentResizeDirection = '' + } + } + } + + // 鼠标按下时的事件处理器,开始调整对话框大小 + const handleMouseDown = (e) => { + if (currentResizeDirection) { + isResizing = true + + const initialX = e.clientX + const initialY = e.clientY + const initialWidth = elDialog.clientWidth + const initialHeight = el.querySelector('.el-dialog__body').clientHeight + + // 调整大小的事件处理器 + const handleResizing = (e: any) => { + if (!isResizing) return + + let newWidth = initialWidth + let newHeight = initialHeight + + // 根据当前调整方向计算新的宽度和高度 + if (currentResizeDirection.includes('right')) { + newWidth = Math.max(minWidthPx, initialWidth + (e.clientX - initialX) * 2) + minWidth.value = `${newWidth}px` + } + + if (currentResizeDirection.includes('left')) { + newWidth = Math.max(minWidthPx, initialWidth - (e.clientX - initialX) * 2) + minWidth.value = `${newWidth}px` + } + + if (currentResizeDirection.includes('bottom')) { + newHeight = Math.max(minHeightPx, initialHeight + (e.clientY - initialY) * 2 - 20) + maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px` + } + + if (currentResizeDirection.includes('top')) { + newHeight = Math.max(minHeightPx, initialHeight - (e.clientY - initialY) * 2 - 20) + maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px` + } + + if (currentResizeDirection === 'top-left') { + newWidth = Math.max(minWidthPx, initialWidth - (e.clientX - initialX) * 2) + minWidth.value = `${newWidth}px` + newHeight = Math.max(minHeightPx, initialHeight - (e.clientY - initialY) * 2 - 20) + maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px` + } + + if (currentResizeDirection === 'top-right') { + newWidth = Math.max(minWidthPx, initialWidth + (e.clientX - initialX) * 2) + minWidth.value = `${newWidth}px` + newHeight = Math.max(minHeightPx, initialHeight - (e.clientY - initialY) * 2 - 20) + maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px` + } + + if (currentResizeDirection === 'bottom-left') { + newWidth = Math.max(minWidthPx, initialWidth - (e.clientX - initialX) * 2) + minWidth.value = `${newWidth}px` + newHeight = Math.max(minHeightPx, initialHeight + (e.clientY - initialY) * 2 - 20) + maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px` + } + + if (currentResizeDirection === 'bottom-right') { + newWidth = Math.max(minWidthPx, initialWidth + (e.clientX - initialX) * 2) + minWidth.value = `${newWidth}px` + newHeight = Math.max(minHeightPx, initialHeight + (e.clientY - initialY) * 2 - 20) + maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px` + } + } + // 停止调整大小的事件处理器 + const stopResizing = () => { + isResizing = false + document.removeEventListener('mousemove', handleResizing) + document.removeEventListener('mouseup', stopResizing) + } + + document.addEventListener('mousemove', handleResizing) + document.addEventListener('mouseup', stopResizing) + } + } + elDialog.addEventListener('mousemove', handleMouseMove) + elDialog.addEventListener('mousedown', handleMouseDown) + } + + return { + setupDrag, + maxHeight, + minWidth + } +} diff --git a/promo-ui2/src/components/Dialog/index.ts b/promo-ui2/src/components/Dialog/index.ts new file mode 100644 index 0000000..1655dad --- /dev/null +++ b/promo-ui2/src/components/Dialog/index.ts @@ -0,0 +1,3 @@ +import Dialog from './src/Dialog.vue' + +export { Dialog } diff --git a/promo-ui2/src/components/Dialog/src/Dialog.vue b/promo-ui2/src/components/Dialog/src/Dialog.vue new file mode 100644 index 0000000..611d702 --- /dev/null +++ b/promo-ui2/src/components/Dialog/src/Dialog.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/promo-ui2/src/components/Dialog/src/ResizeDialog.vue b/promo-ui2/src/components/Dialog/src/ResizeDialog.vue new file mode 100644 index 0000000..d604e12 --- /dev/null +++ b/promo-ui2/src/components/Dialog/src/ResizeDialog.vue @@ -0,0 +1,73 @@ + + diff --git a/promo-ui2/src/components/Echart/index.ts b/promo-ui2/src/components/Echart/index.ts new file mode 100644 index 0000000..4822092 --- /dev/null +++ b/promo-ui2/src/components/Echart/index.ts @@ -0,0 +1,3 @@ +import Echart from './src/Echart.vue' + +export { Echart } diff --git a/promo-ui2/src/components/Echart/src/Echart.vue b/promo-ui2/src/components/Echart/src/Echart.vue new file mode 100644 index 0000000..58cae29 --- /dev/null +++ b/promo-ui2/src/components/Echart/src/Echart.vue @@ -0,0 +1,115 @@ + + + diff --git a/promo-ui2/src/components/Editor/index.ts b/promo-ui2/src/components/Editor/index.ts new file mode 100644 index 0000000..3fbf0a9 --- /dev/null +++ b/promo-ui2/src/components/Editor/index.ts @@ -0,0 +1,8 @@ +import Editor from './src/Editor.vue' +import { IDomEditor } from '@wangeditor/editor' + +export interface EditorExpose { + getEditorRef: () => Promise +} + +export { Editor } diff --git a/promo-ui2/src/components/Editor/src/Editor.vue b/promo-ui2/src/components/Editor/src/Editor.vue new file mode 100644 index 0000000..0bb3452 --- /dev/null +++ b/promo-ui2/src/components/Editor/src/Editor.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/promo-ui2/src/components/Error/index.ts b/promo-ui2/src/components/Error/index.ts new file mode 100644 index 0000000..a52c6f9 --- /dev/null +++ b/promo-ui2/src/components/Error/index.ts @@ -0,0 +1,3 @@ +import Error from './src/Error.vue' + +export { Error } diff --git a/promo-ui2/src/components/Error/src/Error.vue b/promo-ui2/src/components/Error/src/Error.vue new file mode 100644 index 0000000..b18af60 --- /dev/null +++ b/promo-ui2/src/components/Error/src/Error.vue @@ -0,0 +1,57 @@ + + + diff --git a/promo-ui2/src/components/Footer/index.ts b/promo-ui2/src/components/Footer/index.ts new file mode 100644 index 0000000..bd052e0 --- /dev/null +++ b/promo-ui2/src/components/Footer/index.ts @@ -0,0 +1,3 @@ +import Footer from './src/Footer.vue' + +export { Footer } diff --git a/promo-ui2/src/components/Footer/src/Footer.vue b/promo-ui2/src/components/Footer/src/Footer.vue new file mode 100644 index 0000000..50b7ce7 --- /dev/null +++ b/promo-ui2/src/components/Footer/src/Footer.vue @@ -0,0 +1,22 @@ + + + diff --git a/promo-ui2/src/components/Form/index.ts b/promo-ui2/src/components/Form/index.ts new file mode 100644 index 0000000..6688425 --- /dev/null +++ b/promo-ui2/src/components/Form/index.ts @@ -0,0 +1,48 @@ +import Form from './src/Form.vue' +import type { FormSchema, FormSetProps } from './src/types' +export type { + ComponentNameEnum, + ComponentName, + InputComponentProps, + AutocompleteComponentProps, + InputNumberComponentProps, + SelectOption, + SelectComponentProps, + SelectV2ComponentProps, + CascaderComponentProps, + SwitchComponentProps, + RateComponentProps, + ColorPickerComponentProps, + TransferComponentProps, + RadioOption, + RadioGroupComponentProps, + RadioButtonComponentProps, + CheckboxOption, + CheckboxGroupComponentProps, + DividerComponentProps, + DatePickerComponentProps, + DateTimePickerComponentProps, + TimePickerComponentProps, + TimeSelectComponentProps, + ColProps, + FormSetProps, + FormItemProps, + FormSchema, + FormProps, + PlaceholderModel, + InputPasswordComponentProps, + TreeSelectComponentProps +} from './src/types' + +export interface FormExpose { + setValues: (data: Recordable) => void + setProps: (props: Recordable) => void + delSchema: (field: string) => void + addSchema: (formSchema: FormSchema, index?: number) => void + setSchema: (schemaProps: FormSetProps[]) => void + formModel: Recordable + getComponentExpose: (field: string) => any + getFormItemExpose: (field: string) => any +} + +export { Form } diff --git a/promo-ui2/src/components/Form/src/Form.vue b/promo-ui2/src/components/Form/src/Form.vue new file mode 100644 index 0000000..22de784 --- /dev/null +++ b/promo-ui2/src/components/Form/src/Form.vue @@ -0,0 +1,443 @@ + + + diff --git a/promo-ui2/src/components/Form/src/components/useRenderCheckbox.tsx b/promo-ui2/src/components/Form/src/components/useRenderCheckbox.tsx new file mode 100644 index 0000000..63afb10 --- /dev/null +++ b/promo-ui2/src/components/Form/src/components/useRenderCheckbox.tsx @@ -0,0 +1,31 @@ +import { FormSchema, ComponentNameEnum, CheckboxGroupComponentProps } from '../types' +import { ElCheckbox, ElCheckboxButton } from 'element-plus' +import { defineComponent } from 'vue' + +export const useRenderCheckbox = () => { + const renderCheckboxOptions = (item: FormSchema) => { + // 如果有别名,就取别名 + const componentProps = item?.componentProps as CheckboxGroupComponentProps + const valueAlias = componentProps?.props?.value || 'value' + const labelAlias = componentProps?.props?.label || 'label' + const disabledAlias = componentProps?.props?.disabled || 'disabled' + const Com = ( + item.component === ComponentNameEnum.CHECKBOX_GROUP ? ElCheckbox : ElCheckboxButton + ) as ReturnType + return componentProps?.options?.map((option) => { + const { ...other } = option + return ( + + ) + }) + } + + return { + renderCheckboxOptions + } +} diff --git a/promo-ui2/src/components/Form/src/components/useRenderRadio.tsx b/promo-ui2/src/components/Form/src/components/useRenderRadio.tsx new file mode 100644 index 0000000..fc9842b --- /dev/null +++ b/promo-ui2/src/components/Form/src/components/useRenderRadio.tsx @@ -0,0 +1,31 @@ +import { FormSchema, ComponentNameEnum, RadioGroupComponentProps } from '../types' +import { ElRadio, ElRadioButton } from 'element-plus' +import { defineComponent } from 'vue' + +export const useRenderRadio = () => { + const renderRadioOptions = (item: FormSchema) => { + // 如果有别名,就取别名 + const componentProps = item?.componentProps as RadioGroupComponentProps + const valueAlias = componentProps?.props?.value || 'value' + const labelAlias = componentProps?.props?.label || 'label' + const disabledAlias = componentProps?.props?.disabled || 'disabled' + const Com = ( + item.component === ComponentNameEnum.RADIO_GROUP ? ElRadio : ElRadioButton + ) as ReturnType + return componentProps?.options?.map((option) => { + const { ...other } = option + return ( + + ) + }) + } + + return { + renderRadioOptions + } +} diff --git a/promo-ui2/src/components/Form/src/components/useRenderSelect.tsx b/promo-ui2/src/components/Form/src/components/useRenderSelect.tsx new file mode 100644 index 0000000..46389af --- /dev/null +++ b/promo-ui2/src/components/Form/src/components/useRenderSelect.tsx @@ -0,0 +1,58 @@ +import { ElOption, ElOptionGroup } from 'element-plus' +import { FormSchema, SelectComponentProps, SelectOption } from '../types' + +export const useRenderSelect = () => { + // 渲染 select options + const renderSelectOptions = (item: FormSchema) => { + const componentsProps = item?.componentProps as SelectComponentProps + const optionGroupDefaultSlot = componentsProps?.slots?.optionGroupDefault + // 如果有别名,就取别名 + const labelAlias = componentsProps?.props?.label + const keyAlias = componentsProps?.props?.key + return componentsProps?.options?.map((option) => { + if (option?.options?.length) { + return optionGroupDefaultSlot ? ( + optionGroupDefaultSlot(option) + ) : ( + + {{ + default: () => + option?.options?.map((v) => { + return renderSelectOptionItem(item, v) + }) + }} + + ) + } else { + return renderSelectOptionItem(item, option) + } + }) + } + + // 渲染 select option item + const renderSelectOptionItem = (item: FormSchema, option: SelectOption) => { + // 如果有别名,就取别名 + const componentsProps = item.componentProps as SelectComponentProps + const labelAlias = componentsProps?.props?.label + const valueAlias = componentsProps?.props?.value + const keyAlias = componentsProps?.props?.key + const optionDefaultSlot = componentsProps.slots?.optionDefault + + return ( + + {{ + default: () => (optionDefaultSlot ? optionDefaultSlot(option) : undefined) + }} + + ) + } + + return { + renderSelectOptions + } +} diff --git a/promo-ui2/src/components/Form/src/helper/componentMap.ts b/promo-ui2/src/components/Form/src/helper/componentMap.ts new file mode 100644 index 0000000..df2714f --- /dev/null +++ b/promo-ui2/src/components/Form/src/helper/componentMap.ts @@ -0,0 +1,59 @@ +import type { Component } from 'vue' +import { + ElCascader, + ElCheckboxGroup, + ElColorPicker, + ElDatePicker, + ElInput, + ElInputNumber, + ElRadioGroup, + ElRate, + ElSelect, + ElSelectV2, + ElSlider, + ElSwitch, + ElTimePicker, + ElTimeSelect, + ElTransfer, + ElAutocomplete, + ElDivider, + ElTreeSelect, + ElUpload +} from 'element-plus' +import { InputPassword } from '@/components/InputPassword' +import { Editor } from '@/components/Editor' +import { JsonEditor } from '@/components/JsonEditor' +import { IconPicker } from '@/components/IconPicker' +import { IAgree } from '@/components/IAgree' +import { ComponentName } from '../types' + +const componentMap: Recordable = { + RadioGroup: ElRadioGroup, + RadioButton: ElRadioGroup, + CheckboxGroup: ElCheckboxGroup, + CheckboxButton: ElCheckboxGroup, + Input: ElInput, + Autocomplete: ElAutocomplete, + InputNumber: ElInputNumber, + Select: ElSelect, + Cascader: ElCascader, + Switch: ElSwitch, + Slider: ElSlider, + TimePicker: ElTimePicker, + DatePicker: ElDatePicker, + Rate: ElRate, + ColorPicker: ElColorPicker, + Transfer: ElTransfer, + Divider: ElDivider, + TimeSelect: ElTimeSelect, + SelectV2: ElSelectV2, + InputPassword: InputPassword, + Editor: Editor, + TreeSelect: ElTreeSelect, + Upload: ElUpload, + JsonEditor: JsonEditor, + IconPicker: IconPicker, + IAgree: IAgree +} + +export { componentMap } diff --git a/promo-ui2/src/components/Form/src/helper/index.ts b/promo-ui2/src/components/Form/src/helper/index.ts new file mode 100644 index 0000000..2ba1dc2 --- /dev/null +++ b/promo-ui2/src/components/Form/src/helper/index.ts @@ -0,0 +1,169 @@ +import { useI18n } from '@/hooks/web/useI18n' +import { PlaceholderModel, FormSchema, ComponentNameEnum, ColProps } from '../types' +import { isFunction } from '@/utils/is' +import { firstUpperCase, humpToDash } from '@/utils' +import { set, get } from 'lodash-es' + +const { t } = useI18n() + +/** + * + * @param schema 对应组件数据 + * @returns 返回提示信息对象 + * @description 用于自动设置placeholder + */ +export const setTextPlaceholder = (schema: FormSchema): PlaceholderModel => { + const textMap = [ + ComponentNameEnum.INPUT, + ComponentNameEnum.AUTOCOMPLETE, + ComponentNameEnum.INPUT_NUMBER, + ComponentNameEnum.INPUT_PASSWORD + ] + const selectMap = [ + ComponentNameEnum.SELECT, + ComponentNameEnum.TIME_PICKER, + ComponentNameEnum.DATE_PICKER, + ComponentNameEnum.TIME_SELECT, + ComponentNameEnum.SELECT_V2 + ] + if (textMap.includes(schema?.component as ComponentNameEnum)) { + return { + placeholder: t('common.inputText') + } + } + if (selectMap.includes(schema?.component as ComponentNameEnum)) { + // 一些范围选择器 + const twoTextMap = ['datetimerange', 'daterange', 'monthrange', 'datetimerange', 'daterange'] + if ( + twoTextMap.includes( + ((schema?.componentProps as any)?.type || + (schema?.componentProps as any)?.isRange) as string + ) + ) { + return { + startPlaceholder: t('common.startTimeText'), + endPlaceholder: t('common.endTimeText'), + rangeSeparator: '-' + } + } else { + return { + placeholder: t('common.selectText') + } + } + } + return {} +} + +/** + * + * @param col 内置栅格 + * @returns 返回栅格属性 + * @description 合并传入进来的栅格属性 + */ +export const setGridProp = (col: ColProps = {}): ColProps => { + const colProps: ColProps = { + // 如果有span,代表用户优先级更高,所以不需要默认栅格 + ...(col.span + ? {} + : { + xs: 24, + sm: 12, + md: 12, + lg: 12, + xl: 12 + }), + ...col + } + return colProps +} + +/** + * + * @param item 传入的组件属性 + * @returns 默认添加 clearable 属性 + */ +export const setComponentProps = (item: FormSchema): Recordable => { + // const notNeedClearable = ['ColorPicker'] + // 拆分事件并组合 + const onEvents = (item?.componentProps as any)?.on || {} + const newOnEvents: Recordable = {} + + for (const key in onEvents) { + if (onEvents[key]) { + newOnEvents[`on${firstUpperCase(key)}`] = (...args: any[]) => { + onEvents[key](...args) + } + } + } + + const componentProps: Recordable = { + clearable: true, + ...item.componentProps, + ...newOnEvents + } + // 需要删除额外的属性 + if (componentProps.slots) { + delete componentProps.slots + } + if (componentProps.on) { + delete componentProps.on + } + return componentProps +} + +/** + * + * @param formModel 表单数据 + * @param slotsProps 插槽属性 + */ +export const setItemComponentSlots = (slotsProps: Recordable = {}): Recordable => { + const slotObj: Recordable = {} + for (const key in slotsProps) { + if (slotsProps[key]) { + if (isFunction(slotsProps[key])) { + slotObj[humpToDash(key)] = (...args: any[]) => { + return slotsProps[key]?.(...args) + } + } else { + slotObj[humpToDash(key)] = () => { + return slotsProps[key] + } + } + } + } + return slotObj +} + +/** + * + * @param schema Form表单结构化数组 + * @param formModel FormMoel + * @returns FormMoel + * @description 生成对应的formModel + */ +export const initModel = (schema: FormSchema[], formModel: Recordable) => { + const model: Recordable = { ...formModel } + schema.map((v) => { + if (v.remove) { + delete model[v.field] + } else if (v.component !== 'Divider') { + // const hasField = Reflect.has(model, v.field) + const hasField = get(model, v.field) + // 如果先前已经有值存在,则不进行重新赋值,而是采用现有的值 + set( + model, + v.field, + hasField !== void 0 ? get(model, v.field) : v.value !== void 0 ? v.value : undefined + ) + // model[v.field] = hasField ? model[v.field] : v.value !== void 0 ? v.value : undefined + } + }) + // 如果 schema 对应的 field 不存在,则删除 model 中的对应的 field + for (let i = 0; i < schema.length; i++) { + const key = schema[i].field + if (!Object.prototype.hasOwnProperty.call(model, key)) { + delete model[key] + } + } + return model +} diff --git a/promo-ui2/src/components/Form/src/types/index.ts b/promo-ui2/src/components/Form/src/types/index.ts new file mode 100644 index 0000000..ac80193 --- /dev/null +++ b/promo-ui2/src/components/Form/src/types/index.ts @@ -0,0 +1,670 @@ +import { + AutocompleteProps, + InputNumberProps, + CascaderProps, + CascaderNode, + CascaderValue, + SwitchProps, + ComponentSize, + InputProps, + RateProps, + ColorPickerProps, + TransferProps, + RadioGroupProps, + RadioButtonProps, + CheckboxGroupProps, + DividerProps, + DatePickerProps, + FormItemProps as ElFormItemProps, + FormProps as ElFormProps, + ISelectProps, + UploadProps +} from 'element-plus' +import { IEditorConfig } from '@wangeditor/editor' +import { JsonEditorProps } from '@/components/JsonEditor' +import { IAgreeProps } from '@/components/IAgree' +import { CSSProperties } from 'vue' + +export interface PlaceholderModel { + placeholder?: string + startPlaceholder?: string + endPlaceholder?: string + rangeSeparator?: string +} + +export enum ComponentNameEnum { + RADIO_GROUP = 'RadioGroup', + RADIO_BUTTON = 'RadioButton', + CHECKBOX_GROUP = 'CheckboxGroup', + CHECKBOX_BUTTON = 'CheckboxButton', + INPUT = 'Input', + AUTOCOMPLETE = 'Autocomplete', + INPUT_NUMBER = 'InputNumber', + SELECT = 'Select', + CASCADER = 'Cascader', + SWITCH = 'Switch', + SLIDER = 'Slider', + TIME_PICKER = 'TimePicker', + DATE_PICKER = 'DatePicker', + RATE = 'Rate', + COLOR_PICKER = 'ColorPicker', + TRANSFER = 'Transfer', + DIVIDER = 'Divider', + TIME_SELECT = 'TimeSelect', + SELECT_V2 = 'SelectV2', + INPUT_PASSWORD = 'InputPassword', + EDITOR = 'Editor', + TREE_SELECT = 'TreeSelect', + UPLOAD = 'Upload', + JSON_EDITOR = 'JsonEditor', + ICON_PICKER = 'IconPicker', + I_AGREE = 'IAgree' +} + +type CamelCaseComponentName = keyof typeof ComponentNameEnum extends infer K + ? K extends string + ? K extends `${infer A}_${infer B}` + ? `${Capitalize>}${Capitalize>}` + : Capitalize> + : never + : never + +export type ComponentName = CamelCaseComponentName + +export interface InputPasswordComponentProps { + strength?: boolean + style?: CSSProperties +} + +export interface InputComponentProps extends Partial { + rows?: number + on?: { + blur?: (event: FocusEvent) => void + focus?: (event: FocusEvent) => void + change?: (value: string | number) => void + clear?: () => void + input?: (value: string | number) => void + } + slots?: { + prefix?: (...args: any[]) => JSX.Element | null + suffix?: (...args: any[]) => JSX.Element | null + prepend?: (...args: any[]) => JSX.Element | null + append?: (...args: any[]) => JSX.Element | null + } + style?: CSSProperties +} + +export interface AutocompleteComponentProps extends Partial { + on?: { + select?: (item: any) => void + change?: (value: string | number) => void + } + slots?: { + default?: (...args: any[]) => JSX.Element | null + prefix?: (...args: any[]) => JSX.Element | null + suffix?: (...args: any[]) => JSX.Element | null + prepend?: (...args: any[]) => JSX.Element | null + append?: (...args: any[]) => JSX.Element | null + } + style?: CSSProperties +} + +export interface InputNumberComponentProps extends Partial { + on?: { + change?: (currentValue: number | undefined, oldValue: number | undefined) => void + blur?: (event: FocusEvent) => void + focus?: (event: FocusEvent) => void + } + style?: CSSProperties +} + +export interface SelectOption { + label?: string + disabled?: boolean + value?: any + key?: string | number + options?: SelectOption[] + [key: string]: any +} + +export interface SelectComponentProps extends Omit, 'options'> { + /** + * 数据源的字段别名 + */ + props?: { + key?: string + value?: string + label?: string + children?: string + } + on?: { + change?: (value: string | number | boolean | object) => void + visibleChange?: (visible: boolean) => void + removeTag?: (tag: any) => void + clear?: () => void + blur?: (event: FocusEvent) => void + focus?: (event: FocusEvent) => void + } + slots?: { + default?: (options: SelectOption[]) => JSX.Element[] | null + optionGroupDefault?: (item: SelectOption) => JSX.Element + optionDefault?: (option: SelectOption) => JSX.Element | null + prefix?: (...args: any[]) => JSX.Element | null + empty?: (...args: any[]) => JSX.Element | null + } + options?: SelectOption[] + style?: CSSProperties +} + +export interface SelectV2ComponentProps { + multiple?: boolean + disabled?: boolean + valueKey?: string + size?: ComponentSize + clearable?: boolean + clearIcon?: string | JSX.Element | null + collapseTags?: boolean + multipleLimit?: number + name?: string + effect?: string + autocomplete?: string + placeholder?: string + filterable?: boolean + allowCreate?: boolean + reserveKeyword?: boolean + noDataText?: string + popperClass?: string + teleported?: boolean + persistent?: boolean + popperOptions?: any + automaticDropdown?: boolean + height?: number + scrollbarAlwaysOn?: boolean + remote?: boolean + remoteMethod?: (query: string) => void + validateEvent?: boolean + placement?: AutocompleteProps['placement'] + collapseTagsTooltip?: boolean + on?: { + change?: (value: string | number | boolean | object) => void + visibleChange?: (visible: boolean) => void + removeTag?: (tag: any) => void + clear?: () => void + blur?: (event: FocusEvent) => void + focus?: (event: FocusEvent) => void + } + options?: SelectOption[] + slots?: { + default?: (option: SelectOption) => JSX.Element | null + } + style?: CSSProperties +} + +export interface CascaderComponentProps { + options?: Record[] + props?: CascaderProps + size?: ComponentSize + placeholder?: string + disabled?: boolean + clearable?: boolean + showAllLevels?: boolean + collapseTags?: boolean + collapseTagsTooltip?: boolean + separator?: string + filterable?: boolean + filterMethod?: (node: CascaderNode, keyword: string) => boolean + debounce?: number + beforeFilter?: (value: string) => boolean + popperClass?: string + teleported?: boolean + tagType?: ElementPlusInfoType + validateEvent?: boolean + on?: { + change?: (value: CascaderValue) => void + expandChange?: (value: CascaderValue) => void + blur?: (event: FocusEvent) => void + focus?: (event: FocusEvent) => void + visibleChange?: (value: boolean) => void + removeTag?: (value: CascaderNode['valueByOption']) => void + } + slots?: { + default?: (...args: any[]) => JSX.Element | null + empty?: (...args: any[]) => JSX.Element | null + } + style?: CSSProperties +} + +export interface SwitchComponentProps extends Partial { + on?: { + change?: (value: boolean | string | number) => void + } + style?: CSSProperties +} + +export interface RateComponentProps extends Partial { + on?: { + change?: (value: number) => void + } + style?: CSSProperties +} + +export interface ColorPickerComponentProps extends Partial { + on?: { + change?: (value: string) => void + activeChange?: (value: string) => void + } + style?: CSSProperties +} + +export interface TransferComponentProps extends Partial { + on?: { + change?: ( + value: number | string, + direction: 'left' | 'right', + movedKeys: string[] | number[] + ) => void + leftCheckChange?: (value: any[]) => void + rightCheckChange?: (value: any[]) => void + } + slots?: { + default?: (...args: any[]) => JSX.Element | null + leftFooter?: (...args: any[]) => JSX.Element | null + rightFooter?: (...args: any[]) => JSX.Element | null + } + style?: CSSProperties +} + +export interface RadioOption { + label?: string + value?: string | number | boolean + disabled?: boolean + border?: boolean + size?: ComponentSize + name?: string + [key: string]: any +} +export interface RadioGroupComponentProps extends Partial { + options?: RadioOption[] + /** + * 数据源的字段别名 + */ + props?: { + label?: string + value?: string + disabled?: string + } + on?: { + change?: (value: string | number | boolean) => void + } + slots?: { + default?: (...args: any[]) => JSX.Element[] | null + } + style?: CSSProperties +} + +export interface RadioButtonComponentProps extends Partial { + options?: RadioOption[] + /** + * 数据源的字段别名 + */ + props?: { + label?: string + value?: string + disabled?: string + } + on?: { + change?: (value: string | number | boolean) => void + } + slots?: { + default?: (...args: any[]) => JSX.Element[] | null + } + style?: CSSProperties +} + +export interface CheckboxOption { + label?: string + value?: string | number | boolean + disabled?: boolean + trueLabel?: string | number + falseLabel?: string | number + border?: boolean + size?: ComponentSize + name?: string + checked?: boolean + indeterminate?: boolean + validateEvent?: boolean + tabindex?: number | string + id?: string + controls?: boolean + [key: string]: any +} + +export interface CheckboxGroupComponentProps extends Partial { + options?: CheckboxOption[] + /** + * 数据源的字段别名 + */ + props?: { + label?: string + value?: string + disabled?: string + } + on?: { + change?: (value: string | number | boolean) => void + } + slots?: { + default?: (...args: any[]) => JSX.Element[] | null + } + style?: CSSProperties +} + +export interface DividerComponentProps extends Partial { + on?: { + change?: (value: number) => void + input?: (value: number) => void + } + style?: CSSProperties +} + +export interface DatePickerComponentProps extends Partial { + on?: { + change?: (value: string | Date | number | string[]) => void + blur?: (event: FocusEvent) => void + focus?: (event: FocusEvent) => void + calendarChange?: (val: [Date, Date]) => void + panelChange?: (date, mode, view) => void + visibleChange?: (visibility: boolean) => void + } + slots?: { + default?: (...args: any[]) => JSX.Element | null + rangeSeparator?: (...args: any[]) => JSX.Element | null + } + style?: CSSProperties +} + +export interface DateTimePickerComponentProps { + readonly?: boolean + disabled?: boolean + editable?: boolean + clearable?: boolean + size?: ComponentSize + placeholder?: string + startPlaceholder?: string + endPlaceholder?: string + timeArrowControl?: boolean + type?: 'year' | 'month' | 'date' | 'datetime' | 'datetimerange' | 'daterange' | 'week' + format?: string + popperClass?: string + rangeSeparator?: string + defaultValue?: Date | [Date, Date] + defaultTime?: Date | [Date, Date] + valueFormat?: string + id?: string + name?: string + unlinkPanels?: boolean + prefixIcon?: string | JSX.Element + clearIcon?: string | JSX.Element + shortcuts?: Array<{ text: string; value: Date | Function }> + disabledDate?: (date: Date) => boolean + cellClassName?: string | ((date: Date) => string | undefined) + teleported?: boolean + on?: { + change?: (value: string | Date | number | string[]) => void + blur?: (event: FocusEvent) => void + focus?: (event: FocusEvent) => void + calendarChange?: (val: [Date, Date]) => void + visibleChange?: (visibility: boolean) => void + } + slots?: { + default?: (...args: any[]) => JSX.Element | null + rangeSeparator?: (...args: any[]) => JSX.Element | null + } + style?: CSSProperties +} + +export interface TimePickerComponentProps { + readonly?: boolean + disabled?: boolean + editable?: boolean + clearable?: boolean + size?: ComponentSize + placeholder?: string + startPlaceholder?: string + endPlaceholder?: string + isRange?: boolean + arrowControl?: boolean + popperClass?: string + rangeSeparator?: string + format?: string + defaultValue?: Date | [Date, Date] + id?: string + name?: string + label?: string + prefixIcon?: string | JSX.Element + clearIcon?: string | JSX.Element + disabledHours?: (role: string, comparingDate?: any) => number[] + disabledMinutes?: (hour: number, role: string, comparingDate?: any) => number[] + disabledSeconds?: (hour: number, minute: number, role: string, comparingDate?: any) => number[] + teleported?: boolean + tabindex?: number | string + on?: { + change: ( + val: number | string | Date | [number, number] | [string, string] | [Date, Date] + ) => void + blur?: (event: FocusEvent) => void + focus?: (event: FocusEvent) => void + visibleChange?: (visibility: boolean) => void + } + style?: CSSProperties +} + +export interface TimeSelectComponentProps { + disabled?: boolean + editable?: boolean + clearable?: boolean + size?: ComponentSize + placeholder?: string + name?: string + effect?: string + prefixIcon?: string | JSX.Element + clearIcon?: string | JSX.Element + start?: string + end?: string + step?: string + minTime?: string + maxTime?: string + format?: string + on?: { + change?: (val: string) => void + blur?: (event: FocusEvent) => void + focus?: (event: FocusEvent) => void + } + style?: CSSProperties +} + +export interface EditorComponentProps { + editorConfig?: IEditorConfig + style?: CSSProperties +} + +export interface ColProps { + span?: number + xs?: number + sm?: number + md?: number + lg?: number + xl?: number + tag?: string +} + +export interface FormSetProps { + field: string + path: string + value: any +} + +export interface FormItemProps extends Partial { + style?: CSSProperties + slots?: { + default?: (...args: any[]) => JSX.Element | null + label?: (...args: any[]) => JSX.Element | null + error?: (...args: any[]) => JSX.Element | null + } +} + +export interface UploadComponentProps extends Partial { + slots?: { + default?: (...args: any[]) => JSX.Element | null + trigger?: (...args: any[]) => JSX.Element | null + tip?: (...args: any[]) => JSX.Element | null + file?: (...args: any[]) => JSX.Element | null + } + style?: CSSProperties +} + +export interface TreeSelectComponentProps + extends Omit, 'props' | 'on' | 'slots'> { + data?: any[] + emptyText?: string + nodeKey?: string + props?: { + children?: string + label?: string | ((...args: any[]) => string) + disabled?: string | ((...args: any[]) => string) + isLeaf?: string | ((...args: any[]) => string) + class?: string | ((...args: any[]) => string) + } + renderAfterExpand?: boolean + load?: (...args: any[]) => Promise + renderContent?: (...args: any[]) => JSX.Element | null + highlightCurrent?: boolean + defaultExpandAll?: boolean + expandOnClickNode?: boolean + checkOnClickNode?: boolean + autoExpandParent?: boolean + defaultExpandedKeys?: any[] + showCheckbox?: boolean + checkStrictly?: boolean + defaultCheckedKeys?: any[] + currentNodeKey?: string | number + filterNodeMethod?: (...args: any[]) => boolean + accordion?: boolean + indent?: number + icon?: string | ((...args: any[]) => JSX.Element | null) + lazy?: boolean + draggable?: boolean + allowDrag?: (...args: any[]) => boolean + allowDrop?: (...args: any[]) => boolean + on?: { + change?: (value: string | number | boolean | object) => void + visibleChange?: (visible: boolean) => void + removeTag?: (tag: any) => void + clear?: () => void + blur?: (event: FocusEvent) => void + focus?: (event: FocusEvent) => void + nodeClick?: (...args: any[]) => void + nodeContextMenu?: (...args: any[]) => void + checkChange?: (...args: any[]) => void + check?: (...args: any[]) => void + currentChange?: (...args: any[]) => void + nodeExpand?: (...args: any[]) => void + nodeCollapse?: (...args: any[]) => void + nodeDragStart?: (...args: any[]) => void + nodeDragEnter?: (...args: any[]) => void + nodeDragLeave?: (...args: any[]) => void + nodeDragOver?: (...args: any[]) => void + nodeDragEnd?: (...args: any[]) => void + nodeDrop?: (...args: any[]) => void + } + slots?: { + default?: (...args: any[]) => JSX.Element | null + optionGroupDefault?: (item: SelectOption) => JSX.Element + optionDefault?: (option: SelectOption) => JSX.Element | null + prefix?: (...args: any[]) => JSX.Element | null + empty?: (...args: any[]) => JSX.Element | null + } + style?: CSSProperties +} + +export interface FormSchema { + /** + * 唯一标识 + */ + field: string + + /** + * 标题 + */ + label?: string + + /** + * col组件属性 + */ + colProps?: ColProps + + /** + * 表单组件属性,具体可以查看element-plus文档 + */ + componentProps?: + | InputComponentProps + | AutocompleteComponentProps + | InputNumberComponentProps + | SelectComponentProps + | SelectV2ComponentProps + | CascaderComponentProps + | SwitchComponentProps + | RateComponentProps + | ColorPickerComponentProps + | TransferComponentProps + | RadioGroupComponentProps + | RadioButtonComponentProps + | DividerComponentProps + | DatePickerComponentProps + | DateTimePickerComponentProps + | TimePickerComponentProps + | InputPasswordComponentProps + | TreeSelectComponentProps + | UploadComponentProps + | JsonEditorProps + | IAgreeProps + | any + + /** + * formItem组件属性,具体可以查看element-plus文档 + */ + formItemProps?: FormItemProps + + /** + * 渲染的组件名称 + */ + component?: ComponentName + + /** + * 初始值 + */ + value?: any + + /** + * 是否隐藏,如果为true,会连同值一同删除,类似v-if + */ + remove?: boolean + + /** + * 样式隐藏,不会把值一同删掉,类似v-show + */ + hidden?: boolean + + /** + * @returns 远程加载下拉项 + */ + optionApi?: any +} + +export interface FormProps extends Partial { + schema?: FormSchema[] + isCol?: boolean + model?: Recordable + autoSetPlaceholder?: boolean + isCustom?: boolean + [key: string]: any +} diff --git a/promo-ui2/src/components/Highlight/index.ts b/promo-ui2/src/components/Highlight/index.ts new file mode 100644 index 0000000..3e2d9ed --- /dev/null +++ b/promo-ui2/src/components/Highlight/index.ts @@ -0,0 +1,3 @@ +import Highlight from './src/Highlight.vue' + +export { Highlight } diff --git a/promo-ui2/src/components/Highlight/src/Highlight.vue b/promo-ui2/src/components/Highlight/src/Highlight.vue new file mode 100644 index 0000000..ef923a9 --- /dev/null +++ b/promo-ui2/src/components/Highlight/src/Highlight.vue @@ -0,0 +1,65 @@ + diff --git a/promo-ui2/src/components/IAgree/index.ts b/promo-ui2/src/components/IAgree/index.ts new file mode 100644 index 0000000..8c31125 --- /dev/null +++ b/promo-ui2/src/components/IAgree/index.ts @@ -0,0 +1,4 @@ +import IAgree from './src/IAgree.vue' + +export type { LinkItem, IAgreeProps } from './src/types' +export { IAgree } diff --git a/promo-ui2/src/components/IAgree/src/IAgree.vue b/promo-ui2/src/components/IAgree/src/IAgree.vue new file mode 100644 index 0000000..6a36e23 --- /dev/null +++ b/promo-ui2/src/components/IAgree/src/IAgree.vue @@ -0,0 +1,41 @@ + + + diff --git a/promo-ui2/src/components/IAgree/src/types/index.ts b/promo-ui2/src/components/IAgree/src/types/index.ts new file mode 100644 index 0000000..9ddef75 --- /dev/null +++ b/promo-ui2/src/components/IAgree/src/types/index.ts @@ -0,0 +1,10 @@ +export interface LinkItem { + text: string + url?: string + onClick?: () => void +} + +export interface IAgreeProps { + text: string + link: LinkItem[] +} diff --git a/promo-ui2/src/components/Icon/index.ts b/promo-ui2/src/components/Icon/index.ts new file mode 100644 index 0000000..83469d4 --- /dev/null +++ b/promo-ui2/src/components/Icon/index.ts @@ -0,0 +1,5 @@ +import Icon from './src/Icon.vue' + +export type { IconTypes } from './src/types' + +export { Icon } diff --git a/promo-ui2/src/components/Icon/src/Icon.vue b/promo-ui2/src/components/Icon/src/Icon.vue new file mode 100644 index 0000000..0fa4d73 --- /dev/null +++ b/promo-ui2/src/components/Icon/src/Icon.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/promo-ui2/src/components/Icon/src/types/index.ts b/promo-ui2/src/components/Icon/src/types/index.ts new file mode 100644 index 0000000..632e4e8 --- /dev/null +++ b/promo-ui2/src/components/Icon/src/types/index.ts @@ -0,0 +1,6 @@ +export interface IconTypes { + size?: number + color?: string + icon: string + hoverColor?: string +} diff --git a/promo-ui2/src/components/IconPicker/index.ts b/promo-ui2/src/components/IconPicker/index.ts new file mode 100644 index 0000000..b7b3022 --- /dev/null +++ b/promo-ui2/src/components/IconPicker/index.ts @@ -0,0 +1,3 @@ +import IconPicker from './src/IconPicker.vue' + +export { IconPicker } diff --git a/promo-ui2/src/components/IconPicker/src/IconPicker.vue b/promo-ui2/src/components/IconPicker/src/IconPicker.vue new file mode 100644 index 0000000..1b20c4c --- /dev/null +++ b/promo-ui2/src/components/IconPicker/src/IconPicker.vue @@ -0,0 +1,194 @@ + + + + + diff --git a/promo-ui2/src/components/IconPicker/src/data/icons.ant-design.ts b/promo-ui2/src/components/IconPicker/src/data/icons.ant-design.ts new file mode 100644 index 0000000..eab2169 --- /dev/null +++ b/promo-ui2/src/components/IconPicker/src/data/icons.ant-design.ts @@ -0,0 +1,836 @@ +export default { + name: 'Ant Design Icons', + prefix: 'vi-ant-design', + icons: [ + 'vi-ant-design:account-book-filled', + 'vi-ant-design:account-book-outlined', + 'vi-ant-design:account-book-twotone', + 'vi-ant-design:aim-outlined', + 'vi-ant-design:alert-filled', + 'vi-ant-design:alert-outlined', + 'vi-ant-design:alert-twotone', + 'vi-ant-design:alibaba-outlined', + 'vi-ant-design:align-center-outlined', + 'vi-ant-design:align-left-outlined', + 'vi-ant-design:align-right-outlined', + 'vi-ant-design:alipay-circle-filled', + 'vi-ant-design:alipay-circle-outlined', + 'vi-ant-design:alipay-outlined', + 'vi-ant-design:alipay-square-filled', + 'vi-ant-design:aliwangwang-filled', + 'vi-ant-design:aliwangwang-outlined', + 'vi-ant-design:aliyun-outlined', + 'vi-ant-design:amazon-circle-filled', + 'vi-ant-design:amazon-outlined', + 'vi-ant-design:amazon-square-filled', + 'vi-ant-design:android-filled', + 'vi-ant-design:android-outlined', + 'vi-ant-design:ant-cloud-outlined', + 'vi-ant-design:ant-design-outlined', + 'vi-ant-design:apartment-outlined', + 'vi-ant-design:api-filled', + 'vi-ant-design:api-outlined', + 'vi-ant-design:api-twotone', + 'vi-ant-design:apple-filled', + 'vi-ant-design:apple-outlined', + 'vi-ant-design:appstore-add-outlined', + 'vi-ant-design:appstore-filled', + 'vi-ant-design:appstore-outlined', + 'vi-ant-design:appstore-twotone', + 'vi-ant-design:area-chart-outlined', + 'vi-ant-design:arrow-down-outlined', + 'vi-ant-design:arrow-left-outlined', + 'vi-ant-design:arrow-right-outlined', + 'vi-ant-design:arrow-up-outlined', + 'vi-ant-design:arrows-alt-outlined', + 'vi-ant-design:audio-filled', + 'vi-ant-design:audio-muted-outlined', + 'vi-ant-design:audio-outlined', + 'vi-ant-design:audio-twotone', + 'vi-ant-design:audit-outlined', + 'vi-ant-design:backward-filled', + 'vi-ant-design:backward-outlined', + 'vi-ant-design:baidu-outlined', + 'vi-ant-design:bank-filled', + 'vi-ant-design:bank-outlined', + 'vi-ant-design:bank-twotone', + 'vi-ant-design:bar-chart-outlined', + 'vi-ant-design:barcode-outlined', + 'vi-ant-design:bars-outlined', + 'vi-ant-design:behance-circle-filled', + 'vi-ant-design:behance-outlined', + 'vi-ant-design:behance-square-filled', + 'vi-ant-design:behance-square-outlined', + 'vi-ant-design:bell-filled', + 'vi-ant-design:bell-outlined', + 'vi-ant-design:bell-twotone', + 'vi-ant-design:bg-colors-outlined', + 'vi-ant-design:bilibili-filled', + 'vi-ant-design:bilibili-outlined', + 'vi-ant-design:block-outlined', + 'vi-ant-design:bold-outlined', + 'vi-ant-design:book-filled', + 'vi-ant-design:book-outlined', + 'vi-ant-design:book-twotone', + 'vi-ant-design:border-bottom-outlined', + 'vi-ant-design:border-horizontal-outlined', + 'vi-ant-design:border-inner-outlined', + 'vi-ant-design:border-left-outlined', + 'vi-ant-design:border-outer-outlined', + 'vi-ant-design:border-outlined', + 'vi-ant-design:border-right-outlined', + 'vi-ant-design:border-top-outlined', + 'vi-ant-design:border-verticle-outlined', + 'vi-ant-design:borderless-table-outlined', + 'vi-ant-design:box-plot-filled', + 'vi-ant-design:box-plot-outlined', + 'vi-ant-design:box-plot-twotone', + 'vi-ant-design:branches-outlined', + 'vi-ant-design:bug-filled', + 'vi-ant-design:bug-outlined', + 'vi-ant-design:bug-twotone', + 'vi-ant-design:build-filled', + 'vi-ant-design:build-outlined', + 'vi-ant-design:build-twotone', + 'vi-ant-design:bulb-filled', + 'vi-ant-design:bulb-outlined', + 'vi-ant-design:bulb-twotone', + 'vi-ant-design:calculator-filled', + 'vi-ant-design:calculator-outlined', + 'vi-ant-design:calculator-twotone', + 'vi-ant-design:calendar-filled', + 'vi-ant-design:calendar-outlined', + 'vi-ant-design:calendar-twotone', + 'vi-ant-design:camera-filled', + 'vi-ant-design:camera-outlined', + 'vi-ant-design:camera-twotone', + 'vi-ant-design:car-filled', + 'vi-ant-design:car-outlined', + 'vi-ant-design:car-twotone', + 'vi-ant-design:caret-down-filled', + 'vi-ant-design:caret-down-outlined', + 'vi-ant-design:caret-left-filled', + 'vi-ant-design:caret-left-outlined', + 'vi-ant-design:caret-right-filled', + 'vi-ant-design:caret-right-outlined', + 'vi-ant-design:caret-up-filled', + 'vi-ant-design:caret-up-outlined', + 'vi-ant-design:carry-out-filled', + 'vi-ant-design:carry-out-outlined', + 'vi-ant-design:carry-out-twotone', + 'vi-ant-design:check-circle-filled', + 'vi-ant-design:check-circle-outlined', + 'vi-ant-design:check-circle-twotone', + 'vi-ant-design:check-outlined', + 'vi-ant-design:check-square-filled', + 'vi-ant-design:check-square-outlined', + 'vi-ant-design:check-square-twotone', + 'vi-ant-design:chrome-filled', + 'vi-ant-design:chrome-outlined', + 'vi-ant-design:ci-circle-filled', + 'vi-ant-design:ci-circle-outlined', + 'vi-ant-design:ci-circle-twotone', + 'vi-ant-design:ci-outlined', + 'vi-ant-design:ci-twotone', + 'vi-ant-design:clear-outlined', + 'vi-ant-design:clock-circle-filled', + 'vi-ant-design:clock-circle-outlined', + 'vi-ant-design:clock-circle-twotone', + 'vi-ant-design:close-circle-filled', + 'vi-ant-design:close-circle-outlined', + 'vi-ant-design:close-circle-twotone', + 'vi-ant-design:close-outlined', + 'vi-ant-design:close-square-filled', + 'vi-ant-design:close-square-outlined', + 'vi-ant-design:close-square-twotone', + 'vi-ant-design:cloud-download-outlined', + 'vi-ant-design:cloud-filled', + 'vi-ant-design:cloud-outlined', + 'vi-ant-design:cloud-server-outlined', + 'vi-ant-design:cloud-sync-outlined', + 'vi-ant-design:cloud-twotone', + 'vi-ant-design:cloud-upload-outlined', + 'vi-ant-design:cluster-outlined', + 'vi-ant-design:code-filled', + 'vi-ant-design:code-outlined', + 'vi-ant-design:code-sandbox-circle-filled', + 'vi-ant-design:code-sandbox-outlined', + 'vi-ant-design:code-sandbox-square-filled', + 'vi-ant-design:code-twotone', + 'vi-ant-design:codepen-circle-filled', + 'vi-ant-design:codepen-circle-outlined', + 'vi-ant-design:codepen-outlined', + 'vi-ant-design:codepen-square-filled', + 'vi-ant-design:coffee-outlined', + 'vi-ant-design:column-height-outlined', + 'vi-ant-design:column-width-outlined', + 'vi-ant-design:comment-outlined', + 'vi-ant-design:compass-filled', + 'vi-ant-design:compass-outlined', + 'vi-ant-design:compass-twotone', + 'vi-ant-design:compress-outlined', + 'vi-ant-design:console-sql-outlined', + 'vi-ant-design:contacts-filled', + 'vi-ant-design:contacts-outlined', + 'vi-ant-design:contacts-twotone', + 'vi-ant-design:container-filled', + 'vi-ant-design:container-outlined', + 'vi-ant-design:container-twotone', + 'vi-ant-design:control-filled', + 'vi-ant-design:control-outlined', + 'vi-ant-design:control-twotone', + 'vi-ant-design:copy-filled', + 'vi-ant-design:copy-outlined', + 'vi-ant-design:copy-twotone', + 'vi-ant-design:copyright-circle-filled', + 'vi-ant-design:copyright-circle-outlined', + 'vi-ant-design:copyright-circle-twotone', + 'vi-ant-design:copyright-outlined', + 'vi-ant-design:copyright-twotone', + 'vi-ant-design:credit-card-filled', + 'vi-ant-design:credit-card-outlined', + 'vi-ant-design:credit-card-twotone', + 'vi-ant-design:crown-filled', + 'vi-ant-design:crown-outlined', + 'vi-ant-design:crown-twotone', + 'vi-ant-design:customer-service-filled', + 'vi-ant-design:customer-service-outlined', + 'vi-ant-design:customer-service-twotone', + 'vi-ant-design:dash-outlined', + 'vi-ant-design:dashboard-filled', + 'vi-ant-design:dashboard-outlined', + 'vi-ant-design:dashboard-twotone', + 'vi-ant-design:database-filled', + 'vi-ant-design:database-outlined', + 'vi-ant-design:database-twotone', + 'vi-ant-design:delete-column-outlined', + 'vi-ant-design:delete-filled', + 'vi-ant-design:delete-outlined', + 'vi-ant-design:delete-row-outlined', + 'vi-ant-design:delete-twotone', + 'vi-ant-design:delivered-procedure-outlined', + 'vi-ant-design:deployment-unit-outlined', + 'vi-ant-design:desktop-outlined', + 'vi-ant-design:diff-filled', + 'vi-ant-design:diff-outlined', + 'vi-ant-design:diff-twotone', + 'vi-ant-design:dingding-outlined', + 'vi-ant-design:dingtalk-circle-filled', + 'vi-ant-design:dingtalk-outlined', + 'vi-ant-design:dingtalk-square-filled', + 'vi-ant-design:disconnect-outlined', + 'vi-ant-design:discord-filled', + 'vi-ant-design:discord-outlined', + 'vi-ant-design:dislike-filled', + 'vi-ant-design:dislike-outlined', + 'vi-ant-design:dislike-twotone', + 'vi-ant-design:docker-outlined', + 'vi-ant-design:dollar-circle-filled', + 'vi-ant-design:dollar-circle-outlined', + 'vi-ant-design:dollar-circle-twotone', + 'vi-ant-design:dollar-outlined', + 'vi-ant-design:dollar-twotone', + 'vi-ant-design:dot-chart-outlined', + 'vi-ant-design:dot-net-outlined', + 'vi-ant-design:double-left-outlined', + 'vi-ant-design:double-right-outlined', + 'vi-ant-design:down-circle-filled', + 'vi-ant-design:down-circle-outlined', + 'vi-ant-design:down-circle-twotone', + 'vi-ant-design:down-outlined', + 'vi-ant-design:down-square-filled', + 'vi-ant-design:down-square-outlined', + 'vi-ant-design:down-square-twotone', + 'vi-ant-design:download-outlined', + 'vi-ant-design:drag-outlined', + 'vi-ant-design:dribbble-circle-filled', + 'vi-ant-design:dribbble-outlined', + 'vi-ant-design:dribbble-square-filled', + 'vi-ant-design:dribbble-square-outlined', + 'vi-ant-design:dropbox-circle-filled', + 'vi-ant-design:dropbox-outlined', + 'vi-ant-design:dropbox-square-filled', + 'vi-ant-design:edit-filled', + 'vi-ant-design:edit-outlined', + 'vi-ant-design:edit-twotone', + 'vi-ant-design:ellipsis-outlined', + 'vi-ant-design:enter-outlined', + 'vi-ant-design:environment-filled', + 'vi-ant-design:environment-outlined', + 'vi-ant-design:environment-twotone', + 'vi-ant-design:euro-circle-filled', + 'vi-ant-design:euro-circle-outlined', + 'vi-ant-design:euro-circle-twotone', + 'vi-ant-design:euro-outlined', + 'vi-ant-design:euro-twotone', + 'vi-ant-design:exception-outlined', + 'vi-ant-design:exclamation-circle-filled', + 'vi-ant-design:exclamation-circle-outlined', + 'vi-ant-design:exclamation-circle-twotone', + 'vi-ant-design:exclamation-outlined', + 'vi-ant-design:expand-alt-outlined', + 'vi-ant-design:expand-outlined', + 'vi-ant-design:experiment-filled', + 'vi-ant-design:experiment-outlined', + 'vi-ant-design:experiment-twotone', + 'vi-ant-design:export-outlined', + 'vi-ant-design:eye-filled', + 'vi-ant-design:eye-invisible-filled', + 'vi-ant-design:eye-invisible-outlined', + 'vi-ant-design:eye-invisible-twotone', + 'vi-ant-design:eye-outlined', + 'vi-ant-design:eye-twotone', + 'vi-ant-design:facebook-filled', + 'vi-ant-design:facebook-outlined', + 'vi-ant-design:fall-outlined', + 'vi-ant-design:fast-backward-filled', + 'vi-ant-design:fast-backward-outlined', + 'vi-ant-design:fast-forward-filled', + 'vi-ant-design:fast-forward-outlined', + 'vi-ant-design:field-binary-outlined', + 'vi-ant-design:field-number-outlined', + 'vi-ant-design:field-string-outlined', + 'vi-ant-design:field-time-outlined', + 'vi-ant-design:file-add-filled', + 'vi-ant-design:file-add-outlined', + 'vi-ant-design:file-add-twotone', + 'vi-ant-design:file-done-outlined', + 'vi-ant-design:file-excel-filled', + 'vi-ant-design:file-excel-outlined', + 'vi-ant-design:file-excel-twotone', + 'vi-ant-design:file-exclamation-filled', + 'vi-ant-design:file-exclamation-outlined', + 'vi-ant-design:file-exclamation-twotone', + 'vi-ant-design:file-filled', + 'vi-ant-design:file-gif-outlined', + 'vi-ant-design:file-image-filled', + 'vi-ant-design:file-image-outlined', + 'vi-ant-design:file-image-twotone', + 'vi-ant-design:file-jpg-outlined', + 'vi-ant-design:file-markdown-filled', + 'vi-ant-design:file-markdown-outlined', + 'vi-ant-design:file-markdown-twotone', + 'vi-ant-design:file-outlined', + 'vi-ant-design:file-pdf-filled', + 'vi-ant-design:file-pdf-outlined', + 'vi-ant-design:file-pdf-twotone', + 'vi-ant-design:file-ppt-filled', + 'vi-ant-design:file-ppt-outlined', + 'vi-ant-design:file-ppt-twotone', + 'vi-ant-design:file-protect-outlined', + 'vi-ant-design:file-search-outlined', + 'vi-ant-design:file-sync-outlined', + 'vi-ant-design:file-text-filled', + 'vi-ant-design:file-text-outlined', + 'vi-ant-design:file-text-twotone', + 'vi-ant-design:file-twotone', + 'vi-ant-design:file-unknown-filled', + 'vi-ant-design:file-unknown-outlined', + 'vi-ant-design:file-unknown-twotone', + 'vi-ant-design:file-word-filled', + 'vi-ant-design:file-word-outlined', + 'vi-ant-design:file-word-twotone', + 'vi-ant-design:file-zip-filled', + 'vi-ant-design:file-zip-outlined', + 'vi-ant-design:file-zip-twotone', + 'vi-ant-design:filter-filled', + 'vi-ant-design:filter-outlined', + 'vi-ant-design:filter-twotone', + 'vi-ant-design:fire-filled', + 'vi-ant-design:fire-outlined', + 'vi-ant-design:fire-twotone', + 'vi-ant-design:flag-filled', + 'vi-ant-design:flag-outlined', + 'vi-ant-design:flag-twotone', + 'vi-ant-design:folder-add-filled', + 'vi-ant-design:folder-add-outlined', + 'vi-ant-design:folder-add-twotone', + 'vi-ant-design:folder-filled', + 'vi-ant-design:folder-open-filled', + 'vi-ant-design:folder-open-outlined', + 'vi-ant-design:folder-open-twotone', + 'vi-ant-design:folder-outlined', + 'vi-ant-design:folder-twotone', + 'vi-ant-design:folder-view-outlined', + 'vi-ant-design:font-colors-outlined', + 'vi-ant-design:font-size-outlined', + 'vi-ant-design:fork-outlined', + 'vi-ant-design:form-outlined', + 'vi-ant-design:format-painter-filled', + 'vi-ant-design:format-painter-outlined', + 'vi-ant-design:forward-filled', + 'vi-ant-design:forward-outlined', + 'vi-ant-design:frown-filled', + 'vi-ant-design:frown-outlined', + 'vi-ant-design:frown-twotone', + 'vi-ant-design:fullscreen-exit-outlined', + 'vi-ant-design:fullscreen-outlined', + 'vi-ant-design:function-outlined', + 'vi-ant-design:fund-filled', + 'vi-ant-design:fund-outlined', + 'vi-ant-design:fund-projection-screen-outlined', + 'vi-ant-design:fund-twotone', + 'vi-ant-design:fund-view-outlined', + 'vi-ant-design:funnel-plot-filled', + 'vi-ant-design:funnel-plot-outlined', + 'vi-ant-design:funnel-plot-twotone', + 'vi-ant-design:gateway-outlined', + 'vi-ant-design:gif-outlined', + 'vi-ant-design:gift-filled', + 'vi-ant-design:gift-outlined', + 'vi-ant-design:gift-twotone', + 'vi-ant-design:github-filled', + 'vi-ant-design:github-outlined', + 'vi-ant-design:gitlab-filled', + 'vi-ant-design:gitlab-outlined', + 'vi-ant-design:global-outlined', + 'vi-ant-design:gold-filled', + 'vi-ant-design:gold-outlined', + 'vi-ant-design:gold-twotone', + 'vi-ant-design:golden-filled', + 'vi-ant-design:google-circle-filled', + 'vi-ant-design:google-outlined', + 'vi-ant-design:google-plus-circle-filled', + 'vi-ant-design:google-plus-outlined', + 'vi-ant-design:google-plus-square-filled', + 'vi-ant-design:google-square-filled', + 'vi-ant-design:group-outlined', + 'vi-ant-design:harmony-o-s-outlined', + 'vi-ant-design:hdd-filled', + 'vi-ant-design:hdd-outlined', + 'vi-ant-design:hdd-twotone', + 'vi-ant-design:heart-filled', + 'vi-ant-design:heart-outlined', + 'vi-ant-design:heart-twotone', + 'vi-ant-design:heat-map-outlined', + 'vi-ant-design:highlight-filled', + 'vi-ant-design:highlight-outlined', + 'vi-ant-design:highlight-twotone', + 'vi-ant-design:history-outlined', + 'vi-ant-design:holder-outlined', + 'vi-ant-design:home-filled', + 'vi-ant-design:home-outlined', + 'vi-ant-design:home-twotone', + 'vi-ant-design:hourglass-filled', + 'vi-ant-design:hourglass-outlined', + 'vi-ant-design:hourglass-twotone', + 'vi-ant-design:html5-filled', + 'vi-ant-design:html5-outlined', + 'vi-ant-design:html5-twotone', + 'vi-ant-design:idcard-filled', + 'vi-ant-design:idcard-outlined', + 'vi-ant-design:idcard-twotone', + 'vi-ant-design:ie-circle-filled', + 'vi-ant-design:ie-outlined', + 'vi-ant-design:ie-square-filled', + 'vi-ant-design:import-outlined', + 'vi-ant-design:inbox-outlined', + 'vi-ant-design:info-circle-filled', + 'vi-ant-design:info-circle-outlined', + 'vi-ant-design:info-circle-twotone', + 'vi-ant-design:info-outlined', + 'vi-ant-design:insert-row-above-outlined', + 'vi-ant-design:insert-row-below-outlined', + 'vi-ant-design:insert-row-left-outlined', + 'vi-ant-design:insert-row-right-outlined', + 'vi-ant-design:instagram-filled', + 'vi-ant-design:instagram-outlined', + 'vi-ant-design:insurance-filled', + 'vi-ant-design:insurance-outlined', + 'vi-ant-design:insurance-twotone', + 'vi-ant-design:interaction-filled', + 'vi-ant-design:interaction-outlined', + 'vi-ant-design:interaction-twotone', + 'vi-ant-design:issues-close-outlined', + 'vi-ant-design:italic-outlined', + 'vi-ant-design:java-outlined', + 'vi-ant-design:java-script-outlined', + 'vi-ant-design:key-outlined', + 'vi-ant-design:kubernetes-outlined', + 'vi-ant-design:laptop-outlined', + 'vi-ant-design:layout-filled', + 'vi-ant-design:layout-outlined', + 'vi-ant-design:layout-twotone', + 'vi-ant-design:left-circle-filled', + 'vi-ant-design:left-circle-outlined', + 'vi-ant-design:left-circle-twotone', + 'vi-ant-design:left-outlined', + 'vi-ant-design:left-square-filled', + 'vi-ant-design:left-square-outlined', + 'vi-ant-design:left-square-twotone', + 'vi-ant-design:like-filled', + 'vi-ant-design:like-outlined', + 'vi-ant-design:like-twotone', + 'vi-ant-design:line-chart-outlined', + 'vi-ant-design:line-height-outlined', + 'vi-ant-design:line-outlined', + 'vi-ant-design:link-outlined', + 'vi-ant-design:linkedin-filled', + 'vi-ant-design:linkedin-outlined', + 'vi-ant-design:linux-outlined', + 'vi-ant-design:loading-3-quarters-outlined', + 'vi-ant-design:loading-outlined', + 'vi-ant-design:lock-filled', + 'vi-ant-design:lock-outlined', + 'vi-ant-design:lock-twotone', + 'vi-ant-design:login-outlined', + 'vi-ant-design:logout-outlined', + 'vi-ant-design:mac-command-filled', + 'vi-ant-design:mac-command-outlined', + 'vi-ant-design:mail-filled', + 'vi-ant-design:mail-outlined', + 'vi-ant-design:mail-twotone', + 'vi-ant-design:man-outlined', + 'vi-ant-design:medicine-box-filled', + 'vi-ant-design:medicine-box-outlined', + 'vi-ant-design:medicine-box-twotone', + 'vi-ant-design:medium-circle-filled', + 'vi-ant-design:medium-outlined', + 'vi-ant-design:medium-square-filled', + 'vi-ant-design:medium-workmark-outlined', + 'vi-ant-design:meh-filled', + 'vi-ant-design:meh-outlined', + 'vi-ant-design:meh-twotone', + 'vi-ant-design:menu-fold-outlined', + 'vi-ant-design:menu-outlined', + 'vi-ant-design:menu-unfold-outlined', + 'vi-ant-design:merge-cells-outlined', + 'vi-ant-design:merge-filled', + 'vi-ant-design:merge-outlined', + 'vi-ant-design:message-filled', + 'vi-ant-design:message-outlined', + 'vi-ant-design:message-twotone', + 'vi-ant-design:minus-circle-filled', + 'vi-ant-design:minus-circle-outlined', + 'vi-ant-design:minus-circle-twotone', + 'vi-ant-design:minus-outlined', + 'vi-ant-design:minus-square-filled', + 'vi-ant-design:minus-square-outlined', + 'vi-ant-design:minus-square-twotone', + 'vi-ant-design:mobile-filled', + 'vi-ant-design:mobile-outlined', + 'vi-ant-design:mobile-twotone', + 'vi-ant-design:money-collect-filled', + 'vi-ant-design:money-collect-outlined', + 'vi-ant-design:money-collect-twotone', + 'vi-ant-design:monitor-outlined', + 'vi-ant-design:moon-filled', + 'vi-ant-design:moon-outlined', + 'vi-ant-design:more-outlined', + 'vi-ant-design:muted-filled', + 'vi-ant-design:muted-outlined', + 'vi-ant-design:node-collapse-outlined', + 'vi-ant-design:node-expand-outlined', + 'vi-ant-design:node-index-outlined', + 'vi-ant-design:notification-filled', + 'vi-ant-design:notification-outlined', + 'vi-ant-design:notification-twotone', + 'vi-ant-design:number-outlined', + 'vi-ant-design:one-to-one-outlined', + 'vi-ant-design:open-a-i-filled', + 'vi-ant-design:open-a-i-outlined', + 'vi-ant-design:ordered-list-outlined', + 'vi-ant-design:paper-clip-outlined', + 'vi-ant-design:partition-outlined', + 'vi-ant-design:pause-circle-filled', + 'vi-ant-design:pause-circle-outlined', + 'vi-ant-design:pause-circle-twotone', + 'vi-ant-design:pause-outlined', + 'vi-ant-design:pay-circle-filled', + 'vi-ant-design:pay-circle-outlined', + 'vi-ant-design:percentage-outlined', + 'vi-ant-design:phone-filled', + 'vi-ant-design:phone-outlined', + 'vi-ant-design:phone-twotone', + 'vi-ant-design:pic-center-outlined', + 'vi-ant-design:pic-left-outlined', + 'vi-ant-design:pic-right-outlined', + 'vi-ant-design:picture-filled', + 'vi-ant-design:picture-outlined', + 'vi-ant-design:picture-twotone', + 'vi-ant-design:pie-chart-filled', + 'vi-ant-design:pie-chart-outlined', + 'vi-ant-design:pie-chart-twotone', + 'vi-ant-design:pinterest-filled', + 'vi-ant-design:pinterest-outlined', + 'vi-ant-design:play-circle-filled', + 'vi-ant-design:play-circle-outlined', + 'vi-ant-design:play-circle-twotone', + 'vi-ant-design:play-square-filled', + 'vi-ant-design:play-square-outlined', + 'vi-ant-design:play-square-twotone', + 'vi-ant-design:plus-circle-filled', + 'vi-ant-design:plus-circle-outlined', + 'vi-ant-design:plus-circle-twotone', + 'vi-ant-design:plus-outlined', + 'vi-ant-design:plus-square-filled', + 'vi-ant-design:plus-square-outlined', + 'vi-ant-design:plus-square-twotone', + 'vi-ant-design:pound-circle-filled', + 'vi-ant-design:pound-circle-outlined', + 'vi-ant-design:pound-circle-twotone', + 'vi-ant-design:pound-outlined', + 'vi-ant-design:poweroff-outlined', + 'vi-ant-design:printer-filled', + 'vi-ant-design:printer-outlined', + 'vi-ant-design:printer-twotone', + 'vi-ant-design:product-filled', + 'vi-ant-design:product-outlined', + 'vi-ant-design:profile-filled', + 'vi-ant-design:profile-outlined', + 'vi-ant-design:profile-twotone', + 'vi-ant-design:project-filled', + 'vi-ant-design:project-outlined', + 'vi-ant-design:project-twotone', + 'vi-ant-design:property-safety-filled', + 'vi-ant-design:property-safety-outlined', + 'vi-ant-design:property-safety-twotone', + 'vi-ant-design:pull-request-outlined', + 'vi-ant-design:pushpin-filled', + 'vi-ant-design:pushpin-outlined', + 'vi-ant-design:pushpin-twotone', + 'vi-ant-design:python-outlined', + 'vi-ant-design:qq-circle-filled', + 'vi-ant-design:qq-outlined', + 'vi-ant-design:qq-square-filled', + 'vi-ant-design:qrcode-outlined', + 'vi-ant-design:question-circle-filled', + 'vi-ant-design:question-circle-outlined', + 'vi-ant-design:question-circle-twotone', + 'vi-ant-design:question-outlined', + 'vi-ant-design:radar-chart-outlined', + 'vi-ant-design:radius-bottomleft-outlined', + 'vi-ant-design:radius-bottomright-outlined', + 'vi-ant-design:radius-setting-outlined', + 'vi-ant-design:radius-upleft-outlined', + 'vi-ant-design:radius-upright-outlined', + 'vi-ant-design:read-filled', + 'vi-ant-design:read-outlined', + 'vi-ant-design:reconciliation-filled', + 'vi-ant-design:reconciliation-outlined', + 'vi-ant-design:reconciliation-twotone', + 'vi-ant-design:red-envelope-filled', + 'vi-ant-design:red-envelope-outlined', + 'vi-ant-design:red-envelope-twotone', + 'vi-ant-design:reddit-circle-filled', + 'vi-ant-design:reddit-outlined', + 'vi-ant-design:reddit-square-filled', + 'vi-ant-design:redo-outlined', + 'vi-ant-design:reload-outlined', + 'vi-ant-design:rest-filled', + 'vi-ant-design:rest-outlined', + 'vi-ant-design:rest-twotone', + 'vi-ant-design:retweet-outlined', + 'vi-ant-design:right-circle-filled', + 'vi-ant-design:right-circle-outlined', + 'vi-ant-design:right-circle-twotone', + 'vi-ant-design:right-outlined', + 'vi-ant-design:right-square-filled', + 'vi-ant-design:right-square-outlined', + 'vi-ant-design:right-square-twotone', + 'vi-ant-design:rise-outlined', + 'vi-ant-design:robot-filled', + 'vi-ant-design:robot-outlined', + 'vi-ant-design:rocket-filled', + 'vi-ant-design:rocket-outlined', + 'vi-ant-design:rocket-twotone', + 'vi-ant-design:rollback-outlined', + 'vi-ant-design:rotate-left-outlined', + 'vi-ant-design:rotate-right-outlined', + 'vi-ant-design:ruby-outlined', + 'vi-ant-design:safety-certificate-filled', + 'vi-ant-design:safety-certificate-outlined', + 'vi-ant-design:safety-certificate-twotone', + 'vi-ant-design:safety-outlined', + 'vi-ant-design:save-filled', + 'vi-ant-design:save-outlined', + 'vi-ant-design:save-twotone', + 'vi-ant-design:scan-outlined', + 'vi-ant-design:schedule-filled', + 'vi-ant-design:schedule-outlined', + 'vi-ant-design:schedule-twotone', + 'vi-ant-design:scissor-outlined', + 'vi-ant-design:search-outlined', + 'vi-ant-design:security-scan-filled', + 'vi-ant-design:security-scan-outlined', + 'vi-ant-design:security-scan-twotone', + 'vi-ant-design:select-outlined', + 'vi-ant-design:send-outlined', + 'vi-ant-design:setting-filled', + 'vi-ant-design:setting-outlined', + 'vi-ant-design:setting-twotone', + 'vi-ant-design:shake-outlined', + 'vi-ant-design:share-alt-outlined', + 'vi-ant-design:shop-filled', + 'vi-ant-design:shop-outlined', + 'vi-ant-design:shop-twotone', + 'vi-ant-design:shopping-cart-outlined', + 'vi-ant-design:shopping-filled', + 'vi-ant-design:shopping-outlined', + 'vi-ant-design:shopping-twotone', + 'vi-ant-design:shrink-outlined', + 'vi-ant-design:signal-filled', + 'vi-ant-design:signature-filled', + 'vi-ant-design:signature-outlined', + 'vi-ant-design:sisternode-outlined', + 'vi-ant-design:sketch-circle-filled', + 'vi-ant-design:sketch-outlined', + 'vi-ant-design:sketch-square-filled', + 'vi-ant-design:skin-filled', + 'vi-ant-design:skin-outlined', + 'vi-ant-design:skin-twotone', + 'vi-ant-design:skype-filled', + 'vi-ant-design:skype-outlined', + 'vi-ant-design:slack-circle-filled', + 'vi-ant-design:slack-outlined', + 'vi-ant-design:slack-square-filled', + 'vi-ant-design:slack-square-outlined', + 'vi-ant-design:sliders-filled', + 'vi-ant-design:sliders-outlined', + 'vi-ant-design:sliders-twotone', + 'vi-ant-design:small-dash-outlined', + 'vi-ant-design:smile-filled', + 'vi-ant-design:smile-outlined', + 'vi-ant-design:smile-twotone', + 'vi-ant-design:snippets-filled', + 'vi-ant-design:snippets-outlined', + 'vi-ant-design:snippets-twotone', + 'vi-ant-design:solution-outlined', + 'vi-ant-design:sort-ascending-outlined', + 'vi-ant-design:sort-descending-outlined', + 'vi-ant-design:sound-filled', + 'vi-ant-design:sound-outlined', + 'vi-ant-design:sound-twotone', + 'vi-ant-design:split-cells-outlined', + 'vi-ant-design:spotify-filled', + 'vi-ant-design:spotify-outlined', + 'vi-ant-design:star-filled', + 'vi-ant-design:star-outlined', + 'vi-ant-design:star-twotone', + 'vi-ant-design:step-backward-filled', + 'vi-ant-design:step-backward-outlined', + 'vi-ant-design:step-forward-filled', + 'vi-ant-design:step-forward-outlined', + 'vi-ant-design:stock-outlined', + 'vi-ant-design:stop-filled', + 'vi-ant-design:stop-outlined', + 'vi-ant-design:stop-twotone', + 'vi-ant-design:strikethrough-outlined', + 'vi-ant-design:subnode-outlined', + 'vi-ant-design:sun-filled', + 'vi-ant-design:sun-outlined', + 'vi-ant-design:swap-left-outlined', + 'vi-ant-design:swap-outlined', + 'vi-ant-design:swap-right-outlined', + 'vi-ant-design:switcher-filled', + 'vi-ant-design:switcher-outlined', + 'vi-ant-design:switcher-twotone', + 'vi-ant-design:sync-outlined', + 'vi-ant-design:table-outlined', + 'vi-ant-design:tablet-filled', + 'vi-ant-design:tablet-outlined', + 'vi-ant-design:tablet-twotone', + 'vi-ant-design:tag-filled', + 'vi-ant-design:tag-outlined', + 'vi-ant-design:tag-twotone', + 'vi-ant-design:tags-filled', + 'vi-ant-design:tags-outlined', + 'vi-ant-design:tags-twotone', + 'vi-ant-design:taobao-circle-filled', + 'vi-ant-design:taobao-circle-outlined', + 'vi-ant-design:taobao-outlined', + 'vi-ant-design:taobao-square-filled', + 'vi-ant-design:team-outlined', + 'vi-ant-design:thunderbolt-filled', + 'vi-ant-design:thunderbolt-outlined', + 'vi-ant-design:thunderbolt-twotone', + 'vi-ant-design:tik-tok-filled', + 'vi-ant-design:tik-tok-outlined', + 'vi-ant-design:to-top-outlined', + 'vi-ant-design:tool-filled', + 'vi-ant-design:tool-outlined', + 'vi-ant-design:tool-twotone', + 'vi-ant-design:trademark-circle-filled', + 'vi-ant-design:trademark-circle-outlined', + 'vi-ant-design:trademark-circle-twotone', + 'vi-ant-design:trademark-outlined', + 'vi-ant-design:transaction-outlined', + 'vi-ant-design:translation-outlined', + 'vi-ant-design:trophy-filled', + 'vi-ant-design:trophy-outlined', + 'vi-ant-design:trophy-twotone', + 'vi-ant-design:truck-filled', + 'vi-ant-design:truck-outlined', + 'vi-ant-design:twitch-outlined', + 'vi-ant-design:twitter-circle-filled', + 'vi-ant-design:twitter-outlined', + 'vi-ant-design:twitter-square-filled', + 'vi-ant-design:underline-outlined', + 'vi-ant-design:undo-outlined', + 'vi-ant-design:ungroup-outlined', + 'vi-ant-design:unlock-filled', + 'vi-ant-design:unlock-outlined', + 'vi-ant-design:unlock-twotone', + 'vi-ant-design:unordered-list-outlined', + 'vi-ant-design:up-circle-filled', + 'vi-ant-design:up-circle-outlined', + 'vi-ant-design:up-circle-twotone', + 'vi-ant-design:up-outlined', + 'vi-ant-design:up-square-filled', + 'vi-ant-design:up-square-outlined', + 'vi-ant-design:up-square-twotone', + 'vi-ant-design:upload-outlined', + 'vi-ant-design:usb-filled', + 'vi-ant-design:usb-outlined', + 'vi-ant-design:usb-twotone', + 'vi-ant-design:user-add-outlined', + 'vi-ant-design:user-delete-outlined', + 'vi-ant-design:user-outlined', + 'vi-ant-design:user-switch-outlined', + 'vi-ant-design:usergroup-add-outlined', + 'vi-ant-design:usergroup-delete-outlined', + 'vi-ant-design:verified-outlined', + 'vi-ant-design:vertical-align-bottom-outlined', + 'vi-ant-design:vertical-align-middle-outlined', + 'vi-ant-design:vertical-align-top-outlined', + 'vi-ant-design:vertical-left-outlined', + 'vi-ant-design:vertical-right-outlined', + 'vi-ant-design:video-camera-add-outlined', + 'vi-ant-design:video-camera-filled', + 'vi-ant-design:video-camera-outlined', + 'vi-ant-design:video-camera-twotone', + 'vi-ant-design:wallet-filled', + 'vi-ant-design:wallet-outlined', + 'vi-ant-design:wallet-twotone', + 'vi-ant-design:warning-filled', + 'vi-ant-design:warning-outlined', + 'vi-ant-design:warning-twotone', + 'vi-ant-design:wechat-filled', + 'vi-ant-design:wechat-outlined', + 'vi-ant-design:wechat-work-filled', + 'vi-ant-design:wechat-work-outlined', + 'vi-ant-design:weibo-circle-filled', + 'vi-ant-design:weibo-circle-outlined', + 'vi-ant-design:weibo-outlined', + 'vi-ant-design:weibo-square-filled', + 'vi-ant-design:weibo-square-outlined', + 'vi-ant-design:whats-app-outlined', + 'vi-ant-design:wifi-outlined', + 'vi-ant-design:windows-filled', + 'vi-ant-design:windows-outlined', + 'vi-ant-design:woman-outlined', + 'vi-ant-design:x-filled', + 'vi-ant-design:x-outlined', + 'vi-ant-design:yahoo-filled', + 'vi-ant-design:yahoo-outlined', + 'vi-ant-design:youtube-filled', + 'vi-ant-design:youtube-outlined', + 'vi-ant-design:yuque-filled', + 'vi-ant-design:yuque-outlined', + 'vi-ant-design:zhihu-circle-filled', + 'vi-ant-design:zhihu-outlined', + 'vi-ant-design:zhihu-square-filled', + 'vi-ant-design:zoom-in-outlined', + 'vi-ant-design:zoom-out-outlined' + ] +} diff --git a/promo-ui2/src/components/IconPicker/src/data/icons.ep.ts b/promo-ui2/src/components/IconPicker/src/data/icons.ep.ts new file mode 100644 index 0000000..b3918be --- /dev/null +++ b/promo-ui2/src/components/IconPicker/src/data/icons.ep.ts @@ -0,0 +1,299 @@ +export default { + name: 'Element Plus', + prefix: 'vi-ep', + icons: [ + 'vi-ep:add-location', + 'vi-ep:aim', + 'vi-ep:alarm-clock', + 'vi-ep:apple', + 'vi-ep:arrow-down', + 'vi-ep:arrow-down-bold', + 'vi-ep:arrow-left', + 'vi-ep:arrow-left-bold', + 'vi-ep:arrow-right', + 'vi-ep:arrow-right-bold', + 'vi-ep:arrow-up', + 'vi-ep:arrow-up-bold', + 'vi-ep:avatar', + 'vi-ep:back', + 'vi-ep:baseball', + 'vi-ep:basketball', + 'vi-ep:bell', + 'vi-ep:bell-filled', + 'vi-ep:bicycle', + 'vi-ep:bottom', + 'vi-ep:bottom-left', + 'vi-ep:bottom-right', + 'vi-ep:bowl', + 'vi-ep:box', + 'vi-ep:briefcase', + 'vi-ep:brush', + 'vi-ep:brush-filled', + 'vi-ep:burger', + 'vi-ep:calendar', + 'vi-ep:camera', + 'vi-ep:camera-filled', + 'vi-ep:caret-bottom', + 'vi-ep:caret-left', + 'vi-ep:caret-right', + 'vi-ep:caret-top', + 'vi-ep:cellphone', + 'vi-ep:chat-dot-round', + 'vi-ep:chat-dot-square', + 'vi-ep:chat-line-round', + 'vi-ep:chat-line-square', + 'vi-ep:chat-round', + 'vi-ep:chat-square', + 'vi-ep:check', + 'vi-ep:checked', + 'vi-ep:cherry', + 'vi-ep:chicken', + 'vi-ep:chrome-filled', + 'vi-ep:circle-check', + 'vi-ep:circle-check-filled', + 'vi-ep:circle-close', + 'vi-ep:circle-close-filled', + 'vi-ep:circle-plus', + 'vi-ep:circle-plus-filled', + 'vi-ep:clock', + 'vi-ep:close', + 'vi-ep:close-bold', + 'vi-ep:cloudy', + 'vi-ep:coffee', + 'vi-ep:coffee-cup', + 'vi-ep:coin', + 'vi-ep:cold-drink', + 'vi-ep:collection', + 'vi-ep:collection-tag', + 'vi-ep:comment', + 'vi-ep:compass', + 'vi-ep:connection', + 'vi-ep:coordinate', + 'vi-ep:copy-document', + 'vi-ep:cpu', + 'vi-ep:credit-card', + 'vi-ep:crop', + 'vi-ep:d-arrow-left', + 'vi-ep:d-arrow-right', + 'vi-ep:d-caret', + 'vi-ep:data-analysis', + 'vi-ep:data-board', + 'vi-ep:data-line', + 'vi-ep:delete', + 'vi-ep:delete-filled', + 'vi-ep:delete-location', + 'vi-ep:dessert', + 'vi-ep:discount', + 'vi-ep:dish', + 'vi-ep:dish-dot', + 'vi-ep:document', + 'vi-ep:document-add', + 'vi-ep:document-checked', + 'vi-ep:document-copy', + 'vi-ep:document-delete', + 'vi-ep:document-remove', + 'vi-ep:download', + 'vi-ep:drizzling', + 'vi-ep:edit', + 'vi-ep:edit-pen', + 'vi-ep:eleme', + 'vi-ep:eleme-filled', + 'vi-ep:element-plus', + 'vi-ep:expand', + 'vi-ep:failed', + 'vi-ep:female', + 'vi-ep:files', + 'vi-ep:film', + 'vi-ep:filter', + 'vi-ep:finished', + 'vi-ep:first-aid-kit', + 'vi-ep:flag', + 'vi-ep:fold', + 'vi-ep:folder', + 'vi-ep:folder-add', + 'vi-ep:folder-checked', + 'vi-ep:folder-delete', + 'vi-ep:folder-opened', + 'vi-ep:folder-remove', + 'vi-ep:food', + 'vi-ep:football', + 'vi-ep:fork-spoon', + 'vi-ep:fries', + 'vi-ep:full-screen', + 'vi-ep:goblet', + 'vi-ep:goblet-full', + 'vi-ep:goblet-square', + 'vi-ep:goblet-square-full', + 'vi-ep:gold-medal', + 'vi-ep:goods', + 'vi-ep:goods-filled', + 'vi-ep:grape', + 'vi-ep:grid', + 'vi-ep:guide', + 'vi-ep:handbag', + 'vi-ep:headset', + 'vi-ep:help', + 'vi-ep:help-filled', + 'vi-ep:hide', + 'vi-ep:histogram', + 'vi-ep:home-filled', + 'vi-ep:hot-water', + 'vi-ep:house', + 'vi-ep:ice-cream', + 'vi-ep:ice-cream-round', + 'vi-ep:ice-cream-square', + 'vi-ep:ice-drink', + 'vi-ep:ice-tea', + 'vi-ep:info-filled', + 'vi-ep:iphone', + 'vi-ep:key', + 'vi-ep:knife-fork', + 'vi-ep:lightning', + 'vi-ep:link', + 'vi-ep:list', + 'vi-ep:loading', + 'vi-ep:location', + 'vi-ep:location-filled', + 'vi-ep:location-information', + 'vi-ep:lock', + 'vi-ep:lollipop', + 'vi-ep:magic-stick', + 'vi-ep:magnet', + 'vi-ep:male', + 'vi-ep:management', + 'vi-ep:map-location', + 'vi-ep:medal', + 'vi-ep:memo', + 'vi-ep:menu', + 'vi-ep:message', + 'vi-ep:message-box', + 'vi-ep:mic', + 'vi-ep:microphone', + 'vi-ep:milk-tea', + 'vi-ep:minus', + 'vi-ep:money', + 'vi-ep:monitor', + 'vi-ep:moon', + 'vi-ep:moon-night', + 'vi-ep:more', + 'vi-ep:more-filled', + 'vi-ep:mostly-cloudy', + 'vi-ep:mouse', + 'vi-ep:mug', + 'vi-ep:mute', + 'vi-ep:mute-notification', + 'vi-ep:no-smoking', + 'vi-ep:notebook', + 'vi-ep:notification', + 'vi-ep:odometer', + 'vi-ep:office-building', + 'vi-ep:open', + 'vi-ep:operation', + 'vi-ep:opportunity', + 'vi-ep:orange', + 'vi-ep:paperclip', + 'vi-ep:partly-cloudy', + 'vi-ep:pear', + 'vi-ep:phone', + 'vi-ep:phone-filled', + 'vi-ep:picture', + 'vi-ep:picture-filled', + 'vi-ep:picture-rounded', + 'vi-ep:pie-chart', + 'vi-ep:place', + 'vi-ep:platform', + 'vi-ep:plus', + 'vi-ep:pointer', + 'vi-ep:position', + 'vi-ep:postcard', + 'vi-ep:pouring', + 'vi-ep:present', + 'vi-ep:price-tag', + 'vi-ep:printer', + 'vi-ep:promotion', + 'vi-ep:quartz-watch', + 'vi-ep:question-filled', + 'vi-ep:rank', + 'vi-ep:reading', + 'vi-ep:reading-lamp', + 'vi-ep:refresh', + 'vi-ep:refresh-left', + 'vi-ep:refresh-right', + 'vi-ep:refrigerator', + 'vi-ep:remove', + 'vi-ep:remove-filled', + 'vi-ep:right', + 'vi-ep:scale-to-original', + 'vi-ep:school', + 'vi-ep:scissor', + 'vi-ep:search', + 'vi-ep:select', + 'vi-ep:sell', + 'vi-ep:semi-select', + 'vi-ep:service', + 'vi-ep:set-up', + 'vi-ep:setting', + 'vi-ep:share', + 'vi-ep:ship', + 'vi-ep:shop', + 'vi-ep:shopping-bag', + 'vi-ep:shopping-cart', + 'vi-ep:shopping-cart-full', + 'vi-ep:shopping-trolley', + 'vi-ep:smoking', + 'vi-ep:soccer', + 'vi-ep:sold-out', + 'vi-ep:sort', + 'vi-ep:sort-down', + 'vi-ep:sort-up', + 'vi-ep:stamp', + 'vi-ep:star', + 'vi-ep:star-filled', + 'vi-ep:stopwatch', + 'vi-ep:success-filled', + 'vi-ep:sugar', + 'vi-ep:suitcase', + 'vi-ep:suitcase-line', + 'vi-ep:sunny', + 'vi-ep:sunrise', + 'vi-ep:sunset', + 'vi-ep:switch', + 'vi-ep:switch-button', + 'vi-ep:switch-filled', + 'vi-ep:takeaway-box', + 'vi-ep:ticket', + 'vi-ep:tickets', + 'vi-ep:timer', + 'vi-ep:toilet-paper', + 'vi-ep:tools', + 'vi-ep:top', + 'vi-ep:top-left', + 'vi-ep:top-right', + 'vi-ep:trend-charts', + 'vi-ep:trophy', + 'vi-ep:trophy-base', + 'vi-ep:turn-off', + 'vi-ep:umbrella', + 'vi-ep:unlock', + 'vi-ep:upload', + 'vi-ep:upload-filled', + 'vi-ep:user', + 'vi-ep:user-filled', + 'vi-ep:van', + 'vi-ep:video-camera', + 'vi-ep:video-camera-filled', + 'vi-ep:video-pause', + 'vi-ep:video-play', + 'vi-ep:view', + 'vi-ep:wallet', + 'vi-ep:wallet-filled', + 'vi-ep:warn-triangle-filled', + 'vi-ep:warning', + 'vi-ep:warning-filled', + 'vi-ep:watch', + 'vi-ep:watermelon', + 'vi-ep:wind-power', + 'vi-ep:zoom-in', + 'vi-ep:zoom-out' + ] +} diff --git a/promo-ui2/src/components/IconPicker/src/data/icons.tdesign.ts b/promo-ui2/src/components/IconPicker/src/data/icons.tdesign.ts new file mode 100644 index 0000000..77b298b --- /dev/null +++ b/promo-ui2/src/components/IconPicker/src/data/icons.tdesign.ts @@ -0,0 +1,1209 @@ +export default { + name: 'TDesign Icons', + prefix: 'vi-tdesign', + icons: [ + 'vi-tdesign:activity', + 'vi-tdesign:add', + 'vi-tdesign:add-and-subtract', + 'vi-tdesign:add-circle', + 'vi-tdesign:add-rectangle', + 'vi-tdesign:address-book', + 'vi-tdesign:adjustment', + 'vi-tdesign:airplay-wave', + 'vi-tdesign:alarm', + 'vi-tdesign:alarm-add', + 'vi-tdesign:alarm-off', + 'vi-tdesign:align-top', + 'vi-tdesign:align-vertical', + 'vi-tdesign:alpha', + 'vi-tdesign:analytics', + 'vi-tdesign:anchor', + 'vi-tdesign:angry', + 'vi-tdesign:animation', + 'vi-tdesign:animation-1', + 'vi-tdesign:anticlockwise', + 'vi-tdesign:api', + 'vi-tdesign:app', + 'vi-tdesign:apple', + 'vi-tdesign:application', + 'vi-tdesign:architecture-hui-style', + 'vi-tdesign:archway', + 'vi-tdesign:archway-1', + 'vi-tdesign:arrow-down', + 'vi-tdesign:arrow-down-circle', + 'vi-tdesign:arrow-down-rectangle', + 'vi-tdesign:arrow-left', + 'vi-tdesign:arrow-left-circle', + 'vi-tdesign:arrow-left-down', + 'vi-tdesign:arrow-left-down-circle', + 'vi-tdesign:arrow-left-right-1', + 'vi-tdesign:arrow-left-right-2', + 'vi-tdesign:arrow-left-right-3', + 'vi-tdesign:arrow-left-right-circle', + 'vi-tdesign:arrow-left-up', + 'vi-tdesign:arrow-left-up-circle', + 'vi-tdesign:arrow-right', + 'vi-tdesign:arrow-right-circle', + 'vi-tdesign:arrow-right-down', + 'vi-tdesign:arrow-right-down-circle', + 'vi-tdesign:arrow-right-up', + 'vi-tdesign:arrow-right-up-circle', + 'vi-tdesign:arrow-triangle-down', + 'vi-tdesign:arrow-triangle-down-filled', + 'vi-tdesign:arrow-triangle-up', + 'vi-tdesign:arrow-triangle-up-filled', + 'vi-tdesign:arrow-up', + 'vi-tdesign:arrow-up-circle', + 'vi-tdesign:arrow-up-down-1', + 'vi-tdesign:arrow-up-down-2', + 'vi-tdesign:arrow-up-down-3', + 'vi-tdesign:arrow-up-down-circle', + 'vi-tdesign:artboard', + 'vi-tdesign:article', + 'vi-tdesign:assignment', + 'vi-tdesign:assignment-checked', + 'vi-tdesign:assignment-code', + 'vi-tdesign:assignment-error', + 'vi-tdesign:assignment-user', + 'vi-tdesign:attach', + 'vi-tdesign:attic', + 'vi-tdesign:attic-1', + 'vi-tdesign:audio', + 'vi-tdesign:awkward', + 'vi-tdesign:backtop', + 'vi-tdesign:backtop-rectangle', + 'vi-tdesign:backup', + 'vi-tdesign:backward', + 'vi-tdesign:bad-laugh', + 'vi-tdesign:bamboo-shoot', + 'vi-tdesign:banana', + 'vi-tdesign:barbecue', + 'vi-tdesign:barcode', + 'vi-tdesign:barcode-1', + 'vi-tdesign:base-station', + 'vi-tdesign:battery', + 'vi-tdesign:battery-add', + 'vi-tdesign:battery-charging', + 'vi-tdesign:battery-low', + 'vi-tdesign:bean', + 'vi-tdesign:beer', + 'vi-tdesign:beta', + 'vi-tdesign:bifurcate', + 'vi-tdesign:bill', + 'vi-tdesign:blockchain', + 'vi-tdesign:bluetooth', + 'vi-tdesign:bone', + 'vi-tdesign:book', + 'vi-tdesign:book-open', + 'vi-tdesign:bookmark', + 'vi-tdesign:bookmark-add', + 'vi-tdesign:bookmark-checked', + 'vi-tdesign:bookmark-double', + 'vi-tdesign:bookmark-minus', + 'vi-tdesign:braces', + 'vi-tdesign:brackets', + 'vi-tdesign:bread', + 'vi-tdesign:bridge', + 'vi-tdesign:bridge-1', + 'vi-tdesign:bridge-2', + 'vi-tdesign:bridge-3', + 'vi-tdesign:bridge-4', + 'vi-tdesign:bridge-5', + 'vi-tdesign:bridge-6', + 'vi-tdesign:brightness', + 'vi-tdesign:brightness-1', + 'vi-tdesign:broccoli', + 'vi-tdesign:browse', + 'vi-tdesign:browse-gallery', + 'vi-tdesign:browse-off', + 'vi-tdesign:brush', + 'vi-tdesign:bug', + 'vi-tdesign:bug-report', + 'vi-tdesign:building', + 'vi-tdesign:building-1', + 'vi-tdesign:building-2', + 'vi-tdesign:building-3', + 'vi-tdesign:building-4', + 'vi-tdesign:building-5', + 'vi-tdesign:bulletpoint', + 'vi-tdesign:button', + 'vi-tdesign:cabbage', + 'vi-tdesign:cake', + 'vi-tdesign:calculation', + 'vi-tdesign:calculation-1', + 'vi-tdesign:calculator', + 'vi-tdesign:calculator-1', + 'vi-tdesign:calendar', + 'vi-tdesign:calendar-1', + 'vi-tdesign:calendar-2', + 'vi-tdesign:calendar-edit', + 'vi-tdesign:calendar-event', + 'vi-tdesign:call', + 'vi-tdesign:call-1', + 'vi-tdesign:call-cancel', + 'vi-tdesign:call-forwarded', + 'vi-tdesign:call-incoming', + 'vi-tdesign:call-off', + 'vi-tdesign:calm', + 'vi-tdesign:calm-1', + 'vi-tdesign:camera', + 'vi-tdesign:camera-1', + 'vi-tdesign:camera-2', + 'vi-tdesign:camera-off', + 'vi-tdesign:candy', + 'vi-tdesign:card', + 'vi-tdesign:cardmembership', + 'vi-tdesign:caret-down', + 'vi-tdesign:caret-down-small', + 'vi-tdesign:caret-left', + 'vi-tdesign:caret-left-small', + 'vi-tdesign:caret-right', + 'vi-tdesign:caret-right-small', + 'vi-tdesign:caret-up', + 'vi-tdesign:caret-up-small', + 'vi-tdesign:cart', + 'vi-tdesign:cart-add', + 'vi-tdesign:cast', + 'vi-tdesign:castle', + 'vi-tdesign:castle-1', + 'vi-tdesign:castle-2', + 'vi-tdesign:castle-3', + 'vi-tdesign:castle-4', + 'vi-tdesign:castle-5', + 'vi-tdesign:castle-6', + 'vi-tdesign:castle-7', + 'vi-tdesign:cat', + 'vi-tdesign:catalog', + 'vi-tdesign:cd', + 'vi-tdesign:celsius', + 'vi-tdesign:center-focus-strong', + 'vi-tdesign:centimeter', + 'vi-tdesign:certificate', + 'vi-tdesign:certificate-1', + 'vi-tdesign:chart', + 'vi-tdesign:chart-3d', + 'vi-tdesign:chart-add', + 'vi-tdesign:chart-analytics', + 'vi-tdesign:chart-area', + 'vi-tdesign:chart-area-multi', + 'vi-tdesign:chart-bar', + 'vi-tdesign:chart-bubble', + 'vi-tdesign:chart-colum', + 'vi-tdesign:chart-combo', + 'vi-tdesign:chart-line', + 'vi-tdesign:chart-line-data', + 'vi-tdesign:chart-line-data-1', + 'vi-tdesign:chart-line-multi', + 'vi-tdesign:chart-maximum', + 'vi-tdesign:chart-median', + 'vi-tdesign:chart-minimum', + 'vi-tdesign:chart-pie', + 'vi-tdesign:chart-radar', + 'vi-tdesign:chart-radial', + 'vi-tdesign:chart-ring', + 'vi-tdesign:chart-ring-1', + 'vi-tdesign:chart-scatter', + 'vi-tdesign:chart-stacked', + 'vi-tdesign:chat', + 'vi-tdesign:chat-add', + 'vi-tdesign:chat-bubble', + 'vi-tdesign:chat-bubble-1', + 'vi-tdesign:chat-bubble-add', + 'vi-tdesign:chat-bubble-error', + 'vi-tdesign:chat-bubble-help', + 'vi-tdesign:chat-bubble-history', + 'vi-tdesign:chat-bubble-locked', + 'vi-tdesign:chat-bubble-smile', + 'vi-tdesign:chat-checked', + 'vi-tdesign:chat-clear', + 'vi-tdesign:chat-double', + 'vi-tdesign:chat-error', + 'vi-tdesign:chat-heart', + 'vi-tdesign:chat-message', + 'vi-tdesign:chat-off', + 'vi-tdesign:chat-poll', + 'vi-tdesign:chat-setting', + 'vi-tdesign:check', + 'vi-tdesign:check-circle', + 'vi-tdesign:check-circle-filled', + 'vi-tdesign:check-double', + 'vi-tdesign:check-rectangle', + 'vi-tdesign:check-rectangle-filled', + 'vi-tdesign:cheese', + 'vi-tdesign:cherry', + 'vi-tdesign:chevron-down', + 'vi-tdesign:chevron-down-circle', + 'vi-tdesign:chevron-down-double', + 'vi-tdesign:chevron-down-double-s', + 'vi-tdesign:chevron-down-rectangle', + 'vi-tdesign:chevron-down-s', + 'vi-tdesign:chevron-left', + 'vi-tdesign:chevron-left-circle', + 'vi-tdesign:chevron-left-double', + 'vi-tdesign:chevron-left-double-s', + 'vi-tdesign:chevron-left-rectangle', + 'vi-tdesign:chevron-left-s', + 'vi-tdesign:chevron-right', + 'vi-tdesign:chevron-right-circle', + 'vi-tdesign:chevron-right-double', + 'vi-tdesign:chevron-right-double-s', + 'vi-tdesign:chevron-right-rectangle', + 'vi-tdesign:chevron-right-s', + 'vi-tdesign:chevron-up', + 'vi-tdesign:chevron-up-circle', + 'vi-tdesign:chevron-up-double', + 'vi-tdesign:chevron-up-double-s', + 'vi-tdesign:chevron-up-rectangle', + 'vi-tdesign:chevron-up-s', + 'vi-tdesign:chicken', + 'vi-tdesign:chili', + 'vi-tdesign:chimney', + 'vi-tdesign:chimney-1', + 'vi-tdesign:chimney-2', + 'vi-tdesign:chinese-cabbage', + 'vi-tdesign:church', + 'vi-tdesign:circle', + 'vi-tdesign:city', + 'vi-tdesign:city-1', + 'vi-tdesign:city-10', + 'vi-tdesign:city-11', + 'vi-tdesign:city-12', + 'vi-tdesign:city-13', + 'vi-tdesign:city-14', + 'vi-tdesign:city-15', + 'vi-tdesign:city-2', + 'vi-tdesign:city-3', + 'vi-tdesign:city-4', + 'vi-tdesign:city-5', + 'vi-tdesign:city-6', + 'vi-tdesign:city-7', + 'vi-tdesign:city-8', + 'vi-tdesign:city-9', + 'vi-tdesign:city-ancient', + 'vi-tdesign:city-ancient-1', + 'vi-tdesign:city-ancient-2', + 'vi-tdesign:clear', + 'vi-tdesign:clear-formatting', + 'vi-tdesign:clear-formatting-1', + 'vi-tdesign:close', + 'vi-tdesign:close-circle', + 'vi-tdesign:close-circle-filled', + 'vi-tdesign:close-octagon', + 'vi-tdesign:close-rectangle', + 'vi-tdesign:cloud', + 'vi-tdesign:cloud-download', + 'vi-tdesign:cloud-upload', + 'vi-tdesign:cloudy-day', + 'vi-tdesign:cloudy-night', + 'vi-tdesign:cloudy-night-rain', + 'vi-tdesign:cloudy-rain', + 'vi-tdesign:cloudy-sunny', + 'vi-tdesign:code', + 'vi-tdesign:code-1', + 'vi-tdesign:code-off', + 'vi-tdesign:cola', + 'vi-tdesign:collage', + 'vi-tdesign:collection', + 'vi-tdesign:color-invert', + 'vi-tdesign:combination', + 'vi-tdesign:command', + 'vi-tdesign:compass', + 'vi-tdesign:compass-1', + 'vi-tdesign:component-breadcrumb', + 'vi-tdesign:component-checkbox', + 'vi-tdesign:component-divider-horizontal', + 'vi-tdesign:component-divider-vertical', + 'vi-tdesign:component-dropdown', + 'vi-tdesign:component-grid', + 'vi-tdesign:component-input', + 'vi-tdesign:component-layout', + 'vi-tdesign:component-radio', + 'vi-tdesign:component-space', + 'vi-tdesign:component-steps', + 'vi-tdesign:component-switch', + 'vi-tdesign:constraint', + 'vi-tdesign:contrast', + 'vi-tdesign:contrast-1', + 'vi-tdesign:control-platform', + 'vi-tdesign:cooperate', + 'vi-tdesign:coordinate-system', + 'vi-tdesign:copy', + 'vi-tdesign:copyright', + 'vi-tdesign:corn', + 'vi-tdesign:coupon', + 'vi-tdesign:course', + 'vi-tdesign:cpu', + 'vi-tdesign:crack', + 'vi-tdesign:creditcard', + 'vi-tdesign:creditcard-add', + 'vi-tdesign:creditcard-off', + 'vi-tdesign:crooked-smile', + 'vi-tdesign:cry-and-laugh', + 'vi-tdesign:cry-loudly', + 'vi-tdesign:css3', + 'vi-tdesign:cucumber', + 'vi-tdesign:currency-exchange', + 'vi-tdesign:cursor', + 'vi-tdesign:curtain', + 'vi-tdesign:curve', + 'vi-tdesign:cut', + 'vi-tdesign:cut-1', + 'vi-tdesign:dam', + 'vi-tdesign:dam-1', + 'vi-tdesign:dam-2', + 'vi-tdesign:dam-3', + 'vi-tdesign:dam-4', + 'vi-tdesign:dam-5', + 'vi-tdesign:dam-6', + 'vi-tdesign:dam-7', + 'vi-tdesign:dart-board', + 'vi-tdesign:dashboard', + 'vi-tdesign:dashboard-1', + 'vi-tdesign:data', + 'vi-tdesign:data-base', + 'vi-tdesign:data-checked', + 'vi-tdesign:data-display', + 'vi-tdesign:data-error', + 'vi-tdesign:data-search', + 'vi-tdesign:delete', + 'vi-tdesign:delete-1', + 'vi-tdesign:delete-time', + 'vi-tdesign:delta', + 'vi-tdesign:depressed', + 'vi-tdesign:desktop', + 'vi-tdesign:desktop-1', + 'vi-tdesign:despise', + 'vi-tdesign:device', + 'vi-tdesign:discount', + 'vi-tdesign:discount-filled', + 'vi-tdesign:dissatisfaction', + 'vi-tdesign:divide', + 'vi-tdesign:dividers', + 'vi-tdesign:dividers-1', + 'vi-tdesign:doge', + 'vi-tdesign:double-storey', + 'vi-tdesign:download', + 'vi-tdesign:download-1', + 'vi-tdesign:downscale', + 'vi-tdesign:drag-drop', + 'vi-tdesign:drag-move', + 'vi-tdesign:drink', + 'vi-tdesign:drumstick', + 'vi-tdesign:dv', + 'vi-tdesign:dvd', + 'vi-tdesign:earphone', + 'vi-tdesign:earth', + 'vi-tdesign:edit', + 'vi-tdesign:edit-1', + 'vi-tdesign:edit-2', + 'vi-tdesign:edit-off', + 'vi-tdesign:education', + 'vi-tdesign:eggplant', + 'vi-tdesign:ellipsis', + 'vi-tdesign:emo-emotional', + 'vi-tdesign:enter', + 'vi-tdesign:equal', + 'vi-tdesign:error', + 'vi-tdesign:error-circle', + 'vi-tdesign:error-circle-filled', + 'vi-tdesign:error-triangle', + 'vi-tdesign:excited', + 'vi-tdesign:excited-1', + 'vi-tdesign:expand-horizontal', + 'vi-tdesign:expand-vertical', + 'vi-tdesign:explore', + 'vi-tdesign:explore-off', + 'vi-tdesign:exposure', + 'vi-tdesign:extension', + 'vi-tdesign:extension-off', + 'vi-tdesign:face-retouching', + 'vi-tdesign:fact-check', + 'vi-tdesign:fahrenheit-scale', + 'vi-tdesign:feel-at-ease', + 'vi-tdesign:ferocious', + 'vi-tdesign:ferris-wheel', + 'vi-tdesign:file', + 'vi-tdesign:file-1', + 'vi-tdesign:file-add', + 'vi-tdesign:file-add-1', + 'vi-tdesign:file-attachment', + 'vi-tdesign:file-blocked', + 'vi-tdesign:file-code', + 'vi-tdesign:file-code-1', + 'vi-tdesign:file-copy', + 'vi-tdesign:file-download', + 'vi-tdesign:file-excel', + 'vi-tdesign:file-export', + 'vi-tdesign:file-icon', + 'vi-tdesign:file-image', + 'vi-tdesign:file-import', + 'vi-tdesign:file-locked', + 'vi-tdesign:file-minus', + 'vi-tdesign:file-music', + 'vi-tdesign:file-onenote', + 'vi-tdesign:file-outlook', + 'vi-tdesign:file-paste', + 'vi-tdesign:file-pdf', + 'vi-tdesign:file-powerpoint', + 'vi-tdesign:file-restore', + 'vi-tdesign:file-safety', + 'vi-tdesign:file-search', + 'vi-tdesign:file-setting', + 'vi-tdesign:file-teams', + 'vi-tdesign:file-unknown', + 'vi-tdesign:file-unlocked', + 'vi-tdesign:file-word', + 'vi-tdesign:file-zip', + 'vi-tdesign:fill-color', + 'vi-tdesign:fill-color-1', + 'vi-tdesign:film', + 'vi-tdesign:film-1', + 'vi-tdesign:filter', + 'vi-tdesign:filter-1', + 'vi-tdesign:filter-2', + 'vi-tdesign:filter-3', + 'vi-tdesign:filter-clear', + 'vi-tdesign:filter-off', + 'vi-tdesign:fingerprint', + 'vi-tdesign:fingerprint-1', + 'vi-tdesign:fingerprint-2', + 'vi-tdesign:fingerprint-3', + 'vi-tdesign:fish', + 'vi-tdesign:flag', + 'vi-tdesign:flag-1', + 'vi-tdesign:flag-2', + 'vi-tdesign:flag-3', + 'vi-tdesign:flag-4', + 'vi-tdesign:flashlight', + 'vi-tdesign:flight-landing', + 'vi-tdesign:flight-takeoff', + 'vi-tdesign:flip-smiling-face', + 'vi-tdesign:flip-to-back', + 'vi-tdesign:flip-to-front', + 'vi-tdesign:focus', + 'vi-tdesign:fog', + 'vi-tdesign:fog-night', + 'vi-tdesign:fog-sunny', + 'vi-tdesign:folder', + 'vi-tdesign:folder-1', + 'vi-tdesign:folder-add', + 'vi-tdesign:folder-add-1', + 'vi-tdesign:folder-blocked', + 'vi-tdesign:folder-details', + 'vi-tdesign:folder-export', + 'vi-tdesign:folder-import', + 'vi-tdesign:folder-locked', + 'vi-tdesign:folder-minus', + 'vi-tdesign:folder-move', + 'vi-tdesign:folder-off', + 'vi-tdesign:folder-open', + 'vi-tdesign:folder-open-1', + 'vi-tdesign:folder-search', + 'vi-tdesign:folder-setting', + 'vi-tdesign:folder-shared', + 'vi-tdesign:folder-unlocked', + 'vi-tdesign:folder-zip', + 'vi-tdesign:forest', + 'vi-tdesign:fork', + 'vi-tdesign:form', + 'vi-tdesign:format-horizontal-align-bottom', + 'vi-tdesign:format-horizontal-align-center', + 'vi-tdesign:format-horizontal-align-top', + 'vi-tdesign:format-vertical-align-center', + 'vi-tdesign:format-vertical-align-left', + 'vi-tdesign:format-vertical-align-right', + 'vi-tdesign:forward', + 'vi-tdesign:frame', + 'vi-tdesign:frame-1', + 'vi-tdesign:fries', + 'vi-tdesign:fullscreen', + 'vi-tdesign:fullscreen-1', + 'vi-tdesign:fullscreen-2', + 'vi-tdesign:fullscreen-exit', + 'vi-tdesign:fullscreen-exit-1', + 'vi-tdesign:function-curve', + 'vi-tdesign:functions', + 'vi-tdesign:functions-1', + 'vi-tdesign:gamepad', + 'vi-tdesign:gamepad-1', + 'vi-tdesign:gamma', + 'vi-tdesign:garlic', + 'vi-tdesign:gender-female', + 'vi-tdesign:gender-male', + 'vi-tdesign:gesture-applause', + 'vi-tdesign:gesture-click', + 'vi-tdesign:gesture-down', + 'vi-tdesign:gesture-expansion', + 'vi-tdesign:gesture-left', + 'vi-tdesign:gesture-left-slip', + 'vi-tdesign:gesture-pray', + 'vi-tdesign:gesture-pray-1', + 'vi-tdesign:gesture-press', + 'vi-tdesign:gesture-ranslation', + 'vi-tdesign:gesture-ranslation-1', + 'vi-tdesign:gesture-right', + 'vi-tdesign:gesture-right-slip', + 'vi-tdesign:gesture-slide-up', + 'vi-tdesign:gesture-up', + 'vi-tdesign:gesture-up-1', + 'vi-tdesign:gesture-up-2', + 'vi-tdesign:gesture-up-and-down', + 'vi-tdesign:gesture-wipe-down', + 'vi-tdesign:gift', + 'vi-tdesign:giggle', + 'vi-tdesign:git-branch', + 'vi-tdesign:git-commit', + 'vi-tdesign:git-merge', + 'vi-tdesign:git-pull-request', + 'vi-tdesign:git-repository', + 'vi-tdesign:git-repository-commits', + 'vi-tdesign:git-repository-private', + 'vi-tdesign:gps', + 'vi-tdesign:grape', + 'vi-tdesign:greater-than', + 'vi-tdesign:greater-than-or-equal', + 'vi-tdesign:green-onion', + 'vi-tdesign:grid-add', + 'vi-tdesign:grid-view', + 'vi-tdesign:guitar', + 'vi-tdesign:hamburger', + 'vi-tdesign:happy', + 'vi-tdesign:hard-disk-storage', + 'vi-tdesign:hard-drive', + 'vi-tdesign:hashtag', + 'vi-tdesign:hd', + 'vi-tdesign:heart', + 'vi-tdesign:heart-filled', + 'vi-tdesign:help', + 'vi-tdesign:help-circle', + 'vi-tdesign:help-circle-filled', + 'vi-tdesign:help-rectangle', + 'vi-tdesign:highlight', + 'vi-tdesign:highlight-1', + 'vi-tdesign:history', + 'vi-tdesign:history-setting', + 'vi-tdesign:home', + 'vi-tdesign:hospital', + 'vi-tdesign:hospital-1', + 'vi-tdesign:hotspot-wave', + 'vi-tdesign:hourglass', + 'vi-tdesign:houses', + 'vi-tdesign:houses-1', + 'vi-tdesign:houses-2', + 'vi-tdesign:html5', + 'vi-tdesign:https', + 'vi-tdesign:ice-cream', + 'vi-tdesign:icon', + 'vi-tdesign:image', + 'vi-tdesign:image-1', + 'vi-tdesign:image-add', + 'vi-tdesign:image-edit', + 'vi-tdesign:image-error', + 'vi-tdesign:image-off', + 'vi-tdesign:image-search', + 'vi-tdesign:indent-left', + 'vi-tdesign:indent-right', + 'vi-tdesign:indicator', + 'vi-tdesign:info-circle', + 'vi-tdesign:info-circle-filled', + 'vi-tdesign:ink', + 'vi-tdesign:install', + 'vi-tdesign:install-desktop', + 'vi-tdesign:install-mobile', + 'vi-tdesign:institution', + 'vi-tdesign:institution-checked', + 'vi-tdesign:internet', + 'vi-tdesign:ipod', + 'vi-tdesign:joyful', + 'vi-tdesign:jump', + 'vi-tdesign:jump-off', + 'vi-tdesign:keyboard', + 'vi-tdesign:laptop', + 'vi-tdesign:layers', + 'vi-tdesign:layout', + 'vi-tdesign:leaderboard', + 'vi-tdesign:lemon', + 'vi-tdesign:lemon-slice', + 'vi-tdesign:less-than', + 'vi-tdesign:less-than-or-equal', + 'vi-tdesign:letters-a', + 'vi-tdesign:letters-b', + 'vi-tdesign:letters-c', + 'vi-tdesign:letters-d', + 'vi-tdesign:letters-e', + 'vi-tdesign:letters-f', + 'vi-tdesign:letters-g', + 'vi-tdesign:letters-h', + 'vi-tdesign:letters-i', + 'vi-tdesign:letters-j', + 'vi-tdesign:letters-k', + 'vi-tdesign:letters-l', + 'vi-tdesign:letters-m', + 'vi-tdesign:letters-n', + 'vi-tdesign:letters-o', + 'vi-tdesign:letters-p', + 'vi-tdesign:letters-q', + 'vi-tdesign:letters-r', + 'vi-tdesign:letters-s', + 'vi-tdesign:letters-t', + 'vi-tdesign:letters-u', + 'vi-tdesign:letters-v', + 'vi-tdesign:letters-w', + 'vi-tdesign:letters-x', + 'vi-tdesign:letters-y', + 'vi-tdesign:letters-z', + 'vi-tdesign:lightbulb', + 'vi-tdesign:lightbulb-circle', + 'vi-tdesign:lighthouse', + 'vi-tdesign:lighthouse-1', + 'vi-tdesign:lighthouse-2', + 'vi-tdesign:lighting-circle', + 'vi-tdesign:line-height', + 'vi-tdesign:link', + 'vi-tdesign:link-1', + 'vi-tdesign:link-unlink', + 'vi-tdesign:liquor', + 'vi-tdesign:list', + 'vi-tdesign:load', + 'vi-tdesign:loading', + 'vi-tdesign:location', + 'vi-tdesign:location-1', + 'vi-tdesign:location-enlargement', + 'vi-tdesign:location-error', + 'vi-tdesign:location-parking-place', + 'vi-tdesign:location-reduction', + 'vi-tdesign:location-setting', + 'vi-tdesign:lock-off', + 'vi-tdesign:lock-on', + 'vi-tdesign:lock-time', + 'vi-tdesign:login', + 'vi-tdesign:logo-adobe-illustrate', + 'vi-tdesign:logo-adobe-photoshop', + 'vi-tdesign:logo-adobe-photoshop-1', + 'vi-tdesign:logo-android', + 'vi-tdesign:logo-apple', + 'vi-tdesign:logo-apple-filled', + 'vi-tdesign:logo-behance', + 'vi-tdesign:logo-chrome', + 'vi-tdesign:logo-chrome-filled', + 'vi-tdesign:logo-cinema4d', + 'vi-tdesign:logo-codepen', + 'vi-tdesign:logo-codesandbox', + 'vi-tdesign:logo-dribbble', + 'vi-tdesign:logo-facebook', + 'vi-tdesign:logo-figma', + 'vi-tdesign:logo-framer', + 'vi-tdesign:logo-github', + 'vi-tdesign:logo-github-filled', + 'vi-tdesign:logo-gitlab', + 'vi-tdesign:logo-ie', + 'vi-tdesign:logo-ie-filled', + 'vi-tdesign:logo-instagram', + 'vi-tdesign:logo-qq', + 'vi-tdesign:logo-twitter', + 'vi-tdesign:logo-wechat', + 'vi-tdesign:logo-wechat-stroke', + 'vi-tdesign:logo-wecom', + 'vi-tdesign:logo-windows', + 'vi-tdesign:logo-windows-filled', + 'vi-tdesign:logo-youtube', + 'vi-tdesign:logout', + 'vi-tdesign:look-around', + 'vi-tdesign:loudspeaker', + 'vi-tdesign:mail', + 'vi-tdesign:map', + 'vi-tdesign:map-3d', + 'vi-tdesign:map-add', + 'vi-tdesign:map-aiming', + 'vi-tdesign:map-blocked', + 'vi-tdesign:map-bubble', + 'vi-tdesign:map-cancel', + 'vi-tdesign:map-chat', + 'vi-tdesign:map-checked', + 'vi-tdesign:map-collection', + 'vi-tdesign:map-connection', + 'vi-tdesign:map-distance', + 'vi-tdesign:map-double', + 'vi-tdesign:map-edit', + 'vi-tdesign:map-grid', + 'vi-tdesign:map-information', + 'vi-tdesign:map-information-1', + 'vi-tdesign:map-information-2', + 'vi-tdesign:map-location', + 'vi-tdesign:map-locked', + 'vi-tdesign:map-marked', + 'vi-tdesign:map-navigation', + 'vi-tdesign:map-outline', + 'vi-tdesign:map-route-planning', + 'vi-tdesign:map-ruler', + 'vi-tdesign:map-safety', + 'vi-tdesign:map-search', + 'vi-tdesign:map-search-1', + 'vi-tdesign:map-setting', + 'vi-tdesign:map-unlocked', + 'vi-tdesign:mark-as-unread', + 'vi-tdesign:markup', + 'vi-tdesign:mathematics', + 'vi-tdesign:measurement', + 'vi-tdesign:measurement-1', + 'vi-tdesign:measurement-2', + 'vi-tdesign:meat-pepper', + 'vi-tdesign:media-library', + 'vi-tdesign:member', + 'vi-tdesign:menu', + 'vi-tdesign:menu-application', + 'vi-tdesign:menu-fold', + 'vi-tdesign:menu-unfold', + 'vi-tdesign:merge-cells', + 'vi-tdesign:microphone', + 'vi-tdesign:microphone-1', + 'vi-tdesign:microphone-2', + 'vi-tdesign:milk', + 'vi-tdesign:minus', + 'vi-tdesign:minus-circle', + 'vi-tdesign:minus-circle-filled', + 'vi-tdesign:minus-rectangle', + 'vi-tdesign:minus-rectangle-filled', + 'vi-tdesign:mirror', + 'vi-tdesign:mobile', + 'vi-tdesign:mobile-blocked', + 'vi-tdesign:mobile-list', + 'vi-tdesign:mobile-navigation', + 'vi-tdesign:mobile-shortcut', + 'vi-tdesign:mobile-vibrate', + 'vi-tdesign:mode-dark', + 'vi-tdesign:mode-light', + 'vi-tdesign:module', + 'vi-tdesign:money', + 'vi-tdesign:monument', + 'vi-tdesign:moon', + 'vi-tdesign:moon-fall', + 'vi-tdesign:moon-rising', + 'vi-tdesign:more', + 'vi-tdesign:mosque', + 'vi-tdesign:mosque-1', + 'vi-tdesign:mouse', + 'vi-tdesign:move', + 'vi-tdesign:move-1', + 'vi-tdesign:movie-clapper', + 'vi-tdesign:multiply', + 'vi-tdesign:museum', + 'vi-tdesign:museum-1', + 'vi-tdesign:museum-2', + 'vi-tdesign:mushroom', + 'vi-tdesign:mushroom-1', + 'vi-tdesign:music', + 'vi-tdesign:music-1', + 'vi-tdesign:music-2', + 'vi-tdesign:music-rectangle-add', + 'vi-tdesign:navigation-arrow', + 'vi-tdesign:next', + 'vi-tdesign:no-expression', + 'vi-tdesign:noodle', + 'vi-tdesign:notification', + 'vi-tdesign:notification-add', + 'vi-tdesign:notification-circle', + 'vi-tdesign:notification-error', + 'vi-tdesign:notification-filled', + 'vi-tdesign:numbers-0', + 'vi-tdesign:numbers-0-1', + 'vi-tdesign:numbers-1', + 'vi-tdesign:numbers-1-1', + 'vi-tdesign:numbers-2', + 'vi-tdesign:numbers-2-1', + 'vi-tdesign:numbers-3', + 'vi-tdesign:numbers-3-1', + 'vi-tdesign:numbers-4', + 'vi-tdesign:numbers-4-1', + 'vi-tdesign:numbers-5', + 'vi-tdesign:numbers-5-1', + 'vi-tdesign:numbers-6', + 'vi-tdesign:numbers-6-1', + 'vi-tdesign:numbers-7', + 'vi-tdesign:numbers-7-1', + 'vi-tdesign:numbers-8', + 'vi-tdesign:numbers-8-1', + 'vi-tdesign:numbers-9', + 'vi-tdesign:numbers-9-1', + 'vi-tdesign:nut', + 'vi-tdesign:object-storage', + 'vi-tdesign:open-mouth', + 'vi-tdesign:opera', + 'vi-tdesign:order-adjustment-column', + 'vi-tdesign:order-ascending', + 'vi-tdesign:order-descending', + 'vi-tdesign:outbox', + 'vi-tdesign:page-first', + 'vi-tdesign:page-head', + 'vi-tdesign:page-last', + 'vi-tdesign:palace', + 'vi-tdesign:palace-1', + 'vi-tdesign:palace-2', + 'vi-tdesign:palace-3', + 'vi-tdesign:palace-4', + 'vi-tdesign:palette', + 'vi-tdesign:palette-1', + 'vi-tdesign:panorama-horizontal', + 'vi-tdesign:panorama-vertical', + 'vi-tdesign:pantone', + 'vi-tdesign:parabola', + 'vi-tdesign:parentheses', + 'vi-tdesign:paste', + 'vi-tdesign:patio', + 'vi-tdesign:pause', + 'vi-tdesign:pause-circle', + 'vi-tdesign:pause-circle-filled', + 'vi-tdesign:pause-circle-stroke', + 'vi-tdesign:pea', + 'vi-tdesign:peach', + 'vi-tdesign:pear', + 'vi-tdesign:pearl-of-the-orient', + 'vi-tdesign:pen', + 'vi-tdesign:pen-ball', + 'vi-tdesign:pen-brush', + 'vi-tdesign:pen-mark', + 'vi-tdesign:pen-quill', + 'vi-tdesign:pending', + 'vi-tdesign:percent', + 'vi-tdesign:personal-information', + 'vi-tdesign:phone-locked', + 'vi-tdesign:phone-search', + 'vi-tdesign:pi', + 'vi-tdesign:piano', + 'vi-tdesign:pin', + 'vi-tdesign:pin-filled', + 'vi-tdesign:play', + 'vi-tdesign:play-circle', + 'vi-tdesign:play-circle-filled', + 'vi-tdesign:play-circle-stroke', + 'vi-tdesign:play-circle-stroke-add', + 'vi-tdesign:play-demo', + 'vi-tdesign:play-rectangle', + 'vi-tdesign:plus', + 'vi-tdesign:popsicle', + 'vi-tdesign:portrait', + 'vi-tdesign:pout', + 'vi-tdesign:poweroff', + 'vi-tdesign:precise-monitor', + 'vi-tdesign:previous', + 'vi-tdesign:print', + 'vi-tdesign:pumpkin', + 'vi-tdesign:pyramid', + 'vi-tdesign:pyramid-maya', + 'vi-tdesign:qrcode', + 'vi-tdesign:quadratic', + 'vi-tdesign:questionnaire', + 'vi-tdesign:queue', + 'vi-tdesign:radar', + 'vi-tdesign:radio-1', + 'vi-tdesign:radio-2', + 'vi-tdesign:radish', + 'vi-tdesign:rain-heavy', + 'vi-tdesign:rain-light', + 'vi-tdesign:rain-medium', + 'vi-tdesign:rainbow', + 'vi-tdesign:rectangle', + 'vi-tdesign:refresh', + 'vi-tdesign:relation', + 'vi-tdesign:relativity', + 'vi-tdesign:remote-wave', + 'vi-tdesign:remove', + 'vi-tdesign:replay', + 'vi-tdesign:rice', + 'vi-tdesign:rice-ball', + 'vi-tdesign:roast', + 'vi-tdesign:rocket', + 'vi-tdesign:rollback', + 'vi-tdesign:rollfront', + 'vi-tdesign:root-list', + 'vi-tdesign:rotate', + 'vi-tdesign:rotate-locked', + 'vi-tdesign:rotation', + 'vi-tdesign:round', + 'vi-tdesign:router-wave', + 'vi-tdesign:rss', + 'vi-tdesign:ruler', + 'vi-tdesign:sailing-hotel', + 'vi-tdesign:sandwich', + 'vi-tdesign:saturation', + 'vi-tdesign:sausage', + 'vi-tdesign:save', + 'vi-tdesign:saving-pot', + 'vi-tdesign:scan', + 'vi-tdesign:screen-4k', + 'vi-tdesign:screencast', + 'vi-tdesign:screenshot', + 'vi-tdesign:scroll-bar', + 'vi-tdesign:sd-card', + 'vi-tdesign:sd-card-1', + 'vi-tdesign:search', + 'vi-tdesign:search-error', + 'vi-tdesign:secured', + 'vi-tdesign:send', + 'vi-tdesign:send-cancel', + 'vi-tdesign:sensors', + 'vi-tdesign:sensors-1', + 'vi-tdesign:sensors-2', + 'vi-tdesign:sensors-off', + 'vi-tdesign:serenity', + 'vi-tdesign:server', + 'vi-tdesign:service', + 'vi-tdesign:setting', + 'vi-tdesign:setting-1', + 'vi-tdesign:share', + 'vi-tdesign:share-1', + 'vi-tdesign:sharpness', + 'vi-tdesign:shield-error', + 'vi-tdesign:shimen', + 'vi-tdesign:shop', + 'vi-tdesign:shop-1', + 'vi-tdesign:shop-2', + 'vi-tdesign:shop-3', + 'vi-tdesign:shop-4', + 'vi-tdesign:shop-5', + 'vi-tdesign:shrimp', + 'vi-tdesign:shrink-horizontal', + 'vi-tdesign:shrink-vertical', + 'vi-tdesign:shutter', + 'vi-tdesign:shutup', + 'vi-tdesign:sim-card', + 'vi-tdesign:sim-card-1', + 'vi-tdesign:sim-card-2', + 'vi-tdesign:sinister-smile', + 'vi-tdesign:sip', + 'vi-tdesign:slash', + 'vi-tdesign:sleep', + 'vi-tdesign:slice', + 'vi-tdesign:slideshow', + 'vi-tdesign:smile', + 'vi-tdesign:sneer', + 'vi-tdesign:snowflake', + 'vi-tdesign:sonic', + 'vi-tdesign:sound', + 'vi-tdesign:sound-down', + 'vi-tdesign:sound-high', + 'vi-tdesign:sound-low', + 'vi-tdesign:sound-mute', + 'vi-tdesign:sound-mute-1', + 'vi-tdesign:sound-up', + 'vi-tdesign:space', + 'vi-tdesign:speechless-1', + 'vi-tdesign:star', + 'vi-tdesign:star-filled', + 'vi-tdesign:statue-of-jesus', + 'vi-tdesign:sticky-note', + 'vi-tdesign:stop', + 'vi-tdesign:stop-circle', + 'vi-tdesign:stop-circle-filled', + 'vi-tdesign:stop-circle-stroke', + 'vi-tdesign:store', + 'vi-tdesign:street-road', + 'vi-tdesign:street-road-1', + 'vi-tdesign:subtitle', + 'vi-tdesign:subway-line', + 'vi-tdesign:sum', + 'vi-tdesign:sun-fall', + 'vi-tdesign:sun-rising', + 'vi-tdesign:sunny', + 'vi-tdesign:support', + 'vi-tdesign:surprised', + 'vi-tdesign:surprised-1', + 'vi-tdesign:swap', + 'vi-tdesign:swap-left', + 'vi-tdesign:swap-right', + 'vi-tdesign:swear-1', + 'vi-tdesign:swear-2', + 'vi-tdesign:system-2', + 'vi-tdesign:system-3', + 'vi-tdesign:system-application', + 'vi-tdesign:system-blocked', + 'vi-tdesign:system-code', + 'vi-tdesign:system-components', + 'vi-tdesign:system-coordinate', + 'vi-tdesign:system-device', + 'vi-tdesign:system-interface', + 'vi-tdesign:system-location', + 'vi-tdesign:system-locked', + 'vi-tdesign:system-log', + 'vi-tdesign:system-marked', + 'vi-tdesign:system-messages', + 'vi-tdesign:system-regulation', + 'vi-tdesign:system-search', + 'vi-tdesign:system-setting', + 'vi-tdesign:system-storage', + 'vi-tdesign:system-sum', + 'vi-tdesign:system-unlocked', + 'vi-tdesign:tab', + 'vi-tdesign:table', + 'vi-tdesign:table-1', + 'vi-tdesign:table-2', + 'vi-tdesign:table-add', + 'vi-tdesign:table-split', + 'vi-tdesign:tag', + 'vi-tdesign:tangerinr', + 'vi-tdesign:tape', + 'vi-tdesign:task', + 'vi-tdesign:task-1', + 'vi-tdesign:task-add', + 'vi-tdesign:task-add-1', + 'vi-tdesign:task-checked', + 'vi-tdesign:task-error', + 'vi-tdesign:task-location', + 'vi-tdesign:task-marked', + 'vi-tdesign:task-setting', + 'vi-tdesign:task-visible', + 'vi-tdesign:tea', + 'vi-tdesign:teahouse', + 'vi-tdesign:template', + 'vi-tdesign:temple', + 'vi-tdesign:terminal', + 'vi-tdesign:terminal-rectangle', + 'vi-tdesign:terminal-rectangle-1', + 'vi-tdesign:terminal-window', + 'vi-tdesign:textbox', + 'vi-tdesign:textformat-bold', + 'vi-tdesign:textformat-color', + 'vi-tdesign:textformat-italic', + 'vi-tdesign:textformat-strikethrough', + 'vi-tdesign:textformat-underline', + 'vi-tdesign:textformat-wrap', + 'vi-tdesign:theaters', + 'vi-tdesign:thumb-down', + 'vi-tdesign:thumb-down-1', + 'vi-tdesign:thumb-down-2', + 'vi-tdesign:thumb-up', + 'vi-tdesign:thumb-up-1', + 'vi-tdesign:thumb-up-2', + 'vi-tdesign:thunder', + 'vi-tdesign:thunderstorm', + 'vi-tdesign:thunderstorm-night', + 'vi-tdesign:thunderstorm-sunny', + 'vi-tdesign:ticket', + 'vi-tdesign:time', + 'vi-tdesign:time-filled', + 'vi-tdesign:tips', + 'vi-tdesign:tips-double', + 'vi-tdesign:tomato', + 'vi-tdesign:tools', + 'vi-tdesign:tools-circle', + 'vi-tdesign:tornado', + 'vi-tdesign:tower', + 'vi-tdesign:tower-1', + 'vi-tdesign:tower-2', + 'vi-tdesign:tower-3', + 'vi-tdesign:tower-clock', + 'vi-tdesign:town', + 'vi-tdesign:traffic', + 'vi-tdesign:traffic-events', + 'vi-tdesign:transform', + 'vi-tdesign:transform-1', + 'vi-tdesign:transform-2', + 'vi-tdesign:transform-3', + 'vi-tdesign:translate', + 'vi-tdesign:translate-1', + 'vi-tdesign:tree-list', + 'vi-tdesign:tree-round-dot', + 'vi-tdesign:tree-round-dot-vertical', + 'vi-tdesign:tree-square-dot', + 'vi-tdesign:tree-square-dot-vertical', + 'vi-tdesign:trending-down', + 'vi-tdesign:trending-up', + 'vi-tdesign:tv', + 'vi-tdesign:tv-1', + 'vi-tdesign:tv-2', + 'vi-tdesign:typography', + 'vi-tdesign:uncomfortable', + 'vi-tdesign:uncomfortable-1', + 'vi-tdesign:uncomfortable-2', + 'vi-tdesign:undertake', + 'vi-tdesign:undertake-delivery', + 'vi-tdesign:undertake-environment-protection', + 'vi-tdesign:undertake-hold-up', + 'vi-tdesign:undertake-transaction', + 'vi-tdesign:unfold-less', + 'vi-tdesign:unfold-more', + 'vi-tdesign:unhappy', + 'vi-tdesign:unhappy-1', + 'vi-tdesign:uninstall', + 'vi-tdesign:upload', + 'vi-tdesign:upload-1', + 'vi-tdesign:upscale', + 'vi-tdesign:usb', + 'vi-tdesign:user', + 'vi-tdesign:user-1', + 'vi-tdesign:user-add', + 'vi-tdesign:user-arrow-down', + 'vi-tdesign:user-arrow-left', + 'vi-tdesign:user-arrow-right', + 'vi-tdesign:user-arrow-up', + 'vi-tdesign:user-avatar', + 'vi-tdesign:user-blocked', + 'vi-tdesign:user-business', + 'vi-tdesign:user-checked', + 'vi-tdesign:user-checked-1', + 'vi-tdesign:user-circle', + 'vi-tdesign:user-clear', + 'vi-tdesign:user-error-1', + 'vi-tdesign:user-invisible', + 'vi-tdesign:user-list', + 'vi-tdesign:user-locked', + 'vi-tdesign:user-marked', + 'vi-tdesign:user-password', + 'vi-tdesign:user-safety', + 'vi-tdesign:user-search', + 'vi-tdesign:user-setting', + 'vi-tdesign:user-talk', + 'vi-tdesign:user-talk-1', + 'vi-tdesign:user-talk-off-1', + 'vi-tdesign:user-time', + 'vi-tdesign:user-transmit', + 'vi-tdesign:user-unknown', + 'vi-tdesign:user-unlocked', + 'vi-tdesign:user-vip', + 'vi-tdesign:user-visible', + 'vi-tdesign:usergroup', + 'vi-tdesign:usergroup-add', + 'vi-tdesign:usergroup-clear', + 'vi-tdesign:vehicle', + 'vi-tdesign:verified', + 'vi-tdesign:verify', + 'vi-tdesign:video', + 'vi-tdesign:video-camera', + 'vi-tdesign:video-camera-1', + 'vi-tdesign:video-camera-2', + 'vi-tdesign:video-camera-dollar', + 'vi-tdesign:video-camera-minus', + 'vi-tdesign:video-camera-music', + 'vi-tdesign:video-camera-off', + 'vi-tdesign:video-library', + 'vi-tdesign:view-agenda', + 'vi-tdesign:view-column', + 'vi-tdesign:view-in-ar', + 'vi-tdesign:view-list', + 'vi-tdesign:view-module', + 'vi-tdesign:visual-recognition', + 'vi-tdesign:wallet', + 'vi-tdesign:watch', + 'vi-tdesign:watermelon', + 'vi-tdesign:wave-left', + 'vi-tdesign:wave-right', + 'vi-tdesign:wealth', + 'vi-tdesign:wealth-1', + 'vi-tdesign:widget', + 'vi-tdesign:wifi', + 'vi-tdesign:wifi-1', + 'vi-tdesign:wifi-off', + 'vi-tdesign:wifi-off-1', + 'vi-tdesign:window', + 'vi-tdesign:window-1', + 'vi-tdesign:windy', + 'vi-tdesign:windy-rain', + 'vi-tdesign:wink', + 'vi-tdesign:work', + 'vi-tdesign:work-history', + 'vi-tdesign:work-off', + 'vi-tdesign:wry-smile', + 'vi-tdesign:zoom-in', + 'vi-tdesign:zoom-out' + ] +} diff --git a/promo-ui2/src/components/ImageCropping/index.ts b/promo-ui2/src/components/ImageCropping/index.ts new file mode 100644 index 0000000..d6c0f25 --- /dev/null +++ b/promo-ui2/src/components/ImageCropping/index.ts @@ -0,0 +1,3 @@ +import ImageCropping from './src/ImageCropping.vue' + +export { ImageCropping } diff --git a/promo-ui2/src/components/ImageCropping/src/ImageCropping.vue b/promo-ui2/src/components/ImageCropping/src/ImageCropping.vue new file mode 100644 index 0000000..2f8c685 --- /dev/null +++ b/promo-ui2/src/components/ImageCropping/src/ImageCropping.vue @@ -0,0 +1,259 @@ + + + diff --git a/promo-ui2/src/components/ImageViewer/index.ts b/promo-ui2/src/components/ImageViewer/index.ts new file mode 100644 index 0000000..35764d6 --- /dev/null +++ b/promo-ui2/src/components/ImageViewer/index.ts @@ -0,0 +1,33 @@ +import ImageViewer from './src/ImageViewer.vue' +import { isClient } from '@/utils/is' +import { createVNode, render, VNode } from 'vue' +import { ImageViewerProps } from './src/types' + +let instance: Nullable = null + +export function createImageViewer(options: ImageViewerProps) { + if (!isClient) return + const { + urlList, + initialIndex = 0, + infinite = true, + hideOnClickModal = false, + teleported = false, + zIndex = 2000, + show = true + } = options + + const propsData: Partial = {} + const container = document.createElement('div') + propsData.urlList = urlList + propsData.initialIndex = initialIndex + propsData.infinite = infinite + propsData.hideOnClickModal = hideOnClickModal + propsData.teleported = teleported + propsData.zIndex = zIndex + propsData.show = show + + document.body.appendChild(container) + instance = createVNode(ImageViewer, propsData) + render(instance, container) +} diff --git a/promo-ui2/src/components/ImageViewer/src/ImageViewer.vue b/promo-ui2/src/components/ImageViewer/src/ImageViewer.vue new file mode 100644 index 0000000..b9afe17 --- /dev/null +++ b/promo-ui2/src/components/ImageViewer/src/ImageViewer.vue @@ -0,0 +1,34 @@ + + + diff --git a/promo-ui2/src/components/ImageViewer/src/types/index.ts b/promo-ui2/src/components/ImageViewer/src/types/index.ts new file mode 100644 index 0000000..2fff4c0 --- /dev/null +++ b/promo-ui2/src/components/ImageViewer/src/types/index.ts @@ -0,0 +1,9 @@ +export interface ImageViewerProps { + urlList?: string[] + zIndex?: number + initialIndex?: number + infinite?: boolean + hideOnClickModal?: boolean + teleported?: boolean + show?: boolean +} diff --git a/promo-ui2/src/components/Infotip/index.ts b/promo-ui2/src/components/Infotip/index.ts new file mode 100644 index 0000000..eb99944 --- /dev/null +++ b/promo-ui2/src/components/Infotip/index.ts @@ -0,0 +1,5 @@ +import Infotip from './src/Infotip.vue' + +export type { InfoTipSchema } from './src/types' + +export { Infotip } diff --git a/promo-ui2/src/components/Infotip/src/Infotip.vue b/promo-ui2/src/components/Infotip/src/Infotip.vue new file mode 100644 index 0000000..ff9136c --- /dev/null +++ b/promo-ui2/src/components/Infotip/src/Infotip.vue @@ -0,0 +1,53 @@ + + + diff --git a/promo-ui2/src/components/Infotip/src/types/index.ts b/promo-ui2/src/components/Infotip/src/types/index.ts new file mode 100644 index 0000000..50398db --- /dev/null +++ b/promo-ui2/src/components/Infotip/src/types/index.ts @@ -0,0 +1,4 @@ +export interface InfoTipSchema { + label: string + keys?: string[] +} diff --git a/promo-ui2/src/components/InputPassword/index.ts b/promo-ui2/src/components/InputPassword/index.ts new file mode 100644 index 0000000..1dcc38e --- /dev/null +++ b/promo-ui2/src/components/InputPassword/index.ts @@ -0,0 +1,3 @@ +import InputPassword from './src/InputPassword.vue' + +export { InputPassword } diff --git a/promo-ui2/src/components/InputPassword/src/InputPassword.vue b/promo-ui2/src/components/InputPassword/src/InputPassword.vue new file mode 100644 index 0000000..d9aa928 --- /dev/null +++ b/promo-ui2/src/components/InputPassword/src/InputPassword.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/promo-ui2/src/components/JsonEditor/index.ts b/promo-ui2/src/components/JsonEditor/index.ts new file mode 100644 index 0000000..53a2d06 --- /dev/null +++ b/promo-ui2/src/components/JsonEditor/index.ts @@ -0,0 +1,4 @@ +import JsonEditor from './src/JsonEditor.vue' +export type { JsonEditorProps } from './src/types' + +export { JsonEditor } diff --git a/promo-ui2/src/components/JsonEditor/src/JsonEditor.vue b/promo-ui2/src/components/JsonEditor/src/JsonEditor.vue new file mode 100644 index 0000000..a1c0a43 --- /dev/null +++ b/promo-ui2/src/components/JsonEditor/src/JsonEditor.vue @@ -0,0 +1,98 @@ + + + diff --git a/promo-ui2/src/components/JsonEditor/src/types/index.ts b/promo-ui2/src/components/JsonEditor/src/types/index.ts new file mode 100644 index 0000000..d77097f --- /dev/null +++ b/promo-ui2/src/components/JsonEditor/src/types/index.ts @@ -0,0 +1,23 @@ +export interface JsonEditorProps { + value: any + deep?: number + showLength?: boolean + showLineNumbers?: boolean + showLineNumber?: boolean + showIcon?: boolean + showDoubleQuotes?: boolean + virtual?: boolean + height?: number + itemHeight?: number + rootPath?: string + nodeSelectable?: (...args: any[]) => boolean + selectableType?: 'multiple' | 'single' + showSelectController?: boolean + selectOnClickNode?: boolean + highlightSelectedNode?: boolean + collapsedOnClickBrackets?: boolean + renderNodeKey?: (...args: any[]) => any + renderNodeValue?: (...args: any[]) => any + editable?: boolean + editableTrigger?: 'click' | 'dblclick' +} diff --git a/promo-ui2/src/components/LocaleDropdown/index.ts b/promo-ui2/src/components/LocaleDropdown/index.ts new file mode 100644 index 0000000..4d9b98a --- /dev/null +++ b/promo-ui2/src/components/LocaleDropdown/index.ts @@ -0,0 +1,5 @@ +import LocaleDropdown from './src/LocaleDropdown.vue' + +export type { Language, LocaleDropdownType } from './src/types' + +export { LocaleDropdown } diff --git a/promo-ui2/src/components/LocaleDropdown/src/LocaleDropdown.vue b/promo-ui2/src/components/LocaleDropdown/src/LocaleDropdown.vue new file mode 100644 index 0000000..e1a0342 --- /dev/null +++ b/promo-ui2/src/components/LocaleDropdown/src/LocaleDropdown.vue @@ -0,0 +1,52 @@ + + + diff --git a/promo-ui2/src/components/LocaleDropdown/src/types/index.ts b/promo-ui2/src/components/LocaleDropdown/src/types/index.ts new file mode 100644 index 0000000..c749dce --- /dev/null +++ b/promo-ui2/src/components/LocaleDropdown/src/types/index.ts @@ -0,0 +1,10 @@ +export interface Language { + el: Recordable + name: string +} + +export interface LocaleDropdownType { + lang: LocaleType + name?: string + elLocale?: Language +} diff --git a/promo-ui2/src/components/Logo/index.ts b/promo-ui2/src/components/Logo/index.ts new file mode 100644 index 0000000..1c4224c --- /dev/null +++ b/promo-ui2/src/components/Logo/index.ts @@ -0,0 +1,3 @@ +import Logo from './src/Logo.vue' + +export { Logo } diff --git a/promo-ui2/src/components/Logo/src/Logo.vue b/promo-ui2/src/components/Logo/src/Logo.vue new file mode 100644 index 0000000..89cbe90 --- /dev/null +++ b/promo-ui2/src/components/Logo/src/Logo.vue @@ -0,0 +1,80 @@ + + + diff --git a/promo-ui2/src/components/Menu/index.ts b/promo-ui2/src/components/Menu/index.ts new file mode 100644 index 0000000..a6ec696 --- /dev/null +++ b/promo-ui2/src/components/Menu/index.ts @@ -0,0 +1,3 @@ +import Menu from './src/Menu.vue' + +export { Menu } diff --git a/promo-ui2/src/components/Menu/src/Menu.vue b/promo-ui2/src/components/Menu/src/Menu.vue new file mode 100644 index 0000000..083d03c --- /dev/null +++ b/promo-ui2/src/components/Menu/src/Menu.vue @@ -0,0 +1,278 @@ + + + + + diff --git a/promo-ui2/src/components/Menu/src/components/useRenderMenuItem.tsx b/promo-ui2/src/components/Menu/src/components/useRenderMenuItem.tsx new file mode 100644 index 0000000..96914cc --- /dev/null +++ b/promo-ui2/src/components/Menu/src/components/useRenderMenuItem.tsx @@ -0,0 +1,59 @@ +import { ElSubMenu, ElMenuItem } from 'element-plus' +import { unref } from 'vue' +import { hasOneShowingChild } from '../helper' +import { isUrl } from '@/utils/is' +import { useRenderMenuTitle } from './useRenderMenuTitle' +import { pathResolve } from '@/utils/routerHelper' +import { useDesign } from '@/hooks/web/useDesign' + +const { getPrefixCls } = useDesign() +const prefixCls = getPrefixCls('submenu') + +const { renderMenuTitle } = useRenderMenuTitle() + +export const useRenderMenuItem = (menuMode) => + // allRouters: AppRouteRecordRaw[] = [], + { + const renderMenuItem = (routers: AppRouteRecordRaw[], parentPath = '/') => { + return routers + .filter((v) => !v.meta?.hidden) + .map((v) => { + const meta = v.meta ?? {} + const { oneShowingChild, onlyOneChild } = hasOneShowingChild(v.children, v) + const fullPath = isUrl(v.path) ? v.path : pathResolve(parentPath, v.path) // getAllParentPath(allRouters, v.path).join('/') + + if ( + oneShowingChild && + (!onlyOneChild?.children || onlyOneChild?.noShowingChildren) && + !meta?.alwaysShow + ) { + return ( + + {{ + default: () => renderMenuTitle(onlyOneChild ? onlyOneChild?.meta : meta) + }} + + ) + } else { + return ( + + {{ + title: () => renderMenuTitle(meta), + default: () => renderMenuItem(v.children!, fullPath) + }} + + ) + } + }) + } + + return { + renderMenuItem + } + } diff --git a/promo-ui2/src/components/Menu/src/components/useRenderMenuTitle.tsx b/promo-ui2/src/components/Menu/src/components/useRenderMenuTitle.tsx new file mode 100644 index 0000000..8941d9d --- /dev/null +++ b/promo-ui2/src/components/Menu/src/components/useRenderMenuTitle.tsx @@ -0,0 +1,27 @@ +import type { RouteMeta } from 'vue-router' +import { Icon } from '@/components/Icon' +import { useI18n } from '@/hooks/web/useI18n' + +export const useRenderMenuTitle = () => { + const renderMenuTitle = (meta: RouteMeta) => { + const { t } = useI18n() + const { title = 'Please set title', icon } = meta + + return icon ? ( + <> + + + {t(title as string)} + + + ) : ( + + {t(title as string)} + + ) + } + + return { + renderMenuTitle + } +} diff --git a/promo-ui2/src/components/Menu/src/helper.ts b/promo-ui2/src/components/Menu/src/helper.ts new file mode 100644 index 0000000..003cf10 --- /dev/null +++ b/promo-ui2/src/components/Menu/src/helper.ts @@ -0,0 +1,54 @@ +import { ref, unref } from 'vue' +import { findPath } from '@/utils/tree' + +type OnlyOneChildType = AppRouteRecordRaw & { noShowingChildren?: boolean } + +interface HasOneShowingChild { + oneShowingChild?: boolean + onlyOneChild?: OnlyOneChildType +} + +export const getAllParentPath = (treeData: T[], path: string) => { + const menuList = findPath(treeData, (n) => n.path === path) as AppRouteRecordRaw[] + return (menuList || []).map((item) => item.path) +} + +export const hasOneShowingChild = ( + children: AppRouteRecordRaw[] = [], + parent: AppRouteRecordRaw +): HasOneShowingChild => { + const onlyOneChild = ref() + + const showingChildren = children.filter((v) => { + const meta = v.meta ?? {} + if (meta.hidden) { + return false + } else { + // Temp set(will be used if only has one showing child) + onlyOneChild.value = v + return true + } + }) + + // When there is only one child router, the child router is displayed by default + if (showingChildren.length === 1) { + return { + oneShowingChild: true, + onlyOneChild: unref(onlyOneChild) + } + } + + // Show parent if there are no child router to display + if (!showingChildren.length) { + onlyOneChild.value = { ...parent, path: '', noShowingChildren: true } + return { + oneShowingChild: true, + onlyOneChild: unref(onlyOneChild) + } + } + + return { + oneShowingChild: false, + onlyOneChild: unref(onlyOneChild) + } +} diff --git a/promo-ui2/src/components/Permission/index.ts b/promo-ui2/src/components/Permission/index.ts new file mode 100644 index 0000000..250ae75 --- /dev/null +++ b/promo-ui2/src/components/Permission/index.ts @@ -0,0 +1,4 @@ +import Permission from './src/Permission.vue' +import { hasPermi } from './src/utils' + +export { Permission, hasPermi } diff --git a/promo-ui2/src/components/Permission/src/Permission.vue b/promo-ui2/src/components/Permission/src/Permission.vue new file mode 100644 index 0000000..8581a9d --- /dev/null +++ b/promo-ui2/src/components/Permission/src/Permission.vue @@ -0,0 +1,29 @@ + + + diff --git a/promo-ui2/src/components/Permission/src/utils.ts b/promo-ui2/src/components/Permission/src/utils.ts new file mode 100644 index 0000000..4c0a9b5 --- /dev/null +++ b/promo-ui2/src/components/Permission/src/utils.ts @@ -0,0 +1,14 @@ +import { useI18n } from '@/hooks/web/useI18n' +import router from '@/router' + +export const hasPermi = (value: string) => { + const { t } = useI18n() + const permission = (router.currentRoute.value.meta.permission || []) as string[] + if (!value) { + throw new Error(t('permission.hasPermission')) + } + if (permission.includes(value)) { + return true + } + return false +} diff --git a/promo-ui2/src/components/Qrcode/index.ts b/promo-ui2/src/components/Qrcode/index.ts new file mode 100644 index 0000000..263f039 --- /dev/null +++ b/promo-ui2/src/components/Qrcode/index.ts @@ -0,0 +1,5 @@ +import Qrcode from './src/Qrcode.vue' + +export type { QrcodeLogo } from './src/types' + +export { Qrcode } diff --git a/promo-ui2/src/components/Qrcode/src/Qrcode.vue b/promo-ui2/src/components/Qrcode/src/Qrcode.vue new file mode 100644 index 0000000..3b3cc1c --- /dev/null +++ b/promo-ui2/src/components/Qrcode/src/Qrcode.vue @@ -0,0 +1,252 @@ + + + + + diff --git a/promo-ui2/src/components/Qrcode/src/types/index.ts b/promo-ui2/src/components/Qrcode/src/types/index.ts new file mode 100644 index 0000000..86cdf0b --- /dev/null +++ b/promo-ui2/src/components/Qrcode/src/types/index.ts @@ -0,0 +1,9 @@ +export interface QrcodeLogo { + src?: string + logoSize?: number + bgColor?: string + borderSize?: number + crossOrigin?: string + borderRadius?: number + logoRadius?: number +} diff --git a/promo-ui2/src/components/Screenfull/index.ts b/promo-ui2/src/components/Screenfull/index.ts new file mode 100644 index 0000000..faec2d8 --- /dev/null +++ b/promo-ui2/src/components/Screenfull/index.ts @@ -0,0 +1,3 @@ +import Screenfull from './src/Screenfull.vue' + +export { Screenfull } diff --git a/promo-ui2/src/components/Screenfull/src/Screenfull.vue b/promo-ui2/src/components/Screenfull/src/Screenfull.vue new file mode 100644 index 0000000..048df9a --- /dev/null +++ b/promo-ui2/src/components/Screenfull/src/Screenfull.vue @@ -0,0 +1,30 @@ + + + diff --git a/promo-ui2/src/components/Search/index.ts b/promo-ui2/src/components/Search/index.ts new file mode 100644 index 0000000..696ad84 --- /dev/null +++ b/promo-ui2/src/components/Search/index.ts @@ -0,0 +1,15 @@ +import { FormSchema, FormSetProps } from '../Form' +import Search from './src/Search.vue' + +export type { SearchProps } from './src/types' + +export interface SearchExpose { + setValues: (data: Recordable) => void + setProps: (props: Recordable) => void + delSchema: (field: string) => void + addSchema: (formSchema: FormSchema, index?: number) => void + setSchema: (schemaProps: FormSetProps[]) => void + getFormData: () => Promise +} + +export { Search } diff --git a/promo-ui2/src/components/Search/src/Search.vue b/promo-ui2/src/components/Search/src/Search.vue new file mode 100644 index 0000000..3e57ed6 --- /dev/null +++ b/promo-ui2/src/components/Search/src/Search.vue @@ -0,0 +1,271 @@ + + + diff --git a/promo-ui2/src/components/Search/src/components/ActionButton.vue b/promo-ui2/src/components/Search/src/components/ActionButton.vue new file mode 100644 index 0000000..8de1838 --- /dev/null +++ b/promo-ui2/src/components/Search/src/components/ActionButton.vue @@ -0,0 +1,59 @@ + + + diff --git a/promo-ui2/src/components/Search/src/types/index.ts b/promo-ui2/src/components/Search/src/types/index.ts new file mode 100644 index 0000000..7a1901d --- /dev/null +++ b/promo-ui2/src/components/Search/src/types/index.ts @@ -0,0 +1,16 @@ +import { FormSchema } from '@/components/Form' + +export interface SearchProps { + schema?: FormSchema[] + isCol?: boolean + labelWidth?: string | number + layout?: 'inline' | 'bottom' + buttonPosition?: 'left' | 'right' | 'center' + showSearch?: boolean + showReset?: boolean + showExpand?: boolean + expandField?: string + inline?: boolean + removeNoValueItem?: boolean + model?: Recordable +} diff --git a/promo-ui2/src/components/Setting/index.ts b/promo-ui2/src/components/Setting/index.ts new file mode 100644 index 0000000..b64c9ad --- /dev/null +++ b/promo-ui2/src/components/Setting/index.ts @@ -0,0 +1,3 @@ +import Setting from './src/Setting.vue' + +export { Setting } diff --git a/promo-ui2/src/components/Setting/src/Setting.vue b/promo-ui2/src/components/Setting/src/Setting.vue new file mode 100644 index 0000000..ecbe72d --- /dev/null +++ b/promo-ui2/src/components/Setting/src/Setting.vue @@ -0,0 +1,251 @@ + + + + + diff --git a/promo-ui2/src/components/Setting/src/components/ColorRadioPicker.vue b/promo-ui2/src/components/Setting/src/components/ColorRadioPicker.vue new file mode 100644 index 0000000..9774176 --- /dev/null +++ b/promo-ui2/src/components/Setting/src/components/ColorRadioPicker.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/promo-ui2/src/components/Setting/src/components/InterfaceDisplay.vue b/promo-ui2/src/components/Setting/src/components/InterfaceDisplay.vue new file mode 100644 index 0000000..466dc65 --- /dev/null +++ b/promo-ui2/src/components/Setting/src/components/InterfaceDisplay.vue @@ -0,0 +1,227 @@ + + + diff --git a/promo-ui2/src/components/Setting/src/components/LayoutRadioPicker.vue b/promo-ui2/src/components/Setting/src/components/LayoutRadioPicker.vue new file mode 100644 index 0000000..2b6d4b5 --- /dev/null +++ b/promo-ui2/src/components/Setting/src/components/LayoutRadioPicker.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/promo-ui2/src/components/SizeDropdown/index.ts b/promo-ui2/src/components/SizeDropdown/index.ts new file mode 100644 index 0000000..516488d --- /dev/null +++ b/promo-ui2/src/components/SizeDropdown/index.ts @@ -0,0 +1,3 @@ +import SizeDropdown from './src/SizeDropdown.vue' + +export { SizeDropdown } diff --git a/promo-ui2/src/components/SizeDropdown/src/SizeDropdown.vue b/promo-ui2/src/components/SizeDropdown/src/SizeDropdown.vue new file mode 100644 index 0000000..eb55917 --- /dev/null +++ b/promo-ui2/src/components/SizeDropdown/src/SizeDropdown.vue @@ -0,0 +1,39 @@ + + + diff --git a/promo-ui2/src/components/TabMenu/index.ts b/promo-ui2/src/components/TabMenu/index.ts new file mode 100644 index 0000000..b5fd71c --- /dev/null +++ b/promo-ui2/src/components/TabMenu/index.ts @@ -0,0 +1,3 @@ +import TabMenu from './src/TabMenu.vue' + +export { TabMenu } diff --git a/promo-ui2/src/components/TabMenu/src/TabMenu.vue b/promo-ui2/src/components/TabMenu/src/TabMenu.vue new file mode 100644 index 0000000..e6cf366 --- /dev/null +++ b/promo-ui2/src/components/TabMenu/src/TabMenu.vue @@ -0,0 +1,251 @@ + + + diff --git a/promo-ui2/src/components/TabMenu/src/helper.ts b/promo-ui2/src/components/TabMenu/src/helper.ts new file mode 100644 index 0000000..cb4696e --- /dev/null +++ b/promo-ui2/src/components/TabMenu/src/helper.ts @@ -0,0 +1,51 @@ +import { getAllParentPath } from '@/components/Menu/src/helper' +import { isUrl } from '@/utils/is' +import { cloneDeep } from 'lodash-es' +import { reactive } from 'vue' + +export type TabMapTypes = { + [key: string]: string[] +} + +export const tabPathMap = reactive({}) + +export const initTabMap = (routes: AppRouteRecordRaw[]) => { + for (const v of routes) { + const meta = v.meta ?? {} + if (!meta?.hidden) { + tabPathMap[v.path] = [] + } + } +} + +export const filterMenusPath = ( + routes: AppRouteRecordRaw[], + allRoutes: AppRouteRecordRaw[] +): AppRouteRecordRaw[] => { + const res: AppRouteRecordRaw[] = [] + for (const v of routes) { + let data: Nullable = null + const meta = v.meta ?? {} + if (!meta.hidden || meta.canTo) { + const allParentPath = getAllParentPath(allRoutes, v.path) + + const fullPath = isUrl(v.path) ? v.path : allParentPath.join('/') + + data = cloneDeep(v) + data.path = fullPath + if (v.children && data) { + data.children = filterMenusPath(v.children, allRoutes) + } + + if (data) { + res.push(data) + } + + if (allParentPath.length && Reflect.has(tabPathMap, allParentPath[0])) { + tabPathMap[allParentPath[0]].push(fullPath) + } + } + } + + return res +} diff --git a/promo-ui2/src/components/Table/index.ts b/promo-ui2/src/components/Table/index.ts new file mode 100644 index 0000000..472ae5d --- /dev/null +++ b/promo-ui2/src/components/Table/index.ts @@ -0,0 +1,21 @@ +import Table from './src/Table.vue' +import { ElTable } from 'element-plus' +import { TableColumn, TableSetProps } from './src/types' + +export type { + TableColumn, + TableSlotDefault, + Pagination, + TableSetProps, + TableProps +} from './src/types' + +export interface TableExpose { + setProps: (props: Recordable) => void + setColumn: (columnProps: TableSetProps[]) => void + addColumn: (column: TableColumn, index?: number) => void + delColumn: (field: string) => void + elTableRef: ComponentRef +} + +export { Table } diff --git a/promo-ui2/src/components/Table/src/Table.vue b/promo-ui2/src/components/Table/src/Table.vue new file mode 100644 index 0000000..9d496f0 --- /dev/null +++ b/promo-ui2/src/components/Table/src/Table.vue @@ -0,0 +1,587 @@ + diff --git a/promo-ui2/src/components/Table/src/components/ColumnSetting.vue b/promo-ui2/src/components/Table/src/components/ColumnSetting.vue new file mode 100644 index 0000000..f0c3825 --- /dev/null +++ b/promo-ui2/src/components/Table/src/components/ColumnSetting.vue @@ -0,0 +1,166 @@ + + + diff --git a/promo-ui2/src/components/Table/src/components/TableActions.vue b/promo-ui2/src/components/Table/src/components/TableActions.vue new file mode 100644 index 0000000..05336bb --- /dev/null +++ b/promo-ui2/src/components/Table/src/components/TableActions.vue @@ -0,0 +1,105 @@ + diff --git a/promo-ui2/src/components/Table/src/helper/index.ts b/promo-ui2/src/components/Table/src/helper/index.ts new file mode 100644 index 0000000..d8b34a8 --- /dev/null +++ b/promo-ui2/src/components/Table/src/helper/index.ts @@ -0,0 +1,8 @@ +export const setIndex = (reserveIndex: boolean, index: number, size: number, current: number) => { + const newIndex = index + 1 + if (reserveIndex) { + return size * (current - 1) + newIndex + } else { + return newIndex + } +} diff --git a/promo-ui2/src/components/Table/src/types/index.ts b/promo-ui2/src/components/Table/src/types/index.ts new file mode 100644 index 0000000..21200a7 --- /dev/null +++ b/promo-ui2/src/components/Table/src/types/index.ts @@ -0,0 +1,98 @@ +import { TableProps as ElTableProps } from 'element-plus' +export interface TableColumn { + field: string + label?: string + type?: string + /** + * 是否隐藏 + */ + hidden?: boolean + children?: TableColumn[] + slots?: { + default?: (...args: any[]) => JSX.Element | JSX.Element[] | null + header?: (...args: any[]) => JSX.Element | null + } + index?: number | ((index: number) => number) + columnKey?: string + width?: string | number + minWidth?: string | number + fixed?: boolean | 'left' | 'right' + renderHeader?: (...args: any[]) => JSX.Element | null + // sortable?: boolean + sortMethod?: (...args: any[]) => number + sortBy?: string | string[] | ((...args: any[]) => string | string[]) + sortOrders?: (string | null)[] + resizable?: boolean + formatter?: (...args: any[]) => any + showOverflowTooltip?: boolean + align?: 'left' | 'center' | 'right' + headerAlign?: 'left' | 'center' | 'right' + className?: string + labelClassName?: string + selectable?: (...args: any[]) => boolean + reserveSelection?: boolean + filters?: Array<{ text: string; value: string }> + filterPlacement?: string + filterMultiple?: boolean + filterMethod?: (...args: any[]) => boolean + filteredValue?: string[] + [key: string]: any +} + +export interface TableSlotDefault { + row: Recordable + column: TableColumn + $index: number + [key: string]: any +} + +export interface Pagination { + small?: boolean + background?: boolean + pageSize?: number + defaultPageSize?: number + total?: number + pageCount?: number + pagerCount?: number + currentPage?: number + defaultCurrentPage?: number + layout?: string + pageSizes?: number[] + popperClass?: string + prevText?: string + nextText?: string + disabled?: boolean + hideOnSinglePage?: boolean +} + +export interface TableSetProps { + field: string + path: string + value: any +} + +export interface TableProps extends Omit>, 'data'> { + pageSize?: number + currentPage?: number + showAction?: boolean + // 是否所有的超出隐藏,优先级低于schema中的showOverflowTooltip, + showOverflowTooltip?: boolean + // 表头 + columns?: TableColumn[] + // 是否展示分页 + pagination?: Pagination | undefined + // 仅对 type=selection 的列有效,类型为 Boolean,为 true 则会在数据更新之后保留之前选中的数据(需指定 row-key) + reserveSelection?: boolean + // 加载状态 + loading?: boolean + // 是否叠加索引 + reserveIndex?: boolean + // 对齐方式 + align?: 'left' | 'center' | 'right' + // 表头对齐方式 + headerAlign?: 'left' | 'center' | 'right' + imagePreview?: string[] + videoPreview?: string[] + sortable?: boolean + data?: Recordable +} diff --git a/promo-ui2/src/components/TagsView/index.ts b/promo-ui2/src/components/TagsView/index.ts new file mode 100644 index 0000000..30e604a --- /dev/null +++ b/promo-ui2/src/components/TagsView/index.ts @@ -0,0 +1,3 @@ +import TagsView from './src/TagsView.vue' + +export { TagsView } diff --git a/promo-ui2/src/components/TagsView/src/TagsView.vue b/promo-ui2/src/components/TagsView/src/TagsView.vue new file mode 100644 index 0000000..da3336b --- /dev/null +++ b/promo-ui2/src/components/TagsView/src/TagsView.vue @@ -0,0 +1,590 @@ + + + + + diff --git a/promo-ui2/src/components/TagsView/src/helper.ts b/promo-ui2/src/components/TagsView/src/helper.ts new file mode 100644 index 0000000..912eb8e --- /dev/null +++ b/promo-ui2/src/components/TagsView/src/helper.ts @@ -0,0 +1,21 @@ +import type { RouteLocationNormalizedLoaded } from 'vue-router' +import { pathResolve } from '@/utils/routerHelper' + +export const filterAffixTags = (routes: AppRouteRecordRaw[], parentPath = '') => { + let tags: RouteLocationNormalizedLoaded[] = [] + routes.forEach((route) => { + const meta = route.meta ?? {} + const tagPath = pathResolve(parentPath, route.path) + if (meta?.affix) { + tags.push({ ...route, path: tagPath, fullPath: tagPath } as RouteLocationNormalizedLoaded) + } + if (route.children) { + const tempTags: RouteLocationNormalizedLoaded[] = filterAffixTags(route.children, tagPath) + if (tempTags.length >= 1) { + tags = [...tags, ...tempTags] + } + } + }) + + return tags +} diff --git a/promo-ui2/src/components/ThemeSwitch/index.ts b/promo-ui2/src/components/ThemeSwitch/index.ts new file mode 100644 index 0000000..823a276 --- /dev/null +++ b/promo-ui2/src/components/ThemeSwitch/index.ts @@ -0,0 +1,3 @@ +import ThemeSwitch from './src/ThemeSwitch.vue' + +export { ThemeSwitch } diff --git a/promo-ui2/src/components/ThemeSwitch/src/ThemeSwitch.vue b/promo-ui2/src/components/ThemeSwitch/src/ThemeSwitch.vue new file mode 100644 index 0000000..94b02c6 --- /dev/null +++ b/promo-ui2/src/components/ThemeSwitch/src/ThemeSwitch.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/promo-ui2/src/components/Tree/index.ts b/promo-ui2/src/components/Tree/index.ts new file mode 100644 index 0000000..bee5f92 --- /dev/null +++ b/promo-ui2/src/components/Tree/index.ts @@ -0,0 +1,3 @@ +import Tree from './src/Tree.vue' + +export { Tree } diff --git a/promo-ui2/src/components/Tree/src/Tree.vue b/promo-ui2/src/components/Tree/src/Tree.vue new file mode 100644 index 0000000..66597d1 --- /dev/null +++ b/promo-ui2/src/components/Tree/src/Tree.vue @@ -0,0 +1,147 @@ + + + diff --git a/promo-ui2/src/components/UserInfo/index.ts b/promo-ui2/src/components/UserInfo/index.ts new file mode 100644 index 0000000..c3a34ab --- /dev/null +++ b/promo-ui2/src/components/UserInfo/index.ts @@ -0,0 +1,3 @@ +import UserInfo from './src/UserInfo.vue' + +export { UserInfo } diff --git a/promo-ui2/src/components/UserInfo/src/UserInfo.vue b/promo-ui2/src/components/UserInfo/src/UserInfo.vue new file mode 100644 index 0000000..14d7c65 --- /dev/null +++ b/promo-ui2/src/components/UserInfo/src/UserInfo.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/promo-ui2/src/components/UserInfo/src/components/LockDialog.vue b/promo-ui2/src/components/UserInfo/src/components/LockDialog.vue new file mode 100644 index 0000000..a8dc34f --- /dev/null +++ b/promo-ui2/src/components/UserInfo/src/components/LockDialog.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/promo-ui2/src/components/UserInfo/src/components/LockPage.vue b/promo-ui2/src/components/UserInfo/src/components/LockPage.vue new file mode 100644 index 0000000..e1a19c0 --- /dev/null +++ b/promo-ui2/src/components/UserInfo/src/components/LockPage.vue @@ -0,0 +1,281 @@ + + + + + diff --git a/promo-ui2/src/components/VideoPlayer/index.ts b/promo-ui2/src/components/VideoPlayer/index.ts new file mode 100644 index 0000000..247deca --- /dev/null +++ b/promo-ui2/src/components/VideoPlayer/index.ts @@ -0,0 +1,27 @@ +import { VNode, createVNode, render } from 'vue' +import VideoPlayer from './src/VideoPlayer.vue' +import { isClient } from '@/utils/is' +import { VideoPlayerViewer } from '@/components/VideoPlayerViewer' +import { toAnyString } from '@/utils' + +export { VideoPlayer } + +let instance: Nullable = null + +export function createVideoViewer(options: { url: string; poster?: string; show?: boolean }) { + if (!isClient) return + const { url, poster } = options + + const propsData: Partial<{ url: string; poster?: string; show?: boolean; id?: string }> = {} + const container = document.createElement('div') + const id = toAnyString() + container.id = id + propsData.url = url + propsData.poster = poster + propsData.show = true + propsData.id = id + + document.body.appendChild(container) + instance = createVNode(VideoPlayerViewer, propsData) + render(instance, container) +} diff --git a/promo-ui2/src/components/VideoPlayer/src/VideoPlayer.vue b/promo-ui2/src/components/VideoPlayer/src/VideoPlayer.vue new file mode 100644 index 0000000..83c8d30 --- /dev/null +++ b/promo-ui2/src/components/VideoPlayer/src/VideoPlayer.vue @@ -0,0 +1,59 @@ + + + diff --git a/promo-ui2/src/components/VideoPlayerViewer/index.ts b/promo-ui2/src/components/VideoPlayerViewer/index.ts new file mode 100644 index 0000000..7f99155 --- /dev/null +++ b/promo-ui2/src/components/VideoPlayerViewer/index.ts @@ -0,0 +1,3 @@ +import VideoPlayerViewer from './src/VideoPlayerViewer.vue' + +export { VideoPlayerViewer } diff --git a/promo-ui2/src/components/VideoPlayerViewer/src/VideoPlayerViewer.vue b/promo-ui2/src/components/VideoPlayerViewer/src/VideoPlayerViewer.vue new file mode 100644 index 0000000..56968df --- /dev/null +++ b/promo-ui2/src/components/VideoPlayerViewer/src/VideoPlayerViewer.vue @@ -0,0 +1,49 @@ + + diff --git a/promo-ui2/src/components/Waterfall/index.ts b/promo-ui2/src/components/Waterfall/index.ts new file mode 100644 index 0000000..6fed67f --- /dev/null +++ b/promo-ui2/src/components/Waterfall/index.ts @@ -0,0 +1,3 @@ +import Waterfall from './src/Waterfall.vue' + +export { Waterfall } diff --git a/promo-ui2/src/components/Waterfall/src/Waterfall.vue b/promo-ui2/src/components/Waterfall/src/Waterfall.vue new file mode 100644 index 0000000..32a0aa7 --- /dev/null +++ b/promo-ui2/src/components/Waterfall/src/Waterfall.vue @@ -0,0 +1,234 @@ + + + diff --git a/promo-ui2/src/components/index.ts b/promo-ui2/src/components/index.ts new file mode 100644 index 0000000..9a179ea --- /dev/null +++ b/promo-ui2/src/components/index.ts @@ -0,0 +1,10 @@ +import type { App } from 'vue' +import { Icon } from './Icon' +import { Permission } from './Permission' +import { BaseButton } from './Button' + +export const setupGlobCom = (app: App): void => { + app.component('Icon', Icon) + app.component('Permission', Permission) + app.component('BaseButton', BaseButton) +} diff --git a/promo-ui2/src/constants/index.ts b/promo-ui2/src/constants/index.ts new file mode 100644 index 0000000..cdf427e --- /dev/null +++ b/promo-ui2/src/constants/index.ts @@ -0,0 +1,39 @@ +/** + * 请求成功状态码 + */ +export const SUCCESS_CODE = 0 + +/** + * 请求contentType + */ +export const CONTENT_TYPE: AxiosContentType = 'application/json' + +/** + * 请求超时时间 + */ +export const REQUEST_TIMEOUT = 60000 + +/** + * 不重定向白名单 + */ +export const NO_REDIRECT_WHITE_LIST = ['/login'] + +/** + * 不重置路由白名单 + */ +export const NO_RESET_WHITE_LIST = ['Redirect', 'RedirectWrap', 'Login', 'NoFind', 'Root'] + +/** + * 表格默认过滤列设置字段 + */ +export const DEFAULT_FILTER_COLUMN = ['expand', 'selection'] + +/** + * 是否根据headers->content-type自动转换数据格式 + */ +export const TRANSFORM_REQUEST_DATA = true + +/** + * 全局图标前缀 + */ +export const ICON_PREFIX = 'vi-' diff --git a/promo-ui2/src/directives/index.ts b/promo-ui2/src/directives/index.ts new file mode 100644 index 0000000..11b1da8 --- /dev/null +++ b/promo-ui2/src/directives/index.ts @@ -0,0 +1,10 @@ +import type { App } from 'vue' +import { setupPermissionDirective } from './permission/hasPermi' + +/** + * 导出指令:v-xxx + * @methods hasPermi 按钮权限,用法: v-hasPermi + */ +export const setupPermission = (app: App) => { + setupPermissionDirective(app) +} diff --git a/promo-ui2/src/directives/permission/hasPermi.ts b/promo-ui2/src/directives/permission/hasPermi.ts new file mode 100644 index 0000000..0a74d94 --- /dev/null +++ b/promo-ui2/src/directives/permission/hasPermi.ts @@ -0,0 +1,37 @@ +import type { App, Directive, DirectiveBinding } from 'vue' +import { useI18n } from '@/hooks/web/useI18n' +import router from '@/router' + +const { t } = useI18n() + +const hasPermission = (value: string): boolean => { + const permission = (router.currentRoute.value.meta.permission || []) as string[] + if (!value) { + throw new Error(t('permission.hasPermission')) + } + if (permission.includes(value)) { + return true + } + return false +} +function hasPermi(el: Element, binding: DirectiveBinding) { + const value = binding.value + + const flag = hasPermission(value) + if (!flag) { + el.parentNode?.removeChild(el) + } +} +const mounted = (el: Element, binding: DirectiveBinding) => { + hasPermi(el, binding) +} + +const permiDirective: Directive = { + mounted +} + +export const setupPermissionDirective = (app: App) => { + app.directive('hasPermi', permiDirective) +} + +export default permiDirective diff --git a/promo-ui2/src/hooks/event/useEventBus.ts b/promo-ui2/src/hooks/event/useEventBus.ts new file mode 100644 index 0000000..d9ee832 --- /dev/null +++ b/promo-ui2/src/hooks/event/useEventBus.ts @@ -0,0 +1,26 @@ +import mitt from 'mitt' +import { onBeforeUnmount } from 'vue' + +interface Option { + name: string // 事件名称 + callback: Fn // 回调 +} + +const emitter = mitt() + +export const useEventBus = (option?: Option) => { + if (option) { + emitter.on(option.name, option.callback) + + onBeforeUnmount(() => { + emitter.off(option.name) + }) + } + + return { + on: emitter.on, + off: emitter.off, + emit: emitter.emit, + all: emitter.all + } +} diff --git a/promo-ui2/src/hooks/event/useScrollTo.ts b/promo-ui2/src/hooks/event/useScrollTo.ts new file mode 100644 index 0000000..74fd673 --- /dev/null +++ b/promo-ui2/src/hooks/event/useScrollTo.ts @@ -0,0 +1,62 @@ +import { ref, unref } from 'vue' + +export interface ScrollToParams { + el: HTMLElement + to: number + position: string + duration?: number + callback?: () => void +} + +const easeInOutQuad = (t: number, b: number, c: number, d: number) => { + t /= d / 2 + if (t < 1) { + return (c / 2) * t * t + b + } + t-- + return (-c / 2) * (t * (t - 2) - 1) + b +} +const move = (el: HTMLElement, position: string, amount: number) => { + el[position] = amount +} + +export function useScrollTo({ + el, + position = 'scrollLeft', + to, + duration = 500, + callback +}: ScrollToParams) { + const isActiveRef = ref(false) + const start = el[position] + const change = to - start + const increment = 20 + let currentTime = 0 + + function animateScroll() { + if (!unref(isActiveRef)) { + return + } + currentTime += increment + const val = easeInOutQuad(currentTime, start, change, duration) + move(el, position, val) + if (currentTime < duration && unref(isActiveRef)) { + requestAnimationFrame(animateScroll) + } else { + if (callback) { + callback() + } + } + } + + function run() { + isActiveRef.value = true + animateScroll() + } + + function stop() { + isActiveRef.value = false + } + + return { start: run, stop } +} diff --git a/promo-ui2/src/hooks/web/useClipboard.ts b/promo-ui2/src/hooks/web/useClipboard.ts new file mode 100644 index 0000000..fb8c3d6 --- /dev/null +++ b/promo-ui2/src/hooks/web/useClipboard.ts @@ -0,0 +1,47 @@ +import { ref } from 'vue' + +const useClipboard = () => { + const copied = ref(false) + const text = ref('') + const isSupported = ref(false) + + if (!navigator.clipboard && !document.execCommand) { + isSupported.value = false + } else { + isSupported.value = true + } + + const copy = (str: string) => { + if (navigator.clipboard) { + navigator.clipboard.writeText(str).then(() => { + text.value = str + copied.value = true + resetCopied() + }) + return + } + const input = document.createElement('input') + input.setAttribute('readonly', 'readonly') + input.setAttribute('value', str) + document.body.appendChild(input) + input.select() + input.setSelectionRange(0, 9999) + if (document.execCommand('copy')) { + text.value = str + document.execCommand('copy') + copied.value = true + resetCopied() + } + document.body.removeChild(input) + } + + const resetCopied = () => { + setTimeout(() => { + copied.value = false + }, 1500) + } + + return { copy, text, copied, isSupported } +} + +export { useClipboard } diff --git a/promo-ui2/src/hooks/web/useConfigGlobal.ts b/promo-ui2/src/hooks/web/useConfigGlobal.ts new file mode 100644 index 0000000..d1e490b --- /dev/null +++ b/promo-ui2/src/hooks/web/useConfigGlobal.ts @@ -0,0 +1,10 @@ +import { ConfigGlobalTypes } from '@/components/ConfigGlobal' +import { inject } from 'vue' + +export const useConfigGlobal = () => { + const configGlobal = inject('configGlobal', {}) as ConfigGlobalTypes + + return { + configGlobal + } +} diff --git a/promo-ui2/src/hooks/web/useCrudSchemas.ts b/promo-ui2/src/hooks/web/useCrudSchemas.ts new file mode 100644 index 0000000..5e61d65 --- /dev/null +++ b/promo-ui2/src/hooks/web/useCrudSchemas.ts @@ -0,0 +1,163 @@ +import { reactive } from 'vue' +import { eachTree, treeMap, filter } from '@/utils/tree' +import { FormSchema } from '@/components/Form' +import { TableColumn } from '@/components/Table' +import { DescriptionsSchema } from '@/components/Descriptions' + +export type CrudSchema = Omit & { + search?: CrudSearchParams + table?: CrudTableParams + form?: CrudFormParams + detail?: CrudDescriptionsParams + children?: CrudSchema[] +} + +interface CrudSearchParams extends Omit { + // 是否隐藏在查询项 + hidden?: boolean +} + +interface CrudTableParams extends Omit { + // 是否隐藏表头 + hidden?: boolean +} + +interface CrudFormParams extends Omit { + // 是否隐藏表单项 + hidden?: boolean +} + +interface CrudDescriptionsParams extends Omit { + // 是否隐藏表单项 + hidden?: boolean +} + +interface AllSchemas { + searchSchema: FormSchema[] + tableColumns: TableColumn[] + formSchema: FormSchema[] + detailSchema: DescriptionsSchema[] +} + +/** + * @deprecated 不推荐使用,感觉过于繁琐,不是很灵活 可能会在某个版本中删除 + */ +export const useCrudSchemas = ( + crudSchema: CrudSchema[] +): { + allSchemas: AllSchemas +} => { + // 所有结构数据 + const allSchemas = reactive({ + searchSchema: [], + tableColumns: [], + formSchema: [], + detailSchema: [] + }) + + const searchSchema = filterSearchSchema(crudSchema) + // @ts-ignore + allSchemas.searchSchema = searchSchema || [] + + const tableColumns = filterTableSchema(crudSchema) + allSchemas.tableColumns = tableColumns || [] + + const formSchema = filterFormSchema(crudSchema) + allSchemas.formSchema = formSchema + + const detailSchema = filterDescriptionsSchema(crudSchema) + allSchemas.detailSchema = detailSchema + + return { + allSchemas + } +} + +// 过滤 Search 结构 +const filterSearchSchema = (crudSchema: CrudSchema[]): FormSchema[] => { + const searchSchema: FormSchema[] = [] + const length = crudSchema.length + + for (let i = 0; i < length; i++) { + const schemaItem = crudSchema[i] + if (schemaItem.search?.hidden === true) { + continue + } + // 判断是否隐藏 + const searchSchemaItem = { + component: schemaItem?.search?.component || 'Input', + ...schemaItem.search, + field: schemaItem.field, + label: schemaItem.search?.label || schemaItem.label + } + + searchSchema.push(searchSchemaItem) + } + + return searchSchema +} + +// 过滤 table 结构 +const filterTableSchema = (crudSchema: CrudSchema[]): TableColumn[] => { + const tableColumns = treeMap(crudSchema, { + conversion: (schema: CrudSchema) => { + if (!schema?.table?.hidden) { + return { + ...schema, + ...schema.table + } + } + } + }) + + // 第一次过滤会有 undefined 所以需要二次过滤 + return filter(tableColumns as TableColumn[], (data) => { + if (data.children === void 0) { + delete data.children + } + return !!data.field + }) +} + +// 过滤 form 结构 +const filterFormSchema = (crudSchema: CrudSchema[]): FormSchema[] => { + const formSchema: FormSchema[] = [] + const length = crudSchema.length + + for (let i = 0; i < length; i++) { + const formItem = crudSchema[i] + const formSchemaItem = { + component: formItem?.form?.component || 'Input', + ...formItem.form, + field: formItem.field, + label: formItem.form?.label || formItem.label + } + + formSchema.push(formSchemaItem) + } + + return formSchema +} + +// 过滤 descriptions 结构 +const filterDescriptionsSchema = (crudSchema: CrudSchema[]): DescriptionsSchema[] => { + const descriptionsSchema: FormSchema[] = [] + + eachTree(crudSchema, (schemaItem: CrudSchema) => { + // 判断是否隐藏 + if (!schemaItem?.detail?.hidden) { + const descriptionsSchemaItem = { + ...schemaItem.detail, + field: schemaItem.field, + label: schemaItem.detail?.label || schemaItem.label + } + + // 删除不必要的字段 + delete descriptionsSchemaItem.hidden + + descriptionsSchema.push(descriptionsSchemaItem) + } + }) + + return descriptionsSchema +} diff --git a/promo-ui2/src/hooks/web/useDesign.ts b/promo-ui2/src/hooks/web/useDesign.ts new file mode 100644 index 0000000..1ec349f --- /dev/null +++ b/promo-ui2/src/hooks/web/useDesign.ts @@ -0,0 +1,18 @@ +import variables from '@/styles/variables.module.less' + +export const useDesign = () => { + const lessVariables = variables + + /** + * @param scope 类名 + * @returns 返回空间名-类名 + */ + const getPrefixCls = (scope: string) => { + return `${lessVariables.namespace}-${scope}` + } + + return { + variables: lessVariables, + getPrefixCls + } +} diff --git a/promo-ui2/src/hooks/web/useForm.ts b/promo-ui2/src/hooks/web/useForm.ts new file mode 100644 index 0000000..cdf5eac --- /dev/null +++ b/promo-ui2/src/hooks/web/useForm.ts @@ -0,0 +1,149 @@ +import type { Form, FormExpose } from '@/components/Form' +import type { ElForm, ElFormItem } from 'element-plus' +import { ref, unref, nextTick } from 'vue' +import { FormSchema, FormSetProps, FormProps } from '@/components/Form' +import { isEmptyVal, isObject } from '@/utils/is' + +export const useForm = () => { + // From实例 + const formRef = ref() + + // ElForm实例 + const elFormRef = ref>() + + /** + * @param ref Form实例 + * @param elRef ElForm实例 + */ + const register = (ref: typeof Form & FormExpose, elRef: ComponentRef) => { + formRef.value = ref + elFormRef.value = elRef + } + + const getForm = async () => { + await nextTick() + const form = unref(formRef) + if (!form) { + console.error('The form is not registered. Please use the register method to register') + } + return form + } + + // 一些内置的方法 + const methods = { + /** + * @description 设置form组件的props + * @param props form组件的props + */ + setProps: async (props: FormProps = {}) => { + const form = await getForm() + form?.setProps(props) + if (props.model) { + form?.setValues(props.model) + } + }, + + /** + * @description 设置form的值 + * @param data 需要设置的数据 + */ + setValues: async (data: Recordable) => { + const form = await getForm() + form?.setValues(data) + }, + + /** + * @description 设置schema + * @param schemaProps 需要设置的schemaProps + */ + setSchema: async (schemaProps: FormSetProps[]) => { + const form = await getForm() + form?.setSchema(schemaProps) + }, + + /** + * @description 新增schema + * @param formSchema 需要新增数据 + * @param index 在哪里新增 + */ + addSchema: async (formSchema: FormSchema, index?: number) => { + const form = await getForm() + form?.addSchema(formSchema, index) + }, + + /** + * @description 删除schema + * @param field 删除哪个数据 + */ + delSchema: async (field: string) => { + const form = await getForm() + form?.delSchema(field) + }, + + /** + * @description 获取表单数据 + * @returns form data + */ + getFormData: async (filterEmptyVal = true): Promise => { + const form = await getForm() + const model = form?.formModel as any + if (filterEmptyVal) { + // 使用reduce过滤空值,并返回一个新对象 + return Object.keys(model).reduce((prev, next) => { + const value = model[next] + if (!isEmptyVal(value)) { + if (isObject(value)) { + if (Object.keys(value).length > 0) { + prev[next] = value + } + } else { + prev[next] = value + } + } + return prev + }, {}) as T + } else { + return model as T + } + }, + + /** + * @description 获取表单组件的实例 + * @param field 表单项唯一标识 + * @returns component instance + */ + getComponentExpose: async (field: string) => { + const form = await getForm() + return form?.getComponentExpose(field) + }, + + /** + * @description 获取formItem组件的实例 + * @param field 表单项唯一标识 + * @returns formItem instance + */ + getFormItemExpose: async (field: string) => { + const form = await getForm() + return form?.getFormItemExpose(field) as ComponentRef + }, + + /** + * @description 获取ElForm组件的实例 + * @returns ElForm instance + */ + getElFormExpose: async () => { + await getForm() + return unref(elFormRef) + }, + + getFormExpose: async () => { + await getForm() + return unref(formRef) + } + } + + return { + formRegister: register, + formMethods: methods + } +} diff --git a/promo-ui2/src/hooks/web/useGuide.ts b/promo-ui2/src/hooks/web/useGuide.ts new file mode 100644 index 0000000..7fd2fb0 --- /dev/null +++ b/promo-ui2/src/hooks/web/useGuide.ts @@ -0,0 +1,49 @@ +import { Config, driver } from 'driver.js' +import 'driver.js/dist/driver.css' +import { useDesign } from '@/hooks/web/useDesign' +import { useI18n } from '@/hooks/web/useI18n' + +const { t } = useI18n() + +const { variables } = useDesign() + +export const useGuide = (options?: Config) => { + const driverObj = driver( + options || { + showProgress: true, + nextBtnText: t('common.nextLabel'), + prevBtnText: t('common.prevLabel'), + doneBtnText: t('common.doneLabel'), + steps: [ + { + element: `#${variables.namespace}-menu`, + popover: { + title: t('common.menu'), + description: t('common.menuDes'), + side: 'right' + } + }, + { + element: `#${variables.namespace}-tool-header`, + popover: { + title: t('common.tool'), + description: t('common.toolDes'), + side: 'left' + } + }, + { + element: `#${variables.namespace}-tags-view`, + popover: { + title: t('common.tagsView'), + description: t('common.tagsViewDes'), + side: 'bottom' + } + } + ] + } + ) + + return { + ...driverObj + } +} diff --git a/promo-ui2/src/hooks/web/useI18n.ts b/promo-ui2/src/hooks/web/useI18n.ts new file mode 100644 index 0000000..f1e0e7b --- /dev/null +++ b/promo-ui2/src/hooks/web/useI18n.ts @@ -0,0 +1,52 @@ +import { i18n } from '@/plugins/vueI18n' + +type I18nGlobalTranslation = { + (key: string): string + (key: string, locale: string): string + (key: string, locale: string, list: unknown[]): string + (key: string, locale: string, named: Record): string + (key: string, list: unknown[]): string + (key: string, named: Record): string +} + +type I18nTranslationRestParameters = [string, any] + +const getKey = (namespace: string | undefined, key: string) => { + if (!namespace) { + return key + } + if (key.startsWith(namespace)) { + return key + } + return `${namespace}.${key}` +} + +export const useI18n = ( + namespace?: string +): { + t: I18nGlobalTranslation +} => { + const normalFn = { + t: (key: string) => { + return getKey(namespace, key) + } + } + + if (!i18n) { + return normalFn + } + + const { t, ...methods } = i18n.global + + const tFn: I18nGlobalTranslation = (key: string, ...arg: any[]) => { + if (!key) return '' + if (!key.includes('.') && !namespace) return key + return (t as any)(getKey(namespace, key), ...(arg as I18nTranslationRestParameters)) + } + return { + ...methods, + t: tFn + } +} + +export const t = (key: string) => key diff --git a/promo-ui2/src/hooks/web/useIcon.ts b/promo-ui2/src/hooks/web/useIcon.ts new file mode 100644 index 0000000..b76bb29 --- /dev/null +++ b/promo-ui2/src/hooks/web/useIcon.ts @@ -0,0 +1,7 @@ +import { h } from 'vue' +import type { VNode } from 'vue' +import { Icon, IconTypes } from '@/components/Icon' + +export const useIcon = (props: IconTypes): VNode => { + return h(Icon, props) +} diff --git a/promo-ui2/src/hooks/web/useLocale.ts b/promo-ui2/src/hooks/web/useLocale.ts new file mode 100644 index 0000000..c65070e --- /dev/null +++ b/promo-ui2/src/hooks/web/useLocale.ts @@ -0,0 +1,35 @@ +import { i18n } from '@/plugins/vueI18n' +import { useLocaleStoreWithOut } from '@/store/modules/locale' +import { setHtmlPageLang } from '@/plugins/vueI18n/helper' + +const setI18nLanguage = (locale: LocaleType) => { + const localeStore = useLocaleStoreWithOut() + + if (i18n.mode === 'legacy') { + i18n.global.locale = locale + } else { + ;(i18n.global.locale as any).value = locale + } + localeStore.setCurrentLocale({ + lang: locale + }) + setHtmlPageLang(locale) +} + +export const useLocale = () => { + // Switching the language will change the locale of useI18n + // And submit to configuration modification + const changeLocale = async (locale: LocaleType) => { + const globalI18n = i18n.global + + const langModule = await import(`../../locales/${locale}.ts`) + + globalI18n.setLocaleMessage(locale, langModule.default) + + setI18nLanguage(locale) + } + + return { + changeLocale + } +} diff --git a/promo-ui2/src/hooks/web/useMonacoEditor.ts b/promo-ui2/src/hooks/web/useMonacoEditor.ts new file mode 100644 index 0000000..ab464ea --- /dev/null +++ b/promo-ui2/src/hooks/web/useMonacoEditor.ts @@ -0,0 +1,129 @@ +import * as monaco from 'monaco-editor' +import { ref, nextTick, onBeforeUnmount } from 'vue' +import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker' +import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker' +import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker' +import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker' +import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker' + +self.MonacoEnvironment = { + getWorker(_, label) { + if (label === 'json') { + return new jsonWorker() + } + if (label === 'css' || label === 'scss' || label === 'less') { + return new cssWorker() + } + if (label === 'html' || label === 'handlebars' || label === 'razor') { + return new htmlWorker() + } + if (label === 'typescript' || label === 'javascript') { + return new tsWorker() + } + return new editorWorker() + } +} + +export function useMonacoEditor(language: string = 'javascript') { + // 编辑器示例 + let monacoEditor: monaco.editor.IStandaloneCodeEditor | null = null + // 目标元素 + const monacoEditorRef = ref() + + // 创建实例 + function createEditor(editorOption: monaco.editor.IStandaloneEditorConstructionOptions = {}) { + if (!monacoEditorRef.value) return + monacoEditor = monaco.editor.create(monacoEditorRef.value, { + // 初始模型 + model: monaco.editor.createModel('', language), + // 是否启用预览图 + minimap: { enabled: true }, + // 圆角 + roundedSelection: true, + // 主题 + theme: 'vs-dark', + // 主键 + multiCursorModifier: 'ctrlCmd', + // 滚动条 + scrollbar: { + verticalScrollbarSize: 8, + horizontalScrollbarSize: 8 + }, + // 行号 + lineNumbers: 'on', + // tab大小 + tabSize: 2, + //字体大小 + fontSize: 14, + // 控制编辑器在用户键入、粘贴、移动或缩进行时是否应自动调整缩进 + autoIndent: 'advanced', + // 自动布局 + automaticLayout: true, + ...editorOption + }) + return monacoEditor + } + + // 格式化 + async function formatDoc() { + await monacoEditor?.getAction('editor.action.formatDocument')?.run() + } + + // 数据更新 + function updateVal(val: string) { + nextTick(() => { + if (getOption(monaco.editor.EditorOption.readOnly)) { + updateOptions({ readOnly: false }) + } + monacoEditor?.setValue(val) + setTimeout(async () => { + await formatDoc() + }, 10) + }) + } + + // 配置更新 + function updateOptions(opt: monaco.editor.IStandaloneEditorConstructionOptions) { + monacoEditor?.updateOptions(opt) + } + + // 获取配置 + function getOption(name: monaco.editor.EditorOption) { + return monacoEditor?.getOption(name) + } + + // 获取实例 + function getEditor() { + return monacoEditor + } + + function changeLanguage(newLanguage: string) { + const model = monacoEditor?.getModel() + if (model) { + monaco.editor.setModelLanguage(model, newLanguage) + } + } + + function changeTheme(newTheme: string) { + monaco.editor.setTheme(newTheme) + } + + // 页面离开 销毁 + onBeforeUnmount(() => { + if (monacoEditor) { + monacoEditor.dispose() + } + }) + + return { + monacoEditorRef, + createEditor, + getEditor, + updateVal, + updateOptions, + getOption, + formatDoc, + changeLanguage, + changeTheme + } +} diff --git a/promo-ui2/src/hooks/web/useNProgress.ts b/promo-ui2/src/hooks/web/useNProgress.ts new file mode 100644 index 0000000..76859e5 --- /dev/null +++ b/promo-ui2/src/hooks/web/useNProgress.ts @@ -0,0 +1,34 @@ +import { nextTick, unref } from 'vue' +import type { NProgressOptions } from 'nprogress' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import { useCssVar } from '@vueuse/core' + +const primaryColor = useCssVar('--el-color-primary', document.documentElement) + +export const useNProgress = () => { + NProgress.configure({ showSpinner: false } as NProgressOptions) + + const initColor = async () => { + await nextTick() + const bar = document.getElementById('nprogress')?.getElementsByClassName('bar')[0] as ElRef + if (bar) { + bar.style.background = unref(primaryColor.value) as string + } + } + + initColor() + + const start = () => { + NProgress.start() + } + + const done = () => { + NProgress.done() + } + + return { + start, + done + } +} diff --git a/promo-ui2/src/hooks/web/useNetwork.ts b/promo-ui2/src/hooks/web/useNetwork.ts new file mode 100644 index 0000000..66fa446 --- /dev/null +++ b/promo-ui2/src/hooks/web/useNetwork.ts @@ -0,0 +1,21 @@ +import { ref, onBeforeUnmount } from 'vue' + +const useNetwork = () => { + const online = ref(true) + + const updateNetwork = () => { + online.value = navigator.onLine + } + + window.addEventListener('online', updateNetwork) + window.addEventListener('offline', updateNetwork) + + onBeforeUnmount(() => { + window.removeEventListener('online', updateNetwork) + window.removeEventListener('offline', updateNetwork) + }) + + return { online } +} + +export { useNetwork } diff --git a/promo-ui2/src/hooks/web/useNow.ts b/promo-ui2/src/hooks/web/useNow.ts new file mode 100644 index 0000000..09d3176 --- /dev/null +++ b/promo-ui2/src/hooks/web/useNow.ts @@ -0,0 +1,60 @@ +import { dateUtil } from '@/utils/dateUtil' +import { reactive, toRefs } from 'vue' +import { tryOnMounted, tryOnUnmounted } from '@vueuse/core' + +export const useNow = (immediate = true) => { + let timer: IntervalHandle + + const state = reactive({ + year: 0, + month: 0, + week: '', + day: 0, + hour: '', + minute: '', + second: 0, + meridiem: '' + }) + + const update = () => { + const now = dateUtil() + + const h = now.format('HH') + const m = now.format('mm') + const s = now.get('s') + + state.year = now.get('y') + state.month = now.get('M') + 1 + state.week = '星期' + ['日', '一', '二', '三', '四', '五', '六'][now.day()] + state.day = now.get('date') + state.hour = h + state.minute = m + state.second = s + + state.meridiem = now.format('A') + } + + function start() { + update() + clearInterval(timer) + timer = setInterval(() => update(), 1000) + } + + function stop() { + clearInterval(timer) + } + + tryOnMounted(() => { + immediate && start() + }) + + tryOnUnmounted(() => { + stop() + }) + + return { + ...toRefs(state), + start, + stop + } +} diff --git a/promo-ui2/src/hooks/web/usePageLoading.ts b/promo-ui2/src/hooks/web/usePageLoading.ts new file mode 100644 index 0000000..995c941 --- /dev/null +++ b/promo-ui2/src/hooks/web/usePageLoading.ts @@ -0,0 +1,20 @@ +import { useAppStoreWithOut } from '@/store/modules/app' + +export const usePageLoading = () => { + const loadStart = () => { + const appStore = useAppStoreWithOut() + + appStore.setPageLoading(true) + } + + const loadDone = () => { + const appStore = useAppStoreWithOut() + + appStore.setPageLoading(false) + } + + return { + loadStart, + loadDone + } +} diff --git a/promo-ui2/src/hooks/web/useSearch.ts b/promo-ui2/src/hooks/web/useSearch.ts new file mode 100644 index 0000000..788c49f --- /dev/null +++ b/promo-ui2/src/hooks/web/useSearch.ts @@ -0,0 +1,91 @@ +import { ref, unref, nextTick } from 'vue' +import { FormSchema, FormSetProps } from '@/components/Form' +import { SearchExpose, SearchProps } from '@/components/Search' + +export const useSearch = () => { + // Search实例 + const searchRef = ref() + + /** + * @param ref Search实例 + * @param elRef ElForm实例 + */ + const register = (ref: SearchExpose) => { + searchRef.value = ref + } + + const getSearch = async () => { + await nextTick() + const search = unref(searchRef) + if (!search) { + console.error('The Search is not registered. Please use the register method to register') + } + return search + } + + // 一些内置的方法 + const methods = { + /** + * @description 设置search组件的props + * @param field FormItem的field + */ + setProps: async (props: SearchProps = {}) => { + const search = await getSearch() + search?.setProps(props) + if (props.model) { + search?.setValues(props.model) + } + }, + + /** + * @description 设置form的值 + * @param data 需要设置的数据 + */ + setValues: async (data: Recordable) => { + const search = await getSearch() + search?.setValues(data) + }, + + /** + * @description 设置schema + * @param schemaProps 需要设置的schemaProps + */ + setSchema: async (schemaProps: FormSetProps[]) => { + const search = await getSearch() + search?.setSchema(schemaProps) + }, + + /** + * @description 新增schema + * @param formSchema 需要新增数据 + * @param index 在哪里新增 + */ + addSchema: async (formSchema: FormSchema, index?: number) => { + const search = await getSearch() + search?.addSchema(formSchema, index) + }, + + /** + * @description 删除schema + * @param field 删除哪个数据 + */ + delSchema: async (field: string) => { + const search = await getSearch() + search?.delSchema(field) + }, + + /** + * @description 获取表单数据 + * @returns form data + */ + getFormData: async (): Promise => { + const search = await getSearch() + return search?.getFormData() as T + } + } + + return { + searchRegister: register, + searchMethods: methods + } +} diff --git a/promo-ui2/src/hooks/web/useStorage.ts b/promo-ui2/src/hooks/web/useStorage.ts new file mode 100644 index 0000000..e33a6a5 --- /dev/null +++ b/promo-ui2/src/hooks/web/useStorage.ts @@ -0,0 +1,46 @@ +// 获取传入的值的类型 +const getValueType = (value: any) => { + const type = Object.prototype.toString.call(value) + return type.slice(8, -1) +} + +export const useStorage = (type: 'sessionStorage' | 'localStorage' = 'sessionStorage') => { + const setStorage = (key: string, value: any) => { + const valueType = getValueType(value) + window[type].setItem(key, JSON.stringify({ type: valueType, value })) + } + + const getStorage = (key: string) => { + const value = window[type].getItem(key) + if (value) { + const { value: val } = JSON.parse(value) + return val + } else { + return value + } + } + + const removeStorage = (key: string) => { + window[type].removeItem(key) + } + + const clear = (excludes?: string[]) => { + // 获取排除项 + const keys = Object.keys(window[type]) + const defaultExcludes = ['dynamicRouter', 'serverDynamicRouter'] + const excludesArr = excludes ? [...excludes, ...defaultExcludes] : defaultExcludes + const excludesKeys = excludesArr ? keys.filter((key) => !excludesArr.includes(key)) : keys + // 排除项不清除 + excludesKeys.forEach((key) => { + window[type].removeItem(key) + }) + // window[type].clear() + } + + return { + setStorage, + getStorage, + removeStorage, + clear + } +} diff --git a/promo-ui2/src/hooks/web/useTable.ts b/promo-ui2/src/hooks/web/useTable.ts new file mode 100644 index 0000000..d831f1c --- /dev/null +++ b/promo-ui2/src/hooks/web/useTable.ts @@ -0,0 +1,195 @@ +import { useI18n } from '@/hooks/web/useI18n' +import { Table, TableExpose, TableProps, TableSetProps, TableColumn } from '@/components/Table' +import { ElTable, ElMessageBox, ElMessage } from 'element-plus' +import { ref, watch, unref, nextTick, onMounted } from 'vue' + +const { t } = useI18n() + +interface UseTableConfig { + /** + * 是否初始化的时候请求一次 + */ + immediate?: boolean + fetchDataApi: () => Promise<{ + list: any[] + total?: number + }> + fetchDelApi?: () => Promise +} + +export const useTable = (config: UseTableConfig) => { + const { immediate = true } = config + + const loading = ref(false) + const currentPage = ref(1) + const pageSize = ref(10) + const total = ref(0) + const dataList = ref([]) + let isPageSizeChange = false + + watch( + () => currentPage.value, + () => { + if (!isPageSizeChange) methods.getList() + isPageSizeChange = false + } + ) + + watch( + () => pageSize.value, + () => { + if (unref(currentPage) === 1) { + methods.getList() + } else { + currentPage.value = 1 + isPageSizeChange = true + methods.getList() + } + } + ) + + onMounted(() => { + if (immediate) { + methods.getList() + } + }) + + // Table实例 + const tableRef = ref() + + // ElTable实例 + const elTableRef = ref>() + + const register = (ref: typeof Table & TableExpose, elRef: ComponentRef) => { + tableRef.value = ref + elTableRef.value = unref(elRef) + } + + const getTable = async () => { + await nextTick() + const table = unref(tableRef) + if (!table) { + console.error('The table is not registered. Please use the register method to register') + } + return table + } + + const methods = { + /** + * 获取表单数据 + */ + getList: async () => { + loading.value = true + try { + const res = await config?.fetchDataApi() + console.log('fetchDataApi res', res) + if (res) { + dataList.value = res.list + total.value = res.total || 0 + } + } catch (err) { + console.log('fetchDataApi error') + } finally { + loading.value = false + } + }, + + /** + * @description 设置table组件的props + * @param props table组件的props + */ + setProps: async (props: TableProps = {}) => { + const table = await getTable() + table?.setProps(props) + }, + + /** + * @description 设置column + * @param columnProps 需要设置的列 + */ + setColumn: async (columnProps: TableSetProps[]) => { + const table = await getTable() + table?.setColumn(columnProps) + }, + + /** + * @description 新增column + * @param tableColumn 需要新增数据 + * @param index 在哪里新增 + */ + addColumn: async (tableColumn: TableColumn, index?: number) => { + const table = await getTable() + table?.addColumn(tableColumn, index) + }, + + /** + * @description 删除column + * @param field 删除哪个数据 + */ + delColumn: async (field: string) => { + const table = await getTable() + table?.delColumn(field) + }, + + /** + * @description 获取ElTable组件的实例 + * @returns ElTable instance + */ + getElTableExpose: async () => { + await getTable() + return unref(elTableRef) + }, + + refresh: () => { + methods.getList() + }, + + // sortableChange: (e: any) => { + // console.log('sortableChange', e) + // const { oldIndex, newIndex } = e + // dataList.value.splice(newIndex, 0, dataList.value.splice(oldIndex, 1)[0]) + // // to do something + // } + // 删除数据 + delList: async (idsLength: number) => { + const { fetchDelApi } = config + if (!fetchDelApi) { + console.warn('fetchDelApi is undefined') + return + } + ElMessageBox.confirm(t('common.delMessage'), t('common.delWarning'), { + confirmButtonText: t('common.delOk'), + cancelButtonText: t('common.delCancel'), + type: 'warning' + }).then(async () => { + const res = await fetchDelApi() + if (res) { + ElMessage.success(t('common.delSuccess')) + + // 计算出临界点 + const current = + unref(total) % unref(pageSize) === idsLength || unref(pageSize) === 1 + ? unref(currentPage) > 1 + ? unref(currentPage) - 1 + : unref(currentPage) + : unref(currentPage) + + currentPage.value = current + methods.getList() + } + }) + } + } + + return { + tableRegister: register, + tableMethods: methods, + tableState: { + currentPage, + pageSize, + total, + dataList, + loading + } + } +} diff --git a/promo-ui2/src/hooks/web/useTagsView.ts b/promo-ui2/src/hooks/web/useTagsView.ts new file mode 100644 index 0000000..31eadb0 --- /dev/null +++ b/promo-ui2/src/hooks/web/useTagsView.ts @@ -0,0 +1,63 @@ +import { useTagsViewStoreWithOut } from '@/store/modules/tagsView' +import { RouteLocationNormalizedLoaded, useRouter } from 'vue-router' +import { computed, nextTick, unref } from 'vue' + +export const useTagsView = () => { + const tagsViewStore = useTagsViewStoreWithOut() + + const { replace, currentRoute } = useRouter() + + const selectedTag = computed(() => tagsViewStore.getSelectedTag) + + const closeAll = (callback?: Fn) => { + tagsViewStore.delAllViews() + callback?.() + } + + const closeLeft = (callback?: Fn) => { + tagsViewStore.delLeftViews(unref(selectedTag) as RouteLocationNormalizedLoaded) + callback?.() + } + + const closeRight = (callback?: Fn) => { + tagsViewStore.delRightViews(unref(selectedTag) as RouteLocationNormalizedLoaded) + callback?.() + } + + const closeOther = (callback?: Fn) => { + tagsViewStore.delOthersViews(unref(selectedTag) as RouteLocationNormalizedLoaded) + callback?.() + } + + const closeCurrent = (view?: RouteLocationNormalizedLoaded, callback?: Fn) => { + if (view?.meta?.affix) return + tagsViewStore.delView(view || unref(currentRoute)) + + callback?.() + } + + const refreshPage = async (view?: RouteLocationNormalizedLoaded, callback?: Fn) => { + tagsViewStore.delCachedView() + const { path, query } = view || unref(currentRoute) + await nextTick() + replace({ + path: '/redirect' + path, + query: query + }) + callback?.() + } + + const setTitle = (title: string, path?: string) => { + tagsViewStore.setTitle(title, path) + } + + return { + closeAll, + closeLeft, + closeRight, + closeOther, + closeCurrent, + refreshPage, + setTitle + } +} diff --git a/promo-ui2/src/hooks/web/useTimeAgo.ts b/promo-ui2/src/hooks/web/useTimeAgo.ts new file mode 100644 index 0000000..5675e5e --- /dev/null +++ b/promo-ui2/src/hooks/web/useTimeAgo.ts @@ -0,0 +1,50 @@ +import { useTimeAgo as useTimeAgoCore, UseTimeAgoMessages } from '@vueuse/core' +import { computed, unref } from 'vue' +import { useLocaleStoreWithOut } from '@/store/modules/locale' + +const TIME_AGO_MESSAGE_MAP: { + 'zh-CN': UseTimeAgoMessages + en: UseTimeAgoMessages +} = { + 'zh-CN': { + justNow: '刚刚', + invalid: '无效时间', + past: (n) => (n.match(/\d/) ? `${n}前` : n), + future: (n) => (n.match(/\d/) ? `${n}后` : n), + month: (n, past) => (n === 1 ? (past ? '上个月' : '下个月') : `${n} 个月`), + year: (n, past) => (n === 1 ? (past ? '去年' : '明年') : `${n} 年`), + day: (n, past) => (n === 1 ? (past ? '昨天' : '明天') : `${n} 天`), + week: (n, past) => (n === 1 ? (past ? '上周' : '下周') : `${n} 周`), + hour: (n) => `${n} 小时`, + minute: (n) => `${n} 分钟`, + second: (n) => `${n} 秒` + }, + en: { + justNow: '刚刚', + invalid: 'Invalid Date', + past: (n) => (n.match(/\d/) ? `${n} ago` : n), + future: (n) => (n.match(/\d/) ? `in ${n}` : n), + month: (n, past) => + n === 1 ? (past ? 'last month' : 'next month') : `${n} month${n > 1 ? 's' : ''}`, + year: (n, past) => + n === 1 ? (past ? 'last year' : 'next year') : `${n} year${n > 1 ? 's' : ''}`, + day: (n, past) => (n === 1 ? (past ? 'yesterday' : 'tomorrow') : `${n} day${n > 1 ? 's' : ''}`), + week: (n, past) => + n === 1 ? (past ? 'last week' : 'next week') : `${n} week${n > 1 ? 's' : ''}`, + hour: (n) => `${n} hour${n > 1 ? 's' : ''}`, + minute: (n) => `${n} minute${n > 1 ? 's' : ''}`, + second: (n) => `${n} second${n > 1 ? 's' : ''}` + } +} + +export const useTimeAgo = (time: Date | number | string) => { + const localeStore = useLocaleStoreWithOut() + + const currentLocale = computed(() => localeStore.getCurrentLocale) + + const timeAgo = useTimeAgoCore(time, { + messages: TIME_AGO_MESSAGE_MAP[unref(currentLocale).lang] + }) + + return timeAgo +} diff --git a/promo-ui2/src/hooks/web/useTitle.ts b/promo-ui2/src/hooks/web/useTitle.ts new file mode 100644 index 0000000..546267d --- /dev/null +++ b/promo-ui2/src/hooks/web/useTitle.ts @@ -0,0 +1,25 @@ +import { watch, ref } from 'vue' +import { isString } from '@/utils/is' +import { useAppStoreWithOut } from '@/store/modules/app' +import { useI18n } from '@/hooks/web/useI18n' + +export const useTitle = (newTitle?: string) => { + const { t } = useI18n() + const appStore = useAppStoreWithOut() + + const title = ref( + newTitle ? `${appStore.getTitle} - ${t(newTitle as string)}` : appStore.getTitle + ) + + watch( + title, + (n, o) => { + if (isString(n) && n !== o && document) { + document.title = n + } + }, + { immediate: true } + ) + + return title +} diff --git a/promo-ui2/src/hooks/web/useValidator.ts b/promo-ui2/src/hooks/web/useValidator.ts new file mode 100644 index 0000000..00586bd --- /dev/null +++ b/promo-ui2/src/hooks/web/useValidator.ts @@ -0,0 +1,109 @@ +import { useI18n } from '@/hooks/web/useI18n' +import { FormItemRule } from 'element-plus' + +const { t } = useI18n() + +interface LengthRange { + min: number + max: number + message?: string +} + +export const useValidator = () => { + const required = (message?: string): FormItemRule => { + return { + required: true, + message: message || t('common.required') + } + } + + const lengthRange = (options: LengthRange): FormItemRule => { + const { min, max, message } = options + + return { + min, + max, + message: message || t('common.lengthRange', { min, max }) + } + } + + const notSpace = (message?: string): FormItemRule => { + return { + validator: (_, val, callback) => { + if (val?.indexOf(' ') !== -1) { + callback(new Error(message || t('common.notSpace'))) + } else { + callback() + } + } + } + } + + const notSpecialCharacters = (message?: string): FormItemRule => { + return { + validator: (_, val, callback) => { + if (/[`~!@#$%^&*()_+<>?:"{},.\/;'[\]]/gi.test(val)) { + callback(new Error(message || t('common.notSpecialCharacters'))) + } else { + callback() + } + } + } + } + + const phone = (message?: string): FormItemRule => { + return { + validator: (_, val, callback) => { + if (!val) return callback() + if (!/^1[3456789]\d{9}$/.test(val)) { + callback(new Error(message || '请输入正确的手机号码')) + } else { + callback() + } + } + } + } + + const email = (message?: string): FormItemRule => { + return { + validator: (_, val, callback) => { + if (!val) return callback() + if (!/^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/.test(val)) { + callback(new Error(message || '请输入正确的邮箱')) + } else { + callback() + } + } + } + } + + const maxlength = (max: number): FormItemRule => { + return { + max, + message: '长度不能超过' + max + '个字符' + } + } + + const check = (message?: string): FormItemRule => { + return { + validator: (_, val, callback) => { + if (!val) { + callback(new Error(message || t('common.required'))) + } else { + callback() + } + } + } + } + + return { + required, + lengthRange, + notSpace, + notSpecialCharacters, + phone, + email, + maxlength, + check + } +} diff --git a/promo-ui2/src/hooks/web/useWatermark.ts b/promo-ui2/src/hooks/web/useWatermark.ts new file mode 100644 index 0000000..4a31359 --- /dev/null +++ b/promo-ui2/src/hooks/web/useWatermark.ts @@ -0,0 +1,55 @@ +const domSymbol = Symbol('watermark-dom') + +export function useWatermark(appendEl: HTMLElement | null = document.body) { + let func: Fn = () => {} + const id = domSymbol.toString() + const clear = () => { + const domId = document.getElementById(id) + if (domId) { + const el = appendEl + el && el.removeChild(domId) + } + window.removeEventListener('resize', func) + } + const createWatermark = (str: string) => { + clear() + + const can = document.createElement('canvas') + can.width = 300 + can.height = 240 + + const cans = can.getContext('2d') + if (cans) { + cans.rotate((-20 * Math.PI) / 120) + cans.font = '15px Vedana' + cans.fillStyle = 'rgba(0, 0, 0, 0.15)' + cans.textAlign = 'left' + cans.textBaseline = 'middle' + cans.fillText(str, can.width / 20, can.height) + } + + const div = document.createElement('div') + div.id = id + div.style.pointerEvents = 'none' + div.style.top = '0px' + div.style.left = '0px' + div.style.position = 'absolute' + div.style.zIndex = '100000000' + div.style.width = document.documentElement.clientWidth + 'px' + div.style.height = document.documentElement.clientHeight + 'px' + div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat' + const el = appendEl + el && el.appendChild(div) + return id + } + + function setWatermark(str: string) { + createWatermark(str) + func = () => { + createWatermark(str) + } + window.addEventListener('resize', func) + } + + return { setWatermark, clear } +} diff --git a/promo-ui2/src/layout/Layout.vue b/promo-ui2/src/layout/Layout.vue new file mode 100644 index 0000000..8f570c3 --- /dev/null +++ b/promo-ui2/src/layout/Layout.vue @@ -0,0 +1,74 @@ + + + diff --git a/promo-ui2/src/layout/components/AppView.vue b/promo-ui2/src/layout/components/AppView.vue new file mode 100644 index 0000000..c491f1e --- /dev/null +++ b/promo-ui2/src/layout/components/AppView.vue @@ -0,0 +1,37 @@ + + + diff --git a/promo-ui2/src/layout/components/ToolHeader.vue b/promo-ui2/src/layout/components/ToolHeader.vue new file mode 100644 index 0000000..5498347 --- /dev/null +++ b/promo-ui2/src/layout/components/ToolHeader.vue @@ -0,0 +1,85 @@ + + + diff --git a/promo-ui2/src/layout/components/useRenderLayout.tsx b/promo-ui2/src/layout/components/useRenderLayout.tsx new file mode 100644 index 0000000..9f4b3a7 --- /dev/null +++ b/promo-ui2/src/layout/components/useRenderLayout.tsx @@ -0,0 +1,306 @@ +import { computed } from 'vue' +import { useAppStore } from '@/store/modules/app' +import { Menu } from '@/components/Menu' +import { TabMenu } from '@/components/TabMenu' +import { TagsView } from '@/components/TagsView' +import { Logo } from '@/components/Logo' +import AppView from './AppView.vue' +import ToolHeader from './ToolHeader.vue' +import { ElScrollbar } from 'element-plus' +import { useDesign } from '@/hooks/web/useDesign' + +const { getPrefixCls } = useDesign() + +const prefixCls = getPrefixCls('layout') + +const appStore = useAppStore() + +const pageLoading = computed(() => appStore.getPageLoading) + +// 标签页 +const tagsView = computed(() => appStore.getTagsView) + +// 菜单折叠 +const collapse = computed(() => appStore.getCollapse) + +// logo +const logo = computed(() => appStore.logo) + +// 固定头部 +const fixedHeader = computed(() => appStore.getFixedHeader) + +// 是否是移动端 +const mobile = computed(() => appStore.getMobile) + +// 固定菜单 +const fixedMenu = computed(() => appStore.getFixedMenu) + +export const useRenderLayout = () => { + const renderClassic = () => { + return ( + <> +
+ {logo.value ? ( + + ) : undefined} + +
+
+ +
+ + + {tagsView.value ? ( + + ) : undefined} +
+ + +
+
+ + ) + } + + const renderTopLeft = () => { + return ( + <> +
+ {logo.value ? : undefined} + + +
+
+ +
+ + {tagsView.value ? ( + + ) : undefined} + + + +
+
+ + ) + } + + const renderTop = () => { + return ( + <> +
+ {logo.value ? : undefined} + + +
+
+ + {tagsView.value ? ( + + ) : undefined} + + + +
+ + ) + } + + const renderCutMenu = () => { + return ( + <> +
+ {logo.value ? : undefined} + + +
+
+ +
+ + {tagsView.value ? ( + + ) : undefined} + + + +
+
+ + ) + } + + return { + renderClassic, + renderTopLeft, + renderTop, + renderCutMenu + } +} diff --git a/promo-ui2/src/locales/en.ts b/promo-ui2/src/locales/en.ts new file mode 100644 index 0000000..04e4e42 --- /dev/null +++ b/promo-ui2/src/locales/en.ts @@ -0,0 +1,584 @@ +export default { + common: { + inputText: 'Please input', + selectText: 'Please select', + startTimeText: 'Start time', + endTimeText: 'End time', + login: 'Login', + required: 'This is required', + loginOut: 'Login out', + document: 'Document', + reminder: 'Reminder', + loginOutMessage: 'Exit the system?', + back: 'Back', + ok: 'OK', + cancel: 'Cancel', + reload: 'Reload current', + closeTab: 'Close current', + closeTheLeftTab: 'Close left', + closeTheRightTab: 'Close right', + closeOther: 'Close other', + closeAll: 'Close all', + prevLabel: 'Prev', + nextLabel: 'Next', + skipLabel: 'Jump', + doneLabel: 'End', + menu: 'Menu', + menuDes: 'Menu bar rendered in routed structure', + collapse: 'Collapse', + collapseDes: 'Expand and zoom the menu bar', + tagsView: 'Tags view', + tagsViewDes: 'Used to record routing history', + tool: 'Tool', + toolDes: 'Used to set up custom systems', + query: 'Query', + reset: 'Reset', + shrink: 'Put away', + expand: 'Expand', + delMessage: 'Delete the selected data?', + delWarning: 'Warning', + delOk: 'OK', + delCancel: 'Cancel', + delNoData: 'Please select the data to delete', + delSuccess: 'Deleted successfully', + refresh: 'Refresh', + fullscreen: 'Fullscreen', + size: 'Size', + columnSetting: 'Column setting', + lengthRange: 'The length should be between {min} and {max}', + notSpace: 'Spaces are not allowed', + notSpecialCharacters: 'Special characters are not allowed', + isEqual: 'The two are not equal', + // 列设置 + setting: 'Setting' + }, + lock: { + lockScreen: 'Lock screen', + lock: 'Lock', + lockPassword: 'Lock screen password', + unlock: 'Click to unlock', + backToLogin: 'Back to login', + entrySystem: 'Entry the system', + placeholder: 'Please enter the lock screen password', + message: 'Lock screen password error' + }, + error: { + noPermission: `Sorry, you don't have permission to access this page.`, + pageError: 'Sorry, the page you visited does not exist.', + networkError: 'Sorry, the server reported an error.', + returnToHome: 'Return to home' + }, + setting: { + projectSetting: 'Project setting', + theme: 'Theme', + layout: 'Layout', + systemTheme: 'System theme', + menuTheme: 'Menu theme', + interfaceDisplay: 'Interface display', + breadcrumb: 'Breadcrumb', + breadcrumbIcon: 'Breadcrumb icon', + collapseMenu: 'Collapse menu', + hamburgerIcon: 'Hamburger icon', + screenfullIcon: 'Screenfull icon', + sizeIcon: 'Size icon', + localeIcon: 'Locale icon', + tagsView: 'Tags view', + logo: 'Logo', + greyMode: 'Grey mode', + fixedHeader: 'Fixed header', + headerTheme: 'Header theme', + cutMenu: 'Cut Menu', + copy: 'Copy', + clearAndReset: 'Clear cache and reset', + copySuccess: 'Copy success', + copyFailed: 'Copy failed', + footer: 'Footer', + uniqueOpened: 'Unique opened', + tagsViewIcon: 'Tags view icon', + // 开启动态路由 + dynamicRouter: 'Enable dynamic router', + serverDynamicRouter: 'Server dynamic router', + reExperienced: 'Please exit the login experience again', + fixedMenu: 'Fixed menu' + }, + size: { + default: 'Default', + large: 'Large', + small: 'Small' + }, + login: { + welcome: 'Welcome to the system', + message: 'Backstage management system', + username: 'Username', + password: 'Password', + register: 'Register', + checkPassword: 'Confirm password', + login: 'Sign in', + otherLogin: 'Sign in with', + remember: 'Remember me', + hasUser: 'Existing account? Go to login', + forgetPassword: 'Forget password', + usernamePlaceholder: 'Please input username', + passwordPlaceholder: 'Please input password', + code: 'Verification code', + codePlaceholder: 'Please input verification code', + getCode: 'Get code' + }, + router: { + login: 'Login', + level: 'Multi level menu', + menu: 'Menu', + menu1: 'Menu1', + menu11: 'Menu1-1', + menu111: 'Menu1-1-1', + menu12: 'Menu1-2', + menu2: 'Menu2', + dashboard: 'Dashboard', + analysis: 'Analysis', + workplace: 'Workplace', + guide: 'Guide', + component: 'Component', + icon: 'Icon', + echart: 'Echart', + countTo: 'Count to', + watermark: 'Watermark', + qrcode: 'Qrcode', + highlight: 'Highlight', + infotip: 'Infotip', + form: 'Form', + defaultForm: 'All examples', + search: 'Search', + table: 'Table', + defaultTable: 'Basic example', + editor: 'Editor', + richText: 'Rich text', + jsonEditor: 'JSON Editor', + codeEditor: 'Code Editor', + dialog: 'Dialog', + imageViewer: 'Image viewer', + descriptions: 'Descriptions', + example: 'Example', + exampleDialog: 'Example dialog', + examplePage: 'Example page', + exampleAdd: 'Example page - add', + exampleEdit: 'Example page - edit', + exampleDetail: 'Example page - detail', + errorPage: 'Error page', + authorization: 'Authorization', + user: 'User management', + role: 'Role management', + document: 'Document', + inputPassword: 'InputPassword', + sticky: 'Sticky', + treeTable: 'Tree table', + PicturePreview: 'Table Image Preview', + department: 'Department management', + menuManagement: 'Menu management', + // 权限测试页面 + permission: 'Permission test page', + function: 'Function', + multipleTabs: 'Multiple tabs', + details: 'Details', + iconPicker: 'Icon picker', + request: 'Request', + waterfall: 'Waterfall', + imageCropping: 'Image cropping', + videoPlayer: 'Video player', + // 表格视频预览 + tableVideoPreview: 'Table video preview', + cardTable: 'Card table', + personalCenter: 'Personal center', + personal: 'Personal', + avatars: 'Avatars', + iAgree: 'I agree', + tree: 'Tree' + }, + permission: { + hasPermission: 'Please set the operation permission value' + }, + analysis: { + newUser: 'New user', + unreadInformation: 'Unread information', + transactionAmount: 'Transaction amount', + totalShopping: 'Total Shopping', + monthlySales: 'Monthly sales', + userAccessSource: 'User access source', + january: 'January', + february: 'February', + march: 'March', + april: 'April', + may: 'May', + june: 'June', + july: 'July', + august: 'August', + september: 'September', + october: 'October', + november: 'November', + december: 'December', + estimate: 'Estimate', + actual: 'Actual', + directAccess: 'Airect access', + mailMarketing: 'Mail marketing', + allianceAdvertising: 'Alliance advertising', + videoAdvertising: 'Video advertising', + searchEngines: 'Search engines', + weeklyUserActivity: 'Weekly user activity', + activeQuantity: 'Active quantity', + monday: 'Monday', + tuesday: 'Tuesday', + wednesday: 'Wednesday', + thursday: 'Thursday', + friday: 'Friday', + saturday: 'Saturday', + sunday: 'Sunday' + }, + workplace: { + goodMorning: 'Good morning', + happyDay: 'Wish you happy every day!', + toady: `It's sunny today`, + project: 'Project', + access: 'Project access', + toDo: 'To do', + introduction: 'A serious introduction', + more: 'More', + shortcutOperation: 'Shortcut operation', + operation: 'Operation', + index: 'Index', + personal: 'Personal', + team: 'Team', + quote: 'Quote', + contribution: 'Contribution', + hot: 'Hot', + yield: 'Yield', + dynamic: 'Dynamic', + push: 'push', + pushCode: 'Archer push code to Github', + follow: 'Follow' + }, + formDemo: { + input: 'Input', + inputNumber: 'InputNumber', + default: 'Default', + icon: 'Icon', + mixed: 'Mixed', + password: 'Password', + textarea: 'Textarea', + remoteSearch: 'Remote search', + slot: 'Slot', + position: 'Position', + autocomplete: 'Autocomplete', + select: 'Select', + optionSlot: 'Option Slot', + selectGroup: 'Select Group', + selectV2: 'SelectV2', + cascader: 'Cascader', + switch: 'Switch', + rate: 'Rate', + colorPicker: 'Color Picker', + transfer: 'Transfer', + render: 'Render', + radio: 'Radio', + radioGroup: 'Radio Group', + button: 'Button', + checkbox: 'Checkbox', + checkboxButton: 'Checkbox Button', + checkboxGroup: 'Checkbox Group', + slider: 'Slider', + datePicker: 'Date Picker', + shortcuts: 'Shortcuts', + today: 'Today', + yesterday: 'Yesterday', + aWeekAgo: 'A week ago', + week: 'Week', + year: 'Year', + month: 'Month', + dates: 'Dates', + daterange: 'Date Range', + monthrange: 'Month Range', + dateTimePicker: 'DateTimePicker', + dateTimerange: 'Datetime Range', + timePicker: 'Time Picker', + timeSelect: 'Time Select', + inputPassword: 'input Password', + passwordStrength: 'Password Strength', + defaultForm: 'All examples', + formDes: + 'The secondary encapsulation of form components based on ElementPlus realizes data-driven and supports all Form parameters', + example: 'example', + operate: 'operate', + change: 'Change', + restore: 'Restore', + disabled: 'Disabled', + disablement: 'Disablement', + delete: 'Delete', + add: 'Add', + setValue: 'Set value', + resetValue: 'Reset value', + set: 'Set', + subitem: 'Subitem', + formValidation: 'Form validation', + verifyReset: 'Verify reset', + // 富文本编辑器 + richText: 'Rich text', + jsonEditor: 'JSON Editor', + form: 'Form', + // 远程加载 + remoteLoading: 'Remote loading', + // 聚焦 + focus: 'Focus', + treeSelect: 'Tree Select', + showCheckbox: 'Show Checkbox', + selectAnyLevel: 'Select Any Level', + multiple: 'Multiple', + filterable: 'Filterable', + // 自定义节点内容 + customContent: 'Custom content', + // 懒加载 + lazyLoad: 'Lazy load', + upload: 'Upload', + // 用户头像 + userAvatar: 'User avatar', + iconPicker: 'Icon picker', + iAgree: 'I agree' + }, + guideDemo: { + guide: 'Guide', + start: 'Start', + message: + 'The guide page is very useful for some people who enter the project for the first time. You can briefly introduce the functions of the project. The boot page is based on driver.js' + }, + iconDemo: { + icon: 'Icon', + localIcon: 'Local Icon', + iconify: 'Iconify component', + recommendedUse: 'Recommended use', + recommendeDes: + 'Iconify component basically contains all icons. You can query any icon you want. And packaging will only package the icons used.', + accessAddress: 'Access address' + }, + echartDemo: { + echart: 'Echart', + echartDes: + 'Based on the secondary packaging components of eckarts, the width is adaptive. The corresponding chart can be displayed by passing in the options and height attributes.' + }, + countToDemo: { + countTo: 'CountTo', + countToDes: + 'The transformation is based on vue-count-to and supports all vue-count-to parameters.', + suffix: 'Suffix', + prefix: 'Prefix', + separator: 'Separator', + duration: 'Duration', + endVal: 'End val', + startVal: 'Start val', + start: 'Start', + pause: 'Pause', + resume: 'Resume' + }, + watermarkDemo: { + watermark: 'Watermark', + createdWatermark: 'Created watermark', + clearWatermark: 'Clear watermark', + resetWatermark: 'Reset watermark' + }, + qrcodeDemo: { + qrcode: 'Qrcode', + qrcodeDes: 'Secondary packaging based on qrcode', + basicUsage: 'Basic usage', + imgTag: 'Img tag', + style: 'Style config', + click: 'Click event', + asynchronousContent: 'Asynchronous content', + invalid: 'Invalid', + logoConfig: 'Logo config', + logoStyle: 'Logo style', + size: 'size config' + }, + treeDemo: { + treeTitle: 'Tree control (right-click node to customize menu options)', + message: + 'The tree component is based on the secondary packaging of the tree component of ElementPlus' + }, + highlightDemo: { + highlight: 'Highlight', + message: 'The best time to plant a tree is ten years ago, followed by now.', + keys1: 'ten years ago', + keys2: 'now' + }, + infotipDemo: { + infotip: 'Infotip', + infotipDes: 'Secondary packaging of components based on Highlight', + title: 'matters needing attention' + }, + levelDemo: { + menu: 'Multi level menu cache' + }, + searchDemo: { + search: 'Search', + searchDes: + 'Based on the secondary encapsulation of form components, the functions of query and reset are realized', + operate: 'operate', + change: 'Change', + grid: 'grid', + button: 'Button', + restore: 'Restore', + inline: 'inline', + bottom: 'Bottom', + position: 'position', + left: 'left', + center: 'center', + right: 'right', + dynamicOptions: 'Dynamic options', + // 删除单选框 + deleteRadio: 'Delete radio', + // 还原单选框 + restoreRadio: 'Restore radio', + loading: 'Loading', + reset: 'Reset' + }, + stickyDemo: { + sticky: 'Sticky' + }, + tableDemo: { + table: 'Table', + tableDes: 'Secondary packaging of Table components based on ElementPlus', + index: 'Index', + title: 'Title', + author: 'Author', + displayTime: 'Display time', + importance: 'Importance', + pageviews: 'Pageviews', + action: 'Action', + important: 'Important', + good: 'Good', + commonly: 'Commonly', + operate: 'operate', + example: 'example', + show: 'Show', + hidden: 'Hidden', + pagination: 'pagination', + reserveIndex: 'Reserve index', + restoreIndex: 'Restore index', + showSelections: 'Show selections', + hiddenSelections: 'Restore selections', + showExpandedRows: 'Show expanded rows', + hiddenExpandedRows: 'Hidden expanded rows', + changeTitle: 'Change title', + header: 'Header', + selectAllNone: 'Select all / none', + delOrAddAction: 'Delete or add action', + showOrHiddenStripe: 'Show or hidden stripe', + showOrHiddenBorder: 'Show or hidden border', + fixedHeaderOrAuto: 'Fixed header or auto', + getSelections: 'Get selections', + preview: 'Preview', + showOrHiddenSortable: 'Show or hidden sortable', + videoPreview: 'Video preview', + cardTable: 'Card table' + }, + richText: { + richText: 'Rich text', + richTextDes: 'Secondary packaging based on wangeditor', + jsonEditor: 'JSON Editor', + jsonEditorDes: 'Secondary packaging based on vue-json-pretty', + codeEditor: 'Code Editor', + codeEditorDes: 'Secondary packaging based on monaco-editor' + }, + dialogDemo: { + dialog: 'Dialog', + resizeDialog: 'Resize dialog', + dialogDes: 'Secondary packaging of Dialog components based on ElementPlus', + open: 'Open', + close: 'Close', + combineWithForm: 'Combine with form', + submit: 'Submit' + }, + imageViewerDemo: { + open: 'Open', + imageViewer: 'Image viewer', + imageViewerDes: 'Secondary packaging of ImageViewer components based on ElementPlus' + }, + descriptionsDemo: { + descriptions: 'Descriptions', + descriptionsDes: 'Secondary packaging of Descriptions components based on ElementPlus', + username: 'Username', + nickName: 'NickName', + phone: 'Phone', + email: 'Email', + addr: 'Address', + form: 'Combined with Form component' + }, + exampleDemo: { + title: 'Title', + add: 'Add', + del: 'Delete', + edit: 'Edit', + author: 'Author', + displayTime: 'Display time', + importance: 'Importance', + pageviews: 'Pageviews', + important: 'Important', + content: 'Content', + save: 'Save', + detail: 'Detail' + }, + userDemo: { + title: 'User management', + message: + 'Because it is simulated data, only two accounts with different permissions are provided, which can be modified and combined by developers according to the actual situation.', + index: 'Index', + action: 'Action', + username: 'Username', + password: 'Password', + role: 'Role', + remark: 'Remark', + remarkMessage1: 'Back end control routing permission', + remarkMessage2: 'Front end control routing permission', + // 部门列表 + departmentList: 'Department list', + // 搜索部门 + searchDepartment: 'Search department', + account: 'Account', + email: 'Email', + createTime: 'Create time', + // 所属部门 + department: 'Department', + departmentName: 'Department name', + status: 'Status', + enable: 'Enable', + disable: 'Disable', + superiorDepartment: 'Superior department' + }, + menu: { + menuName: 'Menu name', + icon: 'Icon', + // 权限 + permission: 'Permission', + component: 'Component', + path: 'Path', + status: 'Status', + hidden: 'Hidden', + alwaysShow: 'Always show', + noCache: 'No cache', + breadcrumb: 'Breadcrumb', + affix: 'Affix', + noTagsView: 'No tags view', + activeMenu: 'Active menu', + canTo: 'Can to', + name: 'Name' + }, + role: { + roleName: 'Role name', + role: 'Role', + // 菜单分配 + menu: 'Menu allocation' + }, + inputPasswordDemo: { + title: 'InputPassword', + inputPasswordDes: 'Secondary packaging of Input components based on ElementPlus' + }, + avatarsDemo: { + title: + 'Avatar component for avatar list, secondary packaging based on element plus Avatar component' + } +} diff --git a/promo-ui2/src/locales/zh-CN.ts b/promo-ui2/src/locales/zh-CN.ts new file mode 100644 index 0000000..fe33cdc --- /dev/null +++ b/promo-ui2/src/locales/zh-CN.ts @@ -0,0 +1,577 @@ +export default { + app: { + turnover: '流水明细', + subFlow: '下级流水', + mymoney: '我的统计' + }, + common: { + inputText: '请输入', + selectText: '请选择', + startTimeText: '开始时间', + endTimeText: '结束时间', + login: '登录', + required: '该项为必填项', + loginOut: '退出系统', + document: '项目文档', + reminder: '温馨提示', + loginOutMessage: '是否退出本系统?', + back: '返回', + ok: '确定', + cancel: '取消', + reload: '重新加载', + closeTab: '关闭标签页', + closeTheLeftTab: '关闭左侧标签页', + closeTheRightTab: '关闭右侧标签页', + closeOther: '关闭其它标签页', + closeAll: '关闭全部标签页', + prevLabel: '上一步', + nextLabel: '下一步', + skipLabel: '跳过', + doneLabel: '结束', + menu: '菜单', + menuDes: '以路由的结构渲染的菜单栏', + collapse: '展开缩收', + collapseDes: '展开和缩放菜单栏', + tagsView: '标签页', + tagsViewDes: '用于记录路由历史记录', + tool: '工具', + toolDes: '用于设置定制系统', + query: '查询', + reset: '重置', + shrink: '收起', + expand: '展开', + delMessage: '是否删除所选中数据?', + delWarning: '提示', + delOk: '确定', + delCancel: '取消', + delNoData: '请选择需要删除的数据', + delSuccess: '删除成功', + refresh: '刷新', + fullscreen: '全屏', + size: '尺寸', + columnSetting: '列设置', + lengthRange: '长度在 {min} 到 {max} 个字符', + notSpace: '不能包含空格', + notSpecialCharacters: '不能包含特殊字符', + isEqual: '两次输入不一致', + setting: '设置' + }, + lock: { + lockScreen: '锁定屏幕', + lock: '锁定', + lockPassword: '锁屏密码', + unlock: '点击解锁', + backToLogin: '返回登录', + entrySystem: '进入系统', + placeholder: '请输入锁屏密码', + message: '锁屏密码错误' + }, + error: { + noPermission: `抱歉,您无权访问此页面。`, + pageError: '抱歉,您访问的页面不存在。', + networkError: '抱歉,服务器报告错误。', + returnToHome: '返回首页' + }, + setting: { + projectSetting: '项目配置', + theme: '主题', + layout: '布局', + systemTheme: '系统主题', + menuTheme: '菜单主题', + interfaceDisplay: '界面显示', + breadcrumb: '面包屑', + breadcrumbIcon: '面包屑图标', + collapseMenu: '折叠菜单', + hamburgerIcon: '折叠图标', + screenfullIcon: '全屏图标', + sizeIcon: '尺寸图标', + localeIcon: '多语言图标', + tagsView: '标签页', + logo: 'Logo', + greyMode: '灰色模式', + fixedHeader: '固定头部', + headerTheme: '头部主题', + cutMenu: '切割菜单', + copy: '拷贝', + clearAndReset: '清除缓存并且重置', + copySuccess: '拷贝成功', + copyFailed: '拷贝失败', + footer: '页脚', + uniqueOpened: '菜单手风琴', + tagsViewIcon: '标签页图标', + dynamicRouter: '开启动态路由', + serverDynamicRouter: '服务端动态路由', + reExperienced: '请重新退出登录体验', + fixedMenu: '固定菜单' + }, + size: { + default: '默认', + large: '大', + small: '小' + }, + login: { + welcome: '欢迎使用本系统', + message: '开箱主播管理系统', + username: '手机号', + password: '密码', + register: '注册', + checkPassword: '确认密码', + login: '登录', + otherLogin: '其它登录方式', + remember: '记住我', + hasUser: '已有账号?去登录', + forgetPassword: '忘记密码', + usernamePlaceholder: '请输入用户名', + passwordPlaceholder: '请输入密码', + code: '验证码', + codePlaceholder: '请输入验证码', + getCode: '获取验证码' + }, + router: { + login: '登录', + level: '多级菜单', + menu: '菜单', + menu1: '菜单1', + menu11: '菜单1-1', + menu111: '菜单1-1-1', + menu12: '菜单1-2', + menu2: '菜单2', + dashboard: '首页', + analysis: '数据面板', + workplace: '工作台', + guide: '引导', + component: '组件', + icon: '图标', + echart: '图表', + countTo: '数字动画', + watermark: '水印', + qrcode: '二维码', + highlight: '高亮', + infotip: '信息提示', + form: '表单', + defaultForm: '全部示例', + search: '查询', + table: '表格', + defaultTable: '基础示例', + editor: '编辑器', + richText: '富文本', + jsonEditor: 'JSON编辑器', + codeEditor: '代码编辑器', + dialog: '弹窗', + imageViewer: '图片预览', + descriptions: '描述', + example: '综合示例', + exampleDialog: '综合示例 - 弹窗', + examplePage: '综合示例 - 页面', + exampleAdd: '综合示例 - 新增', + exampleEdit: '综合示例 - 编辑', + exampleDetail: '综合示例 - 详情', + errorPage: '错误页面', + authorization: '权限管理', + user: '用户管理', + role: '角色管理', + document: '文档', + inputPassword: '密码输入框', + sticky: '黏性', + treeTable: '树形表格', + PicturePreview: '表格图片预览', + department: '部门管理', + menuManagement: '菜单管理', + permission: '权限测试页', + function: '功能', + multipleTabs: '多开标签页', + details: '详情页', + iconPicker: '图标选择器', + request: '请求', + waterfall: '瀑布流', + imageCropping: '图片裁剪', + videoPlayer: '视频播放器', + tableVideoPreview: '表格视频预览', + cardTable: '卡片表格', + personalCenter: '个人中心', + personal: '个人', + avatars: '头像列表', + iAgree: '我同意', + tree: 'Tree 树形控件' + }, + permission: { + hasPermission: '请设置操作权限值' + }, + analysis: { + newUser: '新增用户', + unreadInformation: '未读消息', + transactionAmount: '成交金额', + totalShopping: '购物总量', + monthlySales: '每月销售额', + userAccessSource: '用户访问来源', + january: '一月', + february: '二月', + march: '三月', + april: '四月', + may: '五月', + june: '六月', + july: '七月', + august: '八月', + september: '九月', + october: '十月', + november: '十一月', + december: '十二月', + estimate: '预计', + actual: '实际', + directAccess: '直接访问', + mailMarketing: '邮件营销', + allianceAdvertising: '联盟广告', + videoAdvertising: '视频广告', + searchEngines: '搜索引擎', + weeklyUserActivity: '每周用户活跃量', + activeQuantity: '活跃量', + monday: '周一', + tuesday: '周二', + wednesday: '周三', + thursday: '周四', + friday: '周五', + saturday: '周六', + sunday: '周日' + }, + workplace: { + goodMorning: '早安', + happyDay: '祝你开心每一天!', + toady: '今日晴', + project: '项目数', + access: '项目访问', + toDo: '待办', + introduction: '一个正经的简介', + more: '更多', + shortcutOperation: '快捷操作', + operation: '操作', + index: '指数', + personal: '个人', + team: '团队', + quote: '引用', + contribution: '贡献', + hot: '热度', + yield: '产量', + dynamic: '动态', + push: '推送', + pushCode: 'Archer 推送 代码到 Github', + follow: '关注' + }, + formDemo: { + input: '输入框', + inputNumber: '数字输入框', + default: '默认', + icon: '图标', + mixed: '复合型', + password: '密码框', + textarea: '多行文本', + remoteSearch: '远程搜索', + slot: '插槽', + position: '位置', + autocomplete: '自动补全', + select: '选择器', + optionSlot: '选项插槽', + selectGroup: '选项分组', + selectV2: '虚拟列表选择器', + cascader: '级联选择器', + switch: '开关', + rate: '评分', + colorPicker: '颜色选择器', + transfer: '穿梭框', + render: '渲染器', + radio: '单选框', + radioGroup: '单选框组', + button: '按钮', + checkbox: '多选框', + checkboxButton: '多选框按钮', + checkboxGroup: '多选框组', + slider: '滑块', + datePicker: '日期选择器', + shortcuts: '快捷选项', + today: '今天', + yesterday: '昨天', + aWeekAgo: '一周前', + week: '周', + year: '年', + month: '月', + dates: '日期', + daterange: '日期范围', + monthrange: '月份范围', + dateTimePicker: '日期时间选择器', + dateTimerange: '日期时间范围', + timePicker: '时间选择器', + timeSelect: '时间选择', + inputPassword: '密码输入框', + passwordStrength: '密码强度', + defaultForm: '全部示例', + formDes: '基于 ElementPlus 的 Form 组件二次封装,实现数据驱动,支持所有 Form 参数', + example: '示例', + operate: '操作', + change: '更改', + restore: '还原', + disabled: '禁用', + disablement: '解除禁用', + delete: '删除', + add: '添加', + setValue: '设置值', + resetValue: '重置值', + set: '设置', + subitem: '子项', + formValidation: '表单验证', + verifyReset: '验证重置', + // 富文本编辑器 + richText: '富文本编辑器', + // JSON编辑器 + jsonEditor: 'JSON编辑器', + form: '表单', + // 远程加载 + remoteLoading: '远程加载', + // 聚焦 + focus: '聚焦', + treeSelect: '树形选择器', + showCheckbox: '显示复选框', + selectAnyLevel: '选择任意级别', + multiple: '多选', + filterable: '可筛选', + customContent: '自定义内容', + lazyLoad: '懒加载', + upload: '上传', + userAvatar: '用户头像', + iconPicker: '图标选择器', + iAgree: '我同意' + }, + guideDemo: { + guide: '引导页', + start: '开始', + message: + '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。引导页基于 driver.js' + }, + iconDemo: { + icon: '图标', + localIcon: '本地图标', + iconify: 'Iconify组件', + recommendedUse: '推荐使用', + recommendeDes: + 'Iconify组件基本包含所有的图标,你可以查询到你想要的任何图标。并且打包只会打包所用到的图标。', + accessAddress: '访问地址' + }, + echartDemo: { + echart: '图表', + echartDes: + '基于 echarts 二次封装组件,自适应宽度,只需传入 options 与 height 属性即可展示对应的图表。' + }, + countToDemo: { + countTo: '数字动画', + countToDes: '基于 vue-count-to 进行改造,支持所有 vue-count-to 参数。', + suffix: '后缀', + prefix: '前缀', + separator: '分割符号', + duration: '持续时间', + endVal: '结束值', + startVal: '开始值', + start: '开始', + pause: '暂停', + resume: '继续' + }, + watermarkDemo: { + watermark: '水印', + createdWatermark: '创建水印', + clearWatermark: '清除水印', + resetWatermark: '重置水印' + }, + qrcodeDemo: { + qrcode: '二维码', + qrcodeDes: '基于 qrcode 二次封装', + basicUsage: '基础用法', + imgTag: 'img标签', + style: '样式配置', + click: '点击事件', + asynchronousContent: '异步内容', + invalid: '失效', + logoConfig: 'logo配置', + logoStyle: 'logo样式', + size: '大小配置' + }, + treeDemo: { + treeTitle: '树形控件(节点右键可自定义菜单选项)', + message: '基于 ElementPlus 的 Tree 组件二次封装' + }, + highlightDemo: { + highlight: '高亮', + message: '种一棵树最好的时间是十年前,其次就是现在。', + keys1: '十年前', + keys2: '现在' + }, + infotipDemo: { + infotip: '信息提示', + infotipDes: '基于 Highlight 组件二次封装', + title: '注意事项' + }, + levelDemo: { + menu: '多级菜单缓存' + }, + searchDemo: { + search: '查询', + searchDes: '基于 Form 组件二次封装,实现查询、重置功能', + operate: '操作', + change: '更改', + grid: '栅格', + button: '按钮', + restore: '还原', + inline: '内联', + bottom: '底部', + position: '位置', + left: '左', + center: '中', + right: '右', + dynamicOptions: '动态选项', + // 删除单选框 + deleteRadio: '删除单选框', + // 还原单选框 + restoreRadio: '还原单选框', + loading: '加载中', + reset: '重置' + }, + stickyDemo: { + sticky: '黏性' + }, + tableDemo: { + table: '表格', + tableDes: '基于 ElementPlus 的 Table 组件二次封装', + index: '序号', + title: '标题', + author: '作者', + displayTime: '创建时间', + importance: '重要性', + pageviews: '阅读数', + action: '操作', + important: '重要', + good: '良好', + commonly: '一般', + operate: '操作', + example: '示例', + show: '显示', + hidden: '隐藏', + pagination: '分页', + reserveIndex: '叠加序号', + restoreIndex: '还原序号', + showSelections: '显示多选', + hiddenSelections: '隐藏多选', + showExpandedRows: '显示展开行', + hiddenExpandedRows: '隐藏展开行', + changeTitle: '修改标题', + header: '头部', + selectAllNone: '全选/全不选', + delOrAddAction: '删除/添加操作列', + showOrHiddenStripe: '显示/隐藏斑马纹', + showOrHiddenBorder: '显示/隐藏边框', + fixedHeaderOrAuto: '固定头部/自动', + getSelections: '获取多选数据', + preview: '封面', + showOrHiddenSortable: '显示/隐藏排序', + videoPreview: '视频预览', + cardTable: '卡片表格' + }, + richText: { + richText: '富文本', + richTextDes: '基于 wangeditor 二次封装', + jsonEditor: 'JSON编辑器', + jsonEditorDes: '基于 vue-json-pretty 二次封装', + codeEditor: '代码编辑器', + codeEditorDes: '基于 monaco-editor 二次封装' + }, + dialogDemo: { + dialog: '弹窗', + resizeDialog: '可自定义调节弹窗大小的弹窗', + dialogDes: '基于 ElementPlus 的 Dialog 组件二次封装', + open: '打开', + close: '关闭', + combineWithForm: '与表单结合', + submit: '提交' + }, + imageViewerDemo: { + open: '打开', + imageViewer: '图片预览', + imageViewerDes: '基于 ElementPlus 的 ImageViewer 组件二次封装' + }, + descriptionsDemo: { + descriptions: '描述', + descriptionsDes: '基于 ElementPlus 的 Descriptions 组件二次封装', + username: '用户名', + nickName: '昵称', + phone: '联系电话', + email: '邮箱', + addr: '地址', + form: '与 Form 组件组合' + }, + exampleDemo: { + title: '标题', + add: '新增', + del: '删除', + edit: '编辑', + author: '作者', + displayTime: '创建时间', + importance: '重要性', + pageviews: '阅读数', + important: '重要', + content: '内容', + save: '保存', + detail: '详情' + }, + userDemo: { + title: '用户管理', + message: '由于是模拟数据,所以只提供了两种不同权限的帐号,开发者可根据实际情况自行改造结合。', + index: '序号', + action: '操作', + username: '用户名', + password: '密码', + role: '角色', + remark: '备注', + remarkMessage1: '后端控制路由权限', + remarkMessage2: '前端控制路由权限', + // 部门列表 + departmentList: '部门列表', + searchDepartment: '搜索部门', + account: '账号', + email: '邮箱', + createTime: '创建时间', + // 所属部门 + department: '所属部门', + departmentName: '部门名称', + status: '状态', + // 启用 + enable: '启用', + // 禁用 + disable: '禁用', + // 上级部门 + superiorDepartment: '上级部门' + }, + menu: { + menuName: '菜单名称', + icon: '图标', + permission: '按钮权限', + component: '组件', + path: '路径', + status: '状态', + hidden: '是否隐藏', + alwaysShow: '是否一直显示', + noCache: '是否清除缓存', + breadcrumb: '是否显示面包屑', + affix: '是否固定在标签页', + noTagsView: '是否隐藏标签页', + activeMenu: '高亮菜单', + canTo: '是否可跳转', + name: '组件名称' + }, + role: { + roleName: '角色名称', + role: '角色', + menu: '菜单分配' + }, + inputPasswordDemo: { + title: '密码输入框', + inputPasswordDes: '基于 ElementPlus 的 Input 组件二次封装' + }, + avatarsDemo: { + title: '头像列表组件,基于element-plus的Avatar组件二次封装' + } +} diff --git a/promo-ui2/src/main.ts b/promo-ui2/src/main.ts new file mode 100644 index 0000000..d0f08af --- /dev/null +++ b/promo-ui2/src/main.ts @@ -0,0 +1,58 @@ +import 'vue/jsx' + +// 引入windi css +import '@/plugins/unocss' + +// 导入全局的svg图标 +import '@/plugins/svgIcon' + +// 初始化多语言 +import { setupI18n } from '@/plugins/vueI18n' + +// 引入状态管理 +import { setupStore } from '@/store' + +// 全局组件 +import { setupGlobCom } from '@/components' + +// 引入element-plus +import { setupElementPlus } from '@/plugins/elementPlus' + +// 引入全局样式 +import '@/styles/index.less' + +// 引入动画 +import '@/plugins/animate.css' + +// 路由 +import { setupRouter } from './router' + +// 权限 +import { setupPermission } from './directives' + +import { createApp } from 'vue' + +import App from './App.vue' + +import './permission' + +// 创建实例 +const setupAll = async () => { + const app = createApp(App) + + await setupI18n(app) + + setupStore(app) + + setupGlobCom(app) + + setupElementPlus(app) + + setupRouter(app) + + setupPermission(app) + + app.mount('#app') +} + +setupAll() diff --git a/promo-ui2/src/permission.ts b/promo-ui2/src/permission.ts new file mode 100644 index 0000000..2b95fab --- /dev/null +++ b/promo-ui2/src/permission.ts @@ -0,0 +1,65 @@ +import router from './router' +import { useAppStoreWithOut } from '@/store/modules/app' +import type { RouteRecordRaw } from 'vue-router' +import { useTitle } from '@/hooks/web/useTitle' +import { useNProgress } from '@/hooks/web/useNProgress' +import { usePermissionStoreWithOut } from '@/store/modules/permission' +import { usePageLoading } from '@/hooks/web/usePageLoading' +import { NO_REDIRECT_WHITE_LIST } from '@/constants' +import { useUserStoreWithOut } from '@/store/modules/user' + +const { start, done } = useNProgress() + +const { loadStart, loadDone } = usePageLoading() + +router.beforeEach(async (to, from, next) => { + start() + loadStart() + const permissionStore = usePermissionStoreWithOut() + const appStore = useAppStoreWithOut() + const userStore = useUserStoreWithOut() + if (userStore.getUserInfo) { + if (to.path === '/login') { + next({ path: '/' }) + } else { + if (permissionStore.getIsAddRouters) { + next() + return + } + + // 开发者可根据实际情况进行修改 + const roleRouters = userStore.getRoleRouters || [] + + // 是否使用动态路由 + console.log('appStore.getDynamicRouter', appStore.getDynamicRouter) + if (appStore.getDynamicRouter) { + appStore.serverDynamicRouter + ? await permissionStore.generateRoutes('server', roleRouters as AppCustomRouteRecordRaw[]) + : await permissionStore.generateRoutes('frontEnd', roleRouters as string[]) + } else { + await permissionStore.generateRoutes('static') + } + + permissionStore.getAddRouters.forEach((route) => { + router.addRoute(route as unknown as RouteRecordRaw) // 动态添加可访问路由表 + }) + const redirectPath = from.query.redirect || to.path + const redirect = decodeURIComponent(redirectPath as string) + const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect } + permissionStore.setIsAddRouters(true) + next(nextData) + } + } else { + if (NO_REDIRECT_WHITE_LIST.indexOf(to.path) !== -1) { + next() + } else { + next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页 + } + } +}) + +router.afterEach((to) => { + useTitle(to?.meta?.title as string) + done() // 结束Progress + loadDone() +}) diff --git a/promo-ui2/src/plugins/animate.css/index.ts b/promo-ui2/src/plugins/animate.css/index.ts new file mode 100644 index 0000000..3e93451 --- /dev/null +++ b/promo-ui2/src/plugins/animate.css/index.ts @@ -0,0 +1 @@ +import 'animate.css' diff --git a/promo-ui2/src/plugins/echarts/index.ts b/promo-ui2/src/plugins/echarts/index.ts new file mode 100644 index 0000000..34f756f --- /dev/null +++ b/promo-ui2/src/plugins/echarts/index.ts @@ -0,0 +1,41 @@ +import * as echarts from 'echarts/core' + +import { + BarChart, + LineChart, + PieChart, + MapChart, + PictorialBarChart, + RadarChart +} from 'echarts/charts' + +import { + TitleComponent, + TooltipComponent, + GridComponent, + PolarComponent, + AriaComponent, + ParallelComponent, + LegendComponent +} from 'echarts/components' + +import { CanvasRenderer } from 'echarts/renderers' + +echarts.use([ + LegendComponent, + TitleComponent, + TooltipComponent, + GridComponent, + PolarComponent, + AriaComponent, + ParallelComponent, + BarChart, + LineChart, + PieChart, + MapChart, + CanvasRenderer, + PictorialBarChart, + RadarChart +]) + +export default echarts diff --git a/promo-ui2/src/plugins/elementPlus/index.ts b/promo-ui2/src/plugins/elementPlus/index.ts new file mode 100644 index 0000000..29a7840 --- /dev/null +++ b/promo-ui2/src/plugins/elementPlus/index.ts @@ -0,0 +1,24 @@ +import type { App } from 'vue' + +// 需要全局引入一些组件,如ElScrollbar,不然一些下拉项样式有问题 +import { ElLoading, ElScrollbar } from 'element-plus' + +const plugins = [ElLoading] + +const components = [ElScrollbar] + +export const setupElementPlus = (app: App) => { + plugins.forEach((plugin) => { + app.use(plugin) + }) + + // 为了开发环境启动更快,一次性引入所有样式 + if (import.meta.env.VITE_USE_ALL_ELEMENT_PLUS_STYLE === 'true') { + import('element-plus/dist/index.css') + return + } + + components.forEach((component) => { + app.component(component.name!, component) + }) +} diff --git a/promo-ui2/src/plugins/svgIcon/index.ts b/promo-ui2/src/plugins/svgIcon/index.ts new file mode 100644 index 0000000..1b11e2d --- /dev/null +++ b/promo-ui2/src/plugins/svgIcon/index.ts @@ -0,0 +1 @@ +import 'virtual:svg-icons-register' diff --git a/promo-ui2/src/plugins/unocss/index.ts b/promo-ui2/src/plugins/unocss/index.ts new file mode 100644 index 0000000..d366b5a --- /dev/null +++ b/promo-ui2/src/plugins/unocss/index.ts @@ -0,0 +1 @@ +import 'virtual:uno.css' diff --git a/promo-ui2/src/plugins/vueI18n/helper.ts b/promo-ui2/src/plugins/vueI18n/helper.ts new file mode 100644 index 0000000..da6bc8c --- /dev/null +++ b/promo-ui2/src/plugins/vueI18n/helper.ts @@ -0,0 +1,3 @@ +export const setHtmlPageLang = (locale: LocaleType) => { + document.querySelector('html')?.setAttribute('lang', locale) +} diff --git a/promo-ui2/src/plugins/vueI18n/index.ts b/promo-ui2/src/plugins/vueI18n/index.ts new file mode 100644 index 0000000..f845b13 --- /dev/null +++ b/promo-ui2/src/plugins/vueI18n/index.ts @@ -0,0 +1,42 @@ +import type { App } from 'vue' +import { createI18n } from 'vue-i18n' +import { useLocaleStoreWithOut } from '@/store/modules/locale' +import type { I18n, I18nOptions } from 'vue-i18n' +import { setHtmlPageLang } from './helper' + +export let i18n: ReturnType + +const createI18nOptions = async (): Promise => { + const localeStore = useLocaleStoreWithOut() + const locale = localeStore.getCurrentLocale + const localeMap = localeStore.getLocaleMap + const defaultLocal = await import(`../../locales/${locale.lang}.ts`) + const message = defaultLocal.default ?? {} + + setHtmlPageLang(locale.lang) + + localeStore.setCurrentLocale({ + lang: locale.lang + // elLocale: elLocal + }) + + return { + legacy: false, + locale: locale.lang, + fallbackLocale: locale.lang, + messages: { + [locale.lang]: message + }, + availableLocales: localeMap.map((v) => v.lang), + sync: true, + silentTranslationWarn: true, + missingWarn: false, + silentFallbackWarn: true + } +} + +export const setupI18n = async (app: App) => { + const options = await createI18nOptions() + i18n = createI18n(options) as I18n + app.use(i18n) +} diff --git a/promo-ui2/src/router/index.ts b/promo-ui2/src/router/index.ts new file mode 100644 index 0000000..6fe05b5 --- /dev/null +++ b/promo-ui2/src/router/index.ts @@ -0,0 +1,200 @@ +import { createRouter, createWebHashHistory } from 'vue-router' +import type { RouteRecordRaw } from 'vue-router' +import type { App } from 'vue' +import { Layout, getParentLayout } from '@/utils/routerHelper' +import { useI18n } from '@/hooks/web/useI18n' +import { NO_RESET_WHITE_LIST } from '@/constants' + +const { t } = useI18n() + +export const constantRouterMap: AppRouteRecordRaw[] = [ + { + path: '/', + component: Layout, + redirect: '/dashboard/index', + name: 'Root', + meta: { + hidden: true + } + }, + { + path: '/redirect', + component: Layout, + name: 'RedirectWrap', + children: [ + { + path: '/redirect/:path(.*)', + name: 'Redirect', + component: () => import('@/views/Redirect/Redirect.vue'), + meta: {} + } + ], + meta: { + hidden: true, + noTagsView: true + } + }, + { + path: '/login', + component: () => import('@/views/Login/Login.vue'), + name: 'Login', + meta: { + hidden: true, + title: t('router.login'), + noTagsView: true + } + }, + { + path: '/personal', + component: Layout, + redirect: '/personal/personal-center', + name: 'Personal', + meta: { + title: t('router.personal'), + hidden: true, + canTo: true + }, + children: [ + { + path: 'personal-center', + component: () => import('@/views/Personal/PersonalCenter/PersonalCenter.vue'), + name: 'PersonalCenter', + meta: { + title: t('router.personalCenter'), + hidden: true, + canTo: true + } + } + ] + }, + { + path: '/404', + component: () => import('@/views/Error/404.vue'), + name: 'NoFind', + meta: { + hidden: true, + title: '404', + noTagsView: true + } + } +] + +export const asyncRouterMap: AppRouteRecordRaw[] = [ + { + path: '/dashboard', + component: Layout, + redirect: '/dashboard/index', + name: 'Dashboard', + meta: {}, + children: [ + { + path: 'index', + component: () => import('@/views/Dashboard/Analysis.vue'), + name: 'Analysis', + meta: { + title: t('router.analysis') + } + } + ] + }, + { + path: '/user', + component: Layout, + name: 'user', + meta: {}, + redirect: '/user', + children: [ + { + path: 'index', + name: 'index', + component: () => import('@/views/User/index.vue'), + meta: { title: '个人信息' } + } + ] + }, + { + path: '/statistics', + component: Layout, + redirect: '/statistics/index', + name: 'Statistics', + meta: {}, + children: [ + { + path: 'index', + component: () => import('@/views/Dashboard/Analysis.vue'), + name: 'Analysis', + meta: { + title: t('router.analysis') + } + } + ] + }, + { + path: '/turnover', + component: Layout, + name: 'turnover', + redirect: '/turnover', + meta: {}, + children: [ + { + path: 'index', + name: 'turnoverindex', + component: () => import('@/views/Turnover/index.vue'), + meta: { title: t('app.turnover') } + } + ] + }, + { + path: '/subFlow', + component: Layout, + name: 'subFlow', + meta: {}, + redirect: '/subFlow', + children: [ + { + path: 'index', + name: 'subFlowindex', + component: () => import('@/views/subFlow/index.vue'), + meta: { title: t('app.subFlow') } + } + ] + }, + { + path: '/mymoney', + component: Layout, + name: 'mymoney', + meta: {}, + redirect: '/mymoney', + children: [ + { + path: 'index', + name: 'mymoneyIndex', + component: () => import('@/views/MyMoney/index.vue'), + meta: { title: t('app.mymoney') } + } + ] + }, + +] + +const router = createRouter({ + history: createWebHashHistory(), + strict: true, + routes: constantRouterMap as RouteRecordRaw[], + scrollBehavior: () => ({ left: 0, top: 0 }) +}) + +export const resetRouter = (): void => { + router.getRoutes().forEach((route) => { + const { name } = route + if (name && !NO_RESET_WHITE_LIST.includes(name as string)) { + router.hasRoute(name) && router.removeRoute(name) + } + }) +} + +export const setupRouter = (app: App) => { + app.use(router) +} + +export default router diff --git a/promo-ui2/src/router/role.ts b/promo-ui2/src/router/role.ts new file mode 100644 index 0000000..b1dee0f --- /dev/null +++ b/promo-ui2/src/router/role.ts @@ -0,0 +1,21 @@ +// 主播 +export const role01: string[] = [ + '/dashboard', + '/dashboard/index', + '/user', + '/user/index', + '/subFlow', + '/subFlow/index', + '/mymoney', + '/mymoney/index' +] + +// 招商 +export const role04: string[] = [ + '/dashboard', + '/dashboard/index', + '/user', + '/user/index', + '/turnover', + '/turnover/index', +] \ No newline at end of file diff --git a/promo-ui2/src/store/index.ts b/promo-ui2/src/store/index.ts new file mode 100644 index 0000000..ee92ca8 --- /dev/null +++ b/promo-ui2/src/store/index.ts @@ -0,0 +1,13 @@ +import type { App } from 'vue' +import { createPinia } from 'pinia' +import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' + +const store = createPinia() + +store.use(piniaPluginPersistedstate) + +export const setupStore = (app: App) => { + app.use(store) +} + +export { store } diff --git a/promo-ui2/src/store/modules/app.ts b/promo-ui2/src/store/modules/app.ts new file mode 100644 index 0000000..d7f57ff --- /dev/null +++ b/promo-ui2/src/store/modules/app.ts @@ -0,0 +1,340 @@ +import { defineStore } from 'pinia' +import { store } from '../index' +import { setCssVar, humpToUnderline } from '@/utils' +import { colorIsDark, hexToRGB, lighten, mix } from '@/utils/color' +import { ElMessage, ComponentSize } from 'element-plus' +import { useCssVar } from '@vueuse/core' +import { unref } from 'vue' +import { useDark } from '@vueuse/core' + +interface AppState { + breadcrumb: boolean + breadcrumbIcon: boolean + collapse: boolean + uniqueOpened: boolean + hamburger: boolean + screenfull: boolean + size: boolean + locale: boolean + tagsView: boolean + tagsViewIcon: boolean + logo: boolean + fixedHeader: boolean + greyMode: boolean + dynamicRouter: boolean + serverDynamicRouter: boolean + pageLoading: boolean + layout: LayoutType + title: string + isDark: boolean + currentSize: ComponentSize + sizeMap: ComponentSize[] + mobile: boolean + footer: boolean + theme: ThemeTypes + fixedMenu: boolean +} + +export const useAppStore = defineStore('app', { + state: (): AppState => { + return { + sizeMap: ['default', 'large', 'small'], + mobile: false, // 是否是移动端 + title: import.meta.env.VITE_APP_TITLE, // 标题 + pageLoading: false, // 路由跳转loading + breadcrumb: true, // 面包屑 + breadcrumbIcon: true, // 面包屑图标 + collapse: false, // 折叠菜单 + uniqueOpened: false, // 是否只保持一个子菜单的展开 + hamburger: true, // 折叠图标 + screenfull: true, // 全屏图标 + size: true, // 尺寸图标 + locale: true, // 多语言图标 + tagsView: true, // 标签页 + tagsViewIcon: true, // 是否显示标签图标 + logo: true, // logo + fixedHeader: true, // 固定toolheader + footer: true, // 显示页脚 + greyMode: false, // 是否开始灰色模式,用于特殊悼念日 + dynamicRouter: true, // 是否动态路由 + serverDynamicRouter: false, // 是否服务端渲染动态路由 + fixedMenu: false, // 是否固定菜单 + + layout: 'classic', // layout布局 + isDark: false, // 是否是暗黑模式 + currentSize: 'default', // 组件尺寸 + theme: { + // 主题色 + elColorPrimary: '#409eff', + // 左侧菜单边框颜色 + leftMenuBorderColor: 'inherit', + // 左侧菜单背景颜色 + leftMenuBgColor: '#001529', + // 左侧菜单浅色背景颜色 + leftMenuBgLightColor: '#0f2438', + // 左侧菜单选中背景颜色 + leftMenuBgActiveColor: 'var(--el-color-primary)', + // 左侧菜单收起选中背景颜色 + leftMenuCollapseBgActiveColor: 'var(--el-color-primary)', + // 左侧菜单字体颜色 + leftMenuTextColor: '#bfcbd9', + // 左侧菜单选中字体颜色 + leftMenuTextActiveColor: '#fff', + // logo字体颜色 + logoTitleTextColor: '#fff', + // logo边框颜色 + logoBorderColor: 'inherit', + // 头部背景颜色 + topHeaderBgColor: '#fff', + // 头部字体颜色 + topHeaderTextColor: 'inherit', + // 头部悬停颜色 + topHeaderHoverColor: '#f6f6f6', + // 头部边框颜色 + topToolBorderColor: '#eee' + } + } + }, + getters: { + getBreadcrumb(): boolean { + return this.breadcrumb + }, + getBreadcrumbIcon(): boolean { + return this.breadcrumbIcon + }, + getCollapse(): boolean { + return this.collapse + }, + getUniqueOpened(): boolean { + return this.uniqueOpened + }, + getHamburger(): boolean { + return this.hamburger + }, + getScreenfull(): boolean { + return this.screenfull + }, + getSize(): boolean { + return this.size + }, + getLocale(): boolean { + return this.locale + }, + getTagsView(): boolean { + return this.tagsView + }, + getTagsViewIcon(): boolean { + return this.tagsViewIcon + }, + getLogo(): boolean { + return this.logo + }, + getFixedHeader(): boolean { + return this.fixedHeader + }, + getGreyMode(): boolean { + return this.greyMode + }, + getDynamicRouter(): boolean { + return this.dynamicRouter + }, + getServerDynamicRouter(): boolean { + return this.serverDynamicRouter + }, + getFixedMenu(): boolean { + return this.fixedMenu + }, + getPageLoading(): boolean { + return this.pageLoading + }, + getLayout(): LayoutType { + return this.layout + }, + getTitle(): string { + return this.title + }, + getIsDark(): boolean { + return this.isDark + }, + getCurrentSize(): ComponentSize { + return this.currentSize + }, + getSizeMap(): ComponentSize[] { + return this.sizeMap + }, + getMobile(): boolean { + return this.mobile + }, + getTheme(): ThemeTypes { + return this.theme + }, + getFooter(): boolean { + return this.footer + } + }, + actions: { + setBreadcrumb(breadcrumb: boolean) { + this.breadcrumb = breadcrumb + }, + setBreadcrumbIcon(breadcrumbIcon: boolean) { + this.breadcrumbIcon = breadcrumbIcon + }, + setCollapse(collapse: boolean) { + this.collapse = collapse + }, + setUniqueOpened(uniqueOpened: boolean) { + this.uniqueOpened = uniqueOpened + }, + setHamburger(hamburger: boolean) { + this.hamburger = hamburger + }, + setScreenfull(screenfull: boolean) { + this.screenfull = screenfull + }, + setSize(size: boolean) { + this.size = size + }, + setLocale(locale: boolean) { + this.locale = locale + }, + setTagsView(tagsView: boolean) { + this.tagsView = tagsView + }, + setTagsViewIcon(tagsViewIcon: boolean) { + this.tagsViewIcon = tagsViewIcon + }, + setLogo(logo: boolean) { + this.logo = logo + }, + setFixedHeader(fixedHeader: boolean) { + this.fixedHeader = fixedHeader + }, + setGreyMode(greyMode: boolean) { + this.greyMode = greyMode + }, + setDynamicRouter(dynamicRouter: boolean) { + this.dynamicRouter = dynamicRouter + }, + setServerDynamicRouter(serverDynamicRouter: boolean) { + this.serverDynamicRouter = serverDynamicRouter + }, + setFixedMenu(fixedMenu: boolean) { + this.fixedMenu = fixedMenu + }, + setPageLoading(pageLoading: boolean) { + this.pageLoading = pageLoading + }, + setLayout(layout: LayoutType) { + if (this.mobile && layout !== 'classic') { + ElMessage.warning('移动端模式下不支持切换其它布局') + return + } + this.layout = layout + }, + setTitle(title: string) { + this.title = title + }, + setIsDark(isDark: boolean) { + this.isDark = isDark + if (this.isDark) { + document.documentElement.classList.add('dark') + document.documentElement.classList.remove('light') + } else { + document.documentElement.classList.add('light') + document.documentElement.classList.remove('dark') + } + this.setPrimaryLight() + }, + setCurrentSize(currentSize: ComponentSize) { + this.currentSize = currentSize + }, + setMobile(mobile: boolean) { + this.mobile = mobile + }, + setTheme(theme: ThemeTypes) { + this.theme = Object.assign(this.theme, theme) + }, + setCssVarTheme() { + for (const key in this.theme) { + setCssVar(`--${humpToUnderline(key)}`, this.theme[key]) + } + this.setPrimaryLight() + }, + setFooter(footer: boolean) { + this.footer = footer + }, + setPrimaryLight() { + if (this.theme.elColorPrimary) { + const elColorPrimary = this.theme.elColorPrimary + const color = this.isDark ? '#000000' : '#ffffff' + const lightList = [3, 5, 7, 8, 9] + lightList.forEach((v) => { + setCssVar(`--el-color-primary-light-${v}`, mix(color, elColorPrimary, v / 10)) + }) + setCssVar(`--el-color-primary-dark-2`, mix(color, elColorPrimary, 0.2)) + } + }, + setMenuTheme(color: string) { + const primaryColor = useCssVar('--el-color-primary', document.documentElement) + const isDarkColor = colorIsDark(color) + const theme: Recordable = { + // 左侧菜单边框颜色 + leftMenuBorderColor: isDarkColor ? 'inherit' : '#eee', + // 左侧菜单背景颜色 + leftMenuBgColor: color, + // 左侧菜单浅色背景颜色 + leftMenuBgLightColor: isDarkColor ? lighten(color!, 6) : color, + // 左侧菜单选中背景颜色 + leftMenuBgActiveColor: isDarkColor + ? 'var(--el-color-primary)' + : hexToRGB(unref(primaryColor) as string, 0.1), + // 左侧菜单收起选中背景颜色 + leftMenuCollapseBgActiveColor: isDarkColor + ? 'var(--el-color-primary)' + : hexToRGB(unref(primaryColor) as string, 0.1), + // 左侧菜单字体颜色 + leftMenuTextColor: isDarkColor ? '#bfcbd9' : '#333', + // 左侧菜单选中字体颜色 + leftMenuTextActiveColor: isDarkColor ? '#fff' : 'var(--el-color-primary)', + // logo字体颜色 + logoTitleTextColor: isDarkColor ? '#fff' : 'inherit', + // logo边框颜色 + logoBorderColor: isDarkColor ? color : '#eee' + } + this.setTheme(theme) + this.setCssVarTheme() + }, + setHeaderTheme(color: string) { + const isDarkColor = colorIsDark(color) + const textColor = isDarkColor ? '#fff' : 'inherit' + const textHoverColor = isDarkColor ? lighten(color!, 6) : '#f6f6f6' + const topToolBorderColor = isDarkColor ? color : '#eee' + setCssVar('--top-header-bg-color', color) + setCssVar('--top-header-text-color', textColor) + setCssVar('--top-header-hover-color', textHoverColor) + this.setTheme({ + topHeaderBgColor: color, + topHeaderTextColor: textColor, + topHeaderHoverColor: textHoverColor, + topToolBorderColor + }) + if (this.getLayout === 'top') { + this.setMenuTheme(color) + } + }, + initTheme() { + const isDark = useDark({ + valueDark: 'dark', + valueLight: 'light' + }) + isDark.value = this.getIsDark + const newTitle = import.meta.env.VITE_APP_TITLE + newTitle !== this.getTitle && this.setTitle(newTitle) + } + }, + persist: true +}) + +export const useAppStoreWithOut = () => { + return useAppStore(store) +} diff --git a/promo-ui2/src/store/modules/locale.ts b/promo-ui2/src/store/modules/locale.ts new file mode 100644 index 0000000..8d2ba35 --- /dev/null +++ b/promo-ui2/src/store/modules/locale.ts @@ -0,0 +1,59 @@ +import { defineStore } from 'pinia' +import { store } from '../index' +import zhCn from 'element-plus/es/locale/lang/zh-cn' +import en from 'element-plus/es/locale/lang/en' +import { useStorage } from '@/hooks/web/useStorage' +import { LocaleDropdownType } from '@/components/LocaleDropdown' + +const { getStorage, setStorage } = useStorage('localStorage') + +const elLocaleMap = { + 'zh-CN': zhCn, + en: en +} +interface LocaleState { + currentLocale: LocaleDropdownType + localeMap: LocaleDropdownType[] +} + +export const useLocaleStore = defineStore('locales', { + state: (): LocaleState => { + return { + currentLocale: { + lang: getStorage('lang') || 'zh-CN', + elLocale: elLocaleMap[getStorage('lang') || 'zh-CN'] + }, + // 多语言 + localeMap: [ + { + lang: 'zh-CN', + name: '简体中文' + }, + { + lang: 'en', + name: 'English' + } + ] + } + }, + getters: { + getCurrentLocale(): LocaleDropdownType { + return this.currentLocale + }, + getLocaleMap(): LocaleDropdownType[] { + return this.localeMap + } + }, + actions: { + setCurrentLocale(localeMap: LocaleDropdownType) { + // this.locale = Object.assign(this.locale, localeMap) + this.currentLocale.lang = localeMap?.lang + this.currentLocale.elLocale = elLocaleMap[localeMap?.lang] + setStorage('lang', localeMap?.lang) + } + } +}) + +export const useLocaleStoreWithOut = () => { + return useLocaleStore(store) +} diff --git a/promo-ui2/src/store/modules/lock.ts b/promo-ui2/src/store/modules/lock.ts new file mode 100644 index 0000000..70ff87f --- /dev/null +++ b/promo-ui2/src/store/modules/lock.ts @@ -0,0 +1,48 @@ +import { defineStore } from 'pinia' +import { store } from '../index' + +interface lockInfo { + isLock?: boolean + password?: string +} + +interface LockState { + lockInfo: lockInfo +} + +export const useLockStore = defineStore('lock', { + state: (): LockState => { + return { + lockInfo: { + // isLock: false, // 是否锁定屏幕 + // password: '' // 锁屏密码 + } + } + }, + getters: { + getLockInfo(): lockInfo { + return this.lockInfo + } + }, + actions: { + setLockInfo(lockInfo: lockInfo) { + this.lockInfo = lockInfo + }, + resetLockInfo() { + this.lockInfo = {} + }, + unLock(password: string) { + if (this.lockInfo?.password === password) { + this.resetLockInfo() + return true + } else { + return false + } + } + }, + persist: true +}) + +export const useLockStoreWithOut = () => { + return useLockStore(store) +} diff --git a/promo-ui2/src/store/modules/permission.ts b/promo-ui2/src/store/modules/permission.ts new file mode 100644 index 0000000..32c107e --- /dev/null +++ b/promo-ui2/src/store/modules/permission.ts @@ -0,0 +1,98 @@ +import { defineStore } from 'pinia' +import { asyncRouterMap, constantRouterMap } from '@/router' +import { + generateRoutesByFrontEnd, + generateRoutesByServer, + flatMultiLevelRoutes +} from '@/utils/routerHelper' +import { store } from '../index' +import { cloneDeep } from 'lodash-es' + +export interface PermissionState { + routers: AppRouteRecordRaw[] + addRouters: AppRouteRecordRaw[] + isAddRouters: boolean + menuTabRouters: AppRouteRecordRaw[] +} + +export const usePermissionStore = defineStore('permission', { + state: (): PermissionState => ({ + routers: [], + addRouters: [], + isAddRouters: false, + menuTabRouters: [] + }), + getters: { + getRouters(): AppRouteRecordRaw[] { + return this.routers + }, + getAddRouters(): AppRouteRecordRaw[] { + return flatMultiLevelRoutes(cloneDeep(this.addRouters)) + }, + getIsAddRouters(): boolean { + return this.isAddRouters + }, + getMenuTabRouters(): AppRouteRecordRaw[] { + return this.menuTabRouters + } + }, + actions: { + generateRoutes( + type: 'server' | 'frontEnd' | 'static', + routers?: AppCustomRouteRecordRaw[] | string[] + ): Promise { + return new Promise((resolve) => { + let routerMap: AppRouteRecordRaw[] = [] + if (type === 'server') { + // 模拟后端过滤菜单 + routerMap = generateRoutesByServer(routers as AppCustomRouteRecordRaw[]) + } else if (type === 'frontEnd') { + // 模拟前端过滤菜单 + routerMap = generateRoutesByFrontEnd(cloneDeep(asyncRouterMap), routers as string[]) + } else { + // 直接读取静态路由表 + routerMap = cloneDeep(asyncRouterMap) + } + // 动态路由,404一定要放到最后面 + this.addRouters = routerMap.concat([ + { + path: '/:path(.*)*', + redirect: '/404', + name: '404Page', + meta: { + hidden: true, + breadcrumb: false + } + } + ]) + // 渲染菜单的所有路由 + this.routers = cloneDeep(constantRouterMap).concat(routerMap) + resolve() + }) + }, + setIsAddRouters(state: boolean): void { + this.isAddRouters = state + }, + setMenuTabRouters(routers: AppRouteRecordRaw[]): void { + this.menuTabRouters = routers + } + }, + persist: [ + { + pick: ['routers'], + storage: localStorage + }, + { + pick: ['addRouters'], + storage: localStorage + }, + { + pick: ['menuTabRouters'], + storage: localStorage + } + ] +}) + +export const usePermissionStoreWithOut = () => { + return usePermissionStore(store) +} diff --git a/promo-ui2/src/store/modules/tagsView.ts b/promo-ui2/src/store/modules/tagsView.ts new file mode 100644 index 0000000..d07a439 --- /dev/null +++ b/promo-ui2/src/store/modules/tagsView.ts @@ -0,0 +1,163 @@ +import router from '@/router' +import type { RouteLocationNormalizedLoaded } from 'vue-router' +import { getRawRoute } from '@/utils/routerHelper' +import { defineStore } from 'pinia' +import { store } from '../index' +import { findIndex } from '@/utils' +import { useUserStoreWithOut } from './user' + +export interface TagsViewState { + visitedViews: RouteLocationNormalizedLoaded[] + cachedViews: Set + selectedTag?: RouteLocationNormalizedLoaded +} + +export const useTagsViewStore = defineStore('tagsView', { + state: (): TagsViewState => ({ + visitedViews: [], + cachedViews: new Set(), + selectedTag: undefined + }), + getters: { + getVisitedViews(): RouteLocationNormalizedLoaded[] { + return this.visitedViews + }, + getCachedViews(): string[] { + return Array.from(this.cachedViews) + }, + getSelectedTag(): RouteLocationNormalizedLoaded | undefined { + return this.selectedTag + } + }, + actions: { + // 新增缓存和tag + addView(view: RouteLocationNormalizedLoaded): void { + this.addVisitedView(view) + this.addCachedView() + }, + // 新增tag + addVisitedView(view: RouteLocationNormalizedLoaded) { + if (this.visitedViews.some((v) => v.path === view.path)) return + if (view.meta?.noTagsView) return + this.visitedViews.push( + Object.assign({}, view, { + title: view.meta?.title || 'no-name' + }) + ) + }, + // 新增缓存 + addCachedView() { + const cacheMap: Set = new Set() + for (const v of this.visitedViews) { + const item = getRawRoute(v) + const needCache = !item?.meta?.noCache + if (!needCache) { + continue + } + const name = item.name as string + cacheMap.add(name) + } + if (Array.from(this.cachedViews).sort().toString() === Array.from(cacheMap).sort().toString()) + return + this.cachedViews = cacheMap + }, + // 删除某个 + delView(view: RouteLocationNormalizedLoaded) { + this.delVisitedView(view) + this.addCachedView() + }, + // 删除tag + delVisitedView(view: RouteLocationNormalizedLoaded) { + for (const [i, v] of this.visitedViews.entries()) { + if (v.path === view.path) { + this.visitedViews.splice(i, 1) + break + } + } + }, + // 删除缓存 + delCachedView() { + const route = router.currentRoute.value + const index = findIndex(this.getCachedViews, (v) => v === route.name) + if (index > -1) { + this.cachedViews.delete(this.getCachedViews[index]) + } + }, + // 删除所有缓存和tag + delAllViews() { + this.delAllVisitedViews() + this.addCachedView() + }, + // 删除所有tag + delAllVisitedViews() { + const userStore = useUserStoreWithOut() + + // const affixTags = this.visitedViews.filter((tag) => tag.meta.affix) + this.visitedViews = userStore.getUserInfo + ? this.visitedViews.filter((tag) => tag?.meta?.affix) + : [] + }, + // 删除其它 + delOthersViews(view: RouteLocationNormalizedLoaded) { + this.delOthersVisitedViews(view) + this.addCachedView() + }, + // 删除其它tag + delOthersVisitedViews(view: RouteLocationNormalizedLoaded) { + this.visitedViews = this.visitedViews.filter((v) => { + return v?.meta?.affix || v.path === view.path + }) + }, + // 删除左侧 + delLeftViews(view: RouteLocationNormalizedLoaded) { + const index = findIndex( + this.visitedViews, + (v) => v.path === view.path + ) + if (index > -1) { + this.visitedViews = this.visitedViews.filter((v, i) => { + return v?.meta?.affix || v.path === view.path || i > index + }) + this.addCachedView() + } + }, + // 删除右侧 + delRightViews(view: RouteLocationNormalizedLoaded) { + const index = findIndex( + this.visitedViews, + (v) => v.path === view.path + ) + if (index > -1) { + this.visitedViews = this.visitedViews.filter((v, i) => { + return v?.meta?.affix || v.path === view.path || i < index + }) + this.addCachedView() + } + }, + updateVisitedView(view: RouteLocationNormalizedLoaded) { + for (let v of this.visitedViews) { + if (v.path === view.path) { + v = Object.assign(v, view) + break + } + } + }, + // 设置当前选中的tag + setSelectedTag(tag: RouteLocationNormalizedLoaded) { + this.selectedTag = tag + }, + setTitle(title: string, path?: string) { + for (const v of this.visitedViews) { + if (v.path === (path ?? this.selectedTag?.path)) { + v.meta.title = title + break + } + } + } + }, + persist: false +}) + +export const useTagsViewStoreWithOut = () => { + return useTagsViewStore(store) +} diff --git a/promo-ui2/src/store/modules/user.ts b/promo-ui2/src/store/modules/user.ts new file mode 100644 index 0000000..8fa7d33 --- /dev/null +++ b/promo-ui2/src/store/modules/user.ts @@ -0,0 +1,102 @@ +import { defineStore } from 'pinia' +import { store } from '../index' +import { UserLoginType, UserType } from '@/api/login/types' +import { ElMessageBox } from 'element-plus' +import { useI18n } from '@/hooks/web/useI18n' +import { loginOutApi } from '@/api/login' +import { useTagsViewStore } from './tagsView' +import router from '@/router' + +interface UserState { + userInfo?: UserType + tokenKey: string + token: string + roleRouters?: string[] | AppCustomRouteRecordRaw[] + rememberMe: boolean + loginInfo?: UserLoginType +} + +export const useUserStore = defineStore('user', { + state: (): UserState => { + return { + userInfo: undefined, + tokenKey: 'Authorization', + token: '', + roleRouters: undefined, + // 记住我 + rememberMe: true, + loginInfo: undefined + } + }, + getters: { + getTokenKey(): string { + return this.tokenKey + }, + getToken(): string { + return this.token + }, + getUserInfo(): UserType | undefined { + return this.userInfo + }, + getRoleRouters(): string[] | AppCustomRouteRecordRaw[] | undefined { + return this.roleRouters + }, + getRememberMe(): boolean { + return this.rememberMe + }, + getLoginInfo(): UserLoginType | undefined { + return this.loginInfo + } + }, + actions: { + setTokenKey(tokenKey: string) { + this.tokenKey = tokenKey + }, + setToken(token: string) { + this.token = token + }, + setUserInfo(userInfo?: UserType) { + this.userInfo = userInfo + }, + setRoleRouters(roleRouters: string[] | AppCustomRouteRecordRaw[]) { + this.roleRouters = roleRouters + }, + logoutConfirm() { + const { t } = useI18n() + ElMessageBox.confirm(t('common.loginOutMessage'), t('common.reminder'), { + confirmButtonText: t('common.ok'), + cancelButtonText: t('common.cancel'), + type: 'warning' + }) + .then(async () => { + const res = true // await loginOutApi().catch(() => {}) + if (res) { + this.reset() + } + }) + .catch(() => {}) + }, + reset() { + const tagsViewStore = useTagsViewStore() + tagsViewStore.delAllViews() + this.setToken('') + this.setUserInfo(undefined) + this.setRoleRouters([]) + router.replace('/login') + }, + logout() { + this.reset() + }, + setRememberMe(rememberMe: boolean) { + this.rememberMe = rememberMe + }, + setLoginInfo(loginInfo: UserLoginType | undefined) { + this.loginInfo = loginInfo + } + }, + persist: true +}) + +export const useUserStoreWithOut = () => { + return useUserStore(store) +} diff --git a/promo-ui2/src/styles/index.less b/promo-ui2/src/styles/index.less new file mode 100644 index 0000000..6825d70 --- /dev/null +++ b/promo-ui2/src/styles/index.less @@ -0,0 +1,7 @@ +@import './var.css'; +@import 'element-plus/theme-chalk/dark/css-vars.css'; + +// 解决抽屉弹出时,body宽度变化的问题 +.el-popup-parent--hidden { + width: 100% !important; +} diff --git a/promo-ui2/src/styles/var.css b/promo-ui2/src/styles/var.css new file mode 100644 index 0000000..fb869ec --- /dev/null +++ b/promo-ui2/src/styles/var.css @@ -0,0 +1,69 @@ +:root { + --login-bg-color: #293146; + + /* left menu start */ + --left-menu-max-width: 200px; + + --left-menu-min-width: 64px; + + --left-menu-bg-color: #001529; + + --left-menu-bg-light-color: #0f2438; + + --left-menu-bg-active-color: var(--el-color-primary); + + --left-menu-text-color: #bfcbd9; + + --left-menu-text-active-color: #fff; + + --left-menu-collapse-bg-active-color: var(--el-color-primary); + /* left menu end */ + + /* logo start */ + --logo-height: 50px; + + --logo-title-text-color: #fff; + /* logo end */ + + /* header start */ + --top-header-bg-color: '#fff'; + + --top-header-text-color: 'inherit'; + + --top-header-hover-color: #f6f6f6; + + --top-tool-height: var(--logo-height); + + --top-tool-p-x: 0; + + --tags-view-height: 35px; + /* header start */ + + /* tab menu start */ + --tab-menu-max-width: 80px; + + --tab-menu-min-width: 30px; + + --tab-menu-collapse-height: 36px; + /* tab menu end */ + + --app-content-padding: 20px; + + --app-content-bg-color: #f5f7f9; + + --app-footer-height: 50px; + + --transition-time-02: 0.2s; +} + +.dark { + --app-content-bg-color: var(--el-bg-color); +} + +*, +:after, +:before { + margin: 0; + padding: 0; + box-sizing: border-box; +} diff --git a/promo-ui2/src/styles/variables.module.less b/promo-ui2/src/styles/variables.module.less new file mode 100644 index 0000000..a773269 --- /dev/null +++ b/promo-ui2/src/styles/variables.module.less @@ -0,0 +1,10 @@ +// 命名空间 +@adminNamespace: v; +// el命名空间 +@elNamespace: el; + +// 导出变量 +:export { + namespace: @adminNamespace; + elNamespace: @elNamespace; +} diff --git a/promo-ui2/src/utils/color.ts b/promo-ui2/src/utils/color.ts new file mode 100644 index 0000000..387ffc6 --- /dev/null +++ b/promo-ui2/src/utils/color.ts @@ -0,0 +1,172 @@ +/** + * 判断是否 十六进制颜色值. + * 输入形式可为 #fff000 #f00 + * + * @param String color 十六进制颜色值 + * @return Boolean + */ +export const isHexColor = (color: string) => { + const reg = /^#([0-9a-fA-F]{3}|[0-9a-fA-f]{6})$/ + return reg.test(color) +} + +/** + * RGB 颜色值转换为 十六进制颜色值. + * r, g, 和 b 需要在 [0, 255] 范围内 + * + * @return String 类似#ff00ff + * @param r + * @param g + * @param b + */ +export const rgbToHex = (r: number, g: number, b: number) => { + // tslint:disable-next-line:no-bitwise + const hex = ((r << 16) | (g << 8) | b).toString(16) + return '#' + new Array(Math.abs(hex.length - 7)).join('0') + hex +} + +/** + * Transform a HEX color to its RGB representation + * @param {string} hex The color to transform + * @returns The RGB representation of the passed color + */ +export const hexToRGB = (hex: string, opacity?: number) => { + let sHex = hex.toLowerCase() + if (isHexColor(hex)) { + if (sHex.length === 4) { + let sColorNew = '#' + for (let i = 1; i < 4; i += 1) { + sColorNew += sHex.slice(i, i + 1).concat(sHex.slice(i, i + 1)) + } + sHex = sColorNew + } + const sColorChange: number[] = [] + for (let i = 1; i < 7; i += 2) { + sColorChange.push(parseInt('0x' + sHex.slice(i, i + 2))) + } + return opacity + ? 'RGBA(' + sColorChange.join(',') + ',' + opacity + ')' + : 'RGB(' + sColorChange.join(',') + ')' + } + return sHex +} + +export const colorIsDark = (color: string) => { + if (!isHexColor(color)) return + const [r, g, b] = hexToRGB(color) + .replace(/(?:\(|\)|rgb|RGB)*/g, '') + .split(',') + .map((item) => Number(item)) + return r * 0.299 + g * 0.578 + b * 0.114 < 192 +} + +/** + * Darkens a HEX color given the passed percentage + * @param {string} color The color to process + * @param {number} amount The amount to change the color by + * @returns {string} The HEX representation of the processed color + */ +export const darken = (color: string, amount: number) => { + color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color + amount = Math.trunc((255 * amount) / 100) + return `#${subtractLight(color.substring(0, 2), amount)}${subtractLight( + color.substring(2, 4), + amount + )}${subtractLight(color.substring(4, 6), amount)}` +} + +/** + * Lightens a 6 char HEX color according to the passed percentage + * @param {string} color The color to change + * @param {number} amount The amount to change the color by + * @returns {string} The processed color represented as HEX + */ +export const lighten = (color: string, amount: number) => { + color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color + amount = Math.trunc((255 * amount) / 100) + return `#${addLight(color.substring(0, 2), amount)}${addLight( + color.substring(2, 4), + amount + )}${addLight(color.substring(4, 6), amount)}` +} + +/* Suma el porcentaje indicado a un color (RR, GG o BB) hexadecimal para aclararlo */ +/** + * Sums the passed percentage to the R, G or B of a HEX color + * @param {string} color The color to change + * @param {number} amount The amount to change the color by + * @returns {string} The processed part of the color + */ +const addLight = (color: string, amount: number) => { + const cc = parseInt(color, 16) + amount + const c = cc > 255 ? 255 : cc + return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}` +} + +/** + * Calculates luminance of an rgb color + * @param {number} r red + * @param {number} g green + * @param {number} b blue + */ +const luminanace = (r: number, g: number, b: number) => { + const a = [r, g, b].map((v) => { + v /= 255 + return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4) + }) + return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722 +} + +/** + * Calculates contrast between two rgb colors + * @param {string} rgb1 rgb color 1 + * @param {string} rgb2 rgb color 2 + */ +const contrast = (rgb1: string[], rgb2: number[]) => { + return ( + (luminanace(~~rgb1[0], ~~rgb1[1], ~~rgb1[2]) + 0.05) / + (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05) + ) +} + +/** + * Determines what the best text color is (black or white) based con the contrast with the background + * @param hexColor - Last selected color by the user + */ +export const calculateBestTextColor = (hexColor: string) => { + const rgbColor = hexToRGB(hexColor.substring(1)) + const contrastWithBlack = contrast(rgbColor.split(','), [0, 0, 0]) + + return contrastWithBlack >= 12 ? '#000000' : '#FFFFFF' +} + +/** + * Subtracts the indicated percentage to the R, G or B of a HEX color + * @param {string} color The color to change + * @param {number} amount The amount to change the color by + * @returns {string} The processed part of the color + */ +const subtractLight = (color: string, amount: number) => { + const cc = parseInt(color, 16) - amount + const c = cc < 0 ? 0 : cc + return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}` +} + +/** + * Mixes two colors. + * + * @param {string} color1 - The first color, should be a 6-digit hexadecimal color code starting with `#`. + * @param {string} color2 - The second color, should be a 6-digit hexadecimal color code starting with `#`. + * @param {number} [weight=0.5] - The weight of color1 in the mix, should be a number between 0 and 1, where 0 represents 100% of color2, and 1 represents 100% of color1. + * @returns {string} The mixed color, a 6-digit hexadecimal color code starting with `#`. + */ +export const mix = (color1: string, color2: string, weight: number = 0.5): string => { + let color = '#' + for (let i = 0; i <= 2; i++) { + const c1 = parseInt(color1.substring(1 + i * 2, 3 + i * 2), 16) + const c2 = parseInt(color2.substring(1 + i * 2, 3 + i * 2), 16) + const c = Math.round(c1 * weight + c2 * (1 - weight)) + color += c.toString(16).padStart(2, '0') + } + return color +} diff --git a/promo-ui2/src/utils/dateUtil.ts b/promo-ui2/src/utils/dateUtil.ts new file mode 100644 index 0000000..36b87e3 --- /dev/null +++ b/promo-ui2/src/utils/dateUtil.ts @@ -0,0 +1,17 @@ +/** + * Independent time operation tool to facilitate subsequent switch to dayjs + */ +import dayjs from 'dayjs' + +const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss' +const DATE_FORMAT = 'YYYY-MM-DD' + +export function formatToDateTime(date?: dayjs.ConfigType, format = DATE_TIME_FORMAT): string { + return dayjs(date).format(format) +} + +export function formatToDate(date?: dayjs.ConfigType, format = DATE_FORMAT): string { + return dayjs(date).format(format) +} + +export const dateUtil = dayjs diff --git a/promo-ui2/src/utils/domUtils.ts b/promo-ui2/src/utils/domUtils.ts new file mode 100644 index 0000000..dbc1989 --- /dev/null +++ b/promo-ui2/src/utils/domUtils.ts @@ -0,0 +1,289 @@ +import { isServer } from './is' +const ieVersion = isServer ? 0 : Number((document as any).documentMode) +const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g +const MOZ_HACK_REGEXP = /^moz([A-Z])/ + +export interface ViewportOffsetResult { + left: number + top: number + right: number + bottom: number + rightIncludeBody: number + bottomIncludeBody: number +} + +/* istanbul ignore next */ +const trim = function (string: string) { + return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '') +} + +/* istanbul ignore next */ +const camelCase = function (name: string) { + return name + .replace(SPECIAL_CHARS_REGEXP, function (_, __, letter, offset) { + return offset ? letter.toUpperCase() : letter + }) + .replace(MOZ_HACK_REGEXP, 'Moz$1') +} + +/* istanbul ignore next */ +export function hasClass(el: Element, cls: string) { + if (!el || !cls) return false + if (cls.indexOf(' ') !== -1) { + throw new Error('className should not contain space.') + } + if (el.classList) { + return el.classList.contains(cls) + } else { + return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1 + } +} + +/* istanbul ignore next */ +export function addClass(el: Element, cls: string) { + if (!el) return + let curClass = el.className + const classes = (cls || '').split(' ') + + for (let i = 0, j = classes.length; i < j; i++) { + const clsName = classes[i] + if (!clsName) continue + + if (el.classList) { + el.classList.add(clsName) + } else if (!hasClass(el, clsName)) { + curClass += ' ' + clsName + } + } + if (!el.classList) { + el.className = curClass + } +} + +/* istanbul ignore next */ +export function removeClass(el: Element, cls: string) { + if (!el || !cls) return + const classes = cls.split(' ') + let curClass = ' ' + el.className + ' ' + + for (let i = 0, j = classes.length; i < j; i++) { + const clsName = classes[i] + if (!clsName) continue + + if (el.classList) { + el.classList.remove(clsName) + } else if (hasClass(el, clsName)) { + curClass = curClass.replace(' ' + clsName + ' ', ' ') + } + } + if (!el.classList) { + el.className = trim(curClass) + } +} + +export function getBoundingClientRect(element: Element): DOMRect | number { + if (!element || !element.getBoundingClientRect) { + return 0 + } + return element.getBoundingClientRect() +} + +/** + * 获取当前元素的left、top偏移 + * left:元素最左侧距离文档左侧的距离 + * top:元素最顶端距离文档顶端的距离 + * right:元素最右侧距离文档右侧的距离 + * bottom:元素最底端距离文档底端的距离 + * rightIncludeBody:元素最左侧距离文档右侧的距离 + * bottomIncludeBody:元素最底端距离文档最底部的距离 + * + * @description: + */ +export function getViewportOffset(element: Element): ViewportOffsetResult { + const doc = document.documentElement + + const docScrollLeft = doc.scrollLeft + const docScrollTop = doc.scrollTop + const docClientLeft = doc.clientLeft + const docClientTop = doc.clientTop + + const pageXOffset = window.pageXOffset + const pageYOffset = window.pageYOffset + + const box = getBoundingClientRect(element) + + const { left: retLeft, top: rectTop, width: rectWidth, height: rectHeight } = box as DOMRect + + const scrollLeft = (pageXOffset || docScrollLeft) - (docClientLeft || 0) + const scrollTop = (pageYOffset || docScrollTop) - (docClientTop || 0) + const offsetLeft = retLeft + pageXOffset + const offsetTop = rectTop + pageYOffset + + const left = offsetLeft - scrollLeft + const top = offsetTop - scrollTop + + const clientWidth = window.document.documentElement.clientWidth + const clientHeight = window.document.documentElement.clientHeight + return { + left: left, + top: top, + right: clientWidth - rectWidth - left, + bottom: clientHeight - rectHeight - top, + rightIncludeBody: clientWidth - left, + bottomIncludeBody: clientHeight - top + } +} + +/* istanbul ignore next */ +export const on = function ( + element: HTMLElement | Document | Window, + event: string, + handler: EventListenerOrEventListenerObject +): void { + if (element && event && handler) { + element.addEventListener(event, handler, false) + } +} + +/* istanbul ignore next */ +export const off = function ( + element: HTMLElement | Document | Window, + event: string, + handler: any +): void { + if (element && event && handler) { + element.removeEventListener(event, handler, false) + } +} + +/* istanbul ignore next */ +export const once = function (el: HTMLElement, event: string, fn: EventListener): void { + const listener = function (this: any, ...args: unknown[]) { + if (fn) { + // @ts-ignore + fn.apply(this, args) + } + off(el, event, listener) + } + on(el, event, listener) +} + +/* istanbul ignore next */ +export const getStyle = + ieVersion < 9 + ? function (element: Element | any, styleName: string) { + if (isServer) return + if (!element || !styleName) return null + styleName = camelCase(styleName) + if (styleName === 'float') { + styleName = 'styleFloat' + } + try { + switch (styleName) { + case 'opacity': + try { + return element.filters.item('alpha').opacity / 100 + } catch (e) { + return 1.0 + } + default: + return element.style[styleName] || element.currentStyle + ? element.currentStyle[styleName] + : null + } + } catch (e) { + return element.style[styleName] + } + } + : function (element: Element | any, styleName: string) { + if (isServer) return + if (!element || !styleName) return null + styleName = camelCase(styleName) + if (styleName === 'float') { + styleName = 'cssFloat' + } + try { + const computed = (document as any).defaultView.getComputedStyle(element, '') + return element.style[styleName] || computed ? computed[styleName] : null + } catch (e) { + return element.style[styleName] + } + } + +/* istanbul ignore next */ +export function setStyle(element: Element | any, styleName: any, value: any) { + if (!element || !styleName) return + + if (typeof styleName === 'object') { + for (const prop in styleName) { + if (Object.prototype.hasOwnProperty.call(styleName, prop)) { + setStyle(element, prop, styleName[prop]) + } + } + } else { + styleName = camelCase(styleName) + if (styleName === 'opacity' && ieVersion < 9) { + element.style.filter = isNaN(value) ? '' : 'alpha(opacity=' + value * 100 + ')' + } else { + element.style[styleName] = value + } + } +} + +/* istanbul ignore next */ +export const isScroll = (el: Element, vertical: any) => { + if (isServer) return + + const determinedDirection = vertical !== null || vertical !== undefined + const overflow = determinedDirection + ? vertical + ? getStyle(el, 'overflow-y') + : getStyle(el, 'overflow-x') + : getStyle(el, 'overflow') + + return overflow.match(/(scroll|auto)/) +} + +/* istanbul ignore next */ +export const getScrollContainer = (el: Element, vertical?: any) => { + if (isServer) return + + let parent: any = el + while (parent) { + if ([window, document, document.documentElement].includes(parent)) { + return window + } + if (isScroll(parent, vertical)) { + return parent + } + parent = parent.parentNode + } + + return parent +} + +/* istanbul ignore next */ +export const isInContainer = (el: Element, container: any) => { + if (isServer || !el || !container) return false + + const elRect = el.getBoundingClientRect() + let containerRect + + if ([window, document, document.documentElement, null, undefined].includes(container)) { + containerRect = { + top: 0, + right: window.innerWidth, + bottom: window.innerHeight, + left: 0 + } + } else { + containerRect = container.getBoundingClientRect() + } + + return ( + elRect.top < containerRect.bottom && + elRect.bottom > containerRect.top && + elRect.right > containerRect.left && + elRect.left < containerRect.right + ) +} diff --git a/promo-ui2/src/utils/index.ts b/promo-ui2/src/utils/index.ts new file mode 100644 index 0000000..d91b679 --- /dev/null +++ b/promo-ui2/src/utils/index.ts @@ -0,0 +1,136 @@ +/** + * + * @param component 需要注册的组件 + * @param alias 组件别名 + * @returns any + */ +export const withInstall = (component: T, alias?: string) => { + const comp = component as any + comp.install = (app: any) => { + app.component(comp.name || comp.displayName, component) + if (alias) { + app.config.globalProperties[alias] = component + } + } + return component as T & Plugin +} + +/** + * @param str 需要转下划线的驼峰字符串 + * @returns 字符串下划线 + */ +export const humpToUnderline = (str: string): string => { + return str.replace(/([A-Z])/g, '-$1').toLowerCase() +} + +/** + * @param str 需要转驼峰的下划线字符串 + * @returns 字符串驼峰 + */ +export const underlineToHump = (str: string): string => { + if (!str) return '' + return str.replace(/\-(\w)/g, (_, letter: string) => { + return letter.toUpperCase() + }) +} + +/** + * 驼峰转横杠 + */ +export const humpToDash = (str: string): string => { + return str.replace(/([A-Z])/g, '-$1').toLowerCase() +} + +export const setCssVar = (prop: string, val: any, dom = document.documentElement) => { + dom.style.setProperty(prop, val) +} + +export const getCssVar = (prop: string, dom = document.documentElement) => { + return getComputedStyle(dom).getPropertyValue(prop) +} + +/** + * 查找数组对象的某个下标 + * @param {Array} ary 查找的数组 + * @param {Functon} fn 判断的方法 + */ +export const findIndex = (ary: Array, fn: Fn): number => { + if (ary.findIndex) { + return ary.findIndex(fn) + } + let index = -1 + ary.some((item: T, i: number, ary: Array) => { + const ret: T = fn(item, i, ary) + if (ret) { + index = i + return ret + } + }) + return index +} + +export const trim = (str: string) => { + return str.replace(/(^\s*)|(\s*$)/g, '') +} + +/** + * @param {Date | number | string} time 需要转换的时间 + * @param {String} fmt 需要转换的格式 如 yyyy-MM-dd、yyyy-MM-dd HH:mm:ss + */ +export function formatTime(time: Date | number | string, fmt: string) { + if (!time) return '' + else { + const date = new Date(time) + const o = { + 'M+': date.getMonth() + 1, + 'd+': date.getDate(), + 'H+': date.getHours(), + 'm+': date.getMinutes(), + 's+': date.getSeconds(), + 'q+': Math.floor((date.getMonth() + 3) / 3), + S: date.getMilliseconds() + } + if (/(y+)/.test(fmt)) { + fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)) + } + for (const k in o) { + if (new RegExp('(' + k + ')').test(fmt)) { + fmt = fmt.replace( + RegExp.$1, + RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length) + ) + } + } + return fmt + } +} + +/** + * 生成随机字符串 + */ +export function toAnyString() { + const str: string = 'xxxxx-xxxxx-4xxxx-yxxxx-xxxxx'.replace(/[xy]/g, (c: string) => { + const r: number = (Math.random() * 16) | 0 + const v: number = c === 'x' ? r : (r & 0x3) | 0x8 + return v.toString() + }) + return str +} + +/** + * 首字母大写 + */ +export function firstUpperCase(str: string) { + return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase()) +} + +/** + * 把对象转为formData + */ +export function objToFormData(obj: Recordable) { + const formData = new FormData() + Object.keys(obj).forEach((key) => { + formData.append(key, obj[key]) + }) + return formData +} diff --git a/promo-ui2/src/utils/is.ts b/promo-ui2/src/utils/is.ts new file mode 100644 index 0000000..1651420 --- /dev/null +++ b/promo-ui2/src/utils/is.ts @@ -0,0 +1,117 @@ +// copy to vben-admin + +const toString = Object.prototype.toString + +export const is = (val: unknown, type: string) => { + return toString.call(val) === `[object ${type}]` +} + +export const isDef = (val?: T): val is T => { + return typeof val !== 'undefined' +} + +export const isUnDef = (val?: T): val is T => { + return !isDef(val) +} + +export const isObject = (val: any): val is Record => { + return val !== null && is(val, 'Object') +} + +export const isEmpty = (val: T): val is T => { + if (isArray(val) || isString(val)) { + return val.length === 0 + } + + if (val instanceof Map || val instanceof Set) { + return val.size === 0 + } + + if (isObject(val)) { + return Object.keys(val).length === 0 + } + + return false +} + +export const isDate = (val: unknown): val is Date => { + return is(val, 'Date') +} + +export const isNull = (val: unknown): val is null => { + return val === null +} + +export const isNullAndUnDef = (val: unknown): val is null | undefined => { + return isUnDef(val) && isNull(val) +} + +export const isNullOrUnDef = (val: unknown): val is null | undefined => { + return isUnDef(val) || isNull(val) +} + +export const isNumber = (val: unknown): val is number => { + return is(val, 'Number') +} + +export const isPromise = (val: unknown): val is Promise => { + return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch) +} + +export const isString = (val: unknown): val is string => { + return is(val, 'String') +} + +export const isFunction = (val: unknown): val is Function => { + return typeof val === 'function' +} + +export const isBoolean = (val: unknown): val is boolean => { + return is(val, 'Boolean') +} + +export const isRegExp = (val: unknown): val is RegExp => { + return is(val, 'RegExp') +} + +export const isArray = (val: any): val is Array => { + return val && Array.isArray(val) +} + +export const isWindow = (val: any): val is Window => { + return typeof window !== 'undefined' && is(val, 'Window') +} + +export const isElement = (val: unknown): val is Element => { + return isObject(val) && !!val.tagName +} + +export const isMap = (val: unknown): val is Map => { + return is(val, 'Map') +} + +export const isServer = typeof window === 'undefined' + +export const isClient = !isServer + +export const isUrl = (path: string): boolean => { + try { + new URL(path) + return true + } catch (_error) { + return false + } +} + +export const isDark = (): boolean => { + return window.matchMedia('(prefers-color-scheme: dark)').matches +} + +// 是否是图片链接 +export const isImgPath = (path: string): boolean => { + return /(https?:\/\/|data:image\/).*?\.(png|jpg|jpeg|gif|svg|webp|ico)/gi.test(path) +} + +export const isEmptyVal = (val: any): boolean => { + return val === '' || val === null || val === undefined +} diff --git a/promo-ui2/src/utils/propTypes.ts b/promo-ui2/src/utils/propTypes.ts new file mode 100644 index 0000000..863f55c --- /dev/null +++ b/promo-ui2/src/utils/propTypes.ts @@ -0,0 +1,24 @@ +import { VueTypeValidableDef, VueTypesInterface, createTypes, toValidableType } from 'vue-types' +import { CSSProperties } from 'vue' + +type PropTypes = VueTypesInterface & { + readonly style: VueTypeValidableDef +} +const newPropTypes = createTypes({ + func: undefined, + bool: undefined, + string: undefined, + number: undefined, + object: undefined, + integer: undefined +}) as PropTypes + +class propTypes extends newPropTypes { + static get style() { + return toValidableType('style', { + type: [String, Object] + }) + } +} + +export { propTypes } diff --git a/promo-ui2/src/utils/routerHelper.ts b/promo-ui2/src/utils/routerHelper.ts new file mode 100644 index 0000000..b4e7548 --- /dev/null +++ b/promo-ui2/src/utils/routerHelper.ts @@ -0,0 +1,197 @@ +import { createRouter, createWebHashHistory } from 'vue-router' +import type { + Router, + RouteLocationNormalized, + RouteRecordNormalized, + RouteRecordRaw +} from 'vue-router' +import { isUrl } from '@/utils/is' +import { omit, cloneDeep } from 'lodash-es' + +const modules = import.meta.glob('../views/**/*.{vue,tsx}') + +/* Layout */ +export const Layout = () => import('@/layout/Layout.vue') + +export const getParentLayout = () => { + return () => + new Promise((resolve) => { + resolve({ + name: 'ParentLayout' + }) + }) +} + +export const getRawRoute = (route: RouteLocationNormalized): RouteLocationNormalized => { + if (!route) return route + const { matched, ...opt } = route + return { + ...opt, + matched: (matched + ? matched.map((item) => ({ + meta: item.meta, + name: item.name, + path: item.path + })) + : undefined) as RouteRecordNormalized[] + } +} + +// 前端控制路由生成 +export const generateRoutesByFrontEnd = ( + routes: AppRouteRecordRaw[], + keys: string[], + basePath = '/' +): AppRouteRecordRaw[] => { + const res: AppRouteRecordRaw[] = [] + + for (const route of routes) { + const meta = route.meta ?? {} + // skip some route + if (meta.hidden && !meta.canTo) { + continue + } + + let data: Nullable = null + + let onlyOneChild: Nullable = null + if (route.children && route.children.length === 1 && !meta.alwaysShow) { + onlyOneChild = ( + isUrl(route.children[0].path) + ? route.children[0].path + : pathResolve(pathResolve(basePath, route.path), route.children[0].path) + ) as string + } + + // 开发者可以根据实际情况进行扩展 + for (const item of keys) { + // 通过路径去匹配 + if (isUrl(item) && (onlyOneChild === item || route.path === item)) { + data = Object.assign({}, route) + } else { + const routePath = (onlyOneChild ?? pathResolve(basePath, route.path)).trim() + if (routePath === item || meta.followRoute === item) { + data = Object.assign({}, route) + } + } + } + + // recursive child routes + if (route.children && data) { + data.children = generateRoutesByFrontEnd( + route.children, + keys, + pathResolve(basePath, data.path) + ) + } + if (data) { + res.push(data as AppRouteRecordRaw) + } + } + return res +} + +// 后端控制路由生成 +export const generateRoutesByServer = (routes: AppCustomRouteRecordRaw[]): AppRouteRecordRaw[] => { + const res: AppRouteRecordRaw[] = [] + + for (const route of routes) { + const data: AppRouteRecordRaw = { + path: route.path, + name: route.name, + redirect: route.redirect, + meta: route.meta + } + if (route.component) { + const comModule = modules[`../${route.component}.vue`] || modules[`../${route.component}.tsx`] + const component = route.component as string + if (!comModule && !component.includes('#')) { + console.error(`未找到${route.component}.vue文件或${route.component}.tsx文件,请创建`) + } else { + // 动态加载路由文件,可根据实际情况进行自定义逻辑 + data.component = + component === '#' ? Layout : component.includes('##') ? getParentLayout() : comModule + } + } + // recursive child routes + if (route.children) { + data.children = generateRoutesByServer(route.children) + } + res.push(data as AppRouteRecordRaw) + } + return res +} + +export const pathResolve = (parentPath: string, path: string) => { + if (isUrl(path)) return path + const childPath = path.startsWith('/') || !path ? path : `/${path}` + return `${parentPath}${childPath}`.replace(/\/\//g, '/').trim() +} + +// 路由降级 +export const flatMultiLevelRoutes = (routes: AppRouteRecordRaw[]) => { + const modules: AppRouteRecordRaw[] = cloneDeep(routes) + for (let index = 0; index < modules.length; index++) { + const route = modules[index] + if (!isMultipleRoute(route)) { + continue + } + promoteRouteLevel(route) + } + return modules +} + +// 层级是否大于2 +const isMultipleRoute = (route: AppRouteRecordRaw) => { + if (!route || !Reflect.has(route, 'children') || !route.children?.length) { + return false + } + + const children = route.children + + let flag = false + for (let index = 0; index < children.length; index++) { + const child = children[index] + if (child.children?.length) { + flag = true + break + } + } + return flag +} + +// 生成二级路由 +const promoteRouteLevel = (route: AppRouteRecordRaw) => { + let router: Router | null = createRouter({ + routes: [route as RouteRecordRaw], + history: createWebHashHistory() + }) + + const routes = router.getRoutes() + addToChildren(routes, route.children || [], route) + router = null + + route.children = route.children?.map((item) => omit(item, 'children')) +} + +// 添加所有子菜单 +const addToChildren = ( + routes: RouteRecordNormalized[], + children: AppRouteRecordRaw[], + routeModule: AppRouteRecordRaw +) => { + for (let index = 0; index < children.length; index++) { + const child = children[index] + const route = routes.find((item) => item.name === child.name) + if (!route) { + continue + } + routeModule.children = routeModule.children || [] + if (!routeModule.children.find((item) => item.name === route.name)) { + routeModule.children?.push(route as unknown as AppRouteRecordRaw) + } + if (child.children?.length) { + addToChildren(routes, child.children, routeModule) + } + } +} diff --git a/promo-ui2/src/utils/tree.ts b/promo-ui2/src/utils/tree.ts new file mode 100644 index 0000000..fe0207d --- /dev/null +++ b/promo-ui2/src/utils/tree.ts @@ -0,0 +1,207 @@ +interface TreeHelperConfig { + id: string + children: string + pid: string +} +const DEFAULT_CONFIG: TreeHelperConfig = { + id: 'id', + children: 'children', + pid: 'pid' +} + +const getConfig = (config: Partial) => Object.assign({}, DEFAULT_CONFIG, config) + +// tree from list +export const listToTree = (list: any[], config: Partial = {}): T[] => { + const conf = getConfig(config) as TreeHelperConfig + const nodeMap = new Map() + const result: T[] = [] + const { id, children, pid } = conf + + for (const node of list) { + node[children] = node[children] || [] + nodeMap.set(node[id], node) + } + for (const node of list) { + const parent = nodeMap.get(node[pid]) + ;(parent ? parent.children : result).push(node) + } + return result +} + +export const treeToList = (tree: any, config: Partial = {}): T => { + config = getConfig(config) + const { children } = config + const result: any = [...tree] + for (let i = 0; i < result.length; i++) { + if (!result[i][children!]) continue + result.splice(i + 1, 0, ...result[i][children!]) + } + return result +} + +export const findNode = ( + tree: any, + func: Fn, + config: Partial = {} +): T | null => { + config = getConfig(config) + const { children } = config + const list = [...tree] + for (const node of list) { + if (func(node)) return node + node[children!] && list.push(...node[children!]) + } + return null +} + +export const findNodeAll = ( + tree: any, + func: Fn, + config: Partial = {} +): T[] => { + config = getConfig(config) + const { children } = config + const list = [...tree] + const result: T[] = [] + for (const node of list) { + func(node) && result.push(node) + node[children!] && list.push(...node[children!]) + } + return result +} + +export const findPath = ( + tree: any, + func: Fn, + config: Partial = {} +): T | T[] | null => { + config = getConfig(config) + const path: T[] = [] + const list = [...tree] + const visitedSet = new Set() + const { children } = config + while (list.length) { + const node = list[0] + if (visitedSet.has(node)) { + path.pop() + list.shift() + } else { + visitedSet.add(node) + node[children!] && list.unshift(...node[children!]) + path.push(node) + if (func(node)) { + return path + } + } + } + return null +} + +export const findPathAll = (tree: any, func: Fn, config: Partial = {}) => { + config = getConfig(config) + const path: any[] = [] + const list = [...tree] + const result: any[] = [] + const visitedSet = new Set(), + { children } = config + while (list.length) { + const node = list[0] + if (visitedSet.has(node)) { + path.pop() + list.shift() + } else { + visitedSet.add(node) + node[children!] && list.unshift(...node[children!]) + path.push(node) + func(node) && result.push([...path]) + } + } + return result +} + +export const filter = ( + tree: T[], + func: (n: T) => boolean, + config: Partial = {} +): T[] => { + config = getConfig(config) + const children = config.children as string + function listFilter(list: T[]) { + return list + .map((node: any) => ({ ...node })) + .filter((node) => { + node[children] = node[children] && listFilter(node[children]) + return func(node) || (node[children] && node[children].length) + }) + } + return listFilter(tree) +} + +export const forEach = ( + tree: T[], + func: (n: T) => any, + config: Partial = {} +): void => { + config = getConfig(config) + const list: any[] = [...tree] + const { children } = config + for (let i = 0; i < list.length; i++) { + // func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿 + if (func(list[i])) { + return + } + children && list[i][children] && list.splice(i + 1, 0, ...list[i][children]) + } +} + +/** + * @description: Extract tree specified structure + */ +export const treeMap = ( + treeData: T[], + opt: { children?: string; conversion: Fn } +): T[] => { + return treeData.map((item) => treeMapEach(item, opt)) +} + +/** + * @description: Extract tree specified structure + */ +export const treeMapEach = ( + data: any, + { children = 'children', conversion }: { children?: string; conversion: Fn } +) => { + const haveChildren = Array.isArray(data[children]) && data[children].length > 0 + const conversionData = conversion(data) || {} + if (haveChildren) { + return { + ...conversionData, + [children]: data[children].map((i: number) => + treeMapEach(i, { + children, + conversion + }) + ) + } + } else { + return { + ...conversionData + } + } +} + +/** + * 递归遍历树结构 + * @param treeDatas 树 + * @param callBack 回调 + * @param parentNode 父节点 + */ +export const eachTree = (treeDatas: any[], callBack: Fn, parentNode = {}) => { + treeDatas.forEach((element) => { + const newNode = callBack(element, parentNode) || element + if (element.children) { + eachTree(element.children, callBack, newNode) + } + }) +} diff --git a/promo-ui2/src/utils/tsxHelper.ts b/promo-ui2/src/utils/tsxHelper.ts new file mode 100644 index 0000000..6087fa3 --- /dev/null +++ b/promo-ui2/src/utils/tsxHelper.ts @@ -0,0 +1,16 @@ +import { Slots } from 'vue' +import { isFunction } from '@/utils/is' + +export const getSlot = (slots: Slots, slot = 'default', data?: Recordable) => { + // Reflect.has 判断一个对象是否存在某个属性 + if (!slots || !Reflect.has(slots, slot)) { + return null + } + if (!isFunction(slots[slot])) { + console.error(`${slot} is not a function!`) + return null + } + const slotFn = slots[slot] + if (!slotFn) return null + return slotFn(data) +} diff --git a/promo-ui2/src/views/Authorization/Department/Department.vue b/promo-ui2/src/views/Authorization/Department/Department.vue new file mode 100644 index 0000000..c889705 --- /dev/null +++ b/promo-ui2/src/views/Authorization/Department/Department.vue @@ -0,0 +1,341 @@ + + + diff --git a/promo-ui2/src/views/Authorization/Department/components/Detail.vue b/promo-ui2/src/views/Authorization/Department/components/Detail.vue new file mode 100644 index 0000000..8262b95 --- /dev/null +++ b/promo-ui2/src/views/Authorization/Department/components/Detail.vue @@ -0,0 +1,20 @@ + + + diff --git a/promo-ui2/src/views/Authorization/Department/components/Write.vue b/promo-ui2/src/views/Authorization/Department/components/Write.vue new file mode 100644 index 0000000..2c3c527 --- /dev/null +++ b/promo-ui2/src/views/Authorization/Department/components/Write.vue @@ -0,0 +1,59 @@ + + + diff --git a/promo-ui2/src/views/Authorization/Menu/Menu.vue b/promo-ui2/src/views/Authorization/Menu/Menu.vue new file mode 100644 index 0000000..1ea4fb3 --- /dev/null +++ b/promo-ui2/src/views/Authorization/Menu/Menu.vue @@ -0,0 +1,212 @@ + + + diff --git a/promo-ui2/src/views/Authorization/Menu/components/AddButtonPermission.vue b/promo-ui2/src/views/Authorization/Menu/components/AddButtonPermission.vue new file mode 100644 index 0000000..32cb3c3 --- /dev/null +++ b/promo-ui2/src/views/Authorization/Menu/components/AddButtonPermission.vue @@ -0,0 +1,68 @@ + + + diff --git a/promo-ui2/src/views/Authorization/Menu/components/Detail.vue b/promo-ui2/src/views/Authorization/Menu/components/Detail.vue new file mode 100644 index 0000000..54b2cad --- /dev/null +++ b/promo-ui2/src/views/Authorization/Menu/components/Detail.vue @@ -0,0 +1,173 @@ + + + diff --git a/promo-ui2/src/views/Authorization/Menu/components/Write.vue b/promo-ui2/src/views/Authorization/Menu/components/Write.vue new file mode 100644 index 0000000..2da8b67 --- /dev/null +++ b/promo-ui2/src/views/Authorization/Menu/components/Write.vue @@ -0,0 +1,409 @@ + + + diff --git a/promo-ui2/src/views/Authorization/Role/Role.vue b/promo-ui2/src/views/Authorization/Role/Role.vue new file mode 100644 index 0000000..4b67c3f --- /dev/null +++ b/promo-ui2/src/views/Authorization/Role/Role.vue @@ -0,0 +1,173 @@ + + + diff --git a/promo-ui2/src/views/Authorization/Role/components/Detail.vue b/promo-ui2/src/views/Authorization/Role/components/Detail.vue new file mode 100644 index 0000000..97c1b57 --- /dev/null +++ b/promo-ui2/src/views/Authorization/Role/components/Detail.vue @@ -0,0 +1,106 @@ + + + diff --git a/promo-ui2/src/views/Authorization/Role/components/Write.vue b/promo-ui2/src/views/Authorization/Role/components/Write.vue new file mode 100644 index 0000000..0169427 --- /dev/null +++ b/promo-ui2/src/views/Authorization/Role/components/Write.vue @@ -0,0 +1,181 @@ + + + diff --git a/promo-ui2/src/views/Authorization/User/User.vue b/promo-ui2/src/views/Authorization/User/User.vue new file mode 100644 index 0000000..12ad2e2 --- /dev/null +++ b/promo-ui2/src/views/Authorization/User/User.vue @@ -0,0 +1,384 @@ + + + diff --git a/promo-ui2/src/views/Authorization/User/components/Detail.vue b/promo-ui2/src/views/Authorization/User/components/Detail.vue new file mode 100644 index 0000000..81dd5ee --- /dev/null +++ b/promo-ui2/src/views/Authorization/User/components/Detail.vue @@ -0,0 +1,20 @@ + + + diff --git a/promo-ui2/src/views/Authorization/User/components/Write.vue b/promo-ui2/src/views/Authorization/User/components/Write.vue new file mode 100644 index 0000000..ec60c8a --- /dev/null +++ b/promo-ui2/src/views/Authorization/User/components/Write.vue @@ -0,0 +1,60 @@ + + + diff --git a/promo-ui2/src/views/Components/Avatars.vue b/promo-ui2/src/views/Components/Avatars.vue new file mode 100644 index 0000000..430c758 --- /dev/null +++ b/promo-ui2/src/views/Components/Avatars.vue @@ -0,0 +1,53 @@ + + + diff --git a/promo-ui2/src/views/Components/CountTo.vue b/promo-ui2/src/views/Components/CountTo.vue new file mode 100644 index 0000000..a547e8e --- /dev/null +++ b/promo-ui2/src/views/Components/CountTo.vue @@ -0,0 +1,100 @@ + + + diff --git a/promo-ui2/src/views/Components/Descriptions.vue b/promo-ui2/src/views/Components/Descriptions.vue new file mode 100644 index 0000000..4b96bf6 --- /dev/null +++ b/promo-ui2/src/views/Components/Descriptions.vue @@ -0,0 +1,192 @@ + + + + + diff --git a/promo-ui2/src/views/Components/Dialog.vue b/promo-ui2/src/views/Components/Dialog.vue new file mode 100644 index 0000000..33726e4 --- /dev/null +++ b/promo-ui2/src/views/Components/Dialog.vue @@ -0,0 +1,165 @@ + + + diff --git a/promo-ui2/src/views/Components/Echart.vue b/promo-ui2/src/views/Components/Echart.vue new file mode 100644 index 0000000..683d6b4 --- /dev/null +++ b/promo-ui2/src/views/Components/Echart.vue @@ -0,0 +1,36 @@ + + + diff --git a/promo-ui2/src/views/Components/Editor/CodeEditor.vue b/promo-ui2/src/views/Components/Editor/CodeEditor.vue new file mode 100644 index 0000000..db80bb0 --- /dev/null +++ b/promo-ui2/src/views/Components/Editor/CodeEditor.vue @@ -0,0 +1,23 @@ + + diff --git a/promo-ui2/src/views/Components/Editor/Editor.vue b/promo-ui2/src/views/Components/Editor/Editor.vue new file mode 100644 index 0000000..9dde431 --- /dev/null +++ b/promo-ui2/src/views/Components/Editor/Editor.vue @@ -0,0 +1,32 @@ + + + diff --git a/promo-ui2/src/views/Components/Editor/JsonEditor.vue b/promo-ui2/src/views/Components/Editor/JsonEditor.vue new file mode 100644 index 0000000..c508fbd --- /dev/null +++ b/promo-ui2/src/views/Components/Editor/JsonEditor.vue @@ -0,0 +1,36 @@ + + + diff --git a/promo-ui2/src/views/Components/Form/DefaultForm.vue b/promo-ui2/src/views/Components/Form/DefaultForm.vue new file mode 100644 index 0000000..4165c16 --- /dev/null +++ b/promo-ui2/src/views/Components/Form/DefaultForm.vue @@ -0,0 +1,1863 @@ + + + + + diff --git a/promo-ui2/src/views/Components/Form/UseFormDemo.vue b/promo-ui2/src/views/Components/Form/UseFormDemo.vue new file mode 100644 index 0000000..de9cc64 --- /dev/null +++ b/promo-ui2/src/views/Components/Form/UseFormDemo.vue @@ -0,0 +1,470 @@ + + + + + diff --git a/promo-ui2/src/views/Components/Highlight.vue b/promo-ui2/src/views/Components/Highlight.vue new file mode 100644 index 0000000..96baedc --- /dev/null +++ b/promo-ui2/src/views/Components/Highlight.vue @@ -0,0 +1,20 @@ + + + diff --git a/promo-ui2/src/views/Components/IAgree.vue b/promo-ui2/src/views/Components/IAgree.vue new file mode 100644 index 0000000..947226b --- /dev/null +++ b/promo-ui2/src/views/Components/IAgree.vue @@ -0,0 +1,21 @@ + + + diff --git a/promo-ui2/src/views/Components/Icon.vue b/promo-ui2/src/views/Components/Icon.vue new file mode 100644 index 0000000..02da12a --- /dev/null +++ b/promo-ui2/src/views/Components/Icon.vue @@ -0,0 +1,61 @@ + + + diff --git a/promo-ui2/src/views/Components/IconPicker.vue b/promo-ui2/src/views/Components/IconPicker.vue new file mode 100644 index 0000000..6643f94 --- /dev/null +++ b/promo-ui2/src/views/Components/IconPicker.vue @@ -0,0 +1,16 @@ + + + diff --git a/promo-ui2/src/views/Components/ImageCropping.vue b/promo-ui2/src/views/Components/ImageCropping.vue new file mode 100644 index 0000000..9e69952 --- /dev/null +++ b/promo-ui2/src/views/Components/ImageCropping.vue @@ -0,0 +1,43 @@ + + + diff --git a/promo-ui2/src/views/Components/ImageViewer.vue b/promo-ui2/src/views/Components/ImageViewer.vue new file mode 100644 index 0000000..a20ce91 --- /dev/null +++ b/promo-ui2/src/views/Components/ImageViewer.vue @@ -0,0 +1,29 @@ + + + diff --git a/promo-ui2/src/views/Components/Infotip.vue b/promo-ui2/src/views/Components/Infotip.vue new file mode 100644 index 0000000..49cff12 --- /dev/null +++ b/promo-ui2/src/views/Components/Infotip.vue @@ -0,0 +1,33 @@ + + + diff --git a/promo-ui2/src/views/Components/InputPassword.vue b/promo-ui2/src/views/Components/InputPassword.vue new file mode 100644 index 0000000..bfae7e5 --- /dev/null +++ b/promo-ui2/src/views/Components/InputPassword.vue @@ -0,0 +1,21 @@ + + + diff --git a/promo-ui2/src/views/Components/Qrcode.vue b/promo-ui2/src/views/Components/Qrcode.vue new file mode 100644 index 0000000..6b7d792 --- /dev/null +++ b/promo-ui2/src/views/Components/Qrcode.vue @@ -0,0 +1,108 @@ + + + diff --git a/promo-ui2/src/views/Components/Search.vue b/promo-ui2/src/views/Components/Search.vue new file mode 100644 index 0000000..0cc9154 --- /dev/null +++ b/promo-ui2/src/views/Components/Search.vue @@ -0,0 +1,363 @@ + + + + + diff --git a/promo-ui2/src/views/Components/Table/CardTable.vue b/promo-ui2/src/views/Components/Table/CardTable.vue new file mode 100644 index 0000000..f07059d --- /dev/null +++ b/promo-ui2/src/views/Components/Table/CardTable.vue @@ -0,0 +1,80 @@ + + + diff --git a/promo-ui2/src/views/Components/Table/DefaultTable.vue b/promo-ui2/src/views/Components/Table/DefaultTable.vue new file mode 100644 index 0000000..1d9124a --- /dev/null +++ b/promo-ui2/src/views/Components/Table/DefaultTable.vue @@ -0,0 +1,105 @@ + + + diff --git a/promo-ui2/src/views/Components/Table/TableImagePreview.vue b/promo-ui2/src/views/Components/Table/TableImagePreview.vue new file mode 100644 index 0000000..ded2d6d --- /dev/null +++ b/promo-ui2/src/views/Components/Table/TableImagePreview.vue @@ -0,0 +1,87 @@ + + + diff --git a/promo-ui2/src/views/Components/Table/TableVideoPreview.vue b/promo-ui2/src/views/Components/Table/TableVideoPreview.vue new file mode 100644 index 0000000..a6607b0 --- /dev/null +++ b/promo-ui2/src/views/Components/Table/TableVideoPreview.vue @@ -0,0 +1,71 @@ + + + diff --git a/promo-ui2/src/views/Components/Table/TreeTable.vue b/promo-ui2/src/views/Components/Table/TreeTable.vue new file mode 100644 index 0000000..90a1dfb --- /dev/null +++ b/promo-ui2/src/views/Components/Table/TreeTable.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/promo-ui2/src/views/Components/Table/UseTableDemo.vue b/promo-ui2/src/views/Components/Table/UseTableDemo.vue new file mode 100644 index 0000000..62e9801 --- /dev/null +++ b/promo-ui2/src/views/Components/Table/UseTableDemo.vue @@ -0,0 +1,283 @@ + + + + + diff --git a/promo-ui2/src/views/Components/Tree.vue b/promo-ui2/src/views/Components/Tree.vue new file mode 100644 index 0000000..661732e --- /dev/null +++ b/promo-ui2/src/views/Components/Tree.vue @@ -0,0 +1,252 @@ + + + + diff --git a/promo-ui2/src/views/Components/VideoPlayer.vue b/promo-ui2/src/views/Components/VideoPlayer.vue new file mode 100644 index 0000000..66dcad4 --- /dev/null +++ b/promo-ui2/src/views/Components/VideoPlayer.vue @@ -0,0 +1,24 @@ + + + diff --git a/promo-ui2/src/views/Components/Waterfall.vue b/promo-ui2/src/views/Components/Waterfall.vue new file mode 100644 index 0000000..c56f8f8 --- /dev/null +++ b/promo-ui2/src/views/Components/Waterfall.vue @@ -0,0 +1,62 @@ + + + diff --git a/promo-ui2/src/views/Dashboard/Analysis.vue b/promo-ui2/src/views/Dashboard/Analysis.vue new file mode 100644 index 0000000..2a77452 --- /dev/null +++ b/promo-ui2/src/views/Dashboard/Analysis.vue @@ -0,0 +1,110 @@ + + + diff --git a/promo-ui2/src/views/Dashboard/Workplace.vue b/promo-ui2/src/views/Dashboard/Workplace.vue new file mode 100644 index 0000000..4e0bd80 --- /dev/null +++ b/promo-ui2/src/views/Dashboard/Workplace.vue @@ -0,0 +1,293 @@ + + + diff --git a/promo-ui2/src/views/Dashboard/components/PanelGroup.vue b/promo-ui2/src/views/Dashboard/components/PanelGroup.vue new file mode 100644 index 0000000..c65e2b8 --- /dev/null +++ b/promo-ui2/src/views/Dashboard/components/PanelGroup.vue @@ -0,0 +1,318 @@ + + + + + diff --git a/promo-ui2/src/views/Dashboard/echarts-data.ts b/promo-ui2/src/views/Dashboard/echarts-data.ts new file mode 100644 index 0000000..e500adf --- /dev/null +++ b/promo-ui2/src/views/Dashboard/echarts-data.ts @@ -0,0 +1,304 @@ +import { EChartsOption } from 'echarts' +import { useI18n } from '@/hooks/web/useI18n' + +const { t } = useI18n() + +export const lineOptions: EChartsOption = { + xAxis: { + data: [ + t('analysis.january'), + t('analysis.february'), + t('analysis.march'), + t('analysis.april'), + t('analysis.may'), + t('analysis.june'), + t('analysis.july'), + t('analysis.august'), + t('analysis.september'), + t('analysis.october'), + t('analysis.november'), + t('analysis.december') + ], + boundaryGap: false, + axisTick: { + show: false + } + }, + grid: { + left: 20, + right: 20, + bottom: 20, + top: 30, + containLabel: true + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross' + }, + padding: [5, 10] + }, + yAxis: { + axisTick: { + show: false + } + }, + legend: { + data: ['日流水', '日邀请'] + }, + series: [ + { + name: t('analysis.estimate'), + smooth: true, + type: 'line', + data: [100, 120, 161, 134, 105, 160, 165, 114, 163, 185, 118, 123], + animationDuration: 2800, + animationEasing: 'cubicInOut' + }, + { + name: t('analysis.actual'), + smooth: true, + type: 'line', + itemStyle: {}, + data: [120, 82, 91, 154, 162, 140, 145, 250, 134, 56, 99, 123], + animationDuration: 2800, + animationEasing: 'quadraticOut' + } + ] +} + +export const pieOptions: EChartsOption = { + title: { + text: t('analysis.userAccessSource'), + left: 'center' + }, + tooltip: { + trigger: 'item', + formatter: '{a}
{b} : {c} ({d}%)' + }, + legend: { + orient: 'vertical', + left: 'left', + data: [ + t('analysis.directAccess'), + t('analysis.mailMarketing'), + t('analysis.allianceAdvertising'), + t('analysis.videoAdvertising'), + t('analysis.searchEngines') + ] + }, + series: [ + { + name: t('analysis.userAccessSource'), + type: 'pie', + radius: '55%', + center: ['50%', '60%'], + data: [ + { value: 335, name: t('analysis.directAccess') }, + { value: 310, name: t('analysis.mailMarketing') }, + { value: 234, name: t('analysis.allianceAdvertising') }, + { value: 135, name: t('analysis.videoAdvertising') }, + { value: 1548, name: t('analysis.searchEngines') } + ] + } + ] +} + +export const barOptions: EChartsOption = { + title: { + text: t('analysis.weeklyUserActivity'), + left: 'center' + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow' + } + }, + grid: { + left: 50, + right: 20, + bottom: 20 + }, + xAxis: { + type: 'category', + data: [ + t('analysis.monday'), + t('analysis.tuesday'), + t('analysis.wednesday'), + t('analysis.thursday'), + t('analysis.friday'), + t('analysis.saturday'), + t('analysis.sunday') + ], + axisTick: { + alignWithLabel: true + } + }, + yAxis: { + type: 'value' + }, + series: [ + { + name: t('analysis.activeQuantity'), + data: [13253, 34235, 26321, 12340, 24643, 1322, 1324], + type: 'bar' + } + ] +} + +export const radarOption: EChartsOption = { + legend: { + data: [t('workplace.personal'), t('workplace.team')] + }, + radar: { + // shape: 'circle', + indicator: [ + { name: t('workplace.quote'), max: 65 }, + { name: t('workplace.contribution'), max: 160 }, + { name: t('workplace.hot'), max: 300 }, + { name: t('workplace.yield'), max: 130 }, + { name: t('workplace.follow'), max: 100 } + ] + }, + series: [ + { + name: `xxx${t('workplace.index')}`, + type: 'radar', + data: [ + { + value: [42, 30, 20, 35, 80], + name: t('workplace.personal') + }, + { + value: [50, 140, 290, 100, 90], + name: t('workplace.team') + } + ] + } + ] +} + +export const wordOptions = { + series: [ + { + type: 'wordCloud', + gridSize: 2, + sizeRange: [12, 50], + rotationRange: [-90, 90], + shape: 'pentagon', + width: 600, + height: 400, + drawOutOfBound: true, + textStyle: { + color: function () { + return ( + 'rgb(' + + [ + Math.round(Math.random() * 160), + Math.round(Math.random() * 160), + Math.round(Math.random() * 160) + ].join(',') + + ')' + ) + } + }, + emphasis: { + textStyle: { + shadowBlur: 10, + shadowColor: '#333' + } + }, + data: [ + { + name: 'Sam S Club', + value: 10000, + textStyle: { + color: 'black' + }, + emphasis: { + textStyle: { + color: 'red' + } + } + }, + { + name: 'Macys', + value: 6181 + }, + { + name: 'Amy Schumer', + value: 4386 + }, + { + name: 'Jurassic World', + value: 4055 + }, + { + name: 'Charter Communications', + value: 2467 + }, + { + name: 'Chick Fil A', + value: 2244 + }, + { + name: 'Planet Fitness', + value: 1898 + }, + { + name: 'Pitch Perfect', + value: 1484 + }, + { + name: 'Express', + value: 1112 + }, + { + name: 'Home', + value: 965 + }, + { + name: 'Johnny Depp', + value: 847 + }, + { + name: 'Lena Dunham', + value: 582 + }, + { + name: 'Lewis Hamilton', + value: 555 + }, + { + name: 'KXAN', + value: 550 + }, + { + name: 'Mary Ellen Mark', + value: 462 + }, + { + name: 'Farrah Abraham', + value: 366 + }, + { + name: 'Rita Ora', + value: 360 + }, + { + name: 'Serena Williams', + value: 282 + }, + { + name: 'NCAA baseball tournament', + value: 273 + }, + { + name: 'Point Break', + value: 265 + } + ] + } + ] +} diff --git a/promo-ui2/src/views/Error/403.vue b/promo-ui2/src/views/Error/403.vue new file mode 100644 index 0000000..615c940 --- /dev/null +++ b/promo-ui2/src/views/Error/403.vue @@ -0,0 +1,17 @@ + + + diff --git a/promo-ui2/src/views/Error/404.vue b/promo-ui2/src/views/Error/404.vue new file mode 100644 index 0000000..0a3fbbe --- /dev/null +++ b/promo-ui2/src/views/Error/404.vue @@ -0,0 +1,17 @@ + + + diff --git a/promo-ui2/src/views/Error/500.vue b/promo-ui2/src/views/Error/500.vue new file mode 100644 index 0000000..ddb5097 --- /dev/null +++ b/promo-ui2/src/views/Error/500.vue @@ -0,0 +1,17 @@ + + + diff --git a/promo-ui2/src/views/Example/Dialog/ExampleDialog.vue b/promo-ui2/src/views/Example/Dialog/ExampleDialog.vue new file mode 100644 index 0000000..b93450d --- /dev/null +++ b/promo-ui2/src/views/Example/Dialog/ExampleDialog.vue @@ -0,0 +1,340 @@ + + + diff --git a/promo-ui2/src/views/Example/Dialog/components/Detail.vue b/promo-ui2/src/views/Example/Dialog/components/Detail.vue new file mode 100644 index 0000000..2496e75 --- /dev/null +++ b/promo-ui2/src/views/Example/Dialog/components/Detail.vue @@ -0,0 +1,20 @@ + + + diff --git a/promo-ui2/src/views/Example/Dialog/components/Write.vue b/promo-ui2/src/views/Example/Dialog/components/Write.vue new file mode 100644 index 0000000..2ea183a --- /dev/null +++ b/promo-ui2/src/views/Example/Dialog/components/Write.vue @@ -0,0 +1,63 @@ + + + diff --git a/promo-ui2/src/views/Example/Page/ExampleAdd.vue b/promo-ui2/src/views/Example/Page/ExampleAdd.vue new file mode 100644 index 0000000..fbe7460 --- /dev/null +++ b/promo-ui2/src/views/Example/Page/ExampleAdd.vue @@ -0,0 +1,51 @@ + + + diff --git a/promo-ui2/src/views/Example/Page/ExampleDetail.vue b/promo-ui2/src/views/Example/Page/ExampleDetail.vue new file mode 100644 index 0000000..6f7e44c --- /dev/null +++ b/promo-ui2/src/views/Example/Page/ExampleDetail.vue @@ -0,0 +1,37 @@ + + + diff --git a/promo-ui2/src/views/Example/Page/ExampleEdit.vue b/promo-ui2/src/views/Example/Page/ExampleEdit.vue new file mode 100644 index 0000000..eb65721 --- /dev/null +++ b/promo-ui2/src/views/Example/Page/ExampleEdit.vue @@ -0,0 +1,66 @@ + + + +@/hooks/event/useEventBus diff --git a/promo-ui2/src/views/Example/Page/ExamplePage.vue b/promo-ui2/src/views/Example/Page/ExamplePage.vue new file mode 100644 index 0000000..c13fe3c --- /dev/null +++ b/promo-ui2/src/views/Example/Page/ExamplePage.vue @@ -0,0 +1,297 @@ + + + +@/hooks/event/useEventBus diff --git a/promo-ui2/src/views/Example/Page/components/Detail.vue b/promo-ui2/src/views/Example/Page/components/Detail.vue new file mode 100644 index 0000000..11d4a94 --- /dev/null +++ b/promo-ui2/src/views/Example/Page/components/Detail.vue @@ -0,0 +1,69 @@ + + + diff --git a/promo-ui2/src/views/Example/Page/components/Write.vue b/promo-ui2/src/views/Example/Page/components/Write.vue new file mode 100644 index 0000000..c4f50ad --- /dev/null +++ b/promo-ui2/src/views/Example/Page/components/Write.vue @@ -0,0 +1,154 @@ + + + diff --git a/promo-ui2/src/views/Function/MultipleTabs.vue b/promo-ui2/src/views/Function/MultipleTabs.vue new file mode 100644 index 0000000..4a623c8 --- /dev/null +++ b/promo-ui2/src/views/Function/MultipleTabs.vue @@ -0,0 +1,18 @@ + + + diff --git a/promo-ui2/src/views/Function/MultipleTabsDemo.vue b/promo-ui2/src/views/Function/MultipleTabsDemo.vue new file mode 100644 index 0000000..9421a9a --- /dev/null +++ b/promo-ui2/src/views/Function/MultipleTabsDemo.vue @@ -0,0 +1,19 @@ + + + diff --git a/promo-ui2/src/views/Function/Request.vue b/promo-ui2/src/views/Function/Request.vue new file mode 100644 index 0000000..b37e67b --- /dev/null +++ b/promo-ui2/src/views/Function/Request.vue @@ -0,0 +1,173 @@ + + + diff --git a/promo-ui2/src/views/Function/Test.vue b/promo-ui2/src/views/Function/Test.vue new file mode 100644 index 0000000..dbb3732 --- /dev/null +++ b/promo-ui2/src/views/Function/Test.vue @@ -0,0 +1,70 @@ + + + diff --git a/promo-ui2/src/views/Guide/Guide.vue b/promo-ui2/src/views/Guide/Guide.vue new file mode 100644 index 0000000..dbdc91b --- /dev/null +++ b/promo-ui2/src/views/Guide/Guide.vue @@ -0,0 +1,19 @@ + + + diff --git a/promo-ui2/src/views/Level/Menu111.vue b/promo-ui2/src/views/Level/Menu111.vue new file mode 100644 index 0000000..015d0f8 --- /dev/null +++ b/promo-ui2/src/views/Level/Menu111.vue @@ -0,0 +1,20 @@ + + + diff --git a/promo-ui2/src/views/Level/Menu12.vue b/promo-ui2/src/views/Level/Menu12.vue new file mode 100644 index 0000000..8e2725d --- /dev/null +++ b/promo-ui2/src/views/Level/Menu12.vue @@ -0,0 +1,20 @@ + + + diff --git a/promo-ui2/src/views/Level/Menu2.vue b/promo-ui2/src/views/Level/Menu2.vue new file mode 100644 index 0000000..72e2abb --- /dev/null +++ b/promo-ui2/src/views/Level/Menu2.vue @@ -0,0 +1,20 @@ + + + diff --git a/promo-ui2/src/views/Login/Login.vue b/promo-ui2/src/views/Login/Login.vue new file mode 100644 index 0000000..081f30a --- /dev/null +++ b/promo-ui2/src/views/Login/Login.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/promo-ui2/src/views/Login/components/LoginForm.vue b/promo-ui2/src/views/Login/components/LoginForm.vue new file mode 100644 index 0000000..e465ef4 --- /dev/null +++ b/promo-ui2/src/views/Login/components/LoginForm.vue @@ -0,0 +1,300 @@ + + + diff --git a/promo-ui2/src/views/Login/components/RegisterForm.vue b/promo-ui2/src/views/Login/components/RegisterForm.vue new file mode 100644 index 0000000..8c2afcd --- /dev/null +++ b/promo-ui2/src/views/Login/components/RegisterForm.vue @@ -0,0 +1,251 @@ + + + diff --git a/promo-ui2/src/views/Login/components/index.ts b/promo-ui2/src/views/Login/components/index.ts new file mode 100644 index 0000000..d08be96 --- /dev/null +++ b/promo-ui2/src/views/Login/components/index.ts @@ -0,0 +1,4 @@ +import LoginForm from './LoginForm.vue' +import RegisterForm from './RegisterForm.vue' + +export { LoginForm, RegisterForm } diff --git a/promo-ui2/src/views/MyMoney/index.vue b/promo-ui2/src/views/MyMoney/index.vue new file mode 100644 index 0000000..099b38a --- /dev/null +++ b/promo-ui2/src/views/MyMoney/index.vue @@ -0,0 +1,105 @@ + + + diff --git a/promo-ui2/src/views/Personal/PersonalCenter/PersonalCenter.vue b/promo-ui2/src/views/Personal/PersonalCenter/PersonalCenter.vue new file mode 100644 index 0000000..dc09883 --- /dev/null +++ b/promo-ui2/src/views/Personal/PersonalCenter/PersonalCenter.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/promo-ui2/src/views/Personal/PersonalCenter/components/EditInfo.vue b/promo-ui2/src/views/Personal/PersonalCenter/components/EditInfo.vue new file mode 100644 index 0000000..bd684b7 --- /dev/null +++ b/promo-ui2/src/views/Personal/PersonalCenter/components/EditInfo.vue @@ -0,0 +1,96 @@ + + + diff --git a/promo-ui2/src/views/Personal/PersonalCenter/components/EditPassword.vue b/promo-ui2/src/views/Personal/PersonalCenter/components/EditPassword.vue new file mode 100644 index 0000000..7b320f3 --- /dev/null +++ b/promo-ui2/src/views/Personal/PersonalCenter/components/EditPassword.vue @@ -0,0 +1,110 @@ + + + diff --git a/promo-ui2/src/views/Personal/PersonalCenter/components/UploadAvatar.vue b/promo-ui2/src/views/Personal/PersonalCenter/components/UploadAvatar.vue new file mode 100644 index 0000000..9b40e21 --- /dev/null +++ b/promo-ui2/src/views/Personal/PersonalCenter/components/UploadAvatar.vue @@ -0,0 +1,30 @@ + + + diff --git a/promo-ui2/src/views/Redirect/Redirect.vue b/promo-ui2/src/views/Redirect/Redirect.vue new file mode 100644 index 0000000..0b7af7b --- /dev/null +++ b/promo-ui2/src/views/Redirect/Redirect.vue @@ -0,0 +1,30 @@ + + diff --git a/promo-ui2/src/views/Turnover/index.vue b/promo-ui2/src/views/Turnover/index.vue new file mode 100644 index 0000000..55c7f3e --- /dev/null +++ b/promo-ui2/src/views/Turnover/index.vue @@ -0,0 +1,89 @@ + + + diff --git a/promo-ui2/src/views/User/index.vue b/promo-ui2/src/views/User/index.vue new file mode 100644 index 0000000..ea2baac --- /dev/null +++ b/promo-ui2/src/views/User/index.vue @@ -0,0 +1,162 @@ + + diff --git a/promo-ui2/src/views/hooks/useClipboard.vue b/promo-ui2/src/views/hooks/useClipboard.vue new file mode 100644 index 0000000..f49125e --- /dev/null +++ b/promo-ui2/src/views/hooks/useClipboard.vue @@ -0,0 +1,26 @@ + + + diff --git a/promo-ui2/src/views/hooks/useCrudSchemas.vue b/promo-ui2/src/views/hooks/useCrudSchemas.vue new file mode 100644 index 0000000..69a2d7f --- /dev/null +++ b/promo-ui2/src/views/hooks/useCrudSchemas.vue @@ -0,0 +1,186 @@ + + + diff --git a/promo-ui2/src/views/hooks/useNetwork.vue b/promo-ui2/src/views/hooks/useNetwork.vue new file mode 100644 index 0000000..dd6ed93 --- /dev/null +++ b/promo-ui2/src/views/hooks/useNetwork.vue @@ -0,0 +1,12 @@ + + + diff --git a/promo-ui2/src/views/hooks/useTagsView.vue b/promo-ui2/src/views/hooks/useTagsView.vue new file mode 100644 index 0000000..1bacedc --- /dev/null +++ b/promo-ui2/src/views/hooks/useTagsView.vue @@ -0,0 +1,59 @@ + + + diff --git a/promo-ui2/src/views/hooks/useValidator.vue b/promo-ui2/src/views/hooks/useValidator.vue new file mode 100644 index 0000000..c3d6131 --- /dev/null +++ b/promo-ui2/src/views/hooks/useValidator.vue @@ -0,0 +1,80 @@ + + + diff --git a/promo-ui2/src/views/hooks/useWatermark.vue b/promo-ui2/src/views/hooks/useWatermark.vue new file mode 100644 index 0000000..bd5cc84 --- /dev/null +++ b/promo-ui2/src/views/hooks/useWatermark.vue @@ -0,0 +1,31 @@ + + + diff --git a/promo-ui2/src/views/subFlow/index.vue b/promo-ui2/src/views/subFlow/index.vue new file mode 100644 index 0000000..6e30161 --- /dev/null +++ b/promo-ui2/src/views/subFlow/index.vue @@ -0,0 +1,209 @@ + + diff --git a/promo-ui2/src/views/subFlow/transaction.vue b/promo-ui2/src/views/subFlow/transaction.vue new file mode 100644 index 0000000..4d60c46 --- /dev/null +++ b/promo-ui2/src/views/subFlow/transaction.vue @@ -0,0 +1,108 @@ + + + diff --git a/promo-ui2/stylelint.config.cjs b/promo-ui2/stylelint.config.cjs new file mode 100644 index 0000000..a50d202 --- /dev/null +++ b/promo-ui2/stylelint.config.cjs @@ -0,0 +1,231 @@ +module.exports = { + root: true, + plugins: ['stylelint-order'], + customSyntax: 'postcss-html', + extends: ['stylelint-config-standard'], + rules: { + 'selector-pseudo-class-no-unknown': [ + true, + { + ignorePseudoClasses: ['global', 'deep'] + } + ], + 'at-rule-no-unknown': [ + true, + { + ignoreAtRules: ['function', 'if', 'each', 'include', 'mixin'] + } + ], + 'media-query-no-invalid': null, + 'function-no-unknown': null, + 'no-empty-source': null, + 'named-grid-areas-no-invalid': null, + 'no-descending-specificity': null, + 'font-family-no-missing-generic-family-keyword': null, + 'rule-empty-line-before': [ + 'always', + { + ignore: ['after-comment', 'first-nested'] + } + ], + 'unit-no-unknown': [ + true, + { + ignoreUnits: ['rpx'] + } + ], + 'order/order': [ + [ + 'dollar-variables', + 'custom-properties', + 'at-rules', + 'declarations', + { + type: 'at-rule', + name: 'supports' + }, + { + type: 'at-rule', + name: 'media' + }, + 'rules' + ], + { + severity: 'warning' + } + ], + // Specify the alphabetical order of the attributes in the declaration block + 'order/properties-order': [ + 'position', + 'top', + 'right', + 'bottom', + 'left', + 'z-index', + 'display', + 'float', + 'width', + 'height', + 'max-width', + 'max-height', + 'min-width', + 'min-height', + 'padding', + 'padding-top', + 'padding-right', + 'padding-bottom', + 'padding-left', + 'margin', + 'margin-top', + 'margin-right', + 'margin-bottom', + 'margin-left', + 'margin-collapse', + 'margin-top-collapse', + 'margin-right-collapse', + 'margin-bottom-collapse', + 'margin-left-collapse', + 'overflow', + 'overflow-x', + 'overflow-y', + 'clip', + 'clear', + 'font', + 'font-family', + 'font-size', + 'font-smoothing', + 'osx-font-smoothing', + 'font-style', + 'font-weight', + 'hyphens', + 'src', + 'line-height', + 'letter-spacing', + 'word-spacing', + 'color', + 'text-align', + 'text-decoration', + 'text-indent', + 'text-overflow', + 'text-rendering', + 'text-size-adjust', + 'text-shadow', + 'text-transform', + 'word-break', + 'word-wrap', + 'white-space', + 'vertical-align', + 'list-style', + 'list-style-type', + 'list-style-position', + 'list-style-image', + 'pointer-events', + 'cursor', + 'background', + 'background-attachment', + 'background-color', + 'background-image', + 'background-position', + 'background-repeat', + 'background-size', + 'border', + 'border-collapse', + 'border-top', + 'border-right', + 'border-bottom', + 'border-left', + 'border-color', + 'border-image', + 'border-top-color', + 'border-right-color', + 'border-bottom-color', + 'border-left-color', + 'border-spacing', + 'border-style', + 'border-top-style', + 'border-right-style', + 'border-bottom-style', + 'border-left-style', + 'border-width', + 'border-top-width', + 'border-right-width', + 'border-bottom-width', + 'border-left-width', + 'border-radius', + 'border-top-right-radius', + 'border-bottom-right-radius', + 'border-bottom-left-radius', + 'border-top-left-radius', + 'border-radius-topright', + 'border-radius-bottomright', + 'border-radius-bottomleft', + 'border-radius-topleft', + 'content', + 'quotes', + 'outline', + 'outline-offset', + 'opacity', + 'filter', + 'visibility', + 'size', + 'zoom', + 'transform', + 'box-align', + 'box-flex', + 'box-orient', + 'box-pack', + 'box-shadow', + 'box-sizing', + 'table-layout', + 'animation', + 'animation-delay', + 'animation-duration', + 'animation-iteration-count', + 'animation-name', + 'animation-play-state', + 'animation-timing-function', + 'animation-fill-mode', + 'transition', + 'transition-delay', + 'transition-duration', + 'transition-property', + 'transition-timing-function', + 'background-clip', + 'backface-visibility', + 'resize', + 'appearance', + 'user-select', + 'interpolation-mode', + 'direction', + 'marks', + 'page', + 'set-link-source', + 'unicode-bidi', + 'speak' + ] + }, + ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'], + overrides: [ + { + files: ['*.vue', '**/*.vue', '*.html', '**/*.html'], + extends: ['stylelint-config-recommended', 'stylelint-config-html'], + rules: { + 'keyframes-name-pattern': null, + 'selector-class-pattern': null, + 'no-duplicate-selectors': null, + 'selector-pseudo-class-no-unknown': [ + true, + { + ignorePseudoClasses: ['deep', 'global'] + } + ], + 'selector-pseudo-element-no-unknown': [ + true, + { + ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted'] + } + ] + } + } + ] +} diff --git a/promo-ui2/tsconfig.json b/promo-ui2/tsconfig.json new file mode 100644 index 0000000..fe67772 --- /dev/null +++ b/promo-ui2/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "target": "esnext", + "useDefineForClassFields": true, + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "jsx": "preserve", + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "jsxImportSource": "vue", + "lib": ["esnext", "dom"], + "baseUrl": "./", + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": true, + "strictFunctionTypes": false, + "noUnusedLocals": true, + "noUnusedParameters": true, + "experimentalDecorators": true, + "noImplicitAny": false, + "skipLibCheck": true, + "paths": { + "@/*": ["src/*"] + }, + "types": [ + "@intlify/unplugin-vue-i18n/types", + "vite/client", + "element-plus/global", + "@types/qrcode", + "vite-plugin-svg-icons/client" + ] + }, + "include": ["src", "types/**/*.d.ts", "mock/**/*.ts"] + // "exclude": ["dist", "node_modules"] +} diff --git a/promo-ui2/types/components.d.ts b/promo-ui2/types/components.d.ts new file mode 100644 index 0000000..15b5a6b --- /dev/null +++ b/promo-ui2/types/components.d.ts @@ -0,0 +1,9 @@ +declare module 'vue' { + export interface GlobalComponents { + Icon: (typeof import('../src/components/Icon/index'))['Icon'] + Permission: (typeof import('../src/components/Permission/index'))['Permission'] + BaseButton: (typeof import('../src/components/Button/index'))['BaseButton'] + } +} + +export {} diff --git a/promo-ui2/types/env.d.ts b/promo-ui2/types/env.d.ts new file mode 100644 index 0000000..3811725 --- /dev/null +++ b/promo-ui2/types/env.d.ts @@ -0,0 +1,14 @@ +/// + +declare module '*.vue' { + import { DefineComponent } from 'vue' + + const component: DefineComponent<{}, {}, any> + export default component +} + +declare global { + interface ImportMeta { + readonly env: ImportMetaEnv + } +} diff --git a/promo-ui2/types/global.d.ts b/promo-ui2/types/global.d.ts new file mode 100644 index 0000000..467c662 --- /dev/null +++ b/promo-ui2/types/global.d.ts @@ -0,0 +1,89 @@ +import type { CSSProperties } from 'vue' +import { RawAxiosRequestHeaders } from 'axios' +declare global { + declare interface Fn { + (...arg: T[]): T + } + + declare type Nullable = T | null + + declare type ElRef = Nullable + + declare type Recordable = Record + + declare type RemoveReadonly = { + -readonly [P in keyof T]: T[P] + } + + declare type ComponentRef = InstanceType + + declare type LocaleType = 'zh-CN' | 'en' + + declare type TimeoutHandle = ReturnType + declare type IntervalHandle = ReturnType + + declare type ElementPlusInfoType = 'success' | 'info' | 'warning' | 'danger' + + declare type LayoutType = 'classic' | 'topLeft' | 'top' | 'cutMenu' + + declare type AxiosContentType = + | 'application/json' + | 'application/x-www-form-urlencoded' + | 'multipart/form-data' + | 'text/plain' + + declare type AxiosMethod = 'get' | 'post' | 'delete' | 'put' + + declare type AxiosResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream' + + declare interface AxiosConfig { + params?: any + data?: any + url?: string + method?: AxiosMethod + headers?: RawAxiosRequestHeaders + responseType?: AxiosResponseType + } + + declare interface IResponse { + code: number + data: T extends any ? T : T & any + rows: any + total: any + } + + declare interface ThemeTypes { + elColorPrimary?: string + leftMenuBorderColor?: string + leftMenuBgColor?: string + leftMenuBgLightColor?: string + leftMenuBgActiveColor?: string + leftMenuCollapseBgActiveColor?: string + leftMenuTextColor?: string + leftMenuTextActiveColor?: string + logoTitleTextColor?: string + logoBorderColor?: string + topHeaderBgColor?: string + topHeaderTextColor?: string + topHeaderHoverColor?: string + topToolBorderColor?: string + } + + declare interface ImportMetaEnv { + readonly VITE_NODE_ENV: string + readonly VITE_APP_TITLE: string + readonly VITE_API_BASE_PATH: string + readonly VITE_BASE_PATH: string + readonly VITE_DROP_DEBUGGER: string + readonly VITE_DROP_CONSOLE: string + readonly VITE_SOURCEMAP: string + readonly VITE_OUT_DIR: string + readonly VITE_USE_BUNDLE_ANALYZER: string + readonly VITE_USE_ALL_ELEMENT_PLUS_STYLE: string + readonly VITE_USE_MOCK: string + readonly VITE_USE_CSS_SPLIT: string + readonly VITE_USE_ONLINE_ICON: string + readonly VITE_ICON_PREFIX: string + readonly VITE_HIDE_GLOBAL_SETTING: string + } +} diff --git a/promo-ui2/types/router.d.ts b/promo-ui2/types/router.d.ts new file mode 100644 index 0000000..32d9447 --- /dev/null +++ b/promo-ui2/types/router.d.ts @@ -0,0 +1,78 @@ +import type { RouteRecordRaw } from 'vue-router' +import { defineComponent } from 'vue' + +/** +* redirect: noredirect 当设置 noredirect 的时候该路由在面包屑导航中不可被点击 +* name:'router-name' 设定路由的名字,一定要填写不然使用时会出现各种问题 +* meta : { + hidden: true 当设置 true 的时候该路由不会再侧边栏出现 如404,login等页面(默认 false) + + alwaysShow: true 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式, + 只有一个时,会将那个子路由当做根路由显示在侧边栏, + 若你想不管路由下面的 children 声明的个数都显示你的根路由, + 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则, + 一直显示根路由(默认 false) + + title: 'title' 设置该路由在侧边栏和面包屑中展示的名字 + + icon: 'svg-name' 设置该路由的图标 + + noCache: true 如果设置为true,则不会被 缓存(默认 false) + + breadcrumb: false 如果设置为false,则不会在breadcrumb面包屑中显示(默认 true) + + affix: true 如果设置为true,则会一直固定在tag项中(默认 false) + + noTagsView: true 如果设置为true,则不会出现在tag中(默认 false) + + activeMenu: '/dashboard' 显示高亮的路由路径 + + canTo: true 设置为true即使hidden为true,也依然可以进行路由跳转(默认 false) + + permission: ['edit','add', 'delete'] 设置该路由的权限 + } +**/ + +interface RouteMetaCustom extends Record { + hidden?: boolean + alwaysShow?: boolean + title?: string + icon?: string + noCache?: boolean + breadcrumb?: boolean + affix?: boolean + activeMenu?: string + noTagsView?: boolean + canTo?: boolean + permission?: string[] +} + +declare module 'vue-router' { + interface RouteMeta extends RouteMetaCustom {} +} + +type Component = + | ReturnType + | (() => Promise) + | (() => Promise) + +declare global { + declare interface AppRouteRecordRaw extends Omit { + name: string + meta: RouteMetaCustom + component?: Component | string + children?: AppRouteRecordRaw[] + props?: Recordable + fullPath?: string + } + + declare interface AppCustomRouteRecordRaw + extends Omit { + name: string + meta: RouteMetaCustom + component: string + path: string + redirect: string + children?: AppCustomRouteRecordRaw[] + } +} diff --git a/promo-ui2/uno.config.ts b/promo-ui2/uno.config.ts new file mode 100644 index 0000000..997f6c8 --- /dev/null +++ b/promo-ui2/uno.config.ts @@ -0,0 +1,146 @@ +import { defineConfig, toEscapedSelector as e, presetUno, presetIcons } from 'unocss' +import transformerVariantGroup from '@unocss/transformer-variant-group' +import { loadEnv } from 'vite' +import { ICON_PREFIX } from './src/constants' + +const root = process.cwd() + +const createPresetIcons = () => { + const isBuild = !!process.argv[4] + let env = {} as any + if (!isBuild) { + env = loadEnv(process.argv[3], root) + } else { + env = loadEnv(process.argv[4], root) + } + // @ts-ignore + if (env.VITE_USE_ONLINE_ICON === 'true') { + return [] + } else { + return [ + presetIcons({ + autoInstall: false, + prefix: ICON_PREFIX + }) + ] + } +} + +export default defineConfig({ + // ...UnoCSS options + rules: [ + [ + /^overflow-ellipsis$/, + ([], { rawSelector }) => { + const selector = e(rawSelector) + return ` +${selector} { + text-overflow: ellipsis; +} +` + } + ], + [ + /^custom-hover$/, + ([], { rawSelector }) => { + const selector = e(rawSelector) + return ` +${selector} { + display: flex; + height: 100%; + padding: 1px 10px 0; + cursor: pointer; + align-items: center; + transition: background var(--transition-time-02); +} +/* you can have multiple rules */ +${selector}:hover { + background-color: var(--top-header-hover-color); +} +.dark ${selector}:hover { + background-color: var(--el-bg-color-overlay); +} +` + } + ], + [ + /^layout-border__left$/, + ([], { rawSelector }) => { + const selector = e(rawSelector) + return ` +${selector}:before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 1px; + height: 100%; + background-color: var(--el-border-color); + z-index: 3; +} +` + } + ], + [ + /^layout-border__right$/, + ([], { rawSelector }) => { + const selector = e(rawSelector) + return ` +${selector}:after { + content: ""; + position: absolute; + top: 0; + right: 0; + width: 1px; + height: 100%; + background-color: var(--el-border-color); + z-index: 3; +} +` + } + ], + [ + /^layout-border__top$/, + ([], { rawSelector }) => { + const selector = e(rawSelector) + return ` +${selector}:before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 1px; + background-color: var(--el-border-color); + z-index: 3; +} +` + } + ], + [ + /^layout-border__bottom$/, + ([], { rawSelector }) => { + const selector = e(rawSelector) + return ` +${selector}:after { + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 1px; + background-color: var(--el-border-color); + z-index: 3; +} +` + } + ] + ], + presets: [presetUno({ dark: 'class', attributify: false }), ...createPresetIcons()], + transformers: [transformerVariantGroup()], + content: { + pipeline: { + include: [/\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html|ts)($|\?)/] + } + } +}) diff --git a/promo-ui2/vite.config.ts b/promo-ui2/vite.config.ts new file mode 100644 index 0000000..1781f70 --- /dev/null +++ b/promo-ui2/vite.config.ts @@ -0,0 +1,181 @@ +import { resolve } from 'path' +import { loadEnv } from 'vite' +import type { UserConfig, ConfigEnv } from 'vite' +import Vue from '@vitejs/plugin-vue' +import VueJsx from '@vitejs/plugin-vue-jsx' +import progress from 'vite-plugin-progress' +import EslintPlugin from 'vite-plugin-eslint' +import { ViteEjsPlugin } from 'vite-plugin-ejs' +import { viteMockServe } from 'vite-plugin-mock' +import PurgeIcons from 'vite-plugin-purge-icons' +import ServerUrlCopy from 'vite-plugin-url-copy' +import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite' +import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' +import { createStyleImportPlugin, ElementPlusResolve } from 'vite-plugin-style-import' +import UnoCSS from 'unocss/vite' +import { visualizer } from 'rollup-plugin-visualizer' + +// https://vitejs.dev/config/ +const root = process.cwd() + +function pathResolve(dir: string) { + return resolve(root, '.', dir) +} + +export default ({ command, mode }: ConfigEnv): UserConfig => { + let env = {} as any + const isBuild = command === 'build' + if (!isBuild) { + env = loadEnv(process.argv[3] === '--mode' ? process.argv[4] : process.argv[3], root) + } else { + env = loadEnv(mode, root) + } + return { + base: env.VITE_BASE_PATH, + plugins: [ + Vue({ + script: { + // 开启defineModel + defineModel: true + } + }), + VueJsx(), + ServerUrlCopy(), + progress(), + env.VITE_USE_ALL_ELEMENT_PLUS_STYLE === 'false' + ? createStyleImportPlugin({ + resolves: [ElementPlusResolve()], + libs: [ + { + libraryName: 'element-plus', + esModule: true, + resolveStyle: (name) => { + if (name === 'click-outside') { + return '' + } + return `element-plus/es/components/${name.replace(/^el-/, '')}/style/css` + } + } + ] + }) + : undefined, + // EslintPlugin({ + // cache: false, + // failOnWarning: false, + // failOnError: false, + // include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件 + // }), + VueI18nPlugin({ + runtimeOnly: true, + compositionOnly: true, + include: [resolve(__dirname, 'src/locales/**')] + }), + createSvgIconsPlugin({ + iconDirs: [pathResolve('src/assets/svgs')], + symbolId: 'icon-[dir]-[name]', + svgoOptions: true + }), + PurgeIcons(), + env.VITE_USE_MOCK === 'true' + ? viteMockServe({ + ignore: /^\_/, + mockPath: 'mock', + localEnabled: !isBuild, + prodEnabled: isBuild, + injectCode: ` + import { setupProdMockServer } from '../mock/_createProductionServer' + + setupProdMockServer() + ` + }) + : undefined, + ViteEjsPlugin({ + title: env.VITE_APP_TITLE + }), + UnoCSS() + ], + + css: { + preprocessorOptions: { + less: { + additionalData: '@import "./src/styles/variables.module.less";', + javascriptEnabled: true + } + } + }, + resolve: { + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.less', '.css'], + alias: [ + { + find: 'vue-i18n', + replacement: 'vue-i18n/dist/vue-i18n.cjs.js' + }, + { + find: /\@\//, + replacement: `${pathResolve('src')}/` + } + ] + }, + esbuild: { + pure: env.VITE_DROP_CONSOLE === 'true' ? ['console.log'] : undefined, + drop: env.VITE_DROP_DEBUGGER === 'true' ? ['debugger'] : undefined + }, + build: { + target: 'es2015', + outDir: env.VITE_OUT_DIR || 'dist', + sourcemap: env.VITE_SOURCEMAP === 'true', + // brotliSize: false, + rollupOptions: { + plugins: env.VITE_USE_BUNDLE_ANALYZER === 'true' ? [visualizer()] : undefined, + // 拆包 + output: { + manualChunks: { + 'vue-chunks': ['vue', 'vue-router', 'pinia', 'vue-i18n'], + 'element-plus': ['element-plus'], + 'wang-editor': ['@wangeditor/editor', '@wangeditor/editor-for-vue'], + echarts: ['echarts', 'echarts-wordcloud'] + } + } + }, + cssCodeSplit: !(env.VITE_USE_CSS_SPLIT === 'false'), + cssTarget: ['chrome31'] + }, + server: { + port: 4000, + proxy: { + // 选项写法 + '/api': { + target: 'http://127.0.0.1:8000', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, '') + } + }, + hmr: { + overlay: false + }, + host: '0.0.0.0' + }, + optimizeDeps: { + include: [ + 'vue', + 'vue-router', + 'vue-types', + 'element-plus/es/locale/lang/zh-cn', + 'element-plus/es/locale/lang/en', + '@iconify/iconify', + '@vueuse/core', + 'axios', + 'qs', + 'echarts', + 'echarts-wordcloud', + 'qrcode', + '@wangeditor/editor', + '@wangeditor/editor-for-vue', + 'vue-json-pretty', + '@zxcvbn-ts/core', + 'dayjs', + 'cropperjs' + ] + } + } +} diff --git a/publish.sh b/publish.sh new file mode 100644 index 0000000..20cf8d8 --- /dev/null +++ b/publish.sh @@ -0,0 +1,5 @@ +#!/bin/bash +./package.sh +./ry.sh stop +./ry.sh start +#tail -f nohup.out \ No newline at end of file diff --git a/pyground/README.md b/pyground/README.md new file mode 100644 index 0000000..85fdb38 --- /dev/null +++ b/pyground/README.md @@ -0,0 +1 @@ +uv sync \ No newline at end of file diff --git a/pyground/pyproject.toml b/pyground/pyproject.toml new file mode 100644 index 0000000..836fbb0 --- /dev/null +++ b/pyground/pyproject.toml @@ -0,0 +1,16 @@ +[project] +name = "pyplayground" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "cryptography>=46.0.5", + "matplotlib>=3.10.8", + "numpy>=2.4.2", + "openpyxl>=3.1.5", + "pandas>=3.0.1", + "pymysql>=1.1.2", + "requests>=2.32.5", + "websockets>=16.0", +] diff --git a/pyground/sugar/README.md b/pyground/sugar/README.md new file mode 100644 index 0000000..c809581 --- /dev/null +++ b/pyground/sugar/README.md @@ -0,0 +1,12 @@ +SugarRush1000.py 是业务核心逻辑文件 +SugarRushAutoRTPTuner.py 游戏RTP调参和校验文件 +SugarRushAutoFreeSpinsTuner.py 购买一次免费旋转的旋转次数调参和校验文件 +SugarRushSimulator.py 根据指定权重参数运行n次产生对局日志,这些日志将转成sql写入数据库,供后端逻辑使用 +write_sql.py 根据日志文件生成sql写入数据库 + + + +数据库数据dump +mysqldump -uroot -p123456 db_name table_name > dump.sql +数据库数据导入 +mysql -uroot -p123456 db_name < sql \ No newline at end of file diff --git a/pyground/sugar/SugarRush1000.py b/pyground/sugar/SugarRush1000.py new file mode 100644 index 0000000..2c6ad0f --- /dev/null +++ b/pyground/sugar/SugarRush1000.py @@ -0,0 +1,489 @@ +import json +import random +from typing import List, Dict, Tuple, Optional + +from symbol_pay_table import get_symbol_pay + +# 符号默认权重 +default_symbol_weights = { + "A": 2, # 高赔率 + "B": 5, # 中高赔率 + "C": 10, # 中等赔率 + "D": 15, # 中低赔率 + "E": 20, # 低赔率 + "F": 30, # 极低赔率 + "G": 40, # 最低赔率 + "S": 1, # Scatter +} + +scotter_count_mapping = {"3": 10, "4": 12, "5": 15, "6": 20, "7": 30} + + +class SugarRush1000: + def __init__( + self, + balance: float = 1000.0, + bet: float = 1.0, + mock_grid=None, + weights=None, + scotter_counts_weights=None, + ): + """ + 初始化游戏 + :param balance: 玩家余额 + :param bet: 单次下注额 + """ + self.balance = balance + self.bet = bet + self.rows = 7 + self.cols = 7 + + # 游戏符号定义和权重 + self.symbol_weights = weights if weights else default_symbol_weights + self.symbols = list(self.symbol_weights.keys()) + self.weights = list(self.symbol_weights.values()) + + if scotter_counts_weights is None: + scotter_counts_weights = {3: 10, 4: 12, 5: 15, 6: 20, 7: 30} + + self.scotter_counts = list(scotter_counts_weights.keys()) + self.scotter_counts_weights = list(scotter_counts_weights.values()) + + # 游戏状态 + self.grid = [] + # 7x7 网格 + self.multipliers = [[0 for _ in range(self.cols)] for _ in range(self.rows)] + self.free_spins = 0 # 剩余免费旋转次数 + self.is_free_spin_status = False + self.max_win_cap = 25000 # 最大赢利倍数限制 + self.is_buy_free = False + self.is_super_free = False # 是否为超级免费旋转模式 + self.spin_total_win = ( + 0 # 本次旋转的总赢利。主要计算免费旋转过程中累积盈利是否超过最大额度 + ) + self.mock_grid = mock_grid # 用于测试的固定网格 + self.drop_sequence = [] # 用于测试的掉落序列 + + def _get_next_drop_symbol(self, count=1, no_scatter: bool = False): + """生成一列中的count个符号,要求一列中最多有一个scatter符号 + no_scatter: 是否排除scatter符号。如果设置为True,则不生成scatter符号,如果设置为False,则则本次生成的符号中顶多有一个scatter符号 + """ + + # 测试辅助方法:从预设序列中获取掉落符号。预设序列不使用最多有一个scatter符号的规则 + result = [] + while count > 0: + if self.drop_sequence: + result.append(self.drop_sequence.pop(0)) + count -= 1 + else: + break + + symbols1 = self.symbols[:] + weights1 = self.weights[:] + tmp = [(s, w) for s, w in zip(symbols1, weights1) if s != "S"] + symbols2, weights2 = map(list, zip(*tmp)) + for _ in range(count): + tmp_symbols = symbols1 + tmp_weights = weights1 + if no_scatter: + tmp_symbols = symbols2 + tmp_weights = weights2 + symbol = random.choices(tmp_symbols, weights=tmp_weights, k=1)[0] + result.append(symbol) + if symbol == "S": + no_scatter = True + return result + + def grid_col_has_scotter(self, col): + for row in range(self.rows): + if self.grid[row][col] == "S": + return True + return False + + def _init_grid(self): + """初始化或重置网格""" + self.grid = [[None for _ in range(self.cols)] for _ in range(self.rows)] + for col in range(self.cols): + symbols = self._get_next_drop_symbol(count=self.rows, no_scatter=False) + for row in range(self.rows): + self.grid[row][col] = symbols[row] + + def buy_free_spins(self, buy_type: Optional[str] = None) -> int: + """:param buy_type: None=普通旋转, 'standard'=买免费旋转(50x), 'super'=买超级免费旋转(200x)""" + result = {} + if not buy_type: + result = {"message": "未购买任何功能", "balance": self.balance} + + cost = 0 + if buy_type == "standard": + cost = self.bet * 50 + elif buy_type == "super": + cost = self.bet * 200 + + if self.balance < cost: + result["message"] = "余额不足,无法购买功能" + return result + + self.is_buy_free = True + self.is_super_free = True if buy_type == "super" else False + + self.balance -= cost + self.free_spins = 0 + + result = { + "cost": cost, + "balance": self.balance, + "is_buy_free": self.is_buy_free, + "is_super_free": self.is_super_free, + } + return result + + def doSpin(self) -> Dict: + """ + 执行一次旋转/翻转操作的核心接口 + + :param buy_type: None=普通旋转, 'standard'=买免费旋转(100x), 'super'=买超级免费旋转(500x) + :return: 包含游戏状态和结果的字典 + """ + + result = { + "balance": self.balance, + "bet": self.bet, + "actual_bet": 0.0, + "is_scotter": False, + "added_spins": 0, + "win": 0.0, + "grid": self.grid, + "multipliers": self.multipliers, + "free_spins_remaining": self.free_spins, + "is_super": self.is_super_free, + "message": "", + "error": 0, + "extra_free": False, + } + + # 1. 扣除下注积分 + if ( + not self.is_buy_free and not self.is_free_spin_status + ): # 普通模式,而且不是买免费旋转 + if self.balance < self.bet: + result["message"] = "游戏结束,余额不足" + result["error"] = 1 + return result + result["actual_bet"] = self.bet + self.balance -= self.bet + + # 初始化游戏状态 + # 不是免费旋转模式则重置乘数点和累计盈利 + if not self.is_free_spin_status: + self.spin_total_win = 0 + self.multipliers = [[0 for _ in range(self.cols)] for _ in range(self.rows)] + + # 免费旋转扣除次数 + if self.free_spins > 0: + self.free_spins -= 1 + + # 1.2 初始化网格 + if self.mock_grid: + self.grid = self.mock_grid + else: + self._init_grid() + + # 2. 处理购买功能,触发>=3个的scotter + buy_free = self.is_buy_free + if self.is_buy_free: + self.is_buy_free = False + # 购买时强制触发Scatter + scatters_count = random.choices( + self.scotter_counts, weights=self.scotter_counts_weights, k=1 + )[0] + self._force_scatters(int(scatters_count)) + + # 2.1 如果是超级免费旋转而且没有初始化,则初始化所有点为x2 + if self.is_super_free and self.free_spins > 0: + self.is_super_free = False + self.multipliers = [[2 for _ in range(self.cols)] for _ in range(self.rows)] + + # 3. 开始翻转循环 + cascade_win = 0.0 + cascade_count = 0 + max_win_limit = self.bet * self.max_win_cap + + steps = [] + while True: + step = { + "multipler": [], + "grid": "", + # 运行后状态 + "score": 0, + "symbol_links": [], + } + steps.append(step) + step["grid"] = "".join([s for row in self.grid for s in row]) + for r in range(self.rows): + for c in range(self.cols): + if self.multipliers[r][c] > 0: + step["multipler"].append(r * self.cols + c) + step["multipler"].append(self.multipliers[r][c]) + + # 1. 查找赢奖组合 + clusters = self._find_clusters() + + if not clusters: + break # 没有赢奖组合,翻转结束 + + cascade_count += 1 + current_spin_win = 0.0 + + # 2. 计算赔付并处理消除 + # 注意:需要先收集所有要消除的位置,避免计算时网格已变 + symbols_to_remove = set() + + for symbol, coords in clusters: + count = len(coords) + if count < 5: + continue + + # 计算该组合的乘数 (包含该区域内所有乘数点的叠加) + cluster_mult = 0 + cluster_info = [] + for r, c in coords: + if self.multipliers[r][c] > 1: + cluster_mult += self.multipliers[r][c] + cluster_info.append([r * 7 + c, self.multipliers[r][c]]) + cluster_mult = max(1, cluster_mult) + + # 基础赔付 * 符号倍率 * 组合乘数 + pay_factor = get_symbol_pay(symbol, count) + win_amount = self.bet * pay_factor * cluster_mult + current_spin_win += win_amount + + symbol_link = { + "symbol": symbol, + "loc": [], + "multipler": [], + "total_multi": 0, + "base_score": 0, + "score": 0, + } + symbol_link["base_score"] = self.bet * pay_factor + symbol_link["score"] = win_amount + symbol_link["total_multi"] = cluster_mult + for r, c in coords: + symbol_link["loc"].append(r * 7 + c) + for index, m in cluster_info: + symbol_link["multipler"].append(index) + symbol_link["multipler"].append(m) + step["symbol_links"].append(symbol_link) + + # 记录消除位置并更新乘数点 + for r, c in coords: + symbols_to_remove.add((r, c)) + # 乘数点逻辑:炸开一次,点数+1 (2的幂次方) + if self.multipliers[r][c] > 0: + self.multipliers[r][c] = self.multipliers[r][c] * 2 + else: + self.multipliers[r][c] = 1 + + if self.multipliers[r][c] > 1024: + self.multipliers[r][c] = 1024 + + step["score"] = current_spin_win + cascade_win += current_spin_win + + # 检查是否超过最大赢利限制 + if cascade_win >= max_win_limit: + cascade_win = max_win_limit + result["message"] = "达到最大赢奖上限 25000x!" + break + + # 3. 执行消除和掉落 + self._apply_cascade(symbols_to_remove) + + # 3.1 最后处理scotter + scatter_count = sum(row.count("S") for row in self.grid) + if scatter_count >= 3: + if not self.is_free_spin_status: + self.multipliers = [[0 for _ in range(self.cols)] for _ in range(self.rows)] + self.is_free_spin_status = True + added_spins = self._add_free_spins(scatter_count) + if added_spins > 0: + result["is_scotter"] = True + result["added_spins"] = added_spins + result["message"] = f"触发免费旋转! +{added_spins}" + if added_spins > 0 and not buy_free: + result["extra_free"] = True + + # 3.2 处理达到最大奖限制 + raw_spin_total_win = self.spin_total_win + self.spin_total_win += cascade_win + if self.spin_total_win >= max_win_limit: + cascade_win = max_win_limit - raw_spin_total_win + self.spin_total_win = max_win_limit + self.free_spins = 0 # 立即结束 + result["message"] = "达到最大赢奖上限 25000x!" + + # 4. 结算 + if cascade_win > 0: + self.balance += cascade_win + + # 免费旋转模式结束 + if self.free_spins <= 0: + self.is_free_spin_status = False + self.multipliers = [[0 for _ in range(self.cols)] for _ in range(self.rows)] + + # 5. 构建结果信息 + result["win"] = cascade_win + result["balance"] = self.balance + result["free_spins_remaining"] = self.free_spins + result["grid"] = self.grid + result["multipliers"] = self.multipliers + result["steps"] = steps + result["spin_total_win"] = self.spin_total_win + return result + + def _find_clusters(self) -> Dict[str, List[Tuple[int, int]]]: + """查找所有相连的符号块 (排除SCATTER)""" + visited = set() + clusters = [] + + for r in range(self.rows): + for c in range(self.cols): + symbol = self.grid[r][c] + if symbol == "S" or (r, c) in visited: + continue + + # BFS 查找连通块 + queue = [(r, c)] + connected = [] + while queue: + curr_r, curr_c = queue.pop(0) + if (curr_r, curr_c) in visited: + continue + if self.grid[curr_r][curr_c] == symbol: + visited.add((curr_r, curr_c)) + connected.append((curr_r, curr_c)) + # 检查上下左右 + for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]: + nr, nc = curr_r + dr, curr_c + dc + if 0 <= nr < self.rows and 0 <= nc < self.cols: + if (nr, nc) not in visited and self.grid[nr][ + nc + ] == symbol: + queue.append((nr, nc)) + + if len(connected) >= 5: + clusters.append((symbol, connected)) + + return clusters + + def _apply_cascade(self, remove_coords: set): + """执行消除和掉落逻辑""" + # 1. 消除 + for r, c in remove_coords: + self.grid[r][c] = None # 标记为空 + + # 2. 掉落 (按列处理) + for c in range(self.cols): + # 提取该列所有非空符号 + existing_syms = [ + self.grid[r][c] for r in range(self.rows) if self.grid[r][c] is not None + ] + # 填充新符号到顶部 + needed = self.rows - len(existing_syms) + new_syms = self._get_next_drop_symbol( + count=needed, no_scatter=self.grid_col_has_scotter(c) + ) + # 重新组合该列 (新符号在上,原有符号在下) + new_col = new_syms + existing_syms + + # 更新回网格 + for r in range(self.rows): + self.grid[r][c] = new_col[r] + + def _force_scatters(self, count: int): + """强制在随机位置放置指定数量的Scatter""" + # 检查是否已经有,有的话改成其他符号 + for r in range(self.rows): + for c in range(self.cols): + if self.grid[r][c] == "S": + self.grid[r][c] = "G" + cols = list(range(self.cols)) + for _ in range(count): + x = random.choice(cols) + y = random.randint(0, self.rows - 1) + self.grid[y][x] = "S" + cols.remove(x) + + def _add_free_spins(self, scatter_count: int) -> int: + """根据Scatter数量增加免费旋转次数""" + if scatter_count < 3: + return 0 + spins = scotter_count_mapping.get(str(scatter_count), 30) + self.free_spins += spins + return spins + + +def no_cluster_grid(): + return [ + ( + ["B" if col % 2 == 0 else "E" for col in range(7)] + if row % 2 == 0 + else ["E" if col % 2 == 0 else "B" for col in range(7)] + ) + for row in range(7) + ] + + +if __name__ == "__main__": + begin_balance = 1_0000_0000 + custom_grid = no_cluster_grid() + custom_grid[0] = ["A"] * 7 # 设置一个乘数点 + game = SugarRush1000( + balance=begin_balance, + weights={ + "A": 11.910798577481279, + "B": 23.10218485800891, + "C": 36.43351025439602, + "D": 46.62768084621008, + "E": 54.778674468859805, + "F": 64.18383150145051, + "G": 75.00641730395597, + "S": 1.5190499131843918, + }, + scotter_counts_weights={ + "3": 7.26421894353717, + "4": 4.1724734692682395, + "5": 0.8119106579617028, + "6": 0.20313929837878217, + "7": 0.04857599989214818, + }, + mock_grid=custom_grid, + ) + + game.drop_sequence = [ + "C", + "C", + "C", + "C", + "C", + "C", + "C", + "C", + "C", + "C", + "C", + "C", + "C", + "C", + "C", + "A", + "B", + "C", + "D", + "A", + "B", + ] # 预设掉落全是 C + + res = game.doSpin() + print(json.dumps(res)) diff --git a/pyground/sugar/SugarRushAutoFreeSpinsTuner.py b/pyground/sugar/SugarRushAutoFreeSpinsTuner.py new file mode 100644 index 0000000..7b5b090 --- /dev/null +++ b/pyground/sugar/SugarRushAutoFreeSpinsTuner.py @@ -0,0 +1,247 @@ +import json +import random +import sys +import numpy as np +from typing import Dict, List + +from SugarRush1000 import SugarRush1000 + +""" +训练购买免费旋转功能时,一次购买可以获得的平均免费旋转次数 +""" + + +def init_symbol_value(): + return { + "3": 0.1, + "4": 0.4, + "5": 1, + "6": 1.5, + "7": 2, + } + + +class SugarRushAutoTuner: + def __init__( + self, + target_rtp: float = 96.5, + batch_spins=20000, + weights=None, + feature: str = "normal", + ): + self.target_rtp = target_rtp + self.rows = 7 + self.cols = 7 + self.bet = 1.0 + self.batch_spins = batch_spins + self.feature = feature + + # 符号定义及其价值(价值越高,对 RTP 影响越大) + self.symbols_config = init_symbol_value() + + # 初始权重 + self.symbol_keys = list(self.symbols_config.keys()) + if weights is not None: + self.weights = weights + else: + self.weights = {k: 20.0 for k in self.symbol_keys} + + # 学习率 (控制调整速度,太小收敛慢,太大容易震荡) + self.learning_rate = 0.002 # 0.01 + + print(f"初始化自动调优器,feature:{feature} 目标 RTP: {self.target_rtp}%") + + def simulate_one_batch(self, spins: int = 20000): + begin_balance = 1_0000_0000 + game = SugarRush1000(balance=begin_balance, weights=self.weights) + + total_bet = 0.0 + total_win = 0.0 + + for _ in range(spins): + # 执行旋转 + res = game.doSpin() + if res["error"]: + break + + # 统计数据 + actual_cost = res["actual_bet"] + if actual_cost > 0: + total_bet += actual_cost + + total_win += res["win"] + + # 校验余额是否正确 + assert ( + f"{abs(begin_balance - game.balance):.2f}" + == f"{abs(total_bet - total_win):.2f}" + ) + + return (total_win / total_bet) * 100 + + def simulate_free_batch(self, spins: int = 20000, buy_type: str = "standard"): + game = SugarRush1000() + scotter_counts = list(self.weights.keys()) + scotter_counts_weights = list(self.weights.values()) + total_free_spins = 0 + for _ in range(spins): + scatters_count = random.choices( + scotter_counts, weights=scotter_counts_weights, k=1 + )[0] + total_free_spins += game._add_free_spins(int(scatters_count)) + print(f"购买 {spins} 次免费旋转,实际免费旋转次数 {total_free_spins} 次") + + return total_free_spins / spins + + def tune(self, iterations: int = 50): + """ + 迭代调整权重 + """ + history = [] + + for i in range(iterations): + # 1. 模拟当前 RTP + current_rtp = 0 + if self.feature == "normal": + current_rtp = self.simulate_one_batch(self.batch_spins) + else: + current_rtp = self.simulate_free_batch(self.batch_spins, self.feature) + + error = current_rtp - self.target_rtp + + history.append(current_rtp) + + print( + f"迭代 {i+1}/{iterations} | RTP: {current_rtp:.4f}% | 目标: {self.target_rtp}% | 误差: {error:+.4f}%" + ) + + # 2. 检查是否收敛 + if abs(error) < 0.1: + print(f"收敛成功!最终 RTP: {current_rtp:.4f}%") + break + + # 3. 动态调整权重 + # 调整策略:如果 RTP 太高,降低高价值符号的权重,提高低价值符号权重 + # 调整量 = 误差 * 学习率 * 符号价值系数 + # 防止初始误差过大导致权重崩塌 + safe_error = max(min(error, 5.0), -5.0) + adjustment_factor = safe_error * self.learning_rate + + # 防止权重变为负数 + min_weight = 0 + + for sym in self.symbol_keys: + value = self.symbols_config[sym] + + # 核心算法: + # 如果 RTP > Target (Error > 0),我们需要降低 RTP。 + # 对于高价值符号 (value大),我们需要减小其权重。 + # Adjustment 应该是负的。所以: - adjustment_factor * value + + # 如果 RTP < Target (Error < 0),我们需要提高 RTP。 + # 对于高价值符号,我们需要增加其权重。 + # Adjustment 应该是正的。所以: - adjustment_factor * value (因为error是负的,负负得正) + + delta = -adjustment_factor * value * self.weights[sym] + + # --- 优化:限制单次最大调整幅度 --- + # 防止某次调整幅度超过权重的 40%,给算法留点“喘息”空间 + max_change_ratio = 0.4 + if abs(delta) > self.weights[sym] * max_change_ratio: + delta = np.sign(delta) * self.weights[sym] * max_change_ratio + + new_weight = self.weights[sym] + delta + + # 限制最小权重,防止符号消失 + if new_weight < min_weight: + new_weight = min_weight + + if new_weight > 500: + new_weight = 500 + + self.weights[sym] = new_weight + + return self.weights, history + + +# --- 运行自动调优 --- +def train_weights(): + print("开始训练权重...") + # 设置随机种子 + seed = 42 + random.seed(seed) + np.random.seed(seed) + + print(f"随机种子: {seed}") + + fast = {"iterations": 100, "spins": 50_0000} + # middle = {"iterations": 15, "spins": 5_0000} + # slow = {"iterations": 10, "spins": 10_0000} + + current = fast + configs = [ + ("fast", fast), + # ("middle", middle), ("slow", slow) + ] + + tuner = SugarRushAutoTuner( + target_rtp=11, + batch_spins=current["spins"], + weights={ + "3": 7.300320151890674, + "4": 4.256044392311739, + "5": 0.8531843171577663, + "6": 0.21882648882051972, + "7": 0.05364159605552199, + }, + feature="standard", + ) + for name, config in configs: + begin_weights = tuner.weights.copy() + print(f"# {name}调优") + tuner.batch_spins = config["spins"] + final_weights, rtp_history = tuner.tune(iterations=config["iterations"]) + + print("\n=== 调优前的符号权重 ===") + for sym, w in begin_weights.items(): + print(f"{sym}: {w:.2f}") + print("\n 符号出现概率 (%) ===") + for sym, w in begin_weights.items(): + print(f"{sym}: {(w/sum(begin_weights.values()))*100:.2f}%") + + print("\n=== 最终调整后的符号权重 ===") + print(json.dumps(final_weights, indent=4, ensure_ascii=False)) + + # 将权重转换为概率百分比 + total_w = sum(final_weights.values()) + print("\n=== 符号出现概率 (%) ===") + for sym, w in final_weights.items(): + print(f"{sym}: {(w/total_w)*100:.2f}%") + + +def verify(): + print("开始进行权重校验:") + for i in range(10): + tuner = SugarRushAutoTuner( + target_rtp=56, + batch_spins=10_0000, + weights={ + "A": 12.048116942034044, + "B": 23.797750079057277, + "C": 38.01412853023891, + "D": 49.64407598502997, + "E": 59.15353799355775, + "F": 70.33559681987649, + "G": 83.45720386881379, + "S": 1.0979547602954776, + }, + ) + print(f"第 {i+1}/10 次校验:") + tuner.tune(iterations=1) + + +if __name__ == "__main__": + if len(sys.argv) >= 2: + verify() + else: + train_weights() diff --git a/pyground/sugar/SugarRushAutoRTPTuner.py b/pyground/sugar/SugarRushAutoRTPTuner.py new file mode 100644 index 0000000..77afb83 --- /dev/null +++ b/pyground/sugar/SugarRushAutoRTPTuner.py @@ -0,0 +1,339 @@ +import json +import random +import sys +import numpy as np +from typing import Dict, List + +from SugarRush1000 import SugarRush1000 + +""" +符号的价值的含义是对RTP调整的影响程度。符号价值越高,符号概率的调整对RTP的影响越大。比如当前RTP过高,要降低RTP,算法优先降低高价值符号的概率,提升低价值符号的概率。结果就是高价值符号出现频率降低,直接拉动了RTP的降低,低价值符号出现频率提升,又提升了级联效果的流畅性。 + +所有符号的价值不需要考虑归一化,但是要考虑比例。 +调整权重的公式:deletaWeight = -(Error)x(LearningRate)x(SymbolsValue)x(CurrentWieght) +相对性原理:算法只关心“符号 A 的价值是符号 B 的多少倍”。如果 A 的价值是 2.0,B 是 1.0。当RTP偏高时,算法会让 A 的权重减少幅度是 B 的 2倍。 + +比例要合理,不能差距过大,否则调优过程非常不稳定。比如符号A Value=1000,符号B value=0.001, 会导致算法对符号A极其敏感,稍微一点误差就会让A的权重剧烈震荡,而符号B几乎完全不动。 + +每个符号的价值配置公式: +Value(symbol)约是BasePay(symbol)xVolatilityFactor +BasePay:是符号的期望赔率 +VolatilityFactor:是符号的波动因子,人工设定的修正值。 + * 对于Scotter:它没有基础赔付,但是能触发免费旋转,Scatter的潜在价值远高于普通符号,所以它的Value应该设置得更高。 + * 对于高赔率符号:系数设为1.2~1.5。高赔率对RTP影响很大,调整它们可以很快让RTP收敛。 + * 对于低赔率符号:系数设为0.8~1.0。它们主要贡献消除次数和触发级联效果,但是本身赔率低,对RTP的边际贡献较低 +""" + +fast = {"iterations": 30, "spins": 100} +middle = {"iterations": 15, "spins": 5_0000} +slow = {"iterations": 10, "spins": 10_0000} + +config = { + "scotter_count_weights": { + "3": 7.26421894353717, + "4": 4.1724734692682395, + "5": 0.8119106579617028, + "6": 0.20313929837878217, + "7": 0.04857599989214818, + }, + "target_rtp": 85, + "values": { + "S": 0.5, + "A": 0.4, + "B": 0.6, + "C": 0.8, + "D": 1, + "E": 1.2, + "F": 1.4, + "G": 0.6, + }, + "weights": { + "A": 19.015957779792195, + "B": 21.291015318701493, + "C": 31.66660200727613, + "D": 35.193596023259865, + "E": 48.7122724047052, + "F": 64.49005324700025, + "G": 21.291015318701493, + "S": 2.6840958157151236, + }, + "iterators": [ + {"name": "fast", "iterations": 30, "spins": 10000}, + ], + "feature": "standard", +} + + +class SugarRushAutoTuner: + def __init__( + self, + target_rtp: float = 96.5, + values=None, + weights=None, + scotter_count_weights=None, + feature: str = "normal", + ): + self.target_rtp = target_rtp + self.rows = 7 + self.cols = 7 + self.bet = 1.0 + self.feature = feature + if scotter_count_weights: + self.scotter_count_weights = scotter_count_weights + # 符号定义及其价值(价值越高,对 RTP 影响越大) + self.symbols_config = values + + # 初始权重 + self.symbol_keys = list(self.symbols_config.keys()) + if weights is not None: + self.weights = weights + else: + self.weights = {k: 20.0 for k in self.symbol_keys} + + # 学习率 (控制调整速度,太小收敛慢,太大容易震荡) + self.learning_rate = 0.01 # 0.002 + + print(f"初始化自动调优器,feature:{feature} 目标 RTP: {self.target_rtp}%") + + def simulate_one_batch(self, spins: int = 20000): + begin_balance = 1_0000_0000 + game = SugarRush1000( + balance=begin_balance, + weights=self.weights, + scotter_counts_weights=self.scotter_count_weights, + ) + + total_bet = 0.0 + total_win = 0.0 + total_scotter = 0 + total_has_scotter = 0 + for _ in range(spins): + # 执行旋转 + res = game.doSpin() + if res["error"]: + break + + # 统计数据 + actual_cost = res["actual_bet"] + if actual_cost > 0: + total_bet += actual_cost + if res["is_scotter"]: + total_scotter += 1 + + has_scotter = res["is_scotter"] + if not has_scotter: + if has_scotter: + break + for row in res["grid"]: + for symbol in row: + if symbol == "S": + has_scotter = True + break + if has_scotter: + total_has_scotter += 1 + + total_win += res["win"] + + # 校验余额是否正确 + assert ( + f"{abs(begin_balance - game.balance):.2f}" + == f"{abs(total_bet - total_win):.2f}" + ) + print( + f"旋转{spins} 次,scotter {total_scotter}次, containe scotter {total_has_scotter}" + ) + return (total_win / total_bet) * 100 + + def simulate_free_batch(self, spins: int = 20000, buy_type: str = "standard"): + begin_balance = 1_0000_0000 + game = SugarRush1000( + balance=begin_balance, + weights=self.weights, + scotter_counts_weights=self.scotter_count_weights, + ) + + total_bet = 0.0 + total_win = 0.0 + + total_spins = 0 + total_free_spins = 0 + total_scotter = 0 + for _ in range(spins): + r = game.buy_free_spins(buy_type) + total_bet += r["cost"] + # 执行旋转 + score = 0 + can_spins = 1 + while can_spins > 0: + can_spins -= 1 + + total_spins += 1 + res = game.doSpin() + if res["error"]: + break + + # 统计数据 + score += res["win"] + total_win += res["win"] + if res["is_scotter"]: + total_scotter += 1 + total_free_spins += res["added_spins"] + if res["free_spins_remaining"] >= 0: + can_spins = res["free_spins_remaining"] + if score != res["spin_total_win"]: + print( + "total_win != res[spin_total_win]", total_win, res["spin_total_win"] + ) + assert score == res["spin_total_win"] + # 校验余额是否正确 + assert ( + f"{abs(begin_balance - game.balance):.2f}" + == f"{abs(total_bet - total_win):.2f}" + ) + + return (total_win / total_bet) * 100 + + def tune(self, iterations: int = 50, batch_spins: int = 20000): + """ + 迭代调整权重 + """ + history = [] + + for i in range(iterations): + # 1. 模拟当前 RTP + current_rtp = 0 + if self.feature == "normal": + current_rtp = self.simulate_one_batch(batch_spins) + else: + current_rtp = self.simulate_free_batch(batch_spins, self.feature) + + error = current_rtp - self.target_rtp + + history.append(current_rtp) + + print( + f"迭代 {i+1}/{iterations} | RTP: {current_rtp:.4f}% | 目标: {self.target_rtp}% | 误差: {error:+.4f}%" + ) + + # 2. 检查是否收敛 + if abs(error) < 3: + print(f"收敛成功!最终 RTP: {current_rtp:.4f}%") + break + + # 3. 动态调整权重 + # 调整策略:如果 RTP 太高,降低高价值符号的权重,提高低价值符号权重 + # 调整量 = 误差 * 学习率 * 符号价值系数 + # 防止初始误差过大导致权重崩塌 + safe_error = max(min(error, 5.0), -5.0) + adjustment_factor = safe_error * self.learning_rate + + # 防止权重变为负数 + min_weight = 0 + + for sym in self.symbol_keys: + value = self.symbols_config[sym] + + # 核心算法: + # 如果 RTP > Target (Error > 0),我们需要降低 RTP。 + # 对于高价值符号 (value大),我们需要减小其权重。 + # Adjustment 应该是负的。所以: - adjustment_factor * value + + # 如果 RTP < Target (Error < 0),我们需要提高 RTP。 + # 对于高价值符号,我们需要增加其权重。 + # Adjustment 应该是正的。所以: - adjustment_factor * value (因为error是负的,负负得正) + + delta = -adjustment_factor * value * self.weights[sym] + + # --- 优化:限制单次最大调整幅度 --- + # 防止某次调整幅度超过权重的 40%,给算法留点“喘息”空间 + max_change_ratio = 0.4 + if abs(delta) > self.weights[sym] * max_change_ratio: + delta = np.sign(delta) * self.weights[sym] * max_change_ratio + + new_weight = self.weights[sym] + delta + + # 限制最小权重,防止符号消失 + if new_weight < min_weight: + new_weight = min_weight + + if new_weight > 500: + new_weight = 500 + + self.weights[sym] = new_weight + + return self.weights, history + + +# --- 运行自动调优 --- +def train_weights(): + print("开始训练权重...") + # 设置随机种子 + seed = random.randint(1, 1000) + random.seed(seed) + np.random.seed(seed) + + print(f"随机种子: {seed}") + print(f"符号价值: ") + print(json.dumps(config["values"], indent=4, ensure_ascii=False)) + + tuner = SugarRushAutoTuner( + target_rtp=config["target_rtp"], + values=config["values"], + weights=config["weights"], + feature=config["feature"], + scotter_count_weights=config["scotter_count_weights"], + ) + + for t in config["iterators"]: + name, iterations, spins = t.values() + begin_weights = tuner.weights.copy() + print(f"# {name}调优") + final_weights, rtp_history = tuner.tune( + iterations=iterations, batch_spins=spins + ) + + print("\n=== 调优前的符号权重 ===") + for sym, w in begin_weights.items(): + print(f"{sym}: {w:.2f}") + print("\n 符号出现概率 (%) ===") + for sym, w in begin_weights.items(): + print(f"{sym}: {(w/sum(begin_weights.values()))*100:.2f}%") + + print("\n=== 最终调整后的符号权重 ===") + print(json.dumps(final_weights, indent=4, ensure_ascii=False)) + + # 将权重转换为概率百分比 + total_w = sum(final_weights.values()) + print("\n=== 符号出现概率 (%) ===") + for sym, w in final_weights.items(): + print(f"{sym}: {(w/total_w)*100:.2f}%") + + +def verify(): + print("开始进行权重校验:") + for i in range(10): + tuner = SugarRushAutoTuner( + target_rtp=config["target_rtp"], + values=config["values"], + weights={ + "A": 18.18442161117576, + "B": 19.994169181485578, + "C": 29.28572430711806, + "D": 32.143058029000244, + "E": 44.060855546850874, + "F": 57.93237542185442, + "G": 19.994169181485578, + "S": 2.5426663862665424, + }, + scotter_count_weights=config["scotter_count_weights"], + feature="super", + ) + print(f"第 {i+1}/10 次校验:") + tuner.tune(iterations=1, batch_spins=10000) + + +if __name__ == "__main__": + if len(sys.argv) >= 2: + verify() + else: + train_weights() diff --git a/pyground/sugar/SugarRushSimulator.py b/pyground/sugar/SugarRushSimulator.py new file mode 100644 index 0000000..6a6b9f1 --- /dev/null +++ b/pyground/sugar/SugarRushSimulator.py @@ -0,0 +1,173 @@ +import json +import random +from typing import List, Dict, Tuple, Optional +import time + +from SugarRush1000 import SugarRush1000 + +""" +此程序主要模拟用户使用过程中的体验指标 + +返奖率 RTP = 总赢 / 总投注 +中奖率/级联率 + 平均多久一次获取一次连线 = 带连线的旋转次数 / 总旋转次数 +免费旋转率 + 平均多久一次获取一次免费旋转 = 命中免费旋转次数 / 总旋转次数(不算免费旋转) + 平均免费旋转次数 = 总免费旋转次数 / 总旋转次数 +""" + + +class SugarRushSimulator: + + def __init__(self, buy_type: Optional[str] = None): + self.buy_type = buy_type + self.f = open( + f"simulator_log_{'normal' if buy_type is None else buy_type}_{time.time()}.log", + "w", + ) + + def simulate_batch(self, weights, scotter_counts_weights, spins: int) -> Dict: + """ + 模拟指定次数的旋转 + :param spins: 模拟次数 + :param buy_type: None=普通混合模式, 'standard'=只测买普通, 'super'=只测买超级 + :return: 统计结果 + """ + + begin_balance = 1_0000_0000 + game = SugarRush1000( + balance=begin_balance, + weights=weights, + scotter_counts_weights=scotter_counts_weights, + ) + + total_bet = 0.0 + total_win = 0.0 + game_spins = [] + for index in range(spins): + spin = { + "gid": index + 1, + "score": 0, + "count": 0, + "feature": "normal" if self.buy_type is None else self.buy_type, + "extra_free": 0, + "steps": [], + } + game_spins.append(spin) + + if self.buy_type and self.buy_type != "normal": + r = game.buy_free_spins(self.buy_type) + total_bet += r["cost"] + + can_spins = 1 + free_spind_id = 0 + aes = 0 + while can_spins > 0: + can_spins -= 1 + + res = game.doSpin() + game.mock_grid = None + if res["error"]: + raise Exception("模拟器异常", res["error"]) + if res["free_spins_remaining"] >= 0: + can_spins = res["free_spins_remaining"] + + actual_cost = res["actual_bet"] + if actual_cost > 0: + total_bet += actual_cost + + free_spind_id += 1 + + for step in res["steps"]: + step["gid"] = spin["gid"] + step["free_spin_id"] = free_spind_id + step["aes"] = aes + aes += 1 + spin["steps"].append(step) + if res["extra_free"]: + spin["extra_free"] += 1 + + spin["score"] = res["spin_total_win"] + spin["count"] = len(spin["steps"]) + + total_win += spin["score"] + + if len(game_spins) >= 100: + self.write_log(game_spins) + game_spins = [] + + if len(game_spins) > 0: + self.write_log(game_spins) + game_spins = [] + print(f"旋转{spins} 次,RTP {(total_win / total_bet) * 100}") + return {} + + def write_log(self, games): + buf = "" + for game in games: + buf += json.dumps(game, ensure_ascii=False) + "\n" + + self.f.write(buf) + self.f.flush() + + +if __name__ == "__main__": + scotter_count_weights = { + "3": 7.26421894353717, + "4": 4.1724734692682395, + "5": 0.8119106579617028, + "6": 0.20313929837878217, + "7": 0.04857599989214818, + } + batchs = [ + # { + # "buy_type": "normal", + # "spins": 20000, + # "weights": { + # "A": 16.559326336631095, + # "B": 17.242437197138468, + # "C": 23.794844051571708, + # "D": 24.480364769888073, + # "E": 31.29327382410323, + # "F": 38.17160528628571, + # "G": 17.242437197138468, + # "S": 2.2546844695944754, + # }, + # "scotter_count_weights": scotter_count_weights, + # }, + { + "buy_type": "standard", + "spins": 5000, + "weights": { + "A": 18.92167765262542, + "B": 21.106974838197058, + "C": 31.250953223681833, + "D": 34.54623003651251, + "E": 47.52175529241897, + "F": 62.474728710698884, + "G": 21.106974838197058, + "S": 2.666109542063759, + }, + "scotter_count_weights": scotter_count_weights, + }, + # { + # "buy_type": "super", + # "spins": 5000, + # "weights": { + # "A": 19.015957779792195, + # "B": 21.291015318701493, + # "C": 31.66660200727613, + # "D": 35.193596023259865, + # "E": 48.7122724047052, + # "F": 64.49005324700025, + # "G": 21.291015318701493, + # "S": 2.6840958157151236, + # }, + # "scotter_count_weights": scotter_count_weights, + # }, + ] + for batch in batchs: + sim = SugarRushSimulator(buy_type=batch["buy_type"]) + sim.simulate_batch( + batch["weights"], batch["scotter_count_weights"], batch["spins"] + ) diff --git a/pyground/sugar/SugarRushWebApiSimulator.py b/pyground/sugar/SugarRushWebApiSimulator.py new file mode 100644 index 0000000..c7ef455 --- /dev/null +++ b/pyground/sugar/SugarRushWebApiSimulator.py @@ -0,0 +1,112 @@ +import json +import random +from typing import List, Dict, Tuple, Optional +import time + +import requests + +""" +此程序主要模拟用户真实使用过程中的RTP值 + +返奖率 RTP = 总赢 / 总投注 +""" + + +class SugarRushSimulator: + def __init__(self): + self.session = requests.Session() + self.headers = { + 'User-Agent': 'Mozilla/5.0 (Compatible; LongConnectionClient/1.0)', + 'Connection': 'keep-alive' # 显式告诉服务器保持连接 + } + response = self.session.post("http://localhost:8081/api/login", json={'username': '19156010758', 'password': '123456'}) + data = self.do_result(response) + if data is not None: + self.session.headers.update({'Authorization': f"Bearer {data['token']}"}) + + def do_result(self, response): + if response.status_code == 200: + resp = json.loads(response.text) + if resp['code'] == 200: + if 'data' in resp: + return resp['data'] + else: + return resp + else: + print(f"发生错误: {resp}") + return None + else: + print(f"请求发生错误: {response.status_code}") + + + def buy_free_spins(self,bet, buy_type: str): + response = self.session.get(f"http://localhost:8081/api/sugar/buy_free_spins?bet={bet}&kind={buy_type}", headers=self.headers, timeout=(5, 10)) + return self.do_result(response) + + def do_spin(self, bet): + response = self.session.get(f"http://localhost:8081/api/sugar/dospin?bet={bet}", headers=self.headers, timeout=(5, 10)) + return self.do_result(response) + + def simulate_batch(self, spins: int, buy_type: Optional[str] = None) -> Dict: + """ + 模拟指定次数的旋转 + :param spins: 模拟次数 + :param buy_type: None=普通混合模式, 'standard'=只测买普通, 'super'=只测买超级 + :return: 统计结果 + """ + + bet = 1 + total_bet = 0.0 + total_win = 0.0 + for _ in range(spins): + + if buy_type and buy_type != "normal": + r = self.buy_free_spins(bet, buy_type) + if r: + total_bet += float(r.get("cost", 0)) + else: + total_bet += bet + + spin_win = 0 + can_spins = True + while can_spins > 0: + + res = self.do_spin(bet) + if len(res['data']) == 0: + can_spins = False + else: + spin_win = res['data'][-1]['tw'] + + total_win += float(spin_win) + + print(f"旋转{spins} 次, 总下注 {total_bet}, 总盈利 {total_win}, RTP {(total_win / total_bet) * 100}") + return {} + + + +if __name__ == "__main__": + + batchs = [ + # { + # "buy_type": "normal", + # "spins": 100, + # }, + # { + # "buy_type": "standard", + # "spins": 20, + # }, + # { + # "buy_type": "super", + # "spins": 20, + # }, + ] + for batch in batchs: + sim = SugarRushSimulator() + start_time = time.time() # 记录开始时间 + sim.simulate_batch( + batch["spins"], + batch['buy_type'] + ) + end_time = time.time() # 记录结束时间 + elapsed_time = end_time - start_time + print(f"Batch {batch['buy_type']} took {elapsed_time:.4f} seconds") # 打印耗时 diff --git a/pyground/sugar/TestSugarRush1000.py b/pyground/sugar/TestSugarRush1000.py new file mode 100644 index 0000000..f7d19ee --- /dev/null +++ b/pyground/sugar/TestSugarRush1000.py @@ -0,0 +1,120 @@ +import unittest + +from SugarRush1000 import SugarRush1000 + +def no_cluster_grid(): + return [ + ( + ["B" if col % 2 == 0 else "E" for col in range(7)] + if row % 2 == 0 + else ["E" if col % 2 == 0 else "B" for col in range(7)] + ) + for row in range(7) + ] + +def print_mid_result(tag, res): + print(f"\n======={tag}=========") + for index, grid in enumerate(res["middle_grids"]): + print(f"step{index+1}---") + print(f"倍数点数") + print(" ",end="") + for i in range(7): + print(f"{i} ",end=" ") + print() + for i,row in enumerate(res["middle_multipliers"][index]): + print(f"{i} {row}") + print(f"消除符号") + for symbol in res["middle_grid_remove_symbols"][index]: + print(symbol) + print(f"score:{res['middle_score'][index]}") + for row in grid: + print(row) + + +class TestSugarRushLogic(unittest.TestCase): + def setUp(self): + self.bet = 1.0 + + def test_basic_cascade_and_drop(self): + # 0. 准备数据:构造一个即将消除的场景 + # 假设中间一行全是 'A',且数量 >=5 + # 为了演示,我们假设 rows=7, cols=7,手动构造特定列 + + # 构造一个特定网格:第3行全是 A (7个),其他是 B + custom_grid = no_cluster_grid() + custom_grid[3] = ["A"] * 7 + + + game = SugarRush1000(bet=self.bet, mock_grid=custom_grid) + game.drop_sequence = ["C", "C", "C", "C", "A", "A", "A"] # 预设掉落全是 C + + # 1. 执行 Spin + result = game.doSpin() + + # 2. 断言验证 + self.assertEqual(result["win"], 1.75) + + # 验证网格状态:第3行的 A 应该消失,顶部的 B 应该掉下来,顶部变成 C + # 原第3行变空,上方的第0,1,2行掉落填补,顶部第0行填入新符号 C + # 所以最终第0行应该是 C,第3行应该是原来的第2行 (B) + self.assertEqual(result["grid"][0][0], "C") # 顶部掉入了新符号 + self.assertEqual(result["grid"][3][0], "B") # 原本上面的 B 掉下来了 + + print("✅ 测试通过:基础翻转与掉落逻辑正常") + + def test_multiplier_mechanic(self): + # 准备数据:测试乘数翻倍 + custom_grid = no_cluster_grid() + custom_grid[0] = ["A"] * 7 # 设置一个乘数点 + + game = SugarRush1000(bet=self.bet, mock_grid=custom_grid) + game.drop_sequence = ["C", "C", "C", "C", "C", "C", "C", + "C", "C", "C", "C", "C", "C", "C", + "C", "A", "B", "C", "D", "A", "B"] # 预设掉落全是 C + result = game.doSpin() + + self.assertEqual(result["middle_multipliers"][3][0][0], 4) + self.assertEqual(result["win"], 16.75) + print("✅ 测试通过:乘数翻倍逻辑正常") + + def test_scatter_trigger(self): + # 0. 准备数据:放置 3 个 Scatter + custom_grid = no_cluster_grid() + custom_grid[0][0] = "S" + custom_grid[1][1] = "S" + custom_grid[2][2] = "S" + + game = SugarRush1000(bet=self.bet, mock_grid=custom_grid) + result = game.doSpin() + + # 1. 断言 + self.assertEqual(result["free_spins_remaining"], 10) # 3个 Scatter 应得 10 次 + self.assertTrue("触发免费旋转" in result["message"]) + + print("✅ 测试通过:Scatter 触发逻辑正常") + + def test_max_win_cap(self): + # 0. 准备数据:构造一个巨大的赢利场景 + # 比如很多高价值符号,且带有高倍乘数 + custom_grid = no_cluster_grid() + custom_grid[0] = ["A"] * 7 # 一行A + custom_grid[1][0] = "S" # 10次免费旋转,但是达到上限后应该清0 + custom_grid[2][1] = "S" + custom_grid[3][2] = "S" + game = SugarRush1000(bet=self.bet, mock_grid=custom_grid) + game.drop_sequence = ["A" for _ in range(7) for _ in range(11)] + + result = game.doSpin() + + # 1. 断言 + max_win = 25000 * self.bet + # 即使全屏超高赔率,赢利也不能超过 25000 + self.assertLessEqual(result["win"], max_win) + if result["win"] >= max_win: + self.assertEqual(result["free_spins_remaining"], 0) # 达到上限应结束 + + print("✅ 测试通过:最大赢奖上限逻辑正常") + + +if __name__ == "__main__": + unittest.main(argv=["first-arg-is-ignored"], exit=False) diff --git a/pyground/sugar/TestSugarRushCascadeAndDrop.py b/pyground/sugar/TestSugarRushCascadeAndDrop.py new file mode 100644 index 0000000..a7c2ba6 --- /dev/null +++ b/pyground/sugar/TestSugarRushCascadeAndDrop.py @@ -0,0 +1,114 @@ +import random +import unittest + +from SugarRush1000 import SugarRush1000 + + +def no_cluster_grid(): + return [ + ( + ["B" if col % 2 == 0 else "E" for col in range(7)] + if row % 2 == 0 + else ["E" if col % 2 == 0 else "B" for col in range(7)] + ) + for row in range(7) + ] + + +def print_mid_result(tag, res): + print(f"\n======={tag}=========") + for index, step in enumerate(res["steps"]): + print(f"step{index+1}---") + print(f"倍数点数 {step['multipler']}") + print(f"消除符号") + for symbol in step["symbol_links"]: + print(symbol) + + print(f"score:{step['score']} ") + for index, row in enumerate(step["grid"]): + if index % 7 == 0 and index != 0: + print() + print(row, end=" ") + print() + + +class TestSugarRushLogic(unittest.TestCase): + def setUp(self): + self.bet = 1.0 + + def test_row_cascade_and_drop(self): + # 0. 准备数据:构造一个即将消除的场景 + custom_grid = no_cluster_grid() + custom_grid[3] = ["A"] * 7 + + # 假设 A 的赔付是 1.0 + game = SugarRush1000(bet=self.bet, mock_grid=custom_grid) + game.drop_sequence = ["C", "C", "C", "C", "A", "A", "A"] # 预设掉落全是 C + + # 1. 执行 Spin + result = game.doSpin() + + # print_mid_result("test_row_cascade_and_drop", result) + + # 2. 断言验证 + self.assertEqual(result["win"], 1.75) + + self.assertEqual(result["grid"][0][0], "C") # 顶部掉入了新符号 + self.assertEqual(result["grid"][3][0], "B") # 原本上面的 B 掉下来了 + + print("✅ 测试通过:基础翻转与行掉落逻辑正常") + + def test_col_cascade_and_drop(self): + # 0. 准备数据:构造一个即将消除的场景 + custom_grid = no_cluster_grid() + for row in range(7): + custom_grid[row][3] = "A" + + # 假设 A 的赔付是 1.0 + game = SugarRush1000(bet=self.bet, mock_grid=custom_grid) + game.drop_sequence = ["C", "C", "C", "C", "A", "A", "A"] # 预设掉落全是 C + + # 1. 执行 Spin + result = game.doSpin() + + # print_mid_result("test_row_cascade_and_drop", result) + + # 2. 断言验证 + self.assertEqual(result["win"], 1.75) + + self.assertEqual(result["grid"][0][3], "C") + self.assertEqual(result["grid"][1][3], "C") + self.assertEqual(result["grid"][2][3], "C") + self.assertEqual(result["grid"][3][3], "C") + self.assertEqual(result["grid"][4][3], "A") + self.assertEqual(result["grid"][5][3], "A") + self.assertEqual(result["grid"][6][3], "A") + + print("✅ 测试通过:基础翻转与列掉落逻辑正常") + + def test_row_col_cascade_and_drop(self): + random.seed(42) + # 0. 准备数据:构造一个即将消除的场景 + custom_grid = no_cluster_grid() + for row in range(3): + custom_grid[row + 2][3] = "A" + for col in range(3): + custom_grid[3][2 + col] = "A" + + # 假设 A 的赔付是 1.0 + game = SugarRush1000(bet=self.bet, mock_grid=custom_grid) + game.drop_sequence = ["C", "C", "C", "C", "A"] # 预设掉落全是 C + + # 1. 执行 Spin + result = game.doSpin() + + # print_mid_result("test_row_col_cascade_and_drop", result) + + # 2. 断言验证 + self.assertEqual(result["win"], 19.1) + + print("✅ 测试通过:基础翻转与行列掉落逻辑正常") + + +if __name__ == "__main__": + unittest.main(argv=["first-arg-is-ignored"], exit=False) diff --git a/pyground/sugar/TestSugarRushFreeSpin.py b/pyground/sugar/TestSugarRushFreeSpin.py new file mode 100644 index 0000000..70919dd --- /dev/null +++ b/pyground/sugar/TestSugarRushFreeSpin.py @@ -0,0 +1,126 @@ +import random +import unittest + +from SugarRush1000 import SugarRush1000 + + +def no_cluster_grid(): + return [ + ( + ["B" if col % 2 == 0 else "E" for col in range(7)] + if row % 2 == 0 + else ["E" if col % 2 == 0 else "B" for col in range(7)] + ) + for row in range(7) + ] + + +def print_mid_result(tag, res): + print(f"\n======={tag}=========") + for index, grid in enumerate(res["middle_grids"]): + print(f"step{index+1}---") + print(f"倍数点数") + print(" ",end="") + for i in range(7): + print(f"{i} ",end=" ") + print() + for i,row in enumerate(res["middle_multipliers"][index]): + print(f"{i} {row}") + print(f"消除符号") + for symbol in res["middle_grid_remove_symbols"][index]: + print(symbol) + print(f"score:{res['middle_score'][index]}") + for row in grid: + print(row) + + +class TestSugarRushLogic(unittest.TestCase): + def setUp(self): + self.bet = 1.0 + + def test_normal_free_spin_s3(self): + # 0. 准备数据:构造一个即将消除的场景 + custom_grid = no_cluster_grid() + custom_grid[0][0] = "S" + custom_grid[1][1] = "S" + custom_grid[2][2] = "S" + + game = SugarRush1000(balance=1000, bet=self.bet, mock_grid=custom_grid) + drop_symbol = [] + for i in range(20): + grids = no_cluster_grid() + [drop_symbol.append(grids[r][c]) for c in range(7) for r in range(7)] + game.drop_sequence = drop_symbol + + # 1. 触发freeSpin + result = game.doSpin() + game.mock_grid = None + self.assertEqual(result["balance"], 999) + self.assertEqual(result["free_spins_remaining"], 10) + + # 2. 使用freeSpin + free_spins = result["free_spins_remaining"] + for i in range(free_spins): + result = game.doSpin() + self.assertEqual(result["balance"], 999) + self.assertEqual(result["free_spins_remaining"], free_spins - i - 1) + + # 3. 回归正常spin + result = game.doSpin() + self.assertEqual(result["balance"], 998) + self.assertEqual(result["free_spins_remaining"], 0) + + print("✅ 测试通过:普通freeSpin正常") + + def test_buy_standard_free_spin(self): + custom_grid = no_cluster_grid() + balance = 1000 + game = SugarRush1000(balance=balance, bet=self.bet, mock_grid=custom_grid) + + # 1. 购买 + result = game.buy_free_spins("standard") + self.assertEqual(result["balance"], balance - 100 * game.bet) + self.assertEqual(result["is_buy_free"], True) + self.assertEqual(result["is_super_free"], False) + self.assertEqual(game.free_spins, 0) + + # 2. 使用 + result = game.doSpin() + self.assertGreaterEqual(game.free_spins, 10) + self.assertEqual(game.is_buy_free, False) + self.assertEqual(game.is_super_free, False) + self.assertEqual(game.multipliers[0][0], 0) + + print("✅ 测试通过:购买普通免费旋转正常") + + def test_buy_super_free_spin(self): + custom_grid = no_cluster_grid() + balance = 1000 + game = SugarRush1000(balance=balance, bet=self.bet, mock_grid=custom_grid) + + # 1. 购买 + result = game.buy_free_spins("super") + self.assertEqual(result["balance"], balance - 500 * game.bet) + self.assertEqual(result["is_buy_free"], True) + self.assertEqual(result["is_super_free"], True) + self.assertEqual(game.free_spins, 0) + + # 2. 使用 + result = game.doSpin() + self.assertGreater(game.free_spins, 10) + self.assertEqual(game.is_buy_free, False) + self.assertEqual(game.is_super_free, True) + self.assertEqual(game.multipliers[0][0], 0) + + # 进入super free spin + result = game.doSpin() + self.assertGreater(game.free_spins, 9) + self.assertEqual(game.is_buy_free, False) + self.assertEqual(game.is_super_free, False) + self.assertEqual(result["steps"][0]["multipler"][1], 2) + + print("✅ 测试通过:购买超级免费旋转正常") + + +if __name__ == "__main__": + unittest.main(argv=["first-arg-is-ignored"], exit=False) diff --git a/pyground/sugar/TestSugarRushGrid.py b/pyground/sugar/TestSugarRushGrid.py new file mode 100644 index 0000000..fda425f --- /dev/null +++ b/pyground/sugar/TestSugarRushGrid.py @@ -0,0 +1,46 @@ +import unittest + +from SugarRush1000 import SugarRush1000 + +def checkGrid(grid): + total_scotter_count = 0 + for col in range(7): + scotter_count = 0 + for row in range(7): + if grid[row][col] == "S": + scotter_count += 1 + total_scotter_count += 1 + if scotter_count > 1: + print(f"col{col} has {scotter_count} scotter") + return False + if total_scotter_count > 7: + print(f"total scotter count {total_scotter_count} > 7") + return False + + return True + +class TestSugarRushLogic(unittest.TestCase): + + def test_scotter_rule(self): + game = SugarRush1000(balance=100000, bet=1) + + # 1. 触发 + for _ in range(10000): + result = game.doSpin() + assert checkGrid(result['grid']) + + + print("✅ 测试通过:生成scotter规则正常") + + def test_free_spin_scotter_rule(self): + game = SugarRush1000(balance=100000, bet=1) + for _ in range(10000): + game.buy_free_spins('standard') + result = game.doSpin() + assert checkGrid(result['grid']) + print("✅ 测试通过:免费旋转scotter规则正常") + + + +if __name__ == "__main__": + unittest.main(argv=["first-arg-is-ignored"], exit=False) diff --git a/pyground/sugar/TestSugarRushMultipler.py b/pyground/sugar/TestSugarRushMultipler.py new file mode 100644 index 0000000..db0d77e --- /dev/null +++ b/pyground/sugar/TestSugarRushMultipler.py @@ -0,0 +1,3 @@ +""" +测试免费旋转过程中,同个位置的倍数应该是连续增长的 +""" \ No newline at end of file diff --git a/pyground/sugar/TestSugarRushSameSymbol.py b/pyground/sugar/TestSugarRushSameSymbol.py new file mode 100644 index 0000000..379efaf --- /dev/null +++ b/pyground/sugar/TestSugarRushSameSymbol.py @@ -0,0 +1,51 @@ +import unittest + +from SugarRush1000 import SugarRush1000 + +def checkGrid(grid): + total_scotter_count = 0 + for col in range(7): + scotter_count = 0 + for row in range(7): + if grid[row][col] == "S": + scotter_count += 1 + total_scotter_count += 1 + if scotter_count > 1: + print(f"col{col} has {scotter_count} scotter") + return False + if total_scotter_count > 7: + print(f"total scotter count {total_scotter_count} > 7") + return False + + return True + +class TestSugarRushLogic(unittest.TestCase): + + def test_same_symbol_clusters(self): + mock_grid = [ + ['A','E','E','A','B','A','F'], + ['E','E','E','B','C','D','E'], + ['E','E','S','C','A','A','A'], + ['A','B','A','D','E','B','B'], + ['B','E','E','A','E','E','A'], + ['E','C','D','E','B','E','B'], + ['A','F','E','A','A','E','A'], + ] + game = SugarRush1000(balance=100000, bet=1, mock_grid=mock_grid) + + # 1. 触发 + import json + result = game.doSpin() + # print(json.dumps(result)) + assert len(result['steps'][0]['symbol_links']) == 2 + assert result['steps'][0]['symbol_links'][0]['symbol'] == 'E' + assert result['steps'][0]['symbol_links'][1]['symbol'] == 'E' + + + + print("✅ 测试通过:同个符号多个cluster消除正常") + + + +if __name__ == "__main__": + unittest.main(argv=["first-arg-is-ignored"], exit=False) diff --git a/pyground/sugar/symbol_pay_table.py b/pyground/sugar/symbol_pay_table.py new file mode 100644 index 0000000..1fc6750 --- /dev/null +++ b/pyground/sugar/symbol_pay_table.py @@ -0,0 +1,137 @@ + + +symbol_pay_table = { + "A": { + "max_cascade": 15, + "15+": 150.0, + "14": 70.0, + "13": 35.0, + "12": 15.0, + "11": 7.5, + "10": 5.0, + "9": 2.5, + "8": 2.0, + "7": 1.75, + "6": 1.5, + "5": 1.0, + }, + "B": { + "max_cascade": 15, + "15+": 100.0, + "14": 60.0, + "13": 30.0, + "12": 12.5, + "11": 6.0, + "10": 4.0, + "9": 2.0, + "8": 1.5, + "7": 1.25, + "6": 1.0, + "5": 0.75, + }, + "C": { + "max_cascade": 15, + "15+": 60.0, + "14": 40.0, + "13": 20.0, + "12": 10.0, + "11": 4.5, + "10": 3.0, + "9": 1.5, + "8": 1.25, + "7": 1.0, + "6": 0.75, + "5": 0.5, + }, + "D": { + "max_cascade": 15, + "15+": 40.0, + "14": 20.0, + "13": 10.0, + "12": 5.0, + "11": 3.0, + "10": 2.0, + "9": 1.25, + "8": 1.0, + "7": 0.75, + "6": 0.5, + "5": 0.4, + }, + "E": { + "max_cascade": 15, + "15+": 30.0, + "14": 15.0, + "13": 8.0, + "12": 3.5, + "11": 2.5, + "10": 1.5, + "9": 1.0, + "8": 0.75, + "7": 0.5, + "6": 0.4, + "5": 0.3, + }, + "F": { + "max_cascade": 15, + "15+": 25.0, + "14": 12.0, + "13": 6.0, + "12": 3.0, + "11": 2.0, + "10": 1.25, + "9": 0.75, + "8": 0.5, + "7": 0.4, + "6": 0.3, + "5": 0.25, + }, + "G": { + "max_cascade": 15, + "15+": 20.0, + "14": 10.0, + "13": 5.0, + "12": 2.5, + "11": 1.5, + "10": 1.0, + "9": 0.5, + "8": 0.4, + "7": 0.3, + "6": 0.25, + "5": 0.2, + }, +} + + +def get_symbol_pay(symbol: str, cluster_size: int) -> float: + if symbol not in symbol_pay_table: + raise ValueError(f"Unknown symbol: {symbol}") + + """根据符号和连锁大小获取赔付倍数""" + if cluster_size >= symbol_pay_table[symbol]["max_cascade"]: + return symbol_pay_table[symbol][f'{symbol_pay_table[symbol]["max_cascade"]}+'] + elif str(cluster_size) in symbol_pay_table[symbol]: + return symbol_pay_table[symbol][str(cluster_size)] + else: + return 0.0 + + +if __name__ == "__main__": + reward = set() + for symbol in symbol_pay_table: + for key in symbol_pay_table[symbol]: + if key != "max_cascade": + reward.add(symbol_pay_table[symbol][key]) + reward = list(reward) + sorted_reward = sorted(reward) + print(sorted_reward) + + max_score=2000 + + a = 0 + b=0 + c=0 + max_a = max_score/0.2 + max_b = max_score/0.25 + max_c = max_score/0.3 + print(f"{max_a}*{max_b}*{max_c}/1000000={max_a*max_b*max_c/1000000}") + # avaiable_reward = a*0.2 + b*0.25 + c*0.3 diff --git a/pyground/sugar/symbol_tool.py b/pyground/sugar/symbol_tool.py new file mode 100644 index 0000000..346124b --- /dev/null +++ b/pyground/sugar/symbol_tool.py @@ -0,0 +1,40 @@ +symbol_pay_table = """ +15+|3000|2000|1200|800|600|500|400 +14|1400|1200|800|400|300|240|200 +13|700|600|400|200|160|120|100 +12|300|250|200|100|70|60|50 +11|150|120|90|60|50|40|30 +10|100|80|60|40|30|25|20 +9|50|40|30|25|20|15|10 +8|40|30|25|20|15|10|8 +7|35|25|20|15|10|8|6 +6|30|20|15|10|8|6|5 +5|20|15|10|8|6|5|4 +""" + +cascade = [line for line in symbol_pay_table.split("\n") if line.strip()] +cascade = [[item for item in line.split("|") if item.strip() ] for line in cascade] + +cols = len(cascade[0]) - 1 + +base_bet = 20 +mapping = {0:'A',1:'B', 2:'C', 3:'D', 4:'E', 5:'F', 6:'G'} +result = {} +for c in range(cols): + result[mapping[c]] = {} + for pay in cascade: + if '+' in pay[0]: + result[mapping[c]]['max_cascade'] = int(pay[0][0:-1]) + result[mapping[c]][pay[0]] = int(pay[c+1]) / 20 + # print(f"symbol{c} cas{pay[0]} pay:{pay[c+1]}") + +import json +print(json.dumps(result, indent=2, ensure_ascii=False)) + + +# 反计算回来校验 +# for symbol in result: +# for key in result[symbol]: +# if key != 'max_cascade': +# print(f"{result[symbol][key] * base_bet}", end="\t") +# print() diff --git a/pyground/sugar/targetScoreGenerator.py b/pyground/sugar/targetScoreGenerator.py new file mode 100644 index 0000000..ecffbde --- /dev/null +++ b/pyground/sugar/targetScoreGenerator.py @@ -0,0 +1,218 @@ +from time import time + +import json +import random +from typing import List, Dict, Tuple, Optional +import time + +from SugarRush1000 import SugarRush1000 + + +class SugarRushSimulator: + + def __init__(self, buy_type: Optional[str] = None): + self.buy_type = buy_type + self.f = open( + f"simulator_log_{'normal' if buy_type is None else buy_type}_{time.time()}.log", + "w", + ) + + def check_target_score(self, score, target_scores): + for i in range(len(target_scores)): + if score[i] < target_scores[i][2]: + return False + return True + + def getIndex(self, score, target_scores): + for i in range(len(target_scores)): + if score > target_scores[i][0] and score <= target_scores[i][1]: + return i + return -1 + + def simulate_batch( + self, weights, scotter_counts_weights, target_scores: List[Tuple[int, int, int]] + ) -> Dict: + """ + 模拟指定次数的旋转 + :param target_scores: 模拟目标分数范围 + :param buy_type: None=普通混合模式, 'standard'=只测买普通, 'super'=只测买超级 + :return: 统计结果 + """ + + begin_balance = 1_0000_0000 + game = SugarRush1000( + balance=begin_balance, + weights=weights, + scotter_counts_weights=scotter_counts_weights, + ) + + total_bet = 0.0 + total_win = 0.0 + game_spins = [] + index = 0 + real_score_counts = [0 for _ in target_scores] + while True: + if self.check_target_score(real_score_counts, target_scores): + break + + spin = { + "gid": index + 1, + "score": 0, + "count": 0, + "feature": "normal" if self.buy_type is None else self.buy_type, + "extra_free": 0, + "steps": [], + } + + bet = 0 + if self.buy_type and self.buy_type != "normal": + r = game.buy_free_spins(self.buy_type) + bet = r["cost"] + + can_spins = 1 + free_spind_id = 0 + aes = 0 + while can_spins > 0: + can_spins -= 1 + + res = game.doSpin() + game.mock_grid = None + if res["error"]: + raise Exception("模拟器异常", res["error"]) + if res["free_spins_remaining"] >= 0: + can_spins = res["free_spins_remaining"] + + actual_cost = res["actual_bet"] + if actual_cost > 0: + bet = actual_cost + + free_spind_id += 1 + + for step in res["steps"]: + step["gid"] = spin["gid"] + step["free_spin_id"] = free_spind_id + step["aes"] = aes + aes += 1 + spin["steps"].append(step) + if res["extra_free"]: + spin["extra_free"] += 1 + + spin["score"] = res["spin_total_win"] + spin["count"] = len(spin["steps"]) + + range_index = self.getIndex(spin["score"], target_scores) + if ( + range_index < 0 + or real_score_counts[range_index] >= target_scores[range_index][2] + ): + continue + + real_score_counts[range_index] += 1 + + total_win += spin["score"] + total_bet += bet + + game_spins.append(spin) + if len(game_spins) >= 100: + self.write_log(game_spins) + game_spins = [] + + index += 1 + + if len(game_spins) > 0: + self.write_log(game_spins) + game_spins = [] + print(f"旋转{index} 次,RTP {(total_win / total_bet) * 100}") + return {} + + def write_log(self, games): + buf = "" + for game in games: + buf += json.dumps(game, ensure_ascii=False) + "\n" + + self.f.write(buf) + self.f.flush() + + +if __name__ == "__main__": + + scotter_count_weights = { + "3": 7.26421894353717, + "4": 4.1724734692682395, + "5": 0.8119106579617028, + "6": 0.20313929837878217, + "7": 0.04857599989214818, + } + batchs = [ + # { + # "buy_type": "normal", + # "weights": { + # "A": 16.559326336631095, + # "B": 17.242437197138468, + # "C": 23.794844051571708, + # "D": 24.480364769888073, + # "E": 31.29327382410323, + # "F": 38.17160528628571, + # "G": 17.242437197138468, + # "S": 2.2546844695944754, + # }, + # "target_score": [(0,1, 500), + # (1,5, 500), + # (5,10, 500), + # (10,20, 500)], + # "scotter_count_weights": scotter_count_weights, + # }, + # { + # "buy_type": "standard", + # "weights": { + # "A": 18.92167765262542, + # "B": 21.106974838197058, + # "C": 31.250953223681833, + # "D": 34.54623003651251, + # "E": 47.52175529241897, + # "F": 62.474728710698884, + # "G": 21.106974838197058, + # "S": 2.666109542063759, + # }, + # "scotter_count_weights": scotter_count_weights, + # "target_score": [(20, 30, 500), + # (30, 50, 500), + # (50, 100, 500), + # (100, 200, 500), + # (200, 300, 500), + # (300, 400, 500), + # (400, 500, 500), + # ], + # }, + { + "buy_type": "super", + "weights": { + "A": 19.015957779792195, + "B": 21.291015318701493, + "C": 31.66660200727613, + "D": 35.193596023259865, + "E": 48.7122724047052, + "F": 64.49005324700025, + "G": 21.291015318701493, + "S": 2.6840958157151236, + }, + "scotter_count_weights": scotter_count_weights, + "target_score": [ + (80, 120, 500), + (120, 200, 500), + (200, 400, 500), + (400, 600, 500), + (800, 1000, 500), + (1000, 1200, 500), + (1200, 1400, 200), + (1400, 1600, 200), + (1600, 1800, 200), + (1800, 2000, 200), + ], + }, + ] + for batch in batchs: + sim = SugarRushSimulator(buy_type=batch["buy_type"]) + sim.simulate_batch( + batch["weights"], batch["scotter_count_weights"], batch["target_score"] + ) diff --git a/pyground/sugar/write_sql.py b/pyground/sugar/write_sql.py new file mode 100644 index 0000000..c4a981c --- /dev/null +++ b/pyground/sugar/write_sql.py @@ -0,0 +1,161 @@ +# 假设有一个数据库辅助类,你需要根据实际环境实现 +import json +import pymysql + + +class DB: + def __init__(self, host, user, password, database, port=3306, charset="utf8mb4"): + """ + 初始化数据库连接 + """ + self.connection = pymysql.connect( + host=host, + user=user, + password=password, + database=database, + port=port, + charset=charset, + cursorclass=pymysql.cursors.DictCursor, + ) + self.last_insert_id = None + + def _execute(self, sql, params=None): + """ + 内部方法:执行SQL并返回cursor + """ + try: + with self.connection.cursor() as cursor: + cursor.execute(sql, params or ()) + return cursor + except Exception as e: + print(f"SQL执行错误: {e}\nSQL: {sql}") + raise + + def getOne(self, sql): + # 模拟查询一行 + cursor = self._execute(sql) + return cursor.fetchone() + + def getone(self, sql): + # PHP代码中混用了getOne和getone,通常功能一致 + return self.getOne(sql) + + def query(self, sql): + """ + 执行SQL (通常用于无返回结果的操作,如UPDATE, DELETE, CREATE等) + """ + try: + with self.connection.cursor() as cursor: + result = cursor.execute(sql) + self.connection.commit() + # 记录最后插入的ID (如果是INSERT操作) + self.last_insert_id = cursor.lastrowid + return result + except Exception as e: + self.connection.rollback() + print(f"Query执行失败: {e}\nSQL: {sql}") + raise + + def query_array(self, sql): + """ + 查询多行 + """ + cursor = self._execute(sql) + return cursor.fetchall() + + def insert_array(self, data, table): + """ + 插入数据 + :param data: 字典,例如 [{'name': 'Alice', 'age': 25}] + :param table: 表名 + """ + if not data: + return False + + columns = ", ".join(data[0].keys()) + # 使用占位符防止SQL注入 + placeholders = ", ".join(["%s"] * len(data[0])) + values = [tuple(item.values()) for item in data] + + sql = f"INSERT INTO {table} ({columns}) VALUES ({placeholders})" + + try: + with self.connection.cursor() as cursor: + cursor.executemany(sql, values) + self.connection.commit() + self.last_insert_id = cursor.lastrowid + return self.last_insert_id + except Exception as e: + self.connection.rollback() + print(f"插入数据失败: {e}") + raise + + def insert_id(self): + """ + 获取最后插入ID + """ + return self.last_insert_id + + def close(self): + """ + 关闭数据库连接 + """ + if self.connection: + self.connection.close() + + +db = DB(host="localhost", user="fire", password="iTSJSPPZM3LSGAPC", database="fire") + +file_name = "simulator_log_super_1776010009.7211218.log" + + +with open(file_name, "r") as f: + type = "" + if "super" in file_name: + type = "_super" + elif "normal" in file_name: + type = "" + elif "standard" in file_name: + type = "_free" + + # db.query("truncate table game_sugar_spin{type}".format(type=type)) + # db.query("truncate table game_sugar_step_info{type}".format(type=type)) + + spins = [] + steps_data = [] + index = 0 + startGid = 10000 + for line in f.readlines(): + index += 1 + data = json.loads(line) + + data["gid"] = startGid + data['gid'] + + steps = data["steps"] + for step in steps: + step["gid"] = data["gid"] + step["symbol_links"] = json.dumps( + step["symbol_links"], separators=(",", ":"), ensure_ascii=False + ) + step["multipler"] = json.dumps( + step["multipler"], separators=(",", ":"), ensure_ascii=False + ) + steps_data.append(step) + + del data["steps"] + spins.append(data) + + if len(spins) > 100: + db.insert_array(spins, f"game_sugar_spin{type}") + spins = [] + print(f"写入进度: {index}个spin") + if len(steps_data) > 300: + db.insert_array(steps_data, f"game_sugar_step_info{type}") + steps_data = [] + + if len(spins) > 0: + db.insert_array(spins, f"game_sugar_spin{type}") + spins = [] + if len(steps_data) > 0: + db.insert_array(steps_data, f"game_sugar_step_info{type}") + steps_data = [] diff --git a/pyground/sugar/统计.py b/pyground/sugar/统计.py new file mode 100644 index 0000000..1f408e3 --- /dev/null +++ b/pyground/sugar/统计.py @@ -0,0 +1,167 @@ +import pandas as pd +import matplotlib.pyplot as plt +import logging +import sys +from datetime import datetime +import pymysql + +# ================= 配置部分 ================= +# 日志配置 +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s", + handlers=[ + logging.StreamHandler(sys.stdout), + logging.FileHandler( + f'game_stats_{datetime.now().strftime("%Y%m%d")}.log', encoding="utf-8" + ), + ], +) + +# 数据库连接配置 (请根据实际情况修改) +DB_CONFIG = { + "host": "localhost", + "user": "root", + "password": "123456", + "database": "fire", + "charset": "utf8mb4", +} + +# ================= 核心逻辑 ================= + + +def get_db_connection(): + """获取数据库连接""" + try: + conn = pymysql.connect(**DB_CONFIG) + logging.info("数据库连接成功") + return conn + except Exception as e: + logging.error(f"数据库连接失败: {e}") + raise + + +def fetch_data(conn, type): + """从数据库获取数据""" + query = f"SELECT gid, score, feature, extra_free, create_time FROM game_sugar_spin{type}" + try: + logging.info(f"正在执行查询: {query}") + df = pd.read_sql(query, conn) + logging.info(f"数据加载完成,共 {len(df)} 行") + return df + except Exception as e: + logging.error(f"数据查询失败: {e}") + raise + + +def analyze_data(df): + """使用 Pandas 进行统计分析""" + logging.info("开始进行统计分析...") + + # 1. 基础统计 + basic_stats = df["score"].describe().to_frame().T + logging.info(f"\n基础统计信息:\n{basic_stats.to_string()}") + + # 2. 统计得分为 0 的数量 + zero_score_count = (df["score"] == 0).sum() + total_count = len(df) + zero_ratio = ( + (zero_score_count / total_count * 100).round(2) if total_count > 0 else 0 + ) + logging.info(f"\n得分为 0 的统计: {zero_score_count} 次 (占比: {zero_ratio}%)") + + # 2.1 统计免费旋转大于0的数量 + extra_free_count = (df["extra_free"] > 0).sum() + total_count = len(df) + zero_ratio = ( + (extra_free_count / total_count * 100).round(2) if total_count > 0 else 0 + ) + logging.info( + f"\n免费旋转次数大于 0 的统计: {extra_free_count} 次 (占比: {zero_ratio}%)" + ) + + # 3. 得分区间分布 + bins = [0, 0.1, 0.5, 1, 10, 50, 100, 500, float("inf")] + labels = ["0-0.1", "0.1-0.5", "0.5-1", "1-10", "11-50", "51-100", "101-500", "500+"] + df["score_range"] = pd.cut(df["score"], bins=bins, labels=labels, right=False) + + # 计算数量 + range_dist = df["score_range"].value_counts().sort_index().to_frame(name="count") + + # 计算比例 (百分比) + range_dist["percentage"] = (range_dist["count"] / len(df) * 100).round(2) + + # 格式化日志输出,拼接百分比 + # 使用 apply 将每一行转换为 "数量 (百分比%)" 的格式 + log_output = range_dist.apply( + lambda row: f"{row['count']} ({row['percentage']}%)", axis=1 + ).to_string(name=False) + + logging.info(f"\n得分区间分布 (数量 | 占比):\n{log_output}") + + return basic_stats, range_dist + + +def visualize_data(range_dist): + """生成可视化图表""" + logging.info("正在生成图表...") + plt.rcParams["font.sans-serif"] = ["SimHei"] # 用来正常显示中文标签 + plt.rcParams["axes.unicode_minus"] = False # 用来正常显示负号 + + fig, axes = plt.subplots(1, 2, figsize=(16, 6)) + + # 图2: 得分区间分布 (饼图) + colors = [ + "#ff9999", + "#ffb3e6", + "#66b3ff", + "#99ff99", + "#ffcc99", + "#c2c2f0", + "#ffb3e6", + ] + range_dist["count"].plot( + kind="pie", ax=axes[1], autopct="%1.1f%%", startangle=90, colors=colors + ) + axes[1].set_title("得分区间分布占比", fontsize=14) + axes[1].set_ylabel("") # 隐藏y轴标签 + + plt.tight_layout() + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"game_score_stats_{timestamp}.png" + plt.savefig(filename) + logging.info(f"图表已保存为: {filename}") + # plt.show() # 如需弹窗显示可取消注释 + + +def main(): + start_time = datetime.now() + logging.info(f"任务开始执行: {start_time}") + + conn = None + try: + conn = get_db_connection() + df = fetch_data(conn, "") + + # 打印前5行数据预览 + # logging.info("数据预览:\n" + df.head().to_string()) + + basic_stats, range_dist = analyze_data(df) + # visualize_data(range_dist) + + basic_stats, range_dist = analyze_data(df.query("extra_free > 0")) + + except Exception as e: + logging.error(f"任务执行出错: {e}") + finally: + if conn: + conn.close() + logging.info("数据库连接已关闭") + + end_time = datetime.now() + duration = (end_time - start_time).total_seconds() + logging.info(f"任务执行完成,耗时: {duration:.2f} 秒") + + +if __name__ == "__main__": + main() diff --git a/pyground/区间动画.md b/pyground/区间动画.md new file mode 100644 index 0000000..ca5709c --- /dev/null +++ b/pyground/区间动画.md @@ -0,0 +1,16 @@ +(0,1] +(1,5] +(5,10] +(10,20] + + +(20,30] +(30,50] +(50,250] +(250, 500] + + +(80,120] +(120,200] +(200, 1000] +(1000, 2000] \ No newline at end of file diff --git a/ruoyi-admin/.DS_Store b/ruoyi-admin/.DS_Store new file mode 100644 index 0000000..1bcbec9 Binary files /dev/null and b/ruoyi-admin/.DS_Store differ diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml new file mode 100644 index 0000000..984891b --- /dev/null +++ b/ruoyi-admin/pom.xml @@ -0,0 +1,174 @@ + + + + ruoyi + com.ruoyi + 4.8.2 + + 4.0.0 + jar + ruoyi-admin + + + web服务入口 + + + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + org.springframework.boot + spring-boot-devtools + true + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + + + com.mysql + mysql-connector-j + + + + + com.ruoyi + ruoyi-framework + + + + + com.ruoyi + ruoyi-quartz + + + + + com.ruoyi + ruoyi-generator + + + + com.ruoyi + service-admin + ${ruoyi.version} + + + + com.ruoyi + service-playingmethod + ${ruoyi.version} + + + + com.ruoyi + game-sugar-service + ${ruoyi.version} + + + + com.ruoyi + game-wheel-service + ${ruoyi.version} + + + + com.ruoyi + service-thirdparty + ${ruoyi.version} + + + + com.ruoyi + service-user + ${ruoyi.version} + + + + com.ruoyi + service-task + 4.8.2 + + + + com.ruoyi + skins-promo + ${ruoyi.version} + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + + repackage + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.1.0 + + false + ${project.artifactId} + + + + + ${project.artifactId} + + + \ No newline at end of file diff --git a/ruoyi-admin/src/.DS_Store b/ruoyi-admin/src/.DS_Store new file mode 100644 index 0000000..5c8e70a Binary files /dev/null and b/ruoyi-admin/src/.DS_Store differ diff --git a/ruoyi-admin/src/main/.DS_Store b/ruoyi-admin/src/main/.DS_Store new file mode 100644 index 0000000..b257c12 Binary files /dev/null and b/ruoyi-admin/src/main/.DS_Store differ diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java new file mode 100644 index 0000000..b3fd83d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java @@ -0,0 +1,35 @@ +package com.ruoyi; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration; + +/** + * 启动程序 + * + * @author ruoyi + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class RuoYiApplication { + // + public static void main(String[] args) { + // System.setProperty("spring.devtools.restart.enabled", "false"); + SpringApplication.run(RuoYiApplication.class, args); + + final Logger logger = LoggerFactory.getLogger(RuoYiApplication.class); + logger.info(""" + + (♥◠‿◠)ノ゙ 若依启动成功 ლ(´ڡ`ლ)゙ \s + .-------. ____ __ \s + | _ _ \\ \\ \\ / / \s + | ( ' ) | \\ _. / ' \s + |(_ o _) / _( )_ .' \s + | (_,_).' __ ___(_ o _)' \s + | |\\ \\ | || |(_,_)' \s + | | \\ `' /| `-' / \s + | | \\ / \\ / \s + ''-' `'-' `-..-' \s"""); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java new file mode 100644 index 0000000..6de67dc --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java @@ -0,0 +1,18 @@ +package com.ruoyi; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author ruoyi + */ +public class RuoYiServletInitializer extends SpringBootServletInitializer +{ + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) + { + return application.sources(RuoYiApplication.class); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java new file mode 100644 index 0000000..d6dcf06 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java @@ -0,0 +1,94 @@ +package com.ruoyi.web.controller.common; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import jakarta.annotation.Resource; +import javax.imageio.ImageIO; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.FastByteArrayOutputStream; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import com.google.code.kaptcha.Producer; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.sign.Base64; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 验证码操作处理 + * + * @author ruoyi + */ +@RestController +public class CaptchaController +{ + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ISysConfigService configService; + /** + * 生成验证码 + */ + @GetMapping("/captchaImage") + public AjaxResult getCode(HttpServletResponse response) throws IOException + { + AjaxResult ajax = AjaxResult.success(); + boolean captchaEnabled = configService.selectCaptchaEnabled(); + ajax.put("captchaEnabled", captchaEnabled); + if (!captchaEnabled) + { + return ajax; + } + + // 保存验证码信息 + String uuid = IdUtils.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + + String capStr = null, code = null; + BufferedImage image = null; + + // 生成验证码 + String captchaType = RuoYiConfig.getCaptchaType(); + if ("math".equals(captchaType)) + { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + image = captchaProducerMath.createImage(capStr); + } + else if ("char".equals(captchaType)) + { + capStr = code = captchaProducer.createText(); + image = captchaProducer.createImage(capStr); + } + + redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try + { + ImageIO.write(image, "jpg", os); + } + catch (IOException e) + { + return AjaxResult.error(e.getMessage()); + } + + ajax.put("uuid", uuid); + ajax.put("img", Base64.encode(os.toByteArray())); + return ajax; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java new file mode 100644 index 0000000..b873cbf --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java @@ -0,0 +1,164 @@ +package com.ruoyi.web.controller.common; + +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.framework.config.ServerConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; + +/** + * 通用请求处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/common") +public class CommonController +{ + private static final Logger log = LoggerFactory.getLogger(CommonController.class); + + @Autowired + private ServerConfig serverConfig; + + private static final String FILE_DELIMETER = ","; + + /** + * 通用下载请求 + * + * @param fileName 文件名称 + * @param delete 是否删除 + */ + @GetMapping("/download") + public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) + { + try + { + if (!FileUtils.checkAllowDownload(fileName)) + { + throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); + } + String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); + String filePath = RuoYiConfig.getDownloadPath() + "/" + fileName; + + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, realFileName); + FileUtils.writeBytes(filePath, response.getOutputStream()); + if (delete) + { + FileUtils.deleteFile(filePath); + } + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } + + /** + * 通用上传请求(单个) + */ + @PostMapping("/upload") + public AjaxResult uploadFile(MultipartFile file,HttpServletRequest request) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + AjaxResult ajax = AjaxResult.success(); + ajax.put("url", url); + ajax.put("fileName", fileName); + ajax.put("newFileName", FileUtils.getName(fileName)); + ajax.put("originalFilename", file.getOriginalFilename()); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 通用上传请求(多个) + */ + @PostMapping("/uploads") + public AjaxResult uploadFiles(List files) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + List urls = new ArrayList(); + List fileNames = new ArrayList(); + List newFileNames = new ArrayList(); + List originalFilenames = new ArrayList(); + for (MultipartFile file : files) + { + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + urls.add(url); + fileNames.add(fileName); + newFileNames.add(FileUtils.getName(fileName)); + originalFilenames.add(file.getOriginalFilename()); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER)); + ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER)); + ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER)); + ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER)); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 本地资源通用下载 + */ + @GetMapping("/download/resource") + public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) + throws Exception + { + try + { + if (!FileUtils.checkAllowDownload(resource)) + { + throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource)); + } + // 本地资源路径 + String localPath = RuoYiConfig.getProfile(); + // 数据库资源地址 + String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX); + // 下载名称 + String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, downloadName); + FileUtils.writeBytes(downloadPath, response.getOutputStream()); + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java new file mode 100644 index 0000000..69470d0 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java @@ -0,0 +1,120 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysCache; + +/** + * 缓存监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/cache") +public class CacheController +{ + @Autowired + private RedisTemplate redisTemplate; + + private final static List caches = new ArrayList(); + { + caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息")); + caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息")); + caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典")); + caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码")); + caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交")); + caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理")); + caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数")); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Properties info = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info()); + Properties commandStats = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info("commandstats")); + Object dbSize = redisTemplate.execute((RedisCallback) connection -> connection.dbSize()); + + Map result = new HashMap<>(3); + result.put("info", info); + result.put("dbSize", dbSize); + + List> pieList = new ArrayList<>(); + commandStats.stringPropertyNames().forEach(key -> { + Map data = new HashMap<>(2); + String property = commandStats.getProperty(key); + data.put("name", StringUtils.removeStart(key, "cmdstat_")); + data.put("value", StringUtils.substringBetween(property, "calls=", ",usec")); + pieList.add(data); + }); + result.put("commandStats", pieList); + return AjaxResult.success(result); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping("/getNames") + public AjaxResult cache() + { + return AjaxResult.success(caches); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping("/getKeys/{cacheName}") + public AjaxResult getCacheKeys(@PathVariable String cacheName) + { + Set cacheKeys = redisTemplate.keys(cacheName + "*"); + return AjaxResult.success(cacheKeys); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping("/getValue/{cacheName}/{cacheKey}") + public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) + { + String cacheValue = redisTemplate.opsForValue().get(cacheKey); + SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue); + return AjaxResult.success(sysCache); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @DeleteMapping("/clearCacheName/{cacheName}") + public AjaxResult clearCacheName(@PathVariable String cacheName) + { + Collection cacheKeys = redisTemplate.keys(cacheName + "*"); + redisTemplate.delete(cacheKeys); + return AjaxResult.success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @DeleteMapping("/clearCacheKey/{cacheKey}") + public AjaxResult clearCacheKey(@PathVariable String cacheKey) + { + redisTemplate.delete(cacheKey); + return AjaxResult.success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @DeleteMapping("/clearCacheAll") + public AjaxResult clearCacheAll() + { + Collection cacheKeys = redisTemplate.keys("*"); + redisTemplate.delete(cacheKeys); + return AjaxResult.success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java new file mode 100644 index 0000000..cc805ad --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java @@ -0,0 +1,27 @@ +package com.ruoyi.web.controller.monitor; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.framework.web.domain.Server; + +/** + * 服务器监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/server") +public class ServerController +{ + @PreAuthorize("@ss.hasPermi('monitor:server:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Server server = new Server(); + server.copyTo(); + return AjaxResult.success(server); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java new file mode 100644 index 0000000..f6fac71 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java @@ -0,0 +1,82 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.web.service.SysPasswordService; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.service.ISysLogininforService; + +/** + * 系统访问记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController +{ + @Autowired + private ISysLogininforService logininforService; + + @Autowired + private SysPasswordService passwordService; + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')") + @GetMapping("/list") + public TableDataInfo list(SysLogininfor logininfor) + { + startPage(); + List list = logininforService.selectLogininforList(logininfor); + return getDataTable(list); + } + + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysLogininfor logininfor) + { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil util = new ExcelUtil(SysLogininfor.class); + util.exportExcel(response, list, "登录日志"); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public AjaxResult remove(@PathVariable Long[] infoIds) + { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + logininforService.cleanLogininfor(); + return success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public AjaxResult unlock(@PathVariable("userName") String userName) + { + passwordService.clearLoginRecordCache(userName); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java new file mode 100644 index 0000000..f056d65 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java @@ -0,0 +1,69 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.service.ISysOperLogService; + +/** + * 操作日志记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController +{ + @Autowired + private ISysOperLogService operLogService; + + @PreAuthorize("@ss.hasPermi('monitor:operlog:list')") + @GetMapping("/list") + public TableDataInfo list(SysOperLog operLog) + { + startPage(); + List list = operLogService.selectOperLogList(operLog); + return getDataTable(list); + } + + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:operlog:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysOperLog operLog) + { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil util = new ExcelUtil(SysOperLog.class); + util.exportExcel(response, list, "操作日志"); + } + + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/{operIds}") + public AjaxResult remove(@PathVariable Long[] operIds) + { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/clean") + public AjaxResult clean() + { + operLogService.cleanOperLog(); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java new file mode 100644 index 0000000..a442863 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java @@ -0,0 +1,83 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysUserOnline; +import com.ruoyi.system.service.ISysUserOnlineService; + +/** + * 在线用户监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/online") +public class SysUserOnlineController extends BaseController +{ + @Autowired + private ISysUserOnlineService userOnlineService; + + @Autowired + private RedisCache redisCache; + + @PreAuthorize("@ss.hasPermi('monitor:online:list')") + @GetMapping("/list") + public TableDataInfo list(String ipaddr, String userName) + { + Collection keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); + List userOnlineList = new ArrayList(); + for (String key : keys) + { + LoginUser user = redisCache.getCacheObject(key); + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) + { + userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user)); + } + else if (StringUtils.isNotEmpty(ipaddr)) + { + userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user)); + } + else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) + { + userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user)); + } + else + { + userOnlineList.add(userOnlineService.loginUserToUserOnline(user)); + } + } + Collections.reverse(userOnlineList); + userOnlineList.removeAll(Collections.singleton(null)); + return getDataTable(userOnlineList); + } + + /** + * 强退用户 + */ + @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public AjaxResult forceLogout(@PathVariable String tokenId) + { + redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java new file mode 100644 index 0000000..91a854f --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java @@ -0,0 +1,133 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 参数配置 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/config") +public class SysConfigController extends BaseController +{ + @Autowired + private ISysConfigService configService; + + /** + * 获取参数配置列表 + */ + @PreAuthorize("@ss.hasPermi('system:config:list')") + @GetMapping("/list") + public TableDataInfo list(SysConfig config) + { + startPage(); + List list = configService.selectConfigList(config); + return getDataTable(list); + } + + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:config:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysConfig config) + { + List list = configService.selectConfigList(config); + ExcelUtil util = new ExcelUtil(SysConfig.class); + util.exportExcel(response, list, "参数数据"); + } + + /** + * 根据参数编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:config:query')") + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long configId) + { + return success(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + */ + @GetMapping(value = "/configKey/{configKey}") + public AjaxResult getConfigKey(@PathVariable String configKey) + { + return success(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:add')") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysConfig config) + { + if (!configService.checkConfigKeyUnique(config)) + { + return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(getUsername()); + return toAjax(configService.insertConfig(config)); + } + + /** + * 修改参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:edit')") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysConfig config) + { + if (!configService.checkConfigKeyUnique(config)) + { + return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(getUsername()); + return toAjax(configService.updateConfig(config)); + } + + /** + * 删除参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public AjaxResult remove(@PathVariable Long[] configIds) + { + configService.deleteConfigByIds(configIds); + return success(); + } + + /** + * 刷新参数缓存 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + configService.resetConfigCache(); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java new file mode 100644 index 0000000..59e7588 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java @@ -0,0 +1,132 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysDeptService; + +/** + * 部门信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController +{ + @Autowired + private ISysDeptService deptService; + + /** + * 获取部门列表 + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list") + public AjaxResult list(SysDept dept) + { + List depts = deptService.selectDeptList(dept); + return success(depts); + } + + /** + * 查询部门列表(排除节点) + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list/exclude/{deptId}") + public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) + { + List depts = deptService.selectDeptList(new SysDept()); + depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")); + return success(depts); + } + + /** + * 根据部门编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:dept:query')") + @GetMapping(value = "/{deptId}") + public AjaxResult getInfo(@PathVariable Long deptId) + { + deptService.checkDeptDataScope(deptId); + return success(deptService.selectDeptById(deptId)); + } + + /** + * 新增部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:add')") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDept dept) + { + if (!deptService.checkDeptNameUnique(dept)) + { + return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + dept.setCreateBy(getUsername()); + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:edit')") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDept dept) + { + Long deptId = dept.getDeptId(); + deptService.checkDeptDataScope(deptId); + if (!deptService.checkDeptNameUnique(dept)) + { + return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + else if (dept.getParentId().equals(deptId)) + { + return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } + else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0) + { + return error("该部门包含未停用的子部门!"); + } + dept.setUpdateBy(getUsername()); + return toAjax(deptService.updateDept(dept)); + } + + /** + * 删除部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:remove')") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public AjaxResult remove(@PathVariable Long deptId) + { + if (deptService.hasChildByDeptId(deptId)) + { + return warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) + { + return warn("部门存在用户,不允许删除"); + } + deptService.checkDeptDataScope(deptId); + return toAjax(deptService.deleteDeptById(deptId)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java new file mode 100644 index 0000000..325cf56 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java @@ -0,0 +1,128 @@ +package com.ruoyi.web.controller.system; + +import java.util.ArrayList; +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictDataService; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@Api(tags = "管理端 字段值管理") +@RestController +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController +{ + @Autowired + private ISysDictDataService dictDataService; + + @Autowired + private ISysDictTypeService dictTypeService; + + @ApiOperation("获取字典值") + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictData dictData) + { + startPage(); + List list = dictDataService.selectDictDataList(dictData); + return getDataTable(list); + } + + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysDictData dictData) + { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil util = new ExcelUtil(SysDictData.class); + util.exportExcel(response, list, "字典数据"); + } + + /** + * 查询字典数据详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictCode}") + public AjaxResult getInfo(@PathVariable Long dictCode) + { + return success(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + */ + @GetMapping(value = "/type/{dictType}") + public AjaxResult dictType(@PathVariable String dictType) + { + List data = dictTypeService.selectDictDataByType(dictType); + if (StringUtils.isNull(data)) + { + data = new ArrayList(); + } + return success(data); + } + + /** + * 新增字典类型 + */ + @ApiOperation("新增字典值") + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictData dict) + { + dict.setCreateBy(getUsername()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改保存字典类型 + */ + @ApiOperation("修改字典值") + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictData dict) + { + dict.setUpdateBy(getUsername()); + return toAjax(dictDataService.updateDictData(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public AjaxResult remove(@PathVariable Long[] dictCodes) + { + dictDataService.deleteDictDataByIds(dictCodes); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java new file mode 100644 index 0000000..3ff314c --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java @@ -0,0 +1,131 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDictType; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/type") +public class SysDictTypeController extends BaseController +{ + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictType dictType) + { + startPage(); + List list = dictTypeService.selectDictTypeList(dictType); + return getDataTable(list); + } + + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysDictType dictType) + { + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil util = new ExcelUtil(SysDictType.class); + util.exportExcel(response, list, "字典类型"); + } + + /** + * 查询字典类型详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictId}") + public AjaxResult getInfo(@PathVariable Long dictId) + { + return success(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictType dict) + { + if (!dictTypeService.checkDictTypeUnique(dict)) + { + return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(getUsername()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictType dict) + { + if (!dictTypeService.checkDictTypeUnique(dict)) + { + return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(getUsername()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public AjaxResult remove(@PathVariable Long[] dictIds) + { + dictTypeService.deleteDictTypeByIds(dictIds); + return success(); + } + + /** + * 刷新字典缓存 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + dictTypeService.resetDictCache(); + return success(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List dictTypes = dictTypeService.selectDictTypeAll(); + return success(dictTypes); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java new file mode 100644 index 0000000..13007eb --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java @@ -0,0 +1,29 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.StringUtils; + +/** + * 首页 + * + * @author ruoyi + */ +@RestController +public class SysIndexController +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** + * 访问首页,提示语 + */ + @RequestMapping("/") + public String index() + { + return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java new file mode 100644 index 0000000..9c30493 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -0,0 +1,88 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import java.util.Set; + +import io.swagger.annotations.Api; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginBody; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.web.service.SysLoginService; +import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 登录验证 + * + * @author ruoyi + */ +@Api(tags = "管理端认证api") +@RestController +public class SysLoginController +{ + @Autowired + private SysLoginService loginService; + + @Autowired + private ISysMenuService menuService; + + @Autowired + private SysPermissionService permissionService; + + /** + * 登录方法 + * + * @param loginBody 登录信息 + * @return 结果 + */ + @PostMapping("/login") + public AjaxResult login(@RequestBody LoginBody loginBody) + { + AjaxResult ajax = AjaxResult.success(); + // 生成令牌 + String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid()); + ajax.put(Constants.TOKEN, token); + return ajax; + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("getInfo") + public AjaxResult getInfo() + { + SysUser user = SecurityUtils.getLoginUser().getUser(); + // 角色集合 + Set roles = permissionService.getRolePermission(user); + // 权限集合 + Set permissions = permissionService.getMenuPermission(user); + AjaxResult ajax = AjaxResult.success(); + ajax.put("user", user); + ajax.put("roles", roles); + ajax.put("permissions", permissions); + return ajax; + } + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("getRouters") + public AjaxResult getRouters() + { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuTreeByUserId(userId); + return AjaxResult.success(menuService.buildMenus(menus)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java new file mode 100644 index 0000000..03b6b65 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java @@ -0,0 +1,142 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 菜单信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController +{ + @Autowired + private ISysMenuService menuService; + + /** + * 获取菜单列表 + */ + @PreAuthorize("@ss.hasPermi('system:menu:list')") + @GetMapping("/list") + public AjaxResult list(SysMenu menu) + { + List menus = menuService.selectMenuList(menu, getUserId()); + return success(menus); + } + + /** + * 根据菜单编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:menu:query')") + @GetMapping(value = "/{menuId}") + public AjaxResult getInfo(@PathVariable Long menuId) + { + return success(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @GetMapping("/treeselect") + public AjaxResult treeselect(SysMenu menu) + { + List menus = menuService.selectMenuList(menu, getUserId()); + return success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) + { + List menus = menuService.selectMenuList(getUserId()); + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); + ajax.put("menus", menuService.buildMenuTreeSelect(menus)); + return ajax; + } + + /** + * 新增菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:add')") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + menu.setCreateBy(getUsername()); + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:edit')") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + else if (menu.getMenuId().equals(menu.getParentId())) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + menu.setUpdateBy(getUsername()); + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:remove')") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public AjaxResult remove(@PathVariable("menuId") Long menuId) + { + if (menuService.hasChildByMenuId(menuId)) + { + return warn("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) + { + return warn("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java new file mode 100644 index 0000000..8622828 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java @@ -0,0 +1,91 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.system.domain.SysNotice; +import com.ruoyi.system.service.ISysNoticeService; + +/** + * 公告 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController +{ + @Autowired + private ISysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @PreAuthorize("@ss.hasPermi('system:notice:list')") + @GetMapping("/list") + public TableDataInfo list(SysNotice notice) + { + startPage(); + List list = noticeService.selectNoticeList(notice); + return getDataTable(list); + } + + /** + * 根据通知公告编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:notice:query')") + @GetMapping(value = "/{noticeId}") + public AjaxResult getInfo(@PathVariable Long noticeId) + { + return success(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:add')") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysNotice notice) + { + notice.setCreateBy(getUsername()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:edit')") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysNotice notice) + { + notice.setUpdateBy(getUsername()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:remove')") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public AjaxResult remove(@PathVariable Long[] noticeIds) + { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java new file mode 100644 index 0000000..5b604cf --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java @@ -0,0 +1,129 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.service.ISysPostService; + +/** + * 岗位信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/post") +public class SysPostController extends BaseController +{ + @Autowired + private ISysPostService postService; + + /** + * 获取岗位列表 + */ + @PreAuthorize("@ss.hasPermi('system:post:list')") + @GetMapping("/list") + public TableDataInfo list(SysPost post) + { + startPage(); + List list = postService.selectPostList(post); + return getDataTable(list); + } + + @Log(title = "岗位管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:post:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysPost post) + { + List list = postService.selectPostList(post); + ExcelUtil util = new ExcelUtil(SysPost.class); + util.exportExcel(response, list, "岗位数据"); + } + + /** + * 根据岗位编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:post:query')") + @GetMapping(value = "/{postId}") + public AjaxResult getInfo(@PathVariable Long postId) + { + return success(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:add')") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysPost post) + { + if (!postService.checkPostNameUnique(post)) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (!postService.checkPostCodeUnique(post)) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setCreateBy(getUsername()); + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:edit')") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysPost post) + { + if (!postService.checkPostNameUnique(post)) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (!postService.checkPostCodeUnique(post)) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setUpdateBy(getUsername()); + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:remove')") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public AjaxResult remove(@PathVariable Long[] postIds) + { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List posts = postService.selectPostAll(); + return success(posts); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java new file mode 100644 index 0000000..be5af6a --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java @@ -0,0 +1,136 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.MimeTypeUtils; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 个人信息 业务处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private TokenService tokenService; + + /** + * 个人信息 + */ + @GetMapping + public AjaxResult profile() + { + LoginUser loginUser = getLoginUser(); + SysUser user = loginUser.getUser(); + AjaxResult ajax = AjaxResult.success(user); + ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername())); + ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername())); + return ajax; + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult updateProfile(@RequestBody SysUser user) + { + LoginUser loginUser = getLoginUser(); + SysUser currentUser = loginUser.getUser(); + currentUser.setNickName(user.getNickName()); + currentUser.setEmail(user.getEmail()); + currentUser.setPhonenumber(user.getPhonenumber()); + currentUser.setSex(user.getSex()); + if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser)) + { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser)) + { + return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + if (userService.updateUserProfile(currentUser) > 0) + { + // 更新缓存用户信息 + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public AjaxResult updatePwd(String oldPassword, String newPassword) + { + LoginUser loginUser = getLoginUser(); + String userName = loginUser.getUsername(); + String password = loginUser.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) + { + return error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(newPassword, password)) + { + return error("新密码不能与旧密码相同"); + } + if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0) + { + // 更新缓存用户密码 + loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword)); + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception + { + if (!file.isEmpty()) + { + LoginUser loginUser = getLoginUser(); + String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION); + if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("imgUrl", avatar); + // 更新缓存用户头像 + loginUser.getUser().setAvatar(avatar); + tokenService.setLoginUser(loginUser); + return ajax; + } + } + return error("上传图片异常,请联系管理员"); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java new file mode 100644 index 0000000..fe19249 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java @@ -0,0 +1,38 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.RegisterBody; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.SysRegisterService; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 注册验证 + * + * @author ruoyi + */ +@RestController +public class SysRegisterController extends BaseController +{ + @Autowired + private SysRegisterService registerService; + + @Autowired + private ISysConfigService configService; + + @PostMapping("/register") + public AjaxResult register(@RequestBody RegisterBody user) + { + if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) + { + return error("当前系统没有开启注册功能!"); + } + String msg = registerService.register(user); + return StringUtils.isEmpty(msg) ? success() : error(msg); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java new file mode 100644 index 0000000..4ba3d1b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java @@ -0,0 +1,262 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 角色信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private TokenService tokenService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysDeptService deptService; + + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/list") + public TableDataInfo list(SysRole role) + { + startPage(); + List list = roleService.selectRoleList(role); + return getDataTable(list); + } + + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:role:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysRole role) + { + List list = roleService.selectRoleList(role); + ExcelUtil util = new ExcelUtil(SysRole.class); + util.exportExcel(response, list, "角色数据"); + } + + /** + * 根据角色编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/{roleId}") + public AjaxResult getInfo(@PathVariable Long roleId) + { + roleService.checkRoleDataScope(roleId); + return success(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:add')") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysRole role) + { + if (!roleService.checkRoleNameUnique(role)) + { + return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (!roleService.checkRoleKeyUnique(role)) + { + return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setCreateBy(getUsername()); + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + if (!roleService.checkRoleNameUnique(role)) + { + return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (!roleService.checkRoleKeyUnique(role)) + { + return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setUpdateBy(getUsername()); + + if (roleService.updateRole(role) > 0) + { + // 更新缓存用户权限 + LoginUser loginUser = getLoginUser(); + if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) + { + loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser())); + loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName())); + tokenService.setLoginUser(loginUser); + } + return success(); + } + return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); + } + + /** + * 修改保存数据权限 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public AjaxResult dataScope(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + role.setUpdateBy(getUsername()); + return toAjax(roleService.updateRoleStatus(role)); + } + + /** + * 删除角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:remove')") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleIds}") + public AjaxResult remove(@PathVariable Long[] roleIds) + { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * 获取角色选择框列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + return success(roleService.selectRoleAll()); + } + + /** + * 查询已分配用户角色列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/allocatedList") + public TableDataInfo allocatedList(SysUser user) + { + startPage(); + List list = userService.selectAllocatedList(user); + return getDataTable(list); + } + + /** + * 查询未分配用户角色列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/unallocatedList") + public TableDataInfo unallocatedList(SysUser user) + { + startPage(); + List list = userService.selectUnallocatedList(user); + return getDataTable(list); + } + + /** + * 取消授权用户 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) + { + return toAjax(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) + { + return toAjax(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) + { + roleService.checkRoleDataScope(roleId); + return toAjax(roleService.insertAuthUsers(roleId, userIds)); + } + + /** + * 获取对应角色部门树列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/deptTree/{roleId}") + public AjaxResult deptTree(@PathVariable("roleId") Long roleId) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); + ajax.put("depts", deptService.selectDeptTreeList(new SysDept())); + return ajax; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java new file mode 100644 index 0000000..b006012 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java @@ -0,0 +1,251 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import java.util.stream.Collectors; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysPostService; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 用户信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysDeptService deptService; + + @Autowired + private ISysPostService postService; + + /** + * 获取用户列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/list") + public TableDataInfo list(SysUser user) + { + startPage(); + List list = userService.selectUserList(user); + return getDataTable(list); + } + + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:user:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysUser user) + { + List list = userService.selectUserList(user); + ExcelUtil util = new ExcelUtil(SysUser.class); + util.exportExcel(response, list, "用户数据"); + } + + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @PreAuthorize("@ss.hasPermi('system:user:import')") + @PostMapping("/importData") + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception + { + ExcelUtil util = new ExcelUtil(SysUser.class); + List userList = util.importExcel(file.getInputStream()); + String operName = getUsername(); + String message = userService.importUser(userList, updateSupport, operName); + return success(message); + } + + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) + { + ExcelUtil util = new ExcelUtil(SysUser.class); + util.importTemplateExcel(response, "用户数据"); + } + + /** + * 根据用户编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping(value = { "/", "/{userId}" }) + public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) + { + userService.checkUserDataScope(userId); + AjaxResult ajax = AjaxResult.success(); + List roles = roleService.selectRoleAll(); + ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + ajax.put("posts", postService.selectPostAll()); + if (StringUtils.isNotNull(userId)) + { + SysUser sysUser = userService.selectUserById(userId); + ajax.put(AjaxResult.DATA_TAG, sysUser); + ajax.put("postIds", postService.selectPostListByUserId(userId)); + ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList())); + } + return ajax; + } + + /** + * 新增用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:add')") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysUser user) + { + if (!userService.checkUserNameUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setCreateBy(getUsername()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysUser user) + { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + if (!userService.checkUserNameUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUser(user)); + } + + /** + * 删除用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:remove')") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userIds}") + public AjaxResult remove(@PathVariable Long[] userIds) + { + if (ArrayUtils.contains(userIds, getUserId())) + { + return error("当前用户不能删除"); + } + return toAjax(userService.deleteUserByIds(userIds)); + } + + /** + * 重置密码 + */ + @PreAuthorize("@ss.hasPermi('system:user:resetPwd')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + user.setUpdateBy(getUsername()); + return toAjax(userService.resetPwd(user)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUserStatus(user)); + } + + /** + * 根据用户编号获取授权角色 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping("/authRole/{userId}") + public AjaxResult authRole(@PathVariable("userId") Long userId) + { + AjaxResult ajax = AjaxResult.success(); + SysUser user = userService.selectUserById(userId); + List roles = roleService.selectRolesByUserId(userId); + ajax.put("user", user); + ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + return ajax; + } + + /** + * 用户授权角色 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public AjaxResult insertAuthRole(Long userId, Long[] roleIds) + { + userService.checkUserDataScope(userId); + userService.insertUserAuth(userId, roleIds); + return success(); + } + + /** + * 获取部门树列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/deptTree") + public AjaxResult deptTree(SysDept dept) + { + return success(deptService.selectDeptTreeList(dept)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java new file mode 100644 index 0000000..a54b3af --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java @@ -0,0 +1,171 @@ +package com.ruoyi.web.controller.tool; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * swagger 用户测试方法 + * + * @author ruoyi + */ +//@Api(tags = "用户信息管理") +@RestController +@RequestMapping("/test/user") +public class TestController extends BaseController +{ + private final static Map users = new LinkedHashMap(); + { + users.put(1, new UserEntity(1, "admin", "admin123", "15888888888")); + users.put(2, new UserEntity(2, "ry", "admin123", "15666666666")); + } + + // @ApiOperation("获取用户列表") + @GetMapping("/list") + public R> userList() + { + List userList = new ArrayList(users.values()); + return R.ok(userList); + } + + // @ApiOperation("获取用户详细") +// @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @GetMapping("/{userId}") + public R getUser(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + return R.ok(users.get(userId)); + } + else + { + return R.fail("用户不存在"); + } + } + + // @ApiOperation("新增用户") +// @ApiImplicitParams({ +// @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class), +// @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class), +// @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class), +// @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class) +// }) + @PostMapping("/save") + public R save(UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + users.put(user.getUserId(), user); + return R.ok(); + } + + // @ApiOperation("更新用户") + @PutMapping("/update") + public R update(@RequestBody UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + if (users.isEmpty() || !users.containsKey(user.getUserId())) + { + return R.fail("用户不存在"); + } + users.remove(user.getUserId()); + users.put(user.getUserId(), user); + return R.ok(); + } + + // @ApiOperation("删除用户信息") +// @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @DeleteMapping("/{userId}") + public R delete(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + users.remove(userId); + return R.ok(); + } + else + { + return R.fail("用户不存在"); + } + } +} + +//@ApiModel(value = "UserEntity", description = "用户实体") +class UserEntity +{ + // @ApiModelProperty("用户ID") + private Integer userId; + + // @ApiModelProperty("用户名称") + private String username; + + // @ApiModelProperty("用户密码") + private String password; + + // @ApiModelProperty("用户手机") + private String mobile; + + public UserEntity() + { + + } + + public UserEntity(Integer userId, String username, String password, String mobile) + { + this.userId = userId; + this.username = username; + this.password = password; + this.mobile = mobile; + } + + public Integer getUserId() + { + return userId; + } + + public void setUserId(Integer userId) + { + this.userId = userId; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getMobile() + { + return mobile; + } + + public void setMobile(String mobile) + { + this.mobile = mobile; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java new file mode 100644 index 0000000..e0a8d1a --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java @@ -0,0 +1,170 @@ +package com.ruoyi.web.core.config; + +import io.swagger.annotations.ApiOperation; +import io.swagger.models.auth.In; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.AuthorizationScope; +import springfox.documentation.service.SecurityReference; +import springfox.documentation.service.SecurityScheme; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; + +import java.util.ArrayList; +import java.util.List; + +@Configuration +public class SwaggerConfig { + + /** + * 后台接口分组 + */ + // @Bean + public Docket adminDocket() { + return new Docket(DocumentationType.OAS_30) + .select() + // 扫描注解 + .apis(RequestHandlerSelectors.basePackage("com.ruoyi.admin.controller")) + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + // 扫描所有.apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build() + .groupName("admin") + // 设置安全模式,访问token + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + /** + * 用户业务接口分组 + */ + @Bean + public Docket userDocket() { + return new Docket(DocumentationType.OAS_30) + .select() + .apis(RequestHandlerSelectors.basePackage("com.ruoyi.user.controller")) + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .paths(PathSelectors.any()) + .build() + .groupName("user") + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + /** + * 第三方业务接口分组 + */ + @Bean + public Docket thirdPartyDocket() { + return new Docket(DocumentationType.OAS_30) + .select() + .apis(RequestHandlerSelectors.basePackage("com.ruoyi.thirdparty")) + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .paths(PathSelectors.any()) + .build() + .groupName("thirdParty") + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + /** + * 玩法业务接口分组 + */ + @Bean + public Docket playingMethodDocket() { + return new Docket(DocumentationType.OAS_30) + .select() + .apis(RequestHandlerSelectors.basePackage("com.ruoyi.playingmethod.controller")) + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .paths(PathSelectors.any()) + .build() + .groupName("playingMethod") + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + /** + * 糖果玩法业务接口分组 + */ + @Bean + public Docket gameSugarMethodDocket() { + return new Docket(DocumentationType.OAS_30) + .select() + .apis(RequestHandlerSelectors.basePackage("com.ruoyi.game.sugar.controller")) + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .paths(PathSelectors.any()) + .build() + .groupName("gameSugarMethod") + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + /** + * 任务业务接口分组 + */ + @Bean + public Docket taskDocket() { + return new Docket(DocumentationType.OAS_30) + .select() + .apis(RequestHandlerSelectors.basePackage("com.ruoyi.task.controller")) + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .paths(PathSelectors.any()) + .build() + .groupName("task") + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + /** + * 推广后台接口分组 + */ + @Bean + public Docket promoDocket() { + return new Docket(DocumentationType.OAS_30) + .select() + .apis(RequestHandlerSelectors.basePackage("com.ruoyi.promo.controller")) + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .paths(PathSelectors.any()) + .build() + .groupName("promo") + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + /** + * 安全模式,这里指定token通过Authorization请求头传递 + */ + private List securitySchemes() { + List apiKeyList = new ArrayList(); + apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue())); + return apiKeyList; + } + + /** + * 安全上下文 + */ + private List securityContexts() { + List securityContexts = new ArrayList<>(); + securityContexts.add(SecurityContext.builder() + .securityReferences(defaultAuth()) + .operationSelector(o -> o.requestMappingPattern().matches("/.*")) + .build()); + return securityContexts; + } + + /** + * 默认的安全引用 + */ + private List defaultAuth() { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List securityReferences = new ArrayList<>(); + securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); + return securityReferences; + } +} diff --git a/ruoyi-admin/src/main/resources/.DS_Store b/ruoyi-admin/src/main/resources/.DS_Store new file mode 100644 index 0000000..ebe0874 Binary files /dev/null and b/ruoyi-admin/src/main/resources/.DS_Store differ diff --git a/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties b/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties new file mode 100644 index 0000000..2b23f85 --- /dev/null +++ b/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties @@ -0,0 +1 @@ +restart.include.json=/com.alibaba.fastjson.*.jar \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml new file mode 100644 index 0000000..0591654 --- /dev/null +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -0,0 +1,256 @@ +# 日志配置 +logging: + level: + com.ruoyi: info + org.springframework: warn +# 项目相关配置 +ruoyi: + # 名称 + name: Skins + # 版本 + version: 3.8.6 + # 版权年份 + copyrightYear: 2023 + # 实例演示开关 + demoEnabled: true + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) +# profile: D:/ruoyi/uploadPath + profile: /www/static + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数字计算 char 字符验证 + captchaType: math + # 项目全局域名配置 + # domainName: http://192.168.3.25:${server.port} + # domainName: http://api.mkcsgo.com/prod-api + domainName: "" + +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://127.0.0.1:3306/fire?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: fire + password: iTSJSPPZM3LSGAPC + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置连接超时时间 + connectTimeout: 30000 + # 配置网络超时时间 + socketTimeout: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true + # redis 配置 + data: + redis: + host: 127.0.0.1 + port: 6379 + password: FireSkins@99999 + lettuce: + # 关键配置:指定协议版本 + client-options: + protocol: RESP2 + redis: + # 地址 +# host: 47.106.150.25 + host: 127.0.0.1 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 4 + # 密码 + password: FireSkins@99999 + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + rabbitmq: +# host: 47.106.150.25 + host: 127.0.0.1 + port: 5672 + username: skins + password: mk + virtual-host: /skins + listener: + simple: + acknowledge-mode: MANUAL +# prefetch: 1 #每次从队列中取一个,轮询分发,默认是公平分发 +# retry: +# max-attempts: 5 # 重试次数 +# enabled: true # 开启重试 +# rabbitmqclt change_password admin admin + +zbt: + # appKey: a4d11711dff611dbbeb7987f7ff96880 + # appKey: bfb344da84660d8407e88f38deac1385 + appKey: ed4dbc0fd014cafa1f9b6992c175945b + language: zh_CN + appId: 730 + baseUrl: https://app.zbt.com + callbackUrl: http://qnxkci.natappfree.cc/admin/zbt/callback +v5item: + baseUrl: https://v2.v5item.com + # merchantKey: e315a295403d4bfab2605cb743be6e6d + # account: 13371345595 + # password: a123456! + merchantKey: 2c4c366d5c294b66addd5d28af34e69c + account: 19385220095 + password: Hrycly200811 + publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi2+C4wEwOZTa3Ep0bjJGtPQUBbJAnO1fCjzWu7IBta+Vi8QQq6ujJFirUPO975Ln+zw2+YpSZVssBAqEOgLbHM72qTBbUC7dq7Sj7QI0cKY/9PWfCcAHohKYKl/IJ7tlA1Cndvh88WdPGoldJc2ETX1fKeoICf4xPQu/7XTfY2lyY5Z14/Lo15KJe6+wnMc2xFWbjjeQ+PiMtC/Iqihf/vLI5APE0qlcB9Kagvt4Efap64NRVJkqT4TrhQCVO0oHDVcUxXOVcu7lWMUBaiv0HclGcyguVLMFdtXLiGp2TY5sMYIq4Prpzm/zs5J5bTavAIBoScWOkUCz3WMdGBgFUwIDAQAB +alipay: + serverUrl: https://openapi.alipay.com/gateway.do + format: JSON + charset: UTF-8 + signType: RSA2 + merchants: + - appId: 2021004130630119 + privateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmzDV4XE3/iR2MLKV3uj+vkTWBL2mRVXKlfDmyOOeAN8yZXeABH9hIB7flxIZ6CsB7NWbQHfPdO2UrP3T43VGMkNYDFgj27mz75zo5sEynOxlW4tL1s5S9nF1/HX+4KlbJLG7qs/pWpmcQorTeDxQHfMUFQ/neGAYxHFBCLAkziHmz2FuAVVW2JRawgOhnwV67gFfeGVuuG2jLJ0oqxfzmE2I2WL9YATSuqy0ELbiS2ntd/clmDUwK1VjjhDV5qD/E5w3Il3avQGihauX2goGLV6nofgMdgG8NSqhlW7rzDTDfhswvrFKPJBtJE3Z/FsuDTIeJyH5dojBw9Uvu3nrNAgMBAAECggEBAItyEgnL8ZF/CmrUMInF9dRLq9WS08kjDLP5BStQk4oIHE7jwpBuFR0zBrVa9ao2LvFSld/MCsKcE1pytNISBUllFOaDl38JmaeHipAMKiltKSQQgZnURre2cBwDh3FqRB/vu9MQIGwsteGchWVYacBQuU5LpCvfHd4U86w+sHqZc+1TF7V2tAxxxfuxI5GR+xHRNOEn/E2Dut6E+jNpX6aD1INgpTxteb8U7/mZSzo9tCC2k07qrDFIr2Zve0BwvJoMBso0OH3fyFHuMirjg14E5fFeD296MUHLZ2rNq8WiA2o8vmdxP/PsVqCyr6zucp3SadlQaDbggYG/PVINAmkCgYEA6ysnXTVx2P3RDaE8TbY39c7imNyIPwJYYqGM9LL/a3Ih+KSAJqsXWycmnluf6/C+U66hv3bs/aWkIT3mgiz6fgL2SIqatJmScIlKLl9K2KdVcukCf4eTUDJc8qjEkm8zKeowzyBUgiCLP5grnylTPO+ByU727zvjBMoop/V0RecCgYEAtZKhI8f5zgYFgmA+mVqYWWKG+DtXbeOA0uyJFuJWCJI7Wk3WuX1AdzEVtshIZrZFipHFTFcklx3Sd7DVwHUThDy/HigUFrqraGHiOAeNal3yhAtcEw7eNlHplUzNoFF5mlossOPMSk0RE3l3YAXUAX9/6WT/67RYGvEiL9mhuysCgYBya9HAUTOuf3iK6CteKE28BMhN1edYuPxmKPJbUQhjr+mFgAx2RAKeMENjA3OhKcaBEtPSQ7v1gfWE0whnyBis3SYVj5LMUnhk1vfH7keNeIVj3ql/gWiUuZxt2N2j3gUH3NGYxcM4eTsnWxPGYiYE3QfJ+ei95+aCOxZi+lNzewKBgEh4xBRyBwTyZlSJcbLn9BMJPnHpzZWwzoRhoL4fjNNS+pmqo9/ZXFe2ocIY4r0MV2ldkXBM/+/JGW5oiIiOskF/tZCl0o7X5v58wzw1133zDY8isZdxwcPQofjTdOdeXbj78QaRhcLKS9yICNust9f5OebAR12e1+O5BzouFV2NAoGBAKPzyFhhvyrF6cXDBPy1g44f4Ce4/+B5ouA+oAiK7AA1y7fkY3QQvUreMSK3t5luOBg5WhrZ+YokyMe7mfGIdK3T0Upw9QUMIIwtyX85FzsXZxxhBn4f83hYx3uVd0NiH2/AupPu3UAls82kJoiSOCyF+AB/Z7hd0lxSNk6zDAPl + alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmzzBx/4+G6YaFdocpDElTVFPMRYSkDNmxEYUD9yrheQdWR8czAjjzx5+NixjIcqNic/uEH3dtmfvfkcNT7mSNG+QhAV7XGOUXKYcQr7L+J7J98UGX3RcO1NPYFLyNcKu/S9IryLYdl6ZiSdNOgGr8xxWfMMpgDbsQlUKPT38ob+u22fQBkXJKBxrbZ9P0kzIoYN5S18qEDS0avAks0kkz05uffgudtopM/JlHW7Ts33s741bw0mN/GpehOjn8DDAlvIXdpZWF4pe/npwZCM/cVdRvGCkW8Z5lRgQPHEFSec56gxMONS5Zdh2IVMNNCUHO4f7sVQVx2luNeISRyDxvwIDAQAB +# - appId: 2021004148601326 +# privateKey: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCAoNFbDw6Cti1lAjL+nMVGgpGwMjaJY7durB54keXo4czzya0hviBtdtUEXZqC/v5xrO0wBO2KVZfXbcG+zY2ZFEerK+UdxAewG0Ngy6tKrrbPbG6eHJg27CFWW3dnlQ4xsYIun2AXd+HlSRamq8hbHhaMUL0zpS1u0IRMj0hknwewDUbHMc1bc/2305XX80r09enMlDjC/XHWtjkitvoTepHv2wOFZcADftmkmBh/IxRpLskxakbi4si9XI2I9diMs7rVh+UJ24ys5aeIFCzFOlsCf4zfz2oF1HN8UJHGvp2I6JwUQIHa4spMnfdl0ziRnkAWoXmeTQok/H4Nl2ahAgMBAAECggEAY/obn5ZNZgWptPgHoOVDZLG6AWSWBSXPfqMjr/1e4SsYvWhFPJPl6o9RtOn4XddK+Bo75XX/F+W9dsGltONaKuCbcA1XMVKb0yLttP+5LPwttX2HYTfCi/XvV4sSfR1bAf8Bu7E+5B7XIvszMusUeNYjGn7rAIhjjt1d54/66dGi4UkeXd7IMaQNom9Cz+Z2w9DtcUPShXYg9oL8f0K3BoYS1u0JdG94Chlj+iPh2vBRvWGkk6kHrbagDjcMf13CtnQuS+SHR7zGLVNcotDElOxGPH7Ac0fYlv5rzW7/YWCs3F8mrEBZYfZ5TGrpuqqWCg3Z3UA3tncYrtxJnKoJEQKBgQDGDjcyXVskiydLAMJO7XzeiqncFO4kq7/UC/2qn38OCBU295ufRVN4T7cP8AsTencF2OakovX3YqJod0K9BmfjiCmaUug3hCIzA/mLz75WJlJYdQLKbvW6pFRR5KpOmlynDwfiF7TtUOkrDeFELDGZTYNEAJHPQL3m3PhvG2tx/QKBgQCmQrI+aZ8laBy8u9Za/MUAL+aj7A9BdezT0a/Kvk23evll8M4oT/fDNYJ0Pzp+cros63rTjVDEhhhAsElflQ1yVJkjyBQkAUibHjewUlp5mmTdmwGd+EEMMd4dyKAIFIu+nbhyKdIoJl438vRp0rM2mWp0iimiD0k8WWy8EE/mdQKBgHXlWG4erHagbw9ysDgTntVKbbqYqvohUtuDF8lNkHBl11sVIgo5Vmqz6SEPn0hFCjzs1L8EUq6khFDzTUMAYS9MipdW6uzBNypvH0lerMrTnQkS/kpKqvQjhh4JpGeN88FubR7lRcpO872xUXsnY48CWTVNsX5R57lZqHlbNSZxAoGAbi9bg4NqRlcmXGo8GFbWlFYqi06Bmcd2abbYwwu147hrGoUpi4vbzpJi33mb4XtVKTnyT8ui0GtyHUT3i5HztGaeixkpR+dV3/95trANZ/PLjxYQT0nciiFua9yvOkurhWtvdrGCprsuy/OGya8quC+aXpvGFbH9vjIhns1MtpUCgYBL2EUUQRvRf8uK1oOes6kdbwmz+WMfylJvrfa9sGnpVWObsycbhaCUcLTtfuVbh79z33TeF6WqS/jsODUmSlbLHIHicH7zgZYkHLBcecFPx1F/gT1nvpr8bJatYfCEV7K1jdwGVCag2usfSn69Iq0js37zPXdaK0s6DWjUCXZvhA== +# alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgK + - appId: 2021003192641091 + privateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6HLvzFovqDXgfJoUiOzJX8kFylowGJ8WxSKPujKfb7HVUVNW7xItZy2KRuoQRkXl1JvhZ9UQRygKyIfiuigObifUtcPEpt6COCN75gXA1m1VxThUC9qHnCJBh6hTNwk+YjpuhMYv6Jbv8sZ5QBuxb3S7O8DNyf0skxCHy+FeMN2LNKQEm32j81c7fIFvQKT0X9N1JezHEdLoNohNnJTip7iqi+kA/8opSMMaP+up02V3+RV3OWe75Sb+cZ/r02q0bPfvOIUYBXJqUTDlzcqzytgELqo3iRTybrDX5ZL4eiRIJymkog3Bkilkp9hKdBix8aGrjHabOVXDuoji7Xn+pAgMBAAECggEAcUEVGqtovygtJf+h2p/dVelk7F8ynHhNdEG5CGxtpYYyK/xoCBhlmGIM/JNc1PgKykDJtqxrHMwqcc38b/IqmdcM7Pciunk06dn6HiknaGe7SfTzEleVVGtAuWTh/gg1uadmi2QEQ1f9GpvXs+lSoeBqIKHJHO7bgmccJaf8VqU/7H3UCLWK2UxtRMfO27hge2G8a350S7iVIyHPoXUz5L2P+5TP0T8C54QZsBp9mzaUfgaCTcw+zgPbYsTX9eP5IL7WflkZq0gPIG5XWfEiV3O2jATiNdOTtDhrEBWvY6ER5qRU0hA8tS9jNiTjgLjtNf+o7C9QlqZfSyuNdG87dQKBgQDfs8399uJHzdu7piKKnE/vhDGeXQPmGHoA+p6XUt/3Ou9nrI81EuItQ/vmLFq0cWyBg7vQFadU2IZxdtFsUZN1N6nE3wHprHmQ+MwaoOaq8f26pEPrau8DyJy3feMyBlgUbG9y/xffM/nMyVjJ04YNNnY/OIwbaLHKPIatwtQzZwKBgQDU+5KPWFD3QutSA005R0Pde2g//N+/nMX4wozx2uQrwbPJze2EAjyfSMsqX9Zgj6FExbLR4kbsnJiLip3nATqAgOyQxKS+M3vPNbJvRxAwI7dlRmq1k+JG3ujN6mDYZxeKvy0Rh8E+iocVhJsKN1xN1NeicJidB8/DeBR3CiZabwKBgCSmP5xZA0+CQXqnyEbvPwVdogdw87+RyQM6DBt8n2bQyAIzOi8Wzelelvlp0N55Kq65V17K/WQgKT6TmdHmN4NfwDeZ1aVj9XKz4DSIoeHlzYzzdpEuLsRu7IX/YOaENY/7B/NOKaoOyOEtJ5fBSNgHUS4gZIvlBiO88M+s+LkHAoGBAJvNZQtjqecXlxmpvKY4EddF3Z9hlzSOtOwjNgP/AodX7fOkpym4kSxcjfLzpNmCeJDcptGO3B9e9uCtiJFifq9eoK/Oik1xUBLmzx9ENv1gQ1rcxlcKVa9OhbOMz0e/MqOW8psuWhksMWF2Xgx9LxLors3v3FSTUFNH1oC2dhQVAoGBANp5gXtK7dG7RM3b9J1AyNQh4GWY9X+pQqfADligwKH9jqGxFo5VgSEQ2avtchjjbPJTigWYGMSP+1fKnvboAxgHK6q6e0N8ITs1jP6+FBre1BMW8D6tFNCerrJ3eOTiMEcJ43dtGOOjCt3xSmRBUfzYFNbFjk3UhPwPCX2NROgF + alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp9pzpS3Czr4w2L32WhpbzL3S5+n7iUITQ3kmBKdaVLojB7/c3H0em9q90quuxKPeH2kOBhPOhLGWUfUcMKObswq/aMgDkoKqA4fAKmT07YVxxduxvEO6z0uwjjP/WGpuFtoGCMhEb5V8xKd+mvpkAblpekJjKPdmAnc/h/sXf8VGDX3czo33zDuxHupR4s1alJKHs3K/p9R0OH5RkxYv0D+I768nNG/ggWmQfdPfwqAeULDO7l4v97jfBaD7cUOtgNW9NCoganO4fSccfZXPSx8i8K8/TSbcbkJtgf2h+g9VMce9xBe9WdTJ7Ke/zMBHXKr4WHJag4Q6RcQPIO5i+QIDAQAB + notifyUrl: http://4qmsbk.natappfree.cc/api/alipay/callBack + realNameAuthenticationReturnUrl: http://xggcsgo.com + authenticationHost: https://jmidcardv1.market.alicloudapi.com + authenticationPath: /idcard/validate + authenticationAppcode: 123 + +jiu-jia-pay: + aliPayUrl: https://zhifu.jiujiaka.com/alipay +# memberId: 96254 +# appKey: 85f5a3fa798bd7 +# apiSecret: 75cef2bf7e14876ee1 + memberId: 96203 + appKey: 7a414cb5fba15e1 + apiSecret: 46bceaeae0720272bb + apiDomain: http://o7csgo.com/ + callbackUrl: ${ruoyi.domainName}/api/jiuJiaPay/callback +yun-xin: + serverUrl: https://api.netease.im/sms + appKey: caa80bbb83e28638ac8cc4aeed46b85e + appSecret: 5daf52e0c256 + templateId: 22512950 +yy-youping: + appKey: 4635531 + publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsKs+kh3AZTsygJHBLnk/RPfhQeP5aMX4fbsiOqe3ppmWAEy3HllHcINzDDl4yUCl6ApOS3gVpwN4TZ645BHEq6MkYZrTa6y4XkzKRawBd8Q+mD9/1NR3pckDZhO6F41VH2+Gj2FbOJ+QA2lZTSrPikHLegM2gaZNFv0Tpc4mxHTF273XwyJi7604BxHbrySzbBTVFEn27ejekJ/fWRajxlHiSrbTvfr8t8t7sosW5c7OBq0JaEa22rjj7OvejyWHbUDA6dZnrFWe53y/DRfhlr46O97VMDhiAVT9nyUtBnpFIFZaJJlSNaOg5sE5rL+WEZB4yITBatxoe+MB2jlZ7wIDAQAB + privateKey: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCwqz6SHcBlOzKAkcEueT9E9+FB4/loxfh9uyI6p7emmZYATLceWUdwg3MMOXjJQKXoCk5LeBWnA3hNnrjkEcSroyRhmtNrrLheTMpFrAF3xD6YP3/U1HelyQNmE7oXjVUfb4aPYVs4n5ADaVlNKs+KQct6AzaBpk0W/ROlzibEdMXbvdfDImLvrTgHEduvJLNsFNUUSfbt6N6Qn99ZFqPGUeJKttO9+vy3y3uyixblzs4GrQloRrbauOPs696PJYdtQMDp1mesVZ7nfL8NF+GWvjo73tUwOGIBVP2fJS0GekUgVlokmVI1o6DmwTmsv5YRkHjIhMFq3Gh74wHaOVnvAgMBAAECggEAc0FUkbiNIr2q5cuw7uO0zga00uqqdJrq0QO7ge6W1j5OaKWjtU3jWFi5Pplj3k+prKbEi2GtJocR/fl1f3zEBIFrI64BDbbhlMueDjP3xS2m9GZAvBVcdTCdYgOunNzcUqTMcU+/VkJqjn80/i3WtcjUuSXNcmmO01eJcNr72Z+K516kcO0bzWfMoownENJZUlE/fm+gv3nTZzcYiGh1EIRAr4xboSQ0IOYq8/aB5lHprqDmT6AQOs7RU392+4rpNisBsq1V6owKHS2KACI8qHKSI3meuosWfHvGRO6jOs9WoiPmDxm5wVdnW009hja/9K7NT3XL+TECd2B7sG05YQKBgQDg1fvMxKfTEratWR9KOHWoRbEtmXfz/Qd7kAT3PyxRIPM8bn/kLCFbr1ky6MIJNAL+32Sf+0/Kcop1g00vx/1ebXh51rgMfZtYbo+JCLFlY62mQ5kZfUppHPtxSWsS8zwJlvrQ0CMLZbSSk3VRKCgo8IncxqrZx44RAXbJca5TXwKBgQDJKB4Y9bEQEQ6+TXJT+iCMuUSMvCsYDXfBD7b++hLEKkTHvUCfaSKqCaj+c4sL9AIXwYIRGvfSjaIwV9sbU8IoJHeST5G4xoUHkLeprPrabaPemuKbcVer1YdnGYc0zRhMZpippqk//Gil5bhEWNs9qaV8CbA15uZyZWskZfmTcQKBgAwW9Yc5O9z5sKD5ndkb2BfIR5KwIecYwAJA6ENzPirOnsWtG2tlhBWuBBncsjJUfaS+1mZT2SKFrjRLbycWleUx9bx3c0YYqvXCXC1+LivJhFjgYachOZMVX9/jlsJaGexaFfqMdpXyThdjnndStXYeGQuqEx3KwTTg40ZJupn5AoGAPVNpuZpN3OQWZpYd4NRE0CI7b9x+FtskQ0uTMrDyv7NTPMHJzc+zrbs8NjbrkT1W1f88tBJQs3RSmgJu3inu62YSvhxcBMUhEWlhVO1TnPwIFXmK1HD9lXmHyjrSKZrNaZ1ttWsF9mfxT4J1ZwDf9jCeLu6VNhmq7/1TaMleCUECgYAjTsRObrXx+m1DniyBrJwPD7sfiZpxIQQBHwBHXkJOrg9rqx+KVYwnP7J5dI/1GE0CEg/APICuJafc51775UyvuJEhX2vsZxVnahb6kJALyUh0+PoPtoX8dTTclGne7MUT2tSiWgwtmbfDNyJzU1DEe9Hqwreep9QKdeey/9UTig== + ornamentsFilePath: www/wwwroot/java/ +zy-zfb: + merKey: ePOACAp3jvyvLP0oxaEqQrA8KdUbqpTo + gateway: http://115.29.205.51:8040 + tranType: 2004 + merchantId: '0236820267' + notifyBaseUrl: ${ruoyi.domainName} +mayi: + gateway: http://www.688fu.com + apiKey: g3aotfg5ud0f6e4qiy4jdkg0ujl7alb1 + memberid: 10071 + payBankCode: 935 + notifyBaseUrl: http://www.mkcsgo.com/ + callBackUrl: ${ruoyi.domainName} +xinghuopay: + serverUrl: https://pay.xinghuoip.com/mapi.php + pid: 1012 + sign: P83k6hAV4k5a54pPb36f3fbi63iikV6E + notifyUrl: http://eur42k.natappfree.cc/api/xinghuopay/notify + returnUrl: http://xggcsgo.com +gfht: + serverUrl: https://nzf.mkweo.cn/mapi.php + pid: 1028 + sign: 8yP2Z9YYlw8I8Yjyy3qGq1282qGg3881 + notifyUrl: http://127.0.0.1:8080/api/gfht/notify + returnUrl: http://www.0811skins.com +baiweipay: + serverUrl: https://baiweipay.com/mapi.php + pid: 1147 + sign: IkxpRBiIR46X1rhIPxCkRM46riRiKpxZ + notifyUrl: http://wbir6c.natappfree.cc/api/baiweipay/notify + returnUrl: http://wbir6c.natappfree.cc +smsbao: + httpUrl: http://api.smsbao.com/sms +# username: mumu123 +# password: hahaha123 + username: ogskins + password: a123456 +winic: + httpUrl: http://service.winic.org:8009/sys_port/gateway/index.asp + username: D6skins + password: x565267 + +mkcsgo: + startLoadOrnaments: false +# startLoadOrnaments: true + fight: + roundTime: 4000 + upgrade: + defaultRequired: 160 + anchorDefaultRequired: 100 + +cs340: + baseUrl: https://open.cs2pifa.com + appKey: open_851d06fdcfcd4c6fa1e4d04f1b9c1a1f + privateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQrxL42IMlfQ8b0JNNlY7M7xS0mujnSyzKOjhANx4BWs1ovlU7pSSOlwVi1POYKlBcje2VfwLB6Smmlwsyw+Gn+YFavFdfYzuhPIeXEtAh9DNYIak2GvN0X7fWTkoHNlL7sOHeXtl7xwBOABW5jUC6LZXCByGp1/lJ9GL5u3jOcDD/x+0PpYhNzZsdGJ+P6441LV54bJkFa0nXYGhuMHpyu9shGZn0uquwLfad4ObxvuzN52mg4MpqAHlgYePH5/JZAKwG45ZVZ/o+voOkQjrhgly0U/2xOTwJ7dU7kuWvEwq96uTP5hV5aWzhYCRhccdF0qUMNlrkpFjvwyhVyyQ1AgMBAAECggEAHEHssX4N6iRgSJ2qaJnNE6J3HQqR6YnkJ4ERL17XRQsw39NQEsrGo7k+7TZVQuBN7+53AqSHcM78CRcQWOAt3PM76ReyRBQZlbhuys3tGX/p+O/b7Bvjpfk1GUOSIh5gx7yYpSYWUHyupaE8z9qY7mzwR6z0Cb3ZP4KkaVkRn3BSiDDnRM7VHrkFm1nQbx80CWNM1B17bse2j6CPBRAEaLsgxF+S9EqZ1+8jB6ByijWdpm04BmxLL5x/M3YUz81LuUqAAGVKl3qNDvgaa+d9BSb+v1O8G6S0hmsWNVc62cHKdxAv6QnRaPZV/ji+tOCRtLNmaXJai917z+rC37d5sQKBgQDwz/4NSzJDr+pvzEv8mp36F3aKLGl7uynkDzyUfo4OifaI4Pw+bW4UDc1pkSwVC0lK4ZNpEcTmXHvxKvduRpCOlr8Io8JKuUqkFLgTwBqZNS/dCcAp+lDc13ep3oTjhGq6giSkXCee//X6UbWHfv9dBY6nkhDRbvzEHjS64st0RQKBgQDd2FqH9OlBpNQNAf2A04TGEfL4WCXjghXqH9U6hYK4C4uefo9IBF2wEwb5KZkHGt0c0cymrF2UpxxpkuUwqCU3RgD0JEF0FuoQ9zLJO1POdG/eTGmYnAUe6gIC4Mu9ftogbEbw9qltX0Wf4QRLamll1jgWWR2j4KngdakWsrwHMQKBgQDi0QVZQBp35pnJ84MeygNCLBdazboM8JzkUfWdaBYlW4z6H+92XVvxR47SgAolSBwgi5dsv2/WCgYzgWTMWWqO4y3L1XKLLzs+kMVQ1QA4jgAnEagN4deIKupq9Uv1gCOGyE37AVHQUQY+X/e7R8L+ut1CuDFfEkrB7jF8VWGMgQKBgH2fQQyR4/AXHTjMcrebjyj2CwcUK0hcZnpwUjdUWQRNDV/PvOUEC+VjsOQw6QW78Y+bQe7z1f4SbyVdWEkoPgLBslKNoT7SpvK6eFi5LqjPCHXyAS2407GAw2jL0LNafLw1dCqJEsHrXCq/qcXm2Q4gsxv8lKsy0h9XoUtIJO9RAoGAI7M/x0jb4zH/FZOupDKQNAaw8rE4chYbxMUyB8YnZccZ/qfVQhsSlITgmCkSpgMDFZtYAzk8kJhmf8+cDYMjwp3hFWTNvtc8bHvW4oG5X2a6hQUmQlooc948Hfvv4fXNkAB81lGch64CQPuwPxPvfGAVB9TCLabqkqjyco+qJpo= + publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0K8S+NiDJX0PG9CTTZWOzO8UtJro50ssyjo4QDceAVrNaL5VO6UkjpcFYtTzmCpQXI3tlX8CwekpppcLMsPhp/mBWrxXX2M7oTyHlxLQIfQzWCGpNhrzdF+31k5KBzZS+7Dh3l7Ze8cATgAVuY1Aui2Vwgchqdf5SfRi+bt4znAw/8ftD6WITc2bHRifj+uONS1eeGyZBWtJ12BobjB6crvbIRmZ9LqrsC32neDm8b7szedpoODKagB5YGHjx+fyWQCsBuOWVWf6Pr6DpEI64YJctFP9sTk8Ce3VO5LlrxMKverkz+YVeWls4WAkYXHHRdKlDDZa5KRY78MoVcskNQIDAQAB + callbackPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmcKFJsOYnPBQtkmIFqYEkYZ8E2cJXCG08JWOBJ7wBacrXcWhqtDclXIWe32d59XK3ghRcEOsSaarvyaNZIafVKoFhlsDq1hhsAB/oBVLjpP4oBkxcwB2oFa/Op32cw166TJEdnjC7hkJdIjsHdcSCUnoYdZEDCj8TWSgIm6uSo6n5d/xBiFAqK4NoYEVC7FiFv5wX1zIYh47ylUXRMQprP/qVvfZknEDv0Qpo6btoIKktBRmHkq/TLlNv5XumlNA1mzeHd2GkjNuJNiT6tm1wEqhgYryqSqc0X+64m4EgL4omGVEXd73iYhqDisFmDu2uoGLxJM7SvGoMj8JVWfHjwIDAQAB + +baidu: + baseUrl: https://ocpc.baidu.com/ocpcapi/api/uploadConvertData + token: l1UbXAra0qpi62IK0NJHtkNMH6eXIX1S@30H8dMqjvCoC4wi9AArgqufTsgwsmXZI + logidUrl: https://cs.upjhpea.cn?bd_vid= \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application-pro.yml b/ruoyi-admin/src/main/resources/application-pro.yml new file mode 100644 index 0000000..0591654 --- /dev/null +++ b/ruoyi-admin/src/main/resources/application-pro.yml @@ -0,0 +1,256 @@ +# 日志配置 +logging: + level: + com.ruoyi: info + org.springframework: warn +# 项目相关配置 +ruoyi: + # 名称 + name: Skins + # 版本 + version: 3.8.6 + # 版权年份 + copyrightYear: 2023 + # 实例演示开关 + demoEnabled: true + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) +# profile: D:/ruoyi/uploadPath + profile: /www/static + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数字计算 char 字符验证 + captchaType: math + # 项目全局域名配置 + # domainName: http://192.168.3.25:${server.port} + # domainName: http://api.mkcsgo.com/prod-api + domainName: "" + +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://127.0.0.1:3306/fire?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: fire + password: iTSJSPPZM3LSGAPC + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置连接超时时间 + connectTimeout: 30000 + # 配置网络超时时间 + socketTimeout: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true + # redis 配置 + data: + redis: + host: 127.0.0.1 + port: 6379 + password: FireSkins@99999 + lettuce: + # 关键配置:指定协议版本 + client-options: + protocol: RESP2 + redis: + # 地址 +# host: 47.106.150.25 + host: 127.0.0.1 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 4 + # 密码 + password: FireSkins@99999 + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + rabbitmq: +# host: 47.106.150.25 + host: 127.0.0.1 + port: 5672 + username: skins + password: mk + virtual-host: /skins + listener: + simple: + acknowledge-mode: MANUAL +# prefetch: 1 #每次从队列中取一个,轮询分发,默认是公平分发 +# retry: +# max-attempts: 5 # 重试次数 +# enabled: true # 开启重试 +# rabbitmqclt change_password admin admin + +zbt: + # appKey: a4d11711dff611dbbeb7987f7ff96880 + # appKey: bfb344da84660d8407e88f38deac1385 + appKey: ed4dbc0fd014cafa1f9b6992c175945b + language: zh_CN + appId: 730 + baseUrl: https://app.zbt.com + callbackUrl: http://qnxkci.natappfree.cc/admin/zbt/callback +v5item: + baseUrl: https://v2.v5item.com + # merchantKey: e315a295403d4bfab2605cb743be6e6d + # account: 13371345595 + # password: a123456! + merchantKey: 2c4c366d5c294b66addd5d28af34e69c + account: 19385220095 + password: Hrycly200811 + publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi2+C4wEwOZTa3Ep0bjJGtPQUBbJAnO1fCjzWu7IBta+Vi8QQq6ujJFirUPO975Ln+zw2+YpSZVssBAqEOgLbHM72qTBbUC7dq7Sj7QI0cKY/9PWfCcAHohKYKl/IJ7tlA1Cndvh88WdPGoldJc2ETX1fKeoICf4xPQu/7XTfY2lyY5Z14/Lo15KJe6+wnMc2xFWbjjeQ+PiMtC/Iqihf/vLI5APE0qlcB9Kagvt4Efap64NRVJkqT4TrhQCVO0oHDVcUxXOVcu7lWMUBaiv0HclGcyguVLMFdtXLiGp2TY5sMYIq4Prpzm/zs5J5bTavAIBoScWOkUCz3WMdGBgFUwIDAQAB +alipay: + serverUrl: https://openapi.alipay.com/gateway.do + format: JSON + charset: UTF-8 + signType: RSA2 + merchants: + - appId: 2021004130630119 + privateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmzDV4XE3/iR2MLKV3uj+vkTWBL2mRVXKlfDmyOOeAN8yZXeABH9hIB7flxIZ6CsB7NWbQHfPdO2UrP3T43VGMkNYDFgj27mz75zo5sEynOxlW4tL1s5S9nF1/HX+4KlbJLG7qs/pWpmcQorTeDxQHfMUFQ/neGAYxHFBCLAkziHmz2FuAVVW2JRawgOhnwV67gFfeGVuuG2jLJ0oqxfzmE2I2WL9YATSuqy0ELbiS2ntd/clmDUwK1VjjhDV5qD/E5w3Il3avQGihauX2goGLV6nofgMdgG8NSqhlW7rzDTDfhswvrFKPJBtJE3Z/FsuDTIeJyH5dojBw9Uvu3nrNAgMBAAECggEBAItyEgnL8ZF/CmrUMInF9dRLq9WS08kjDLP5BStQk4oIHE7jwpBuFR0zBrVa9ao2LvFSld/MCsKcE1pytNISBUllFOaDl38JmaeHipAMKiltKSQQgZnURre2cBwDh3FqRB/vu9MQIGwsteGchWVYacBQuU5LpCvfHd4U86w+sHqZc+1TF7V2tAxxxfuxI5GR+xHRNOEn/E2Dut6E+jNpX6aD1INgpTxteb8U7/mZSzo9tCC2k07qrDFIr2Zve0BwvJoMBso0OH3fyFHuMirjg14E5fFeD296MUHLZ2rNq8WiA2o8vmdxP/PsVqCyr6zucp3SadlQaDbggYG/PVINAmkCgYEA6ysnXTVx2P3RDaE8TbY39c7imNyIPwJYYqGM9LL/a3Ih+KSAJqsXWycmnluf6/C+U66hv3bs/aWkIT3mgiz6fgL2SIqatJmScIlKLl9K2KdVcukCf4eTUDJc8qjEkm8zKeowzyBUgiCLP5grnylTPO+ByU727zvjBMoop/V0RecCgYEAtZKhI8f5zgYFgmA+mVqYWWKG+DtXbeOA0uyJFuJWCJI7Wk3WuX1AdzEVtshIZrZFipHFTFcklx3Sd7DVwHUThDy/HigUFrqraGHiOAeNal3yhAtcEw7eNlHplUzNoFF5mlossOPMSk0RE3l3YAXUAX9/6WT/67RYGvEiL9mhuysCgYBya9HAUTOuf3iK6CteKE28BMhN1edYuPxmKPJbUQhjr+mFgAx2RAKeMENjA3OhKcaBEtPSQ7v1gfWE0whnyBis3SYVj5LMUnhk1vfH7keNeIVj3ql/gWiUuZxt2N2j3gUH3NGYxcM4eTsnWxPGYiYE3QfJ+ei95+aCOxZi+lNzewKBgEh4xBRyBwTyZlSJcbLn9BMJPnHpzZWwzoRhoL4fjNNS+pmqo9/ZXFe2ocIY4r0MV2ldkXBM/+/JGW5oiIiOskF/tZCl0o7X5v58wzw1133zDY8isZdxwcPQofjTdOdeXbj78QaRhcLKS9yICNust9f5OebAR12e1+O5BzouFV2NAoGBAKPzyFhhvyrF6cXDBPy1g44f4Ce4/+B5ouA+oAiK7AA1y7fkY3QQvUreMSK3t5luOBg5WhrZ+YokyMe7mfGIdK3T0Upw9QUMIIwtyX85FzsXZxxhBn4f83hYx3uVd0NiH2/AupPu3UAls82kJoiSOCyF+AB/Z7hd0lxSNk6zDAPl + alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmzzBx/4+G6YaFdocpDElTVFPMRYSkDNmxEYUD9yrheQdWR8czAjjzx5+NixjIcqNic/uEH3dtmfvfkcNT7mSNG+QhAV7XGOUXKYcQr7L+J7J98UGX3RcO1NPYFLyNcKu/S9IryLYdl6ZiSdNOgGr8xxWfMMpgDbsQlUKPT38ob+u22fQBkXJKBxrbZ9P0kzIoYN5S18qEDS0avAks0kkz05uffgudtopM/JlHW7Ts33s741bw0mN/GpehOjn8DDAlvIXdpZWF4pe/npwZCM/cVdRvGCkW8Z5lRgQPHEFSec56gxMONS5Zdh2IVMNNCUHO4f7sVQVx2luNeISRyDxvwIDAQAB +# - appId: 2021004148601326 +# privateKey: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCAoNFbDw6Cti1lAjL+nMVGgpGwMjaJY7durB54keXo4czzya0hviBtdtUEXZqC/v5xrO0wBO2KVZfXbcG+zY2ZFEerK+UdxAewG0Ngy6tKrrbPbG6eHJg27CFWW3dnlQ4xsYIun2AXd+HlSRamq8hbHhaMUL0zpS1u0IRMj0hknwewDUbHMc1bc/2305XX80r09enMlDjC/XHWtjkitvoTepHv2wOFZcADftmkmBh/IxRpLskxakbi4si9XI2I9diMs7rVh+UJ24ys5aeIFCzFOlsCf4zfz2oF1HN8UJHGvp2I6JwUQIHa4spMnfdl0ziRnkAWoXmeTQok/H4Nl2ahAgMBAAECggEAY/obn5ZNZgWptPgHoOVDZLG6AWSWBSXPfqMjr/1e4SsYvWhFPJPl6o9RtOn4XddK+Bo75XX/F+W9dsGltONaKuCbcA1XMVKb0yLttP+5LPwttX2HYTfCi/XvV4sSfR1bAf8Bu7E+5B7XIvszMusUeNYjGn7rAIhjjt1d54/66dGi4UkeXd7IMaQNom9Cz+Z2w9DtcUPShXYg9oL8f0K3BoYS1u0JdG94Chlj+iPh2vBRvWGkk6kHrbagDjcMf13CtnQuS+SHR7zGLVNcotDElOxGPH7Ac0fYlv5rzW7/YWCs3F8mrEBZYfZ5TGrpuqqWCg3Z3UA3tncYrtxJnKoJEQKBgQDGDjcyXVskiydLAMJO7XzeiqncFO4kq7/UC/2qn38OCBU295ufRVN4T7cP8AsTencF2OakovX3YqJod0K9BmfjiCmaUug3hCIzA/mLz75WJlJYdQLKbvW6pFRR5KpOmlynDwfiF7TtUOkrDeFELDGZTYNEAJHPQL3m3PhvG2tx/QKBgQCmQrI+aZ8laBy8u9Za/MUAL+aj7A9BdezT0a/Kvk23evll8M4oT/fDNYJ0Pzp+cros63rTjVDEhhhAsElflQ1yVJkjyBQkAUibHjewUlp5mmTdmwGd+EEMMd4dyKAIFIu+nbhyKdIoJl438vRp0rM2mWp0iimiD0k8WWy8EE/mdQKBgHXlWG4erHagbw9ysDgTntVKbbqYqvohUtuDF8lNkHBl11sVIgo5Vmqz6SEPn0hFCjzs1L8EUq6khFDzTUMAYS9MipdW6uzBNypvH0lerMrTnQkS/kpKqvQjhh4JpGeN88FubR7lRcpO872xUXsnY48CWTVNsX5R57lZqHlbNSZxAoGAbi9bg4NqRlcmXGo8GFbWlFYqi06Bmcd2abbYwwu147hrGoUpi4vbzpJi33mb4XtVKTnyT8ui0GtyHUT3i5HztGaeixkpR+dV3/95trANZ/PLjxYQT0nciiFua9yvOkurhWtvdrGCprsuy/OGya8quC+aXpvGFbH9vjIhns1MtpUCgYBL2EUUQRvRf8uK1oOes6kdbwmz+WMfylJvrfa9sGnpVWObsycbhaCUcLTtfuVbh79z33TeF6WqS/jsODUmSlbLHIHicH7zgZYkHLBcecFPx1F/gT1nvpr8bJatYfCEV7K1jdwGVCag2usfSn69Iq0js37zPXdaK0s6DWjUCXZvhA== +# alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgK + - appId: 2021003192641091 + privateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6HLvzFovqDXgfJoUiOzJX8kFylowGJ8WxSKPujKfb7HVUVNW7xItZy2KRuoQRkXl1JvhZ9UQRygKyIfiuigObifUtcPEpt6COCN75gXA1m1VxThUC9qHnCJBh6hTNwk+YjpuhMYv6Jbv8sZ5QBuxb3S7O8DNyf0skxCHy+FeMN2LNKQEm32j81c7fIFvQKT0X9N1JezHEdLoNohNnJTip7iqi+kA/8opSMMaP+up02V3+RV3OWe75Sb+cZ/r02q0bPfvOIUYBXJqUTDlzcqzytgELqo3iRTybrDX5ZL4eiRIJymkog3Bkilkp9hKdBix8aGrjHabOVXDuoji7Xn+pAgMBAAECggEAcUEVGqtovygtJf+h2p/dVelk7F8ynHhNdEG5CGxtpYYyK/xoCBhlmGIM/JNc1PgKykDJtqxrHMwqcc38b/IqmdcM7Pciunk06dn6HiknaGe7SfTzEleVVGtAuWTh/gg1uadmi2QEQ1f9GpvXs+lSoeBqIKHJHO7bgmccJaf8VqU/7H3UCLWK2UxtRMfO27hge2G8a350S7iVIyHPoXUz5L2P+5TP0T8C54QZsBp9mzaUfgaCTcw+zgPbYsTX9eP5IL7WflkZq0gPIG5XWfEiV3O2jATiNdOTtDhrEBWvY6ER5qRU0hA8tS9jNiTjgLjtNf+o7C9QlqZfSyuNdG87dQKBgQDfs8399uJHzdu7piKKnE/vhDGeXQPmGHoA+p6XUt/3Ou9nrI81EuItQ/vmLFq0cWyBg7vQFadU2IZxdtFsUZN1N6nE3wHprHmQ+MwaoOaq8f26pEPrau8DyJy3feMyBlgUbG9y/xffM/nMyVjJ04YNNnY/OIwbaLHKPIatwtQzZwKBgQDU+5KPWFD3QutSA005R0Pde2g//N+/nMX4wozx2uQrwbPJze2EAjyfSMsqX9Zgj6FExbLR4kbsnJiLip3nATqAgOyQxKS+M3vPNbJvRxAwI7dlRmq1k+JG3ujN6mDYZxeKvy0Rh8E+iocVhJsKN1xN1NeicJidB8/DeBR3CiZabwKBgCSmP5xZA0+CQXqnyEbvPwVdogdw87+RyQM6DBt8n2bQyAIzOi8Wzelelvlp0N55Kq65V17K/WQgKT6TmdHmN4NfwDeZ1aVj9XKz4DSIoeHlzYzzdpEuLsRu7IX/YOaENY/7B/NOKaoOyOEtJ5fBSNgHUS4gZIvlBiO88M+s+LkHAoGBAJvNZQtjqecXlxmpvKY4EddF3Z9hlzSOtOwjNgP/AodX7fOkpym4kSxcjfLzpNmCeJDcptGO3B9e9uCtiJFifq9eoK/Oik1xUBLmzx9ENv1gQ1rcxlcKVa9OhbOMz0e/MqOW8psuWhksMWF2Xgx9LxLors3v3FSTUFNH1oC2dhQVAoGBANp5gXtK7dG7RM3b9J1AyNQh4GWY9X+pQqfADligwKH9jqGxFo5VgSEQ2avtchjjbPJTigWYGMSP+1fKnvboAxgHK6q6e0N8ITs1jP6+FBre1BMW8D6tFNCerrJ3eOTiMEcJ43dtGOOjCt3xSmRBUfzYFNbFjk3UhPwPCX2NROgF + alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp9pzpS3Czr4w2L32WhpbzL3S5+n7iUITQ3kmBKdaVLojB7/c3H0em9q90quuxKPeH2kOBhPOhLGWUfUcMKObswq/aMgDkoKqA4fAKmT07YVxxduxvEO6z0uwjjP/WGpuFtoGCMhEb5V8xKd+mvpkAblpekJjKPdmAnc/h/sXf8VGDX3czo33zDuxHupR4s1alJKHs3K/p9R0OH5RkxYv0D+I768nNG/ggWmQfdPfwqAeULDO7l4v97jfBaD7cUOtgNW9NCoganO4fSccfZXPSx8i8K8/TSbcbkJtgf2h+g9VMce9xBe9WdTJ7Ke/zMBHXKr4WHJag4Q6RcQPIO5i+QIDAQAB + notifyUrl: http://4qmsbk.natappfree.cc/api/alipay/callBack + realNameAuthenticationReturnUrl: http://xggcsgo.com + authenticationHost: https://jmidcardv1.market.alicloudapi.com + authenticationPath: /idcard/validate + authenticationAppcode: 123 + +jiu-jia-pay: + aliPayUrl: https://zhifu.jiujiaka.com/alipay +# memberId: 96254 +# appKey: 85f5a3fa798bd7 +# apiSecret: 75cef2bf7e14876ee1 + memberId: 96203 + appKey: 7a414cb5fba15e1 + apiSecret: 46bceaeae0720272bb + apiDomain: http://o7csgo.com/ + callbackUrl: ${ruoyi.domainName}/api/jiuJiaPay/callback +yun-xin: + serverUrl: https://api.netease.im/sms + appKey: caa80bbb83e28638ac8cc4aeed46b85e + appSecret: 5daf52e0c256 + templateId: 22512950 +yy-youping: + appKey: 4635531 + publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsKs+kh3AZTsygJHBLnk/RPfhQeP5aMX4fbsiOqe3ppmWAEy3HllHcINzDDl4yUCl6ApOS3gVpwN4TZ645BHEq6MkYZrTa6y4XkzKRawBd8Q+mD9/1NR3pckDZhO6F41VH2+Gj2FbOJ+QA2lZTSrPikHLegM2gaZNFv0Tpc4mxHTF273XwyJi7604BxHbrySzbBTVFEn27ejekJ/fWRajxlHiSrbTvfr8t8t7sosW5c7OBq0JaEa22rjj7OvejyWHbUDA6dZnrFWe53y/DRfhlr46O97VMDhiAVT9nyUtBnpFIFZaJJlSNaOg5sE5rL+WEZB4yITBatxoe+MB2jlZ7wIDAQAB + privateKey: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCwqz6SHcBlOzKAkcEueT9E9+FB4/loxfh9uyI6p7emmZYATLceWUdwg3MMOXjJQKXoCk5LeBWnA3hNnrjkEcSroyRhmtNrrLheTMpFrAF3xD6YP3/U1HelyQNmE7oXjVUfb4aPYVs4n5ADaVlNKs+KQct6AzaBpk0W/ROlzibEdMXbvdfDImLvrTgHEduvJLNsFNUUSfbt6N6Qn99ZFqPGUeJKttO9+vy3y3uyixblzs4GrQloRrbauOPs696PJYdtQMDp1mesVZ7nfL8NF+GWvjo73tUwOGIBVP2fJS0GekUgVlokmVI1o6DmwTmsv5YRkHjIhMFq3Gh74wHaOVnvAgMBAAECggEAc0FUkbiNIr2q5cuw7uO0zga00uqqdJrq0QO7ge6W1j5OaKWjtU3jWFi5Pplj3k+prKbEi2GtJocR/fl1f3zEBIFrI64BDbbhlMueDjP3xS2m9GZAvBVcdTCdYgOunNzcUqTMcU+/VkJqjn80/i3WtcjUuSXNcmmO01eJcNr72Z+K516kcO0bzWfMoownENJZUlE/fm+gv3nTZzcYiGh1EIRAr4xboSQ0IOYq8/aB5lHprqDmT6AQOs7RU392+4rpNisBsq1V6owKHS2KACI8qHKSI3meuosWfHvGRO6jOs9WoiPmDxm5wVdnW009hja/9K7NT3XL+TECd2B7sG05YQKBgQDg1fvMxKfTEratWR9KOHWoRbEtmXfz/Qd7kAT3PyxRIPM8bn/kLCFbr1ky6MIJNAL+32Sf+0/Kcop1g00vx/1ebXh51rgMfZtYbo+JCLFlY62mQ5kZfUppHPtxSWsS8zwJlvrQ0CMLZbSSk3VRKCgo8IncxqrZx44RAXbJca5TXwKBgQDJKB4Y9bEQEQ6+TXJT+iCMuUSMvCsYDXfBD7b++hLEKkTHvUCfaSKqCaj+c4sL9AIXwYIRGvfSjaIwV9sbU8IoJHeST5G4xoUHkLeprPrabaPemuKbcVer1YdnGYc0zRhMZpippqk//Gil5bhEWNs9qaV8CbA15uZyZWskZfmTcQKBgAwW9Yc5O9z5sKD5ndkb2BfIR5KwIecYwAJA6ENzPirOnsWtG2tlhBWuBBncsjJUfaS+1mZT2SKFrjRLbycWleUx9bx3c0YYqvXCXC1+LivJhFjgYachOZMVX9/jlsJaGexaFfqMdpXyThdjnndStXYeGQuqEx3KwTTg40ZJupn5AoGAPVNpuZpN3OQWZpYd4NRE0CI7b9x+FtskQ0uTMrDyv7NTPMHJzc+zrbs8NjbrkT1W1f88tBJQs3RSmgJu3inu62YSvhxcBMUhEWlhVO1TnPwIFXmK1HD9lXmHyjrSKZrNaZ1ttWsF9mfxT4J1ZwDf9jCeLu6VNhmq7/1TaMleCUECgYAjTsRObrXx+m1DniyBrJwPD7sfiZpxIQQBHwBHXkJOrg9rqx+KVYwnP7J5dI/1GE0CEg/APICuJafc51775UyvuJEhX2vsZxVnahb6kJALyUh0+PoPtoX8dTTclGne7MUT2tSiWgwtmbfDNyJzU1DEe9Hqwreep9QKdeey/9UTig== + ornamentsFilePath: www/wwwroot/java/ +zy-zfb: + merKey: ePOACAp3jvyvLP0oxaEqQrA8KdUbqpTo + gateway: http://115.29.205.51:8040 + tranType: 2004 + merchantId: '0236820267' + notifyBaseUrl: ${ruoyi.domainName} +mayi: + gateway: http://www.688fu.com + apiKey: g3aotfg5ud0f6e4qiy4jdkg0ujl7alb1 + memberid: 10071 + payBankCode: 935 + notifyBaseUrl: http://www.mkcsgo.com/ + callBackUrl: ${ruoyi.domainName} +xinghuopay: + serverUrl: https://pay.xinghuoip.com/mapi.php + pid: 1012 + sign: P83k6hAV4k5a54pPb36f3fbi63iikV6E + notifyUrl: http://eur42k.natappfree.cc/api/xinghuopay/notify + returnUrl: http://xggcsgo.com +gfht: + serverUrl: https://nzf.mkweo.cn/mapi.php + pid: 1028 + sign: 8yP2Z9YYlw8I8Yjyy3qGq1282qGg3881 + notifyUrl: http://127.0.0.1:8080/api/gfht/notify + returnUrl: http://www.0811skins.com +baiweipay: + serverUrl: https://baiweipay.com/mapi.php + pid: 1147 + sign: IkxpRBiIR46X1rhIPxCkRM46riRiKpxZ + notifyUrl: http://wbir6c.natappfree.cc/api/baiweipay/notify + returnUrl: http://wbir6c.natappfree.cc +smsbao: + httpUrl: http://api.smsbao.com/sms +# username: mumu123 +# password: hahaha123 + username: ogskins + password: a123456 +winic: + httpUrl: http://service.winic.org:8009/sys_port/gateway/index.asp + username: D6skins + password: x565267 + +mkcsgo: + startLoadOrnaments: false +# startLoadOrnaments: true + fight: + roundTime: 4000 + upgrade: + defaultRequired: 160 + anchorDefaultRequired: 100 + +cs340: + baseUrl: https://open.cs2pifa.com + appKey: open_851d06fdcfcd4c6fa1e4d04f1b9c1a1f + privateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQrxL42IMlfQ8b0JNNlY7M7xS0mujnSyzKOjhANx4BWs1ovlU7pSSOlwVi1POYKlBcje2VfwLB6Smmlwsyw+Gn+YFavFdfYzuhPIeXEtAh9DNYIak2GvN0X7fWTkoHNlL7sOHeXtl7xwBOABW5jUC6LZXCByGp1/lJ9GL5u3jOcDD/x+0PpYhNzZsdGJ+P6441LV54bJkFa0nXYGhuMHpyu9shGZn0uquwLfad4ObxvuzN52mg4MpqAHlgYePH5/JZAKwG45ZVZ/o+voOkQjrhgly0U/2xOTwJ7dU7kuWvEwq96uTP5hV5aWzhYCRhccdF0qUMNlrkpFjvwyhVyyQ1AgMBAAECggEAHEHssX4N6iRgSJ2qaJnNE6J3HQqR6YnkJ4ERL17XRQsw39NQEsrGo7k+7TZVQuBN7+53AqSHcM78CRcQWOAt3PM76ReyRBQZlbhuys3tGX/p+O/b7Bvjpfk1GUOSIh5gx7yYpSYWUHyupaE8z9qY7mzwR6z0Cb3ZP4KkaVkRn3BSiDDnRM7VHrkFm1nQbx80CWNM1B17bse2j6CPBRAEaLsgxF+S9EqZ1+8jB6ByijWdpm04BmxLL5x/M3YUz81LuUqAAGVKl3qNDvgaa+d9BSb+v1O8G6S0hmsWNVc62cHKdxAv6QnRaPZV/ji+tOCRtLNmaXJai917z+rC37d5sQKBgQDwz/4NSzJDr+pvzEv8mp36F3aKLGl7uynkDzyUfo4OifaI4Pw+bW4UDc1pkSwVC0lK4ZNpEcTmXHvxKvduRpCOlr8Io8JKuUqkFLgTwBqZNS/dCcAp+lDc13ep3oTjhGq6giSkXCee//X6UbWHfv9dBY6nkhDRbvzEHjS64st0RQKBgQDd2FqH9OlBpNQNAf2A04TGEfL4WCXjghXqH9U6hYK4C4uefo9IBF2wEwb5KZkHGt0c0cymrF2UpxxpkuUwqCU3RgD0JEF0FuoQ9zLJO1POdG/eTGmYnAUe6gIC4Mu9ftogbEbw9qltX0Wf4QRLamll1jgWWR2j4KngdakWsrwHMQKBgQDi0QVZQBp35pnJ84MeygNCLBdazboM8JzkUfWdaBYlW4z6H+92XVvxR47SgAolSBwgi5dsv2/WCgYzgWTMWWqO4y3L1XKLLzs+kMVQ1QA4jgAnEagN4deIKupq9Uv1gCOGyE37AVHQUQY+X/e7R8L+ut1CuDFfEkrB7jF8VWGMgQKBgH2fQQyR4/AXHTjMcrebjyj2CwcUK0hcZnpwUjdUWQRNDV/PvOUEC+VjsOQw6QW78Y+bQe7z1f4SbyVdWEkoPgLBslKNoT7SpvK6eFi5LqjPCHXyAS2407GAw2jL0LNafLw1dCqJEsHrXCq/qcXm2Q4gsxv8lKsy0h9XoUtIJO9RAoGAI7M/x0jb4zH/FZOupDKQNAaw8rE4chYbxMUyB8YnZccZ/qfVQhsSlITgmCkSpgMDFZtYAzk8kJhmf8+cDYMjwp3hFWTNvtc8bHvW4oG5X2a6hQUmQlooc948Hfvv4fXNkAB81lGch64CQPuwPxPvfGAVB9TCLabqkqjyco+qJpo= + publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0K8S+NiDJX0PG9CTTZWOzO8UtJro50ssyjo4QDceAVrNaL5VO6UkjpcFYtTzmCpQXI3tlX8CwekpppcLMsPhp/mBWrxXX2M7oTyHlxLQIfQzWCGpNhrzdF+31k5KBzZS+7Dh3l7Ze8cATgAVuY1Aui2Vwgchqdf5SfRi+bt4znAw/8ftD6WITc2bHRifj+uONS1eeGyZBWtJ12BobjB6crvbIRmZ9LqrsC32neDm8b7szedpoODKagB5YGHjx+fyWQCsBuOWVWf6Pr6DpEI64YJctFP9sTk8Ce3VO5LlrxMKverkz+YVeWls4WAkYXHHRdKlDDZa5KRY78MoVcskNQIDAQAB + callbackPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmcKFJsOYnPBQtkmIFqYEkYZ8E2cJXCG08JWOBJ7wBacrXcWhqtDclXIWe32d59XK3ghRcEOsSaarvyaNZIafVKoFhlsDq1hhsAB/oBVLjpP4oBkxcwB2oFa/Op32cw166TJEdnjC7hkJdIjsHdcSCUnoYdZEDCj8TWSgIm6uSo6n5d/xBiFAqK4NoYEVC7FiFv5wX1zIYh47ylUXRMQprP/qVvfZknEDv0Qpo6btoIKktBRmHkq/TLlNv5XumlNA1mzeHd2GkjNuJNiT6tm1wEqhgYryqSqc0X+64m4EgL4omGVEXd73iYhqDisFmDu2uoGLxJM7SvGoMj8JVWfHjwIDAQAB + +baidu: + baseUrl: https://ocpc.baidu.com/ocpcapi/api/uploadConvertData + token: l1UbXAra0qpi62IK0NJHtkNMH6eXIX1S@30H8dMqjvCoC4wi9AArgqufTsgwsmXZI + logidUrl: https://cs.upjhpea.cn?bd_vid= \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application-test.yml b/ruoyi-admin/src/main/resources/application-test.yml new file mode 100644 index 0000000..e6c4c38 --- /dev/null +++ b/ruoyi-admin/src/main/resources/application-test.yml @@ -0,0 +1,260 @@ +server: + # 服务器的HTTP端口,默认为8080 + port: 8082 + address: 0.0.0.0 +# 日志配置 +logging: + level: + com.ruoyi: info + org.springframework: warn +# 项目相关配置 +ruoyi: + # 名称 + name: Skins + # 版本 + version: 3.8.6 + # 版权年份 + copyrightYear: 2023 + # 实例演示开关 + demoEnabled: true + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) +# profile: D:/ruoyi/uploadPath + profile: /www/static + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数字计算 char 字符验证 + captchaType: math + # 项目全局域名配置 + # domainName: http://192.168.3.25:${server.port} + # domainName: http://api.mkcsgo.com/prod-api + domainName: "" + +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://127.0.0.1:3307/fire?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: fire + password: iTSJSPPZM3LSGAPC + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置连接超时时间 + connectTimeout: 30000 + # 配置网络超时时间 + socketTimeout: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true + # redis 配置 + data: + redis: + host: 127.0.0.1 + port: 6380 + password: FireSkins@99999 + lettuce: + # 关键配置:指定协议版本 + client-options: + protocol: RESP2 + redis: + # 地址 +# host: 47.106.150.25 + host: 127.0.0.1 + # 端口,默认为6379 + port: 6380 + # 数据库索引 + database: 4 + # 密码 + password: FireSkins@99999 + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + rabbitmq: +# host: 47.106.150.25 + host: 127.0.0.1 + port: 5673 + username: skins + password: mk + virtual-host: /skins + listener: + simple: + acknowledge-mode: MANUAL +# prefetch: 1 #每次从队列中取一个,轮询分发,默认是公平分发 +# retry: +# max-attempts: 5 # 重试次数 +# enabled: true # 开启重试 +# rabbitmqclt change_password admin admin + +zbt: + # appKey: a4d11711dff611dbbeb7987f7ff96880 + # appKey: bfb344da84660d8407e88f38deac1385 + appKey: ed4dbc0fd014cafa1f9b6992c175945b + language: zh_CN + appId: 730 + baseUrl: https://app.zbt.com + callbackUrl: http://qnxkci.natappfree.cc/admin/zbt/callback +v5item: + baseUrl: https://v2.v5item.com + # merchantKey: e315a295403d4bfab2605cb743be6e6d + # account: 13371345595 + # password: a123456! + merchantKey: 2c4c366d5c294b66addd5d28af34e69c + account: 19385220095 + password: Hrycly200811 + publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi2+C4wEwOZTa3Ep0bjJGtPQUBbJAnO1fCjzWu7IBta+Vi8QQq6ujJFirUPO975Ln+zw2+YpSZVssBAqEOgLbHM72qTBbUC7dq7Sj7QI0cKY/9PWfCcAHohKYKl/IJ7tlA1Cndvh88WdPGoldJc2ETX1fKeoICf4xPQu/7XTfY2lyY5Z14/Lo15KJe6+wnMc2xFWbjjeQ+PiMtC/Iqihf/vLI5APE0qlcB9Kagvt4Efap64NRVJkqT4TrhQCVO0oHDVcUxXOVcu7lWMUBaiv0HclGcyguVLMFdtXLiGp2TY5sMYIq4Prpzm/zs5J5bTavAIBoScWOkUCz3WMdGBgFUwIDAQAB +alipay: + serverUrl: https://openapi.alipay.com/gateway.do + format: JSON + charset: UTF-8 + signType: RSA2 + merchants: + - appId: 2021004130630119 + privateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmzDV4XE3/iR2MLKV3uj+vkTWBL2mRVXKlfDmyOOeAN8yZXeABH9hIB7flxIZ6CsB7NWbQHfPdO2UrP3T43VGMkNYDFgj27mz75zo5sEynOxlW4tL1s5S9nF1/HX+4KlbJLG7qs/pWpmcQorTeDxQHfMUFQ/neGAYxHFBCLAkziHmz2FuAVVW2JRawgOhnwV67gFfeGVuuG2jLJ0oqxfzmE2I2WL9YATSuqy0ELbiS2ntd/clmDUwK1VjjhDV5qD/E5w3Il3avQGihauX2goGLV6nofgMdgG8NSqhlW7rzDTDfhswvrFKPJBtJE3Z/FsuDTIeJyH5dojBw9Uvu3nrNAgMBAAECggEBAItyEgnL8ZF/CmrUMInF9dRLq9WS08kjDLP5BStQk4oIHE7jwpBuFR0zBrVa9ao2LvFSld/MCsKcE1pytNISBUllFOaDl38JmaeHipAMKiltKSQQgZnURre2cBwDh3FqRB/vu9MQIGwsteGchWVYacBQuU5LpCvfHd4U86w+sHqZc+1TF7V2tAxxxfuxI5GR+xHRNOEn/E2Dut6E+jNpX6aD1INgpTxteb8U7/mZSzo9tCC2k07qrDFIr2Zve0BwvJoMBso0OH3fyFHuMirjg14E5fFeD296MUHLZ2rNq8WiA2o8vmdxP/PsVqCyr6zucp3SadlQaDbggYG/PVINAmkCgYEA6ysnXTVx2P3RDaE8TbY39c7imNyIPwJYYqGM9LL/a3Ih+KSAJqsXWycmnluf6/C+U66hv3bs/aWkIT3mgiz6fgL2SIqatJmScIlKLl9K2KdVcukCf4eTUDJc8qjEkm8zKeowzyBUgiCLP5grnylTPO+ByU727zvjBMoop/V0RecCgYEAtZKhI8f5zgYFgmA+mVqYWWKG+DtXbeOA0uyJFuJWCJI7Wk3WuX1AdzEVtshIZrZFipHFTFcklx3Sd7DVwHUThDy/HigUFrqraGHiOAeNal3yhAtcEw7eNlHplUzNoFF5mlossOPMSk0RE3l3YAXUAX9/6WT/67RYGvEiL9mhuysCgYBya9HAUTOuf3iK6CteKE28BMhN1edYuPxmKPJbUQhjr+mFgAx2RAKeMENjA3OhKcaBEtPSQ7v1gfWE0whnyBis3SYVj5LMUnhk1vfH7keNeIVj3ql/gWiUuZxt2N2j3gUH3NGYxcM4eTsnWxPGYiYE3QfJ+ei95+aCOxZi+lNzewKBgEh4xBRyBwTyZlSJcbLn9BMJPnHpzZWwzoRhoL4fjNNS+pmqo9/ZXFe2ocIY4r0MV2ldkXBM/+/JGW5oiIiOskF/tZCl0o7X5v58wzw1133zDY8isZdxwcPQofjTdOdeXbj78QaRhcLKS9yICNust9f5OebAR12e1+O5BzouFV2NAoGBAKPzyFhhvyrF6cXDBPy1g44f4Ce4/+B5ouA+oAiK7AA1y7fkY3QQvUreMSK3t5luOBg5WhrZ+YokyMe7mfGIdK3T0Upw9QUMIIwtyX85FzsXZxxhBn4f83hYx3uVd0NiH2/AupPu3UAls82kJoiSOCyF+AB/Z7hd0lxSNk6zDAPl + alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmzzBx/4+G6YaFdocpDElTVFPMRYSkDNmxEYUD9yrheQdWR8czAjjzx5+NixjIcqNic/uEH3dtmfvfkcNT7mSNG+QhAV7XGOUXKYcQr7L+J7J98UGX3RcO1NPYFLyNcKu/S9IryLYdl6ZiSdNOgGr8xxWfMMpgDbsQlUKPT38ob+u22fQBkXJKBxrbZ9P0kzIoYN5S18qEDS0avAks0kkz05uffgudtopM/JlHW7Ts33s741bw0mN/GpehOjn8DDAlvIXdpZWF4pe/npwZCM/cVdRvGCkW8Z5lRgQPHEFSec56gxMONS5Zdh2IVMNNCUHO4f7sVQVx2luNeISRyDxvwIDAQAB +# - appId: 2021004148601326 +# privateKey: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCAoNFbDw6Cti1lAjL+nMVGgpGwMjaJY7durB54keXo4czzya0hviBtdtUEXZqC/v5xrO0wBO2KVZfXbcG+zY2ZFEerK+UdxAewG0Ngy6tKrrbPbG6eHJg27CFWW3dnlQ4xsYIun2AXd+HlSRamq8hbHhaMUL0zpS1u0IRMj0hknwewDUbHMc1bc/2305XX80r09enMlDjC/XHWtjkitvoTepHv2wOFZcADftmkmBh/IxRpLskxakbi4si9XI2I9diMs7rVh+UJ24ys5aeIFCzFOlsCf4zfz2oF1HN8UJHGvp2I6JwUQIHa4spMnfdl0ziRnkAWoXmeTQok/H4Nl2ahAgMBAAECggEAY/obn5ZNZgWptPgHoOVDZLG6AWSWBSXPfqMjr/1e4SsYvWhFPJPl6o9RtOn4XddK+Bo75XX/F+W9dsGltONaKuCbcA1XMVKb0yLttP+5LPwttX2HYTfCi/XvV4sSfR1bAf8Bu7E+5B7XIvszMusUeNYjGn7rAIhjjt1d54/66dGi4UkeXd7IMaQNom9Cz+Z2w9DtcUPShXYg9oL8f0K3BoYS1u0JdG94Chlj+iPh2vBRvWGkk6kHrbagDjcMf13CtnQuS+SHR7zGLVNcotDElOxGPH7Ac0fYlv5rzW7/YWCs3F8mrEBZYfZ5TGrpuqqWCg3Z3UA3tncYrtxJnKoJEQKBgQDGDjcyXVskiydLAMJO7XzeiqncFO4kq7/UC/2qn38OCBU295ufRVN4T7cP8AsTencF2OakovX3YqJod0K9BmfjiCmaUug3hCIzA/mLz75WJlJYdQLKbvW6pFRR5KpOmlynDwfiF7TtUOkrDeFELDGZTYNEAJHPQL3m3PhvG2tx/QKBgQCmQrI+aZ8laBy8u9Za/MUAL+aj7A9BdezT0a/Kvk23evll8M4oT/fDNYJ0Pzp+cros63rTjVDEhhhAsElflQ1yVJkjyBQkAUibHjewUlp5mmTdmwGd+EEMMd4dyKAIFIu+nbhyKdIoJl438vRp0rM2mWp0iimiD0k8WWy8EE/mdQKBgHXlWG4erHagbw9ysDgTntVKbbqYqvohUtuDF8lNkHBl11sVIgo5Vmqz6SEPn0hFCjzs1L8EUq6khFDzTUMAYS9MipdW6uzBNypvH0lerMrTnQkS/kpKqvQjhh4JpGeN88FubR7lRcpO872xUXsnY48CWTVNsX5R57lZqHlbNSZxAoGAbi9bg4NqRlcmXGo8GFbWlFYqi06Bmcd2abbYwwu147hrGoUpi4vbzpJi33mb4XtVKTnyT8ui0GtyHUT3i5HztGaeixkpR+dV3/95trANZ/PLjxYQT0nciiFua9yvOkurhWtvdrGCprsuy/OGya8quC+aXpvGFbH9vjIhns1MtpUCgYBL2EUUQRvRf8uK1oOes6kdbwmz+WMfylJvrfa9sGnpVWObsycbhaCUcLTtfuVbh79z33TeF6WqS/jsODUmSlbLHIHicH7zgZYkHLBcecFPx1F/gT1nvpr8bJatYfCEV7K1jdwGVCag2usfSn69Iq0js37zPXdaK0s6DWjUCXZvhA== +# alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgK + - appId: 2021003192641091 + privateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6HLvzFovqDXgfJoUiOzJX8kFylowGJ8WxSKPujKfb7HVUVNW7xItZy2KRuoQRkXl1JvhZ9UQRygKyIfiuigObifUtcPEpt6COCN75gXA1m1VxThUC9qHnCJBh6hTNwk+YjpuhMYv6Jbv8sZ5QBuxb3S7O8DNyf0skxCHy+FeMN2LNKQEm32j81c7fIFvQKT0X9N1JezHEdLoNohNnJTip7iqi+kA/8opSMMaP+up02V3+RV3OWe75Sb+cZ/r02q0bPfvOIUYBXJqUTDlzcqzytgELqo3iRTybrDX5ZL4eiRIJymkog3Bkilkp9hKdBix8aGrjHabOVXDuoji7Xn+pAgMBAAECggEAcUEVGqtovygtJf+h2p/dVelk7F8ynHhNdEG5CGxtpYYyK/xoCBhlmGIM/JNc1PgKykDJtqxrHMwqcc38b/IqmdcM7Pciunk06dn6HiknaGe7SfTzEleVVGtAuWTh/gg1uadmi2QEQ1f9GpvXs+lSoeBqIKHJHO7bgmccJaf8VqU/7H3UCLWK2UxtRMfO27hge2G8a350S7iVIyHPoXUz5L2P+5TP0T8C54QZsBp9mzaUfgaCTcw+zgPbYsTX9eP5IL7WflkZq0gPIG5XWfEiV3O2jATiNdOTtDhrEBWvY6ER5qRU0hA8tS9jNiTjgLjtNf+o7C9QlqZfSyuNdG87dQKBgQDfs8399uJHzdu7piKKnE/vhDGeXQPmGHoA+p6XUt/3Ou9nrI81EuItQ/vmLFq0cWyBg7vQFadU2IZxdtFsUZN1N6nE3wHprHmQ+MwaoOaq8f26pEPrau8DyJy3feMyBlgUbG9y/xffM/nMyVjJ04YNNnY/OIwbaLHKPIatwtQzZwKBgQDU+5KPWFD3QutSA005R0Pde2g//N+/nMX4wozx2uQrwbPJze2EAjyfSMsqX9Zgj6FExbLR4kbsnJiLip3nATqAgOyQxKS+M3vPNbJvRxAwI7dlRmq1k+JG3ujN6mDYZxeKvy0Rh8E+iocVhJsKN1xN1NeicJidB8/DeBR3CiZabwKBgCSmP5xZA0+CQXqnyEbvPwVdogdw87+RyQM6DBt8n2bQyAIzOi8Wzelelvlp0N55Kq65V17K/WQgKT6TmdHmN4NfwDeZ1aVj9XKz4DSIoeHlzYzzdpEuLsRu7IX/YOaENY/7B/NOKaoOyOEtJ5fBSNgHUS4gZIvlBiO88M+s+LkHAoGBAJvNZQtjqecXlxmpvKY4EddF3Z9hlzSOtOwjNgP/AodX7fOkpym4kSxcjfLzpNmCeJDcptGO3B9e9uCtiJFifq9eoK/Oik1xUBLmzx9ENv1gQ1rcxlcKVa9OhbOMz0e/MqOW8psuWhksMWF2Xgx9LxLors3v3FSTUFNH1oC2dhQVAoGBANp5gXtK7dG7RM3b9J1AyNQh4GWY9X+pQqfADligwKH9jqGxFo5VgSEQ2avtchjjbPJTigWYGMSP+1fKnvboAxgHK6q6e0N8ITs1jP6+FBre1BMW8D6tFNCerrJ3eOTiMEcJ43dtGOOjCt3xSmRBUfzYFNbFjk3UhPwPCX2NROgF + alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp9pzpS3Czr4w2L32WhpbzL3S5+n7iUITQ3kmBKdaVLojB7/c3H0em9q90quuxKPeH2kOBhPOhLGWUfUcMKObswq/aMgDkoKqA4fAKmT07YVxxduxvEO6z0uwjjP/WGpuFtoGCMhEb5V8xKd+mvpkAblpekJjKPdmAnc/h/sXf8VGDX3czo33zDuxHupR4s1alJKHs3K/p9R0OH5RkxYv0D+I768nNG/ggWmQfdPfwqAeULDO7l4v97jfBaD7cUOtgNW9NCoganO4fSccfZXPSx8i8K8/TSbcbkJtgf2h+g9VMce9xBe9WdTJ7Ke/zMBHXKr4WHJag4Q6RcQPIO5i+QIDAQAB + notifyUrl: http://4qmsbk.natappfree.cc/api/alipay/callBack + realNameAuthenticationReturnUrl: http://xggcsgo.com + authenticationHost: https://jmidcardv1.market.alicloudapi.com + authenticationPath: /idcard/validate + authenticationAppcode: 123 + +jiu-jia-pay: + aliPayUrl: https://zhifu.jiujiaka.com/alipay +# memberId: 96254 +# appKey: 85f5a3fa798bd7 +# apiSecret: 75cef2bf7e14876ee1 + memberId: 96203 + appKey: 7a414cb5fba15e1 + apiSecret: 46bceaeae0720272bb + apiDomain: http://o7csgo.com/ + callbackUrl: ${ruoyi.domainName}/api/jiuJiaPay/callback +yun-xin: + serverUrl: https://api.netease.im/sms + appKey: caa80bbb83e28638ac8cc4aeed46b85e + appSecret: 5daf52e0c256 + templateId: 22512950 +yy-youping: + appKey: 4635531 + publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsKs+kh3AZTsygJHBLnk/RPfhQeP5aMX4fbsiOqe3ppmWAEy3HllHcINzDDl4yUCl6ApOS3gVpwN4TZ645BHEq6MkYZrTa6y4XkzKRawBd8Q+mD9/1NR3pckDZhO6F41VH2+Gj2FbOJ+QA2lZTSrPikHLegM2gaZNFv0Tpc4mxHTF273XwyJi7604BxHbrySzbBTVFEn27ejekJ/fWRajxlHiSrbTvfr8t8t7sosW5c7OBq0JaEa22rjj7OvejyWHbUDA6dZnrFWe53y/DRfhlr46O97VMDhiAVT9nyUtBnpFIFZaJJlSNaOg5sE5rL+WEZB4yITBatxoe+MB2jlZ7wIDAQAB + privateKey: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCwqz6SHcBlOzKAkcEueT9E9+FB4/loxfh9uyI6p7emmZYATLceWUdwg3MMOXjJQKXoCk5LeBWnA3hNnrjkEcSroyRhmtNrrLheTMpFrAF3xD6YP3/U1HelyQNmE7oXjVUfb4aPYVs4n5ADaVlNKs+KQct6AzaBpk0W/ROlzibEdMXbvdfDImLvrTgHEduvJLNsFNUUSfbt6N6Qn99ZFqPGUeJKttO9+vy3y3uyixblzs4GrQloRrbauOPs696PJYdtQMDp1mesVZ7nfL8NF+GWvjo73tUwOGIBVP2fJS0GekUgVlokmVI1o6DmwTmsv5YRkHjIhMFq3Gh74wHaOVnvAgMBAAECggEAc0FUkbiNIr2q5cuw7uO0zga00uqqdJrq0QO7ge6W1j5OaKWjtU3jWFi5Pplj3k+prKbEi2GtJocR/fl1f3zEBIFrI64BDbbhlMueDjP3xS2m9GZAvBVcdTCdYgOunNzcUqTMcU+/VkJqjn80/i3WtcjUuSXNcmmO01eJcNr72Z+K516kcO0bzWfMoownENJZUlE/fm+gv3nTZzcYiGh1EIRAr4xboSQ0IOYq8/aB5lHprqDmT6AQOs7RU392+4rpNisBsq1V6owKHS2KACI8qHKSI3meuosWfHvGRO6jOs9WoiPmDxm5wVdnW009hja/9K7NT3XL+TECd2B7sG05YQKBgQDg1fvMxKfTEratWR9KOHWoRbEtmXfz/Qd7kAT3PyxRIPM8bn/kLCFbr1ky6MIJNAL+32Sf+0/Kcop1g00vx/1ebXh51rgMfZtYbo+JCLFlY62mQ5kZfUppHPtxSWsS8zwJlvrQ0CMLZbSSk3VRKCgo8IncxqrZx44RAXbJca5TXwKBgQDJKB4Y9bEQEQ6+TXJT+iCMuUSMvCsYDXfBD7b++hLEKkTHvUCfaSKqCaj+c4sL9AIXwYIRGvfSjaIwV9sbU8IoJHeST5G4xoUHkLeprPrabaPemuKbcVer1YdnGYc0zRhMZpippqk//Gil5bhEWNs9qaV8CbA15uZyZWskZfmTcQKBgAwW9Yc5O9z5sKD5ndkb2BfIR5KwIecYwAJA6ENzPirOnsWtG2tlhBWuBBncsjJUfaS+1mZT2SKFrjRLbycWleUx9bx3c0YYqvXCXC1+LivJhFjgYachOZMVX9/jlsJaGexaFfqMdpXyThdjnndStXYeGQuqEx3KwTTg40ZJupn5AoGAPVNpuZpN3OQWZpYd4NRE0CI7b9x+FtskQ0uTMrDyv7NTPMHJzc+zrbs8NjbrkT1W1f88tBJQs3RSmgJu3inu62YSvhxcBMUhEWlhVO1TnPwIFXmK1HD9lXmHyjrSKZrNaZ1ttWsF9mfxT4J1ZwDf9jCeLu6VNhmq7/1TaMleCUECgYAjTsRObrXx+m1DniyBrJwPD7sfiZpxIQQBHwBHXkJOrg9rqx+KVYwnP7J5dI/1GE0CEg/APICuJafc51775UyvuJEhX2vsZxVnahb6kJALyUh0+PoPtoX8dTTclGne7MUT2tSiWgwtmbfDNyJzU1DEe9Hqwreep9QKdeey/9UTig== + ornamentsFilePath: www/wwwroot/java/ +zy-zfb: + merKey: ePOACAp3jvyvLP0oxaEqQrA8KdUbqpTo + gateway: http://115.29.205.51:8040 + tranType: 2004 + merchantId: '0236820267' + notifyBaseUrl: ${ruoyi.domainName} +mayi: + gateway: http://www.688fu.com + apiKey: g3aotfg5ud0f6e4qiy4jdkg0ujl7alb1 + memberid: 10071 + payBankCode: 935 + notifyBaseUrl: http://www.mkcsgo.com/ + callBackUrl: ${ruoyi.domainName} +xinghuopay: + serverUrl: https://pay.xinghuoip.com/mapi.php + pid: 1012 + sign: P83k6hAV4k5a54pPb36f3fbi63iikV6E + notifyUrl: http://eur42k.natappfree.cc/api/xinghuopay/notify + returnUrl: http://xggcsgo.com +gfht: + serverUrl: https://nzf.mkweo.cn/mapi.php + pid: 1028 + sign: 8yP2Z9YYlw8I8Yjyy3qGq1282qGg3881 + notifyUrl: http://127.0.0.1:8080/api/gfht/notify + returnUrl: http://www.0811skins.com +baiweipay: + serverUrl: https://baiweipay.com/mapi.php + pid: 1147 + sign: IkxpRBiIR46X1rhIPxCkRM46riRiKpxZ + notifyUrl: http://wbir6c.natappfree.cc/api/baiweipay/notify + returnUrl: http://wbir6c.natappfree.cc +smsbao: + httpUrl: http://api.smsbao.com/sms +# username: mumu123 +# password: hahaha123 + username: ogskins + password: a123456 +winic: + httpUrl: http://service.winic.org:8009/sys_port/gateway/index.asp + username: D6skins + password: x565267 + +mkcsgo: + startLoadOrnaments: false +# startLoadOrnaments: true + fight: + roundTime: 4000 + upgrade: + defaultRequired: 160 + anchorDefaultRequired: 100 + +cs340: + baseUrl: https://open.cs2pifa.com + appKey: open_851d06fdcfcd4c6fa1e4d04f1b9c1a1f + privateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQrxL42IMlfQ8b0JNNlY7M7xS0mujnSyzKOjhANx4BWs1ovlU7pSSOlwVi1POYKlBcje2VfwLB6Smmlwsyw+Gn+YFavFdfYzuhPIeXEtAh9DNYIak2GvN0X7fWTkoHNlL7sOHeXtl7xwBOABW5jUC6LZXCByGp1/lJ9GL5u3jOcDD/x+0PpYhNzZsdGJ+P6441LV54bJkFa0nXYGhuMHpyu9shGZn0uquwLfad4ObxvuzN52mg4MpqAHlgYePH5/JZAKwG45ZVZ/o+voOkQjrhgly0U/2xOTwJ7dU7kuWvEwq96uTP5hV5aWzhYCRhccdF0qUMNlrkpFjvwyhVyyQ1AgMBAAECggEAHEHssX4N6iRgSJ2qaJnNE6J3HQqR6YnkJ4ERL17XRQsw39NQEsrGo7k+7TZVQuBN7+53AqSHcM78CRcQWOAt3PM76ReyRBQZlbhuys3tGX/p+O/b7Bvjpfk1GUOSIh5gx7yYpSYWUHyupaE8z9qY7mzwR6z0Cb3ZP4KkaVkRn3BSiDDnRM7VHrkFm1nQbx80CWNM1B17bse2j6CPBRAEaLsgxF+S9EqZ1+8jB6ByijWdpm04BmxLL5x/M3YUz81LuUqAAGVKl3qNDvgaa+d9BSb+v1O8G6S0hmsWNVc62cHKdxAv6QnRaPZV/ji+tOCRtLNmaXJai917z+rC37d5sQKBgQDwz/4NSzJDr+pvzEv8mp36F3aKLGl7uynkDzyUfo4OifaI4Pw+bW4UDc1pkSwVC0lK4ZNpEcTmXHvxKvduRpCOlr8Io8JKuUqkFLgTwBqZNS/dCcAp+lDc13ep3oTjhGq6giSkXCee//X6UbWHfv9dBY6nkhDRbvzEHjS64st0RQKBgQDd2FqH9OlBpNQNAf2A04TGEfL4WCXjghXqH9U6hYK4C4uefo9IBF2wEwb5KZkHGt0c0cymrF2UpxxpkuUwqCU3RgD0JEF0FuoQ9zLJO1POdG/eTGmYnAUe6gIC4Mu9ftogbEbw9qltX0Wf4QRLamll1jgWWR2j4KngdakWsrwHMQKBgQDi0QVZQBp35pnJ84MeygNCLBdazboM8JzkUfWdaBYlW4z6H+92XVvxR47SgAolSBwgi5dsv2/WCgYzgWTMWWqO4y3L1XKLLzs+kMVQ1QA4jgAnEagN4deIKupq9Uv1gCOGyE37AVHQUQY+X/e7R8L+ut1CuDFfEkrB7jF8VWGMgQKBgH2fQQyR4/AXHTjMcrebjyj2CwcUK0hcZnpwUjdUWQRNDV/PvOUEC+VjsOQw6QW78Y+bQe7z1f4SbyVdWEkoPgLBslKNoT7SpvK6eFi5LqjPCHXyAS2407GAw2jL0LNafLw1dCqJEsHrXCq/qcXm2Q4gsxv8lKsy0h9XoUtIJO9RAoGAI7M/x0jb4zH/FZOupDKQNAaw8rE4chYbxMUyB8YnZccZ/qfVQhsSlITgmCkSpgMDFZtYAzk8kJhmf8+cDYMjwp3hFWTNvtc8bHvW4oG5X2a6hQUmQlooc948Hfvv4fXNkAB81lGch64CQPuwPxPvfGAVB9TCLabqkqjyco+qJpo= + publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0K8S+NiDJX0PG9CTTZWOzO8UtJro50ssyjo4QDceAVrNaL5VO6UkjpcFYtTzmCpQXI3tlX8CwekpppcLMsPhp/mBWrxXX2M7oTyHlxLQIfQzWCGpNhrzdF+31k5KBzZS+7Dh3l7Ze8cATgAVuY1Aui2Vwgchqdf5SfRi+bt4znAw/8ftD6WITc2bHRifj+uONS1eeGyZBWtJ12BobjB6crvbIRmZ9LqrsC32neDm8b7szedpoODKagB5YGHjx+fyWQCsBuOWVWf6Pr6DpEI64YJctFP9sTk8Ce3VO5LlrxMKverkz+YVeWls4WAkYXHHRdKlDDZa5KRY78MoVcskNQIDAQAB + callbackPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmcKFJsOYnPBQtkmIFqYEkYZ8E2cJXCG08JWOBJ7wBacrXcWhqtDclXIWe32d59XK3ghRcEOsSaarvyaNZIafVKoFhlsDq1hhsAB/oBVLjpP4oBkxcwB2oFa/Op32cw166TJEdnjC7hkJdIjsHdcSCUnoYdZEDCj8TWSgIm6uSo6n5d/xBiFAqK4NoYEVC7FiFv5wX1zIYh47ylUXRMQprP/qVvfZknEDv0Qpo6btoIKktBRmHkq/TLlNv5XumlNA1mzeHd2GkjNuJNiT6tm1wEqhgYryqSqc0X+64m4EgL4omGVEXd73iYhqDisFmDu2uoGLxJM7SvGoMj8JVWfHjwIDAQAB + +baidu: + baseUrl: https://ocpc.baidu.com/ocpcapi/api/uploadConvertData + token: l1UbXAra0qpi62IK0NJHtkNMH6eXIX1S@30H8dMqjvCoC4wi9AArgqufTsgwsmXZI + logidUrl: https://cs.upjhpea.cn?bd_vid= \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml new file mode 100644 index 0000000..e852a5b --- /dev/null +++ b/ruoyi-admin/src/main/resources/application.yml @@ -0,0 +1,99 @@ +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8081 + address: 0.0.0.0 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +#logging: +# level: +# com.ruoyi: debug +# org.springframework: debug + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + threads: + virtual: + enabled: true + jackson: + time-zone: GMT+8 + date-format: yyyy-MM-dd HH:mm:ss + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + profiles: + # 开发环境配置 dev test pro ---------------------------------------------------------------------------------------- + active: dev + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + # 服务模块 + devtools: + restart: + # 热部署开关 ------------------------------------------------------------------------------------------------------- + enabled: false + +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) + expireTime: 1440 + +# MyBatis/mybatis-plus 配置 +mybatis-plus: + # 搜索指定包别名 + typeAliasesPackage: com.ruoyi.**.domain + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 加载全局的配置文件 + configLocation: classpath:mybatis/mybatis-config.xml + global-config: + db-config: + logic-delete-field: delFlag + logic-delete-value: 2 + logic-not-delete-value: 0 + id-type: auto + +# PageHelper分页插件 +pagehelper: + helperDialect: mysql + supportMethodsArguments: true + params: count=countSql + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* diff --git a/ruoyi-admin/src/main/resources/banner.txt b/ruoyi-admin/src/main/resources/banner.txt new file mode 100644 index 0000000..fee1ce3 --- /dev/null +++ b/ruoyi-admin/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Application Version: ${ruoyi.version} +Spring Boot Version: ${spring-boot.version} \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..93de005 --- /dev/null +++ b/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -0,0 +1,38 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +login.blocked=很遗憾,访问IP已被列入系统黑名单 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.password.not.valid=* 5-50个字符 + +user.email.not.valid=邮箱格式错误 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml new file mode 100644 index 0000000..9a5e8e4 --- /dev/null +++ b/ruoyi-admin/src/main/resources/logback.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/sys-info.log + + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/sys-error.log + + + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + ${log.path}/sys-user.log + + + ${log.path}/sys-user.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 0000000..ad337e9 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-common/.DS_Store b/ruoyi-common/.DS_Store new file mode 100644 index 0000000..4ddf8bc Binary files /dev/null and b/ruoyi-common/.DS_Store differ diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml new file mode 100644 index 0000000..57eb5f1 --- /dev/null +++ b/ruoyi-common/pom.xml @@ -0,0 +1,204 @@ + + + + ruoyi + com.ruoyi + 4.8.2 + + 4.0.0 + + ruoyi-common + + + common通用工具 + + + + + io.micrometer + micrometer-core + + + + + org.springframework + spring-context-support + + + + + org.springframework + spring-web + + + + + + + + + + + + + + + + org.springframework.boot + spring-boot-starter-security + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.apache.commons + commons-lang3 + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.baomidou + dynamic-datasource-spring-boot-starter + 3.5.2 + + + + + com.alibaba.fastjson2 + fastjson2 + + + + + commons-io + commons-io + + + + + org.apache.poi + poi-ooxml + + + + + org.yaml + snakeyaml + + + + + io.jsonwebtoken + jjwt + + + + + javax.xml.bind + jaxb-api + 2.3.1 + + + org.glassfish.jaxb + jaxb-runtime + 2.3.1 + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.apache.commons + commons-pool2 + + + + + + nl.basjes.parse.useragent + yauaa + + + eu.bitwalker + UserAgentUtils + + + + + jakarta.servlet + jakarta.servlet-api + + + + + com.baomidou + mybatis-plus-spring-boot4-starter + 3.5.15 + + + com.baomidou + mybatis-plus-jsqlparser + 3.5.15 + + + + org.projectlombok + lombok + + + + + cn.hutool + hutool-all + 5.8.23 + + + + org.springframework.boot + spring-boot-starter-amqp + + + + org.redisson + redisson-spring-boot-starter + 3.16.2 + + + + org.springframework.boot + spring-boot-starter-websocket + + + + + io.springfox + springfox-boot-starter + ${swagger.version} + + + + + \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java new file mode 100644 index 0000000..1d6d4f4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 匿名访问不鉴权注解 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Anonymous +{ +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java new file mode 100644 index 0000000..be49c80 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java @@ -0,0 +1,33 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 数据权限过滤注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataScope +{ + /** + * 部门表的别名 + */ + public String deptAlias() default ""; + + /** + * 用户表的别名 + */ + public String userAlias() default ""; + + /** + * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来 + */ + public String permission() default ""; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java new file mode 100644 index 0000000..79cd191 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java @@ -0,0 +1,28 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.enums.DataSourceType; + +/** + * 自定义多数据源切换注解 + * + * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface DataSource +{ + /** + * 切换数据源名称 + */ + public DataSourceType value() default DataSourceType.MASTER; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java new file mode 100644 index 0000000..58a4bc6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java @@ -0,0 +1,187 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.math.BigDecimal; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import com.ruoyi.common.utils.poi.ExcelHandlerAdapter; + +/** + * 自定义导出Excel数据注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Excel +{ + /** + * 导出时在excel中排序 + */ + public int sort() default Integer.MAX_VALUE; + + /** + * 导出到Excel中的名字. + */ + public String name() default ""; + + /** + * 日期格式, 如: yyyy-MM-dd + */ + public String dateFormat() default ""; + + /** + * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) + */ + public String dictType() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + public String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + public String separator() default ","; + + /** + * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) + */ + public int scale() default -1; + + /** + * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN + */ + public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; + + /** + * 导出时在excel中每个列的高度 + */ + public double height() default 14; + + /** + * 导出时在excel中每个列的宽度 + */ + public double width() default 16; + + /** + * 文字后缀,如% 90 变成90% + */ + public String suffix() default ""; + + /** + * 当值为空时,字段的默认值 + */ + public String defaultValue() default ""; + + /** + * 提示信息 + */ + public String prompt() default ""; + + /** + * 设置只能选择不能输入的列内容. + */ + public String[] combo() default {}; + + /** + * 是否需要纵向合并单元格,应对需求:含有list集合单元格) + */ + public boolean needMerge() default false; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + public boolean isExport() default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + public String targetAttr() default ""; + + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + public boolean isStatistics() default false; + + /** + * 导出类型(0数字 1字符串 2图片) + */ + public ColumnType cellType() default ColumnType.STRING; + + /** + * 导出列头背景颜色 + */ + public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; + + /** + * 导出列头字体颜色 + */ + public IndexedColors headerColor() default IndexedColors.WHITE; + + /** + * 导出单元格背景颜色 + */ + public IndexedColors backgroundColor() default IndexedColors.WHITE; + + /** + * 导出单元格字体颜色 + */ + public IndexedColors color() default IndexedColors.BLACK; + + /** + * 导出字段对齐方式 + */ + public HorizontalAlignment align() default HorizontalAlignment.CENTER; + + /** + * 自定义数据处理器 + */ + public Class handler() default ExcelHandlerAdapter.class; + + /** + * 自定义数据处理器参数 + */ + public String[] args() default {}; + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + Type type() default Type.ALL; + + public enum Type + { + ALL(0), EXPORT(1), IMPORT(2); + private final int value; + + Type(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } + + public enum ColumnType + { + NUMERIC(0), STRING(1), IMAGE(2); + private final int value; + + ColumnType(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java new file mode 100644 index 0000000..1f1cc81 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author ruoyi + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels +{ + public Excel[] value(); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java new file mode 100644 index 0000000..1eb8e49 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java @@ -0,0 +1,51 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.enums.OperatorType; + +/** + * 自定义操作日志记录注解 + * + * @author ruoyi + * + */ +@Target({ ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log +{ + /** + * 模块 + */ + public String title() default ""; + + /** + * 功能 + */ + public BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + public OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + public boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + public boolean isSaveResponseData() default true; + + /** + * 排除指定的请求参数 + */ + public String[] excludeParamNames() default {}; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/NewUserInfo.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/NewUserInfo.java new file mode 100644 index 0000000..fa12300 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/NewUserInfo.java @@ -0,0 +1,12 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.*; + +/** + * 方法之前更新用户缓存 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface NewUserInfo { +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/NoRepeatSubmit.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/NoRepeatSubmit.java new file mode 100644 index 0000000..d7a3042 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/NoRepeatSubmit.java @@ -0,0 +1,4 @@ +package com.ruoyi.common.annotation; + +public @interface NoRepeatSubmit { +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java new file mode 100644 index 0000000..0f024c7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java @@ -0,0 +1,40 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.enums.LimitType; + +/** + * 限流注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RateLimiter +{ + /** + * 限流key + */ + public String key() default CacheConstants.RATE_LIMIT_KEY; + + /** + * 限流时间,单位秒 + */ + public int time() default 60; + + /** + * 限流次数 + */ + public int count() default 100; + + /** + * 限流类型 + */ + public LimitType limitType() default LimitType.DEFAULT; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java new file mode 100644 index 0000000..b769748 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java @@ -0,0 +1,31 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义注解防止表单重复提交 + * + * @author ruoyi + * + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit +{ + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + public int interval() default 5000; + + /** + * 提示消息 + */ + public String message() default "不允许重复提交,请稍候再试"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/UpdateUserCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/UpdateUserCache.java new file mode 100644 index 0000000..fe2231f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/UpdateUserCache.java @@ -0,0 +1,12 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.*; + +/** + * 自定义更新用户缓存注解 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface UpdateUserCache { +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/UserPermission.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/UserPermission.java new file mode 100644 index 0000000..45336a3 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/UserPermission.java @@ -0,0 +1,35 @@ +package com.ruoyi.common.annotation; /** + * Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng) + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.lang.annotation.*; + +/** + * 权限注解 用于检查权限 规定访问权限 + * + * @example @Permission({role1,role2}) + * @example @Permission + */ +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface UserPermission { + + /** + *

角色英文名称

+ *

使用注解时加上这个值表示限制只有某个角色的才可以访问对应的资源

+ *

常用在某些资源限制只有超级管理员角色才可访问

+ */ +// String[] value() default {}; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/ResTemplateConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/ResTemplateConfig.java new file mode 100644 index 0000000..3e54966 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/ResTemplateConfig.java @@ -0,0 +1,25 @@ +package com.ruoyi.common.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class ResTemplateConfig { + + @Bean + public RestTemplate restTemplate(ClientHttpRequestFactory factory) { + return new RestTemplate(factory); + } + + @Bean + public ClientHttpRequestFactory simpleClientHttpRequestFactory() { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + // 超时设置 + factory.setReadTimeout(5000); // ms + factory.setConnectTimeout(15000); // ms + return factory; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java new file mode 100644 index 0000000..0ef2860 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java @@ -0,0 +1,112 @@ +package com.ruoyi.common.config; + +import lombok.Getter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "ruoyi") +public class RuoYiConfig +{ + /** 上传路径 */ + @Getter + private static String profile; + /** 获取地址开关 */ + @Getter + private static boolean addressEnabled; + /** 验证码类型 */ + @Getter + private static String captchaType; + /** + * 项目全局域名配置 + */ + @Getter + private static String domainName; + /** 项目名称 */ + @Getter + private String name; + /** 版本 */ + @Getter + private String version; + /** 版权年份 */ + @Getter + private String copyrightYear; + /** 实例演示开关 */ + @Getter + private boolean demoEnabled; + + public void setName(String name) + { + this.name = name; + } + + public void setVersion(String version) + { + this.version = version; + } + + public void setCopyrightYear(String copyrightYear) + { + this.copyrightYear = copyrightYear; + } + + public void setDemoEnabled(boolean demoEnabled) + { + this.demoEnabled = demoEnabled; + } + + public void setProfile(String profile) + { + RuoYiConfig.profile = profile; + } + + public void setAddressEnabled(boolean addressEnabled) + { + RuoYiConfig.addressEnabled = addressEnabled; + } + + public void setCaptchaType(String captchaType) { + RuoYiConfig.captchaType = captchaType; + } + + public void setDomainName(String domainName) { + RuoYiConfig.domainName = domainName; + } + + /** + * 获取导入上传路径 + */ + public static String getImportPath() + { + return getProfile() + "/import"; + } + + /** + * 获取头像上传路径 + */ + public static String getAvatarPath() + { + return getProfile() + "/avatar"; + } + + /** + * 获取下载路径 + */ + public static String getDownloadPath() + { + return getProfile() + "/download"; + } + + /** + * 获取上传路径 + */ + public static String getUploadPath() + { + return getProfile() + "/upload"; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java new file mode 100644 index 0000000..0080343 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java @@ -0,0 +1,44 @@ +package com.ruoyi.common.constant; + +/** + * 缓存的key 常量 + * + * @author ruoyi + */ +public class CacheConstants +{ + /** + * 登录用户 redis key + */ + public static final String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + public static final String SYS_DICT_KEY = "sys_dict:"; + + /** + * 防重提交 redis key + */ + public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; + + /** + * 限流 redis key + */ + public static final String RATE_LIMIT_KEY = "rate_limit:"; + + /** + * 登录账户密码错误次数 redis key + */ + public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java new file mode 100644 index 0000000..355851f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -0,0 +1,167 @@ +package com.ruoyi.common.constant; + +import io.jsonwebtoken.Claims; + +/** + * 通用常量信息 + * + * @author ruoyi + */ +public class Constants +{ + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + public static final String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + public static final String FAIL = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 所有权限标识 + */ + public static final String ALL_PERMISSION = "*:*:*"; + + /** + * 管理员角色权限标识 + */ + public static final String SUPER_ADMIN = "admin"; + + /** + * 角色权限分隔符 + */ + public static final String ROLE_DELIMETER = ","; + + /** + * 权限标识分隔符 + */ + public static final String PERMISSION_DELIMETER = ","; + + /** + * 验证码有效期(分钟) + */ + public static final Integer CAPTCHA_EXPIRATION = 2; + + /** + * 令牌 + */ + public static final String TOKEN = "token"; + + /** + * 令牌前缀 + */ + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * 令牌前缀 + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + + /** + * 用户ID + */ + public static final String JWT_USERID = "userid"; + + /** + * 用户名称 + */ + public static final String JWT_USERNAME = Claims.SUBJECT; + + /** + * 用户头像 + */ + public static final String JWT_AVATAR = "avatar"; + + /** + * 创建时间 + */ + public static final String JWT_CREATED = "created"; + + /** + * 用户权限 + */ + public static final String JWT_AUTHORITIES = "authorities"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + + /** + * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) + */ + public static final String[] JSON_WHITELIST_STR = { "org.springframework", "com.ruoyi" }; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" }; + + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config" }; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java new file mode 100644 index 0000000..7d899d4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java @@ -0,0 +1,117 @@ +package com.ruoyi.common.constant; + +/** + * 代码生成通用常量 + * + * @author ruoyi + */ +public class GenConstants +{ + /** 单表(增删改查) */ + public static final String TPL_CRUD = "crud"; + + /** 树表(增删改查) */ + public static final String TPL_TREE = "tree"; + + /** 主子表(增删改查) */ + public static final String TPL_SUB = "sub"; + + /** 树编码字段 */ + public static final String TREE_CODE = "treeCode"; + + /** 树父编码字段 */ + public static final String TREE_PARENT_CODE = "treeParentCode"; + + /** 树名称字段 */ + public static final String TREE_NAME = "treeName"; + + /** 上级菜单ID字段 */ + public static final String PARENT_MENU_ID = "parentMenuId"; + + /** 上级菜单名称字段 */ + public static final String PARENT_MENU_NAME = "parentMenuName"; + + /** 数据库字符串类型 */ + public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" }; + + /** 数据库文本类型 */ + public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; + + /** 数据库时间类型 */ + public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; + + /** 数据库数字类型 */ + public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", + "bit", "bigint", "float", "double", "decimal" }; + + /** 页面不需要编辑字段 */ + public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; + + /** 页面不需要显示的列表字段 */ + public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time" }; + + /** 页面不需要查询字段 */ + public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark" }; + + /** Entity基类字段 */ + public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; + + /** Tree基类字段 */ + public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" }; + + /** 文本框 */ + public static final String HTML_INPUT = "input"; + + /** 文本域 */ + public static final String HTML_TEXTAREA = "textarea"; + + /** 下拉框 */ + public static final String HTML_SELECT = "select"; + + /** 单选框 */ + public static final String HTML_RADIO = "radio"; + + /** 复选框 */ + public static final String HTML_CHECKBOX = "checkbox"; + + /** 日期控件 */ + public static final String HTML_DATETIME = "datetime"; + + /** 图片上传控件 */ + public static final String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** 文件上传控件 */ + public static final String HTML_FILE_UPLOAD = "fileUpload"; + + /** 富文本控件 */ + public static final String HTML_EDITOR = "editor"; + + /** 字符串类型 */ + public static final String TYPE_STRING = "String"; + + /** 整型 */ + public static final String TYPE_INTEGER = "Integer"; + + /** 长整型 */ + public static final String TYPE_LONG = "Long"; + + /** 浮点型 */ + public static final String TYPE_DOUBLE = "Double"; + + /** 高精度计算类型 */ + public static final String TYPE_BIGDECIMAL = "BigDecimal"; + + /** 时间类型 */ + public static final String TYPE_DATE = "Date"; + + /** 模糊查询 */ + public static final String QUERY_LIKE = "LIKE"; + + /** 相等查询 */ + public static final String QUERY_EQ = "EQ"; + + /** 需要 */ + public static final String REQUIRE = "1"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java new file mode 100644 index 0000000..a983c77 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java @@ -0,0 +1,94 @@ +package com.ruoyi.common.constant; + +/** + * 返回状态码 + * + * @author ruoyi + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + public static final int WARN = 601; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java new file mode 100644 index 0000000..62ad815 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java @@ -0,0 +1,50 @@ +package com.ruoyi.common.constant; + +/** + * 任务调度通用常量 + * + * @author ruoyi + */ +public class ScheduleConstants +{ + public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** 执行目标key */ + public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** 默认 */ + public static final String MISFIRE_DEFAULT = "0"; + + /** 立即触发执行 */ + public static final String MISFIRE_IGNORE_MISFIRES = "1"; + + /** 触发一次执行 */ + public static final String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** 不触发立即执行 */ + public static final String MISFIRE_DO_NOTHING = "3"; + + public enum Status + { + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/ThirdParty/Platform.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ThirdParty/Platform.java new file mode 100644 index 0000000..b8fb406 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ThirdParty/Platform.java @@ -0,0 +1,17 @@ +package com.ruoyi.common.constant.ThirdParty; + +public enum Platform { + + ZBT(1), + YY(2); + + private Integer code; + + Platform(Integer code){ + this.code = code; + } + + public Integer getCode(){ + return this.code; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java new file mode 100644 index 0000000..c44941b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java @@ -0,0 +1,83 @@ +package com.ruoyi.common.constant; + +/** + * 用户常量信息 + * + * @author ruoyi + */ +public class UserConstants +{ + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static final String NORMAL = "0"; + + /** 异常状态 */ + public static final String EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static final String USER_DISABLE = "1"; + + /** 角色封禁状态 */ + public static final String ROLE_DISABLE = "1"; + + /** 部门正常状态 */ + public static final String DEPT_NORMAL = "0"; + + /** 部门停用状态 */ + public static final String DEPT_DISABLE = "1"; + + /** 字典正常状态 */ + public static final String DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static final String YES = "Y"; + + /** 是否菜单外链(是) */ + public static final String YES_FRAME = "0"; + + /** 是否菜单外链(否) */ + public static final String NO_FRAME = "1"; + + /** 菜单类型(目录) */ + public static final String TYPE_DIR = "M"; + + /** 菜单类型(菜单) */ + public static final String TYPE_MENU = "C"; + + /** 菜单类型(按钮) */ + public static final String TYPE_BUTTON = "F"; + + /** Layout组件标识 */ + public final static String LAYOUT = "Layout"; + + /** ParentView组件标识 */ + public final static String PARENT_VIEW = "ParentView"; + + /** InnerLink组件标识 */ + public final static String INNER_LINK = "InnerLink"; + + /** 校验是否唯一的返回标识 */ + public final static boolean UNIQUE = true; + public final static boolean NOT_UNIQUE = false; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 2; + public static final int USERNAME_MAX_LENGTH = 20; + /** + * 用户名长度限制 + */ + public static final int NICKNAME_MIN_LENGTH = 1; + public static final int NICKNAME_MAX_LENGTH = 7; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 5; + public static final int PASSWORD_MAX_LENGTH = 20; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java new file mode 100644 index 0000000..b5346ee --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java @@ -0,0 +1,223 @@ +package com.ruoyi.common.core.controller; + +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.core.page.PageDomain; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.page.TableSupport; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.sql.SqlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.List; + +/** + * web层通用数据处理 + * + * @author ruoyi + */ +public class BaseController +{ + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 + */ + @InitBinder + public void initBinder(WebDataBinder binder) + { + // Date 类型转换 + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() + { + @Override + public void setAsText(String text) + { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * 设置请求分页数据 + */ + protected void startPage() + { + PageUtils.startPage(); + } + + /** + * 设置请求排序数据 + */ + protected void startOrderBy() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + if (StringUtils.isNotEmpty(pageDomain.getOrderBy())) + { + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + PageHelper.orderBy(orderBy); + } + } + + /** + * 清理分页的线程变量 + */ + protected void clearPage() + { + PageUtils.clearPage(); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected TableDataInfo getDataTable(List list) + { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + /** + * 响应请求分页数据 + */ + protected PageDataInfo getPageData(List list) { + PageDataInfo pageDataInfo = new PageDataInfo(); + pageDataInfo.setCode(HttpStatus.SUCCESS); + pageDataInfo.setMsg("查询成功"); + pageDataInfo.setRows(list); + pageDataInfo.setTotal(new PageInfo(list).getTotal()); + return pageDataInfo; + } + + /** + * 返回成功 + */ + public AjaxResult success() + { + return AjaxResult.success(); + } + + /** + * 返回失败消息 + */ + public AjaxResult error() + { + return AjaxResult.error(); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(String message) + { + return AjaxResult.success(message); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(Object data) + { + return AjaxResult.success(data); + } + + /** + * 返回失败消息 + */ + public AjaxResult error(String message) + { + return AjaxResult.error(message); + } + + /** + * 返回警告消息 + */ + public AjaxResult warn(String message) + { + return AjaxResult.warn(message); + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected AjaxResult toAjax(int rows) + { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected AjaxResult toAjax(boolean result) + { + return result ? success() : error(); + } + + /** + * 页面跳转 + */ + public String redirect(String url) + { + return StringUtils.format("redirect:{}", url); + } + + /** + * 获取用户缓存信息 + */ + public LoginUser getLoginUser() + { + return SecurityUtils.getLoginUser(); + } + + /** + * 获取登录用户id + */ + public Long getUserId() + { + return getLoginUser().getUserId(); + } + + /** + * 获取登录部门id + */ + public Long getDeptId() + { + return getLoginUser().getDeptId(); + } + + /** + * 获取登录用户名 + */ + public String getUsername() + { + return getLoginUser().getUsername(); + } + + /** + * 获取登录用户Token + */ + public String getToken() { + return getLoginUser().getToken(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java new file mode 100644 index 0000000..a7abfe4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java @@ -0,0 +1,216 @@ +package com.ruoyi.common.core.domain; + +import java.util.HashMap; +import java.util.Objects; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.utils.StringUtils; + +/** + * 操作消息提醒 + * + * @author ruoyi + */ +public class AjaxResult extends HashMap +{ + private static final long serialVersionUID = 1L; + + /** 状态码 */ + public static final String CODE_TAG = "code"; + + /** 返回内容 */ + public static final String MSG_TAG = "msg"; + + /** 数据对象 */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public AjaxResult() + { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public AjaxResult(int code, String msg, Object data) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) + { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() + { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(Object data) + { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) + { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, Object data) + { + return new AjaxResult(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult warn(String msg) + { + return AjaxResult.warn(msg, null); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult warn(String msg, Object data) + { + return new AjaxResult(HttpStatus.WARN, msg, data); + } + + /** + * 返回错误消息 + * + * @return 错误消息 + */ + public static AjaxResult error() + { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 错误消息 + */ + public static AjaxResult error(String msg) + { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 错误消息 + */ + public static AjaxResult error(String msg, Object data) + { + return new AjaxResult(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 错误消息 + */ + public static AjaxResult error(int code, String msg) + { + return new AjaxResult(code, msg, null); + } + + /** + * 是否为成功消息 + * + * @return 结果 + */ + public boolean isSuccess() + { + return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG)); + } + + /** + * 是否为警告消息 + * + * @return 结果 + */ + public boolean isWarn() + { + return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG)); + } + + /** + * 是否为错误消息 + * + * @return 结果 + */ + public boolean isError() + { + return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG)); + } + + /** + * 方便链式调用 + * + * @param key 键 + * @param value 值 + * @return 数据对象 + */ + @Override + public AjaxResult put(String key, Object value) + { + super.put(key, value); + return this; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java new file mode 100644 index 0000000..15bf66b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java @@ -0,0 +1,118 @@ +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Entity基类 + * + * @author ruoyi + */ +public class BaseEntity implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 搜索值 */ + @JsonIgnore + private String searchValue; + + /** 创建者 */ + private String createBy; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 备注 */ + private String remark; + + /** 请求参数 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private Map params; + + public String getSearchValue() + { + return searchValue; + } + + public void setSearchValue(String searchValue) + { + this.searchValue = searchValue; + } + + public String getCreateBy() + { + return createBy; + } + + public void setCreateBy(String createBy) + { + this.createBy = createBy; + } + + public Date getCreateTime() + { + return createTime; + } + + public void setCreateTime(Date createTime) + { + this.createTime = createTime; + } + + public String getUpdateBy() + { + return updateBy; + } + + public void setUpdateBy(String updateBy) + { + this.updateBy = updateBy; + } + + public Date getUpdateTime() + { + return updateTime; + } + + public void setUpdateTime(Date updateTime) + { + this.updateTime = updateTime; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } + + public Map getParams() + { + if (params == null) + { + params = new HashMap<>(); + } + return params; + } + + public void setParams(Map params) + { + this.params = params; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java new file mode 100644 index 0000000..0930f07 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java @@ -0,0 +1,115 @@ +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import com.ruoyi.common.constant.HttpStatus; + +/** + * 响应信息主体 + * + * @author ruoyi + */ +public class R implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 成功 */ + public static final int SUCCESS = HttpStatus.SUCCESS; + + /** 失败 */ + public static final int FAIL = HttpStatus.ERROR; + + private Integer code; + + private String msg; + + private T data; + + public static R ok() + { + return restResult(null, SUCCESS, "操作成功"); + } + + public static R ok(T data) + { + return restResult(data, SUCCESS, "操作成功"); + } + + public static R ok(T data, String msg) + { + return restResult(data, SUCCESS, msg); + } + + public static R fail() + { + return restResult(null, FAIL, "操作失败"); + } + + public static R fail(String msg) + { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) + { + return restResult(data, FAIL, "操作失败"); + } + + public static R fail(T data, String msg) + { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) + { + return restResult(null, code, msg); + } + + private static R restResult(T data, int code, String msg) + { + R apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } + + public Integer getCode() + { + return code; + } + + public void setCode(Integer code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public T getData() + { + return data; + } + + public void setData(T data) + { + this.data = data; + } + + public static Boolean isError(R ret) + { + return !isSuccess(ret); + } + + public static Boolean isSuccess(R ret) + { + return R.SUCCESS == ret.getCode(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java new file mode 100644 index 0000000..a180a18 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java @@ -0,0 +1,79 @@ +package com.ruoyi.common.core.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author ruoyi + */ +public class TreeEntity extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 祖级列表 */ + private String ancestors; + + /** 子部门 */ + private List children = new ArrayList<>(); + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java new file mode 100644 index 0000000..bd835db --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java @@ -0,0 +1,77 @@ +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysMenu; + +/** + * Treeselect树结构实体类 + * + * @author ruoyi + */ +public class TreeSelect implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + private Long id; + + /** 节点名称 */ + private String label; + + /** 子节点 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + public TreeSelect() + { + + } + + public TreeSelect(SysDept dept) + { + this.id = dept.getDeptId(); + this.label = dept.getDeptName(); + this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public TreeSelect(SysMenu menu) + { + this.id = menu.getMenuId(); + this.label = menu.getMenuName(); + this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getLabel() + { + return label; + } + + public void setLabel(String label) + { + this.label = label; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java new file mode 100644 index 0000000..37236db --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java @@ -0,0 +1,203 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.ArrayList; +import java.util.List; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 部门表 sys_dept + * + * @author ruoyi + */ +public class SysDept extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 部门ID */ + private Long deptId; + + /** 父部门ID */ + private Long parentId; + + /** 祖级列表 */ + private String ancestors; + + /** 部门名称 */ + private String deptName; + + /** 显示顺序 */ + private Integer orderNum; + + /** 负责人 */ + private String leader; + + /** 联系电话 */ + private String phone; + + /** 邮箱 */ + private String email; + + /** 部门状态:0正常,1停用 */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 父部门名称 */ + private String parentName; + + /** 子部门 */ + private List children = new ArrayList(); + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + @NotBlank(message = "部门名称不能为空") + @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getLeader() + { + return leader; + } + + public void setLeader(String leader) + { + this.leader = leader; + } + + @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") + public String getPhone() + { + return phone; + } + + public void setPhone(String phone) + { + this.phone = phone; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("deptId", getDeptId()) + .append("parentId", getParentId()) + .append("ancestors", getAncestors()) + .append("deptName", getDeptName()) + .append("orderNum", getOrderNum()) + .append("leader", getLeader()) + .append("phone", getPhone()) + .append("email", getEmail()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java new file mode 100644 index 0000000..6b035a6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java @@ -0,0 +1,176 @@ +package com.ruoyi.common.core.domain.entity; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 字典数据表 sys_dict_data + * + * @author ruoyi + */ +public class SysDictData extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典编码 */ + @Excel(name = "字典编码", cellType = ColumnType.NUMERIC) + private Long dictCode; + + /** 字典排序 */ + @Excel(name = "字典排序", cellType = ColumnType.NUMERIC) + private Long dictSort; + + /** 字典标签 */ + @Excel(name = "字典标签") + private String dictLabel; + + /** 字典键值 */ + @Excel(name = "字典键值") + private String dictValue; + + /** 字典类型 */ + @Excel(name = "字典类型") + private String dictType; + + /** 样式属性(其他样式扩展) */ + private String cssClass; + + /** 表格字典样式 */ + private String listClass; + + /** 是否默认(Y是 N否) */ + @Excel(name = "是否默认", readConverterExp = "Y=是,N=否") + private String isDefault; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictCode() + { + return dictCode; + } + + public void setDictCode(Long dictCode) + { + this.dictCode = dictCode; + } + + public Long getDictSort() + { + return dictSort; + } + + public void setDictSort(Long dictSort) + { + this.dictSort = dictSort; + } + + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") + public String getDictLabel() + { + return dictLabel; + } + + public void setDictLabel(String dictLabel) + { + this.dictLabel = dictLabel; + } + + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") + public String getDictValue() + { + return dictValue; + } + + public void setDictValue(String dictValue) + { + this.dictValue = dictValue; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") + public String getCssClass() + { + return cssClass; + } + + public void setCssClass(String cssClass) + { + this.cssClass = cssClass; + } + + public String getListClass() + { + return listClass; + } + + public void setListClass(String listClass) + { + this.listClass = listClass; + } + + public boolean getDefault() + { + return UserConstants.YES.equals(this.isDefault); + } + + public String getIsDefault() + { + return isDefault; + } + + public void setIsDefault(String isDefault) + { + this.isDefault = isDefault; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictCode", getDictCode()) + .append("dictSort", getDictSort()) + .append("dictLabel", getDictLabel()) + .append("dictValue", getDictValue()) + .append("dictType", getDictType()) + .append("cssClass", getCssClass()) + .append("listClass", getListClass()) + .append("isDefault", getIsDefault()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java new file mode 100644 index 0000000..ac5d290 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java @@ -0,0 +1,96 @@ +package com.ruoyi.common.core.domain.entity; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 字典类型表 sys_dict_type + * + * @author ruoyi + */ +public class SysDictType extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典主键 */ + @Excel(name = "字典主键", cellType = ColumnType.NUMERIC) + private Long dictId; + + /** 字典名称 */ + @Excel(name = "字典名称") + private String dictName; + + /** 字典类型 */ + @Excel(name = "字典类型") + private String dictType; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictId() + { + return dictId; + } + + public void setDictId(Long dictId) + { + this.dictId = dictId; + } + + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") + public String getDictName() + { + return dictName; + } + + public void setDictName(String dictName) + { + this.dictName = dictName; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictId", getDictId()) + .append("dictName", getDictName()) + .append("dictType", getDictType()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java new file mode 100644 index 0000000..581f5eb --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java @@ -0,0 +1,259 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.ArrayList; +import java.util.List; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 菜单权限表 sys_menu + * + * @author ruoyi + */ +public class SysMenu extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 菜单ID */ + private Long menuId; + + /** 菜单名称 */ + private String menuName; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 路由地址 */ + private String path; + + /** 组件路径 */ + private String component; + + /** 路由参数 */ + private String query; + + /** 是否为外链(0是 1否) */ + private String isFrame; + + /** 是否缓存(0缓存 1不缓存) */ + private String isCache; + + /** 类型(M目录 C菜单 F按钮) */ + private String menuType; + + /** 显示状态(0显示 1隐藏) */ + private String visible; + + /** 菜单状态(0正常 1停用) */ + private String status; + + /** 权限字符串 */ + private String perms; + + /** 菜单图标 */ + private String icon; + + /** 子菜单 */ + private List children = new ArrayList(); + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") + public String getMenuName() + { + return menuName; + } + + public void setMenuName(String menuName) + { + this.menuName = menuName; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public String getIsFrame() + { + return isFrame; + } + + public void setIsFrame(String isFrame) + { + this.isFrame = isFrame; + } + + public String getIsCache() + { + return isCache; + } + + public void setIsCache(String isCache) + { + this.isCache = isCache; + } + + @NotBlank(message = "菜单类型不能为空") + public String getMenuType() + { + return menuType; + } + + public void setMenuType(String menuType) + { + this.menuType = menuType; + } + + public String getVisible() + { + return visible; + } + + public void setVisible(String visible) + { + this.visible = visible; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") + public String getPerms() + { + return perms; + } + + public void setPerms(String perms) + { + this.perms = perms; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("menuId", getMenuId()) + .append("menuName", getMenuName()) + .append("parentId", getParentId()) + .append("orderNum", getOrderNum()) + .append("path", getPath()) + .append("component", getComponent()) + .append("isFrame", getIsFrame()) + .append("IsCache", getIsCache()) + .append("menuType", getMenuType()) + .append("visible", getVisible()) + .append("status ", getStatus()) + .append("perms", getPerms()) + .append("icon", getIcon()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java new file mode 100644 index 0000000..2f8ed14 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java @@ -0,0 +1,241 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.Set; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 角色表 sys_role + * + * @author ruoyi + */ +public class SysRole extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 角色ID */ + @Excel(name = "角色序号", cellType = ColumnType.NUMERIC) + private Long roleId; + + /** 角色名称 */ + @Excel(name = "角色名称") + private String roleName; + + /** 角色权限 */ + @Excel(name = "角色权限") + private String roleKey; + + /** 角色排序 */ + @Excel(name = "角色排序") + private Integer roleSort; + + /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ + @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") + private String dataScope; + + /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ + private boolean menuCheckStrictly; + + /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ + private boolean deptCheckStrictly; + + /** 角色状态(0正常 1停用) */ + @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 用户是否存在此角色标识 默认不存在 */ + private boolean flag = false; + + /** 菜单组 */ + private Long[] menuIds; + + /** 部门组(数据权限) */ + private Long[] deptIds; + + /** 角色菜单权限 */ + private Set permissions; + + public SysRole() + { + + } + + public SysRole(Long roleId) + { + this.roleId = roleId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public boolean isAdmin() + { + return isAdmin(this.roleId); + } + + public static boolean isAdmin(Long roleId) + { + return roleId != null && 1L == roleId; + } + + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") + public String getRoleName() + { + return roleName; + } + + public void setRoleName(String roleName) + { + this.roleName = roleName; + } + + @NotBlank(message = "权限字符不能为空") + @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") + public String getRoleKey() + { + return roleKey; + } + + public void setRoleKey(String roleKey) + { + this.roleKey = roleKey; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getRoleSort() + { + return roleSort; + } + + public void setRoleSort(Integer roleSort) + { + this.roleSort = roleSort; + } + + public String getDataScope() + { + return dataScope; + } + + public void setDataScope(String dataScope) + { + this.dataScope = dataScope; + } + + public boolean isMenuCheckStrictly() + { + return menuCheckStrictly; + } + + public void setMenuCheckStrictly(boolean menuCheckStrictly) + { + this.menuCheckStrictly = menuCheckStrictly; + } + + public boolean isDeptCheckStrictly() + { + return deptCheckStrictly; + } + + public void setDeptCheckStrictly(boolean deptCheckStrictly) + { + this.deptCheckStrictly = deptCheckStrictly; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + public Long[] getMenuIds() + { + return menuIds; + } + + public void setMenuIds(Long[] menuIds) + { + this.menuIds = menuIds; + } + + public Long[] getDeptIds() + { + return deptIds; + } + + public void setDeptIds(Long[] deptIds) + { + this.deptIds = deptIds; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("roleName", getRoleName()) + .append("roleKey", getRoleKey()) + .append("roleSort", getRoleSort()) + .append("dataScope", getDataScope()) + .append("menuCheckStrictly", isMenuCheckStrictly()) + .append("deptCheckStrictly", isDeptCheckStrictly()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java new file mode 100644 index 0000000..45d2d71 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java @@ -0,0 +1,324 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.Date; +import java.util.List; +import jakarta.validation.constraints.*; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.annotation.Excel.Type; +import com.ruoyi.common.annotation.Excels; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.xss.Xss; + +/** + * 用户对象 sys_user + * + * @author ruoyi + */ +public class SysUser extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 用户ID */ + @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号") + private Long userId; + + /** 部门ID */ + @Excel(name = "部门编号", type = Type.IMPORT) + private Long deptId; + + /** 用户账号 */ + @Excel(name = "登录名称") + private String userName; + + /** 用户昵称 */ + @Excel(name = "用户名称") + private String nickName; + + /** 用户邮箱 */ + @Excel(name = "用户邮箱") + private String email; + + /** 手机号码 */ + @Excel(name = "手机号码") + private String phonenumber; + + /** 用户性别 */ + @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") + private String sex; + + /** 用户头像 */ + private String avatar; + + /** 密码 */ + private String password; + + /** 帐号状态(0正常 1停用) */ + @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 最后登录IP */ + @Excel(name = "最后登录IP", type = Type.EXPORT) + private String loginIp; + + /** 最后登录时间 */ + @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) + private Date loginDate; + + /** 部门对象 */ + @Excels({ + @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), + @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) + }) + private SysDept dept; + + /** 角色对象 */ + private List roles; + + /** 角色组 */ + private Long[] roleIds; + + /** 岗位组 */ + private Long[] postIds; + + /** 角色ID */ + private Long roleId; + + public SysUser() + { + + } + + public SysUser(Long userId) + { + this.userId = userId; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public boolean isAdmin() + { + return isAdmin(this.userId); + } + + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") + public String getNickName() + { + return nickName; + } + + public void setNickName(String nickName) + { + this.nickName = nickName; + } + + @Xss(message = "用户账号不能包含脚本字符") + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") + public String getPhonenumber() + { + return phonenumber; + } + + public void setPhonenumber(String phonenumber) + { + this.phonenumber = phonenumber; + } + + public String getSex() + { + return sex; + } + + public void setSex(String sex) + { + this.sex = sex; + } + + public String getAvatar() + { + return avatar; + } + + public void setAvatar(String avatar) + { + this.avatar = avatar; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getLoginIp() + { + return loginIp; + } + + public void setLoginIp(String loginIp) + { + this.loginIp = loginIp; + } + + public Date getLoginDate() + { + return loginDate; + } + + public void setLoginDate(Date loginDate) + { + this.loginDate = loginDate; + } + + public SysDept getDept() + { + return dept; + } + + public void setDept(SysDept dept) + { + this.dept = dept; + } + + public List getRoles() + { + return roles; + } + + public void setRoles(List roles) + { + this.roles = roles; + } + + public Long[] getRoleIds() + { + return roleIds; + } + + public void setRoleIds(Long[] roleIds) + { + this.roleIds = roleIds; + } + + public Long[] getPostIds() + { + return postIds; + } + + public void setPostIds(Long[] postIds) + { + this.postIds = postIds; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("deptId", getDeptId()) + .append("userName", getUserName()) + .append("nickName", getNickName()) + .append("email", getEmail()) + .append("phonenumber", getPhonenumber()) + .append("sex", getSex()) + .append("avatar", getAvatar()) + .append("password", getPassword()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("loginIp", getLoginIp()) + .append("loginDate", getLoginDate()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .append("dept", getDept()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/UserData.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/UserData.java new file mode 100644 index 0000000..064dfaa --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/UserData.java @@ -0,0 +1,37 @@ +package com.ruoyi.common.core.domain.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class UserData { + + private Integer userId; + private String userName; + private String nickName; + private String userType; + private Integer vipLevel; + private Integer promotionLevel; + private String password; + private String email; + private String avatar; + private BigDecimal accountAmount; + private BigDecimal accountCredits; + private String invitationCode; + private String parentInvitationCode; + private Long steamId; + private String transactionLink; + private String isRealCheck; + private String realName; + private Integer subordinateNumber; + private String deliveryAddress; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java new file mode 100644 index 0000000..b5bc8c8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java @@ -0,0 +1,69 @@ +package com.ruoyi.common.core.domain.model; + +/** + * 用户登录对象 + * + * @author ruoyi + */ +public class LoginBody +{ + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + /** + * 验证码 + */ + private String code; + + /** + * 唯一标识 + */ + private String uuid; + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getCode() + { + return code; + } + + public void setCode(String code) + { + this.code = code; + } + + public String getUuid() + { + return uuid; + } + + public void setUuid(String uuid) + { + this.uuid = uuid; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java new file mode 100644 index 0000000..0350c9d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java @@ -0,0 +1,181 @@ +package com.ruoyi.common.core.domain.model; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.entity.UserData; +import lombok.Data; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.util.ObjectUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author ruoyi + */ +@Data +public class LoginUser implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 用户信息 + */ + private SysUser user; + + private UserData userData; + + public LoginUser(Long userId, UserData userData) { + this.userId = userId; + this.userData = userData; + } + + public LoginUser() + { + } + + public LoginUser(SysUser user, Set permissions) + { + this.user = user; + this.permissions = permissions; + } + + public LoginUser(Long userId, Long deptId, SysUser user, Set permissions) + { + this.userId = userId; + this.deptId = deptId; + this.user = user; + this.permissions = permissions; + } + + @JSONField(serialize = false) + @Override + public String getPassword() + { + if (!ObjectUtils.isEmpty(user)) { + return user.getPassword(); + } + if (!ObjectUtils.isEmpty(userData)) { + return userData.getPassword(); + } + return null; + } + + @Override + public String getUsername() + { + if (!ObjectUtils.isEmpty(user)) { + return user.getUserName(); + } + if (!ObjectUtils.isEmpty(userData)) { + return userData.getUserName(); + } + return null; + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isEnabled() + { + return true; + } + + @Override + public Collection getAuthorities() + { + return Collections.emptyList(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java new file mode 100644 index 0000000..868a1fc --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java @@ -0,0 +1,11 @@ +package com.ruoyi.common.core.domain.model; + +/** + * 用户注册对象 + * + * @author ruoyi + */ +public class RegisterBody extends LoginBody +{ + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDataInfo.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDataInfo.java new file mode 100644 index 0000000..00918a3 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDataInfo.java @@ -0,0 +1,36 @@ +package com.ruoyi.common.core.page; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author ruoyi + */ +@Data +public class PageDataInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 总记录数 + */ + private long total; + + /** + * 列表数据 + */ + private List rows; + + /** + * 消息状态码 + */ + private int code; + + /** + * 消息内容 + */ + private String msg; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java new file mode 100644 index 0000000..8966cb4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java @@ -0,0 +1,101 @@ +package com.ruoyi.common.core.page; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 分页数据 + * + * @author ruoyi + */ +public class PageDomain +{ + /** 当前记录起始索引 */ + private Integer pageNum; + + /** 每页显示记录数 */ + private Integer pageSize; + + /** 排序列 */ + private String orderByColumn; + + /** 排序的方向desc或者asc */ + private String isAsc = "asc"; + + /** 分页参数合理化 */ + private Boolean reasonable = true; + + public String getOrderBy() + { + if (StringUtils.isEmpty(orderByColumn)) + { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum() + { + return pageNum; + } + + public void setPageNum(Integer pageNum) + { + this.pageNum = pageNum; + } + + public Integer getPageSize() + { + return pageSize; + } + + public void setPageSize(Integer pageSize) + { + this.pageSize = pageSize; + } + + public String getOrderByColumn() + { + return orderByColumn; + } + + public void setOrderByColumn(String orderByColumn) + { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc() + { + return isAsc; + } + + public void setIsAsc(String isAsc) + { + if (StringUtils.isNotEmpty(isAsc)) + { + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) + { + isAsc = "asc"; + } + else if ("descending".equals(isAsc)) + { + isAsc = "desc"; + } + this.isAsc = isAsc; + } + } + + public Boolean getReasonable() + { + if (StringUtils.isNull(reasonable)) + { + return Boolean.TRUE; + } + return reasonable; + } + + public void setReasonable(Boolean reasonable) + { + this.reasonable = reasonable; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java new file mode 100644 index 0000000..847685b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java @@ -0,0 +1,85 @@ +package com.ruoyi.common.core.page; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author ruoyi + */ +public class TableDataInfo implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private long total; + + /** 列表数据 */ + private List rows; + + /** 消息状态码 */ + private int code; + + /** 消息内容 */ + private String msg; + + /** + * 表格数据对象 + */ + public TableDataInfo() + { + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, int total) + { + this.rows = list; + this.total = total; + } + + public long getTotal() + { + return total; + } + + public void setTotal(long total) + { + this.total = total; + } + + public List getRows() + { + return rows; + } + + public void setRows(List rows) + { + this.rows = rows; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java new file mode 100644 index 0000000..a120c30 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java @@ -0,0 +1,56 @@ +package com.ruoyi.common.core.page; + +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author ruoyi + */ +public class TableSupport +{ + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain() + { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest() + { + return getPageDomain(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java new file mode 100644 index 0000000..c09df1c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java @@ -0,0 +1,284 @@ +package com.ruoyi.common.core.redis; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +/** + * spring redis 工具类 + * + * @author ruoyi + **/ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +@Component +public class RedisCache +{ + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) + { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(final String key) + { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public boolean deleteObject(final Collection collection) + { + return redisTemplate.delete(collection) > 0; + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public long setCacheList(final String key, final List dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet(final String key, final Set dataSet) + { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public Set getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap(final String key, final Map dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * + * @param key + * @param dataMap + * @param TTL + * @param unit + * @param + */ + public void setCacheMap(final String key, final Map dataMap,Integer TTL,TimeUnit unit) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + redisTemplate.opsForHash().getOperations().expire(key,TTL,unit); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public T getCacheMapValue(final String key, final String hKey) + { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public List getMultiCacheMapValue(final String key, final Collection hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return 是否成功 + */ + public boolean deleteCacheMapValue(final String key, final String hKey) + { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection keys(final String pattern) + { + return redisTemplate.keys(pattern); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java new file mode 100644 index 0000000..84124aa --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java @@ -0,0 +1,86 @@ +package com.ruoyi.common.core.text; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符集工具类 + * + * @author ruoyi + */ +public class CharsetKit +{ + /** ISO-8859-1 */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** UTF-8 */ + public static final String UTF_8 = "UTF-8"; + /** GBK */ + public static final String GBK = "GBK"; + + /** ISO-8859-1 */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** UTF-8 */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** GBK */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) + { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) + { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) + { + if (null == srcCharset) + { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) + { + destCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) + { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() + { + return Charset.defaultCharset().name(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java new file mode 100644 index 0000000..0066112 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java @@ -0,0 +1,1006 @@ +package com.ruoyi.common.core.text; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; +import com.ruoyi.common.utils.StringUtils; +import org.apache.commons.lang3.ArrayUtils; + +/** + * 类型转换器 + * + * @author ruoyi + */ +public class Convert +{ + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + case "yes": + case "ok": + case "1": + return true; + case "false": + case "no": + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return BigDecimal.valueOf((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else if (obj instanceof Byte[]) + { + byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj); + return str(bytes, charset); + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) + { + char[] c = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) + { + char[] c = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + String returnString = new String(c); + + return returnString; + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + // 优化double计算精度丢失问题 + BigDecimal nNum = new BigDecimal(n); + BigDecimal decimal = new BigDecimal(10); + BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN); + double d = scale.doubleValue(); + s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java new file mode 100644 index 0000000..c78ac77 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java @@ -0,0 +1,92 @@ +package com.ruoyi.common.core.text; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author ruoyi + */ +public class StrFormatter +{ + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) + { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) + { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) + { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) + { + if (handledPosition == 0) + { + return strPattern; + } + else + { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } + else + { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) + { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) + { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + else + { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } + else + { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java new file mode 100644 index 0000000..10b7306 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java @@ -0,0 +1,20 @@ +package com.ruoyi.common.enums; + +/** + * 操作状态 + * + * @author ruoyi + * + */ +public enum BusinessStatus +{ + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java new file mode 100644 index 0000000..2e17c4a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.enums; + +/** + * 业务操作类型 + * + * @author ruoyi + */ +public enum BusinessType +{ + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java new file mode 100644 index 0000000..0d945be --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.enums; + +/** + * 数据源 + * + * @author ruoyi + */ +public enum DataSourceType +{ + /** + * 主库 + */ + MASTER, + + /** + * 从库 + */ + SLAVE +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java new file mode 100644 index 0000000..be6f739 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java @@ -0,0 +1,36 @@ +package com.ruoyi.common.enums; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.lang.Nullable; + +/** + * 请求方式 + * + * @author ruoyi + */ +public enum HttpMethod +{ + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map mappings = new HashMap<>(16); + + static + { + for (HttpMethod httpMethod : values()) + { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) + { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) + { + return (this == resolve(method)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java new file mode 100644 index 0000000..c609fd8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java @@ -0,0 +1,20 @@ +package com.ruoyi.common.enums; + +/** + * 限流类型 + * + * @author ruoyi + */ + +public enum LimitType +{ + /** + * 默认策略全局限流 + */ + DEFAULT, + + /** + * 根据请求者IP进行限流 + */ + IP +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java new file mode 100644 index 0000000..bdd143c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.enums; + +/** + * 操作人类别 + * + * @author ruoyi + */ +public enum OperatorType +{ + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java new file mode 100644 index 0000000..d7ff44a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java @@ -0,0 +1,30 @@ +package com.ruoyi.common.enums; + +/** + * 用户状态 + * + * @author ruoyi + */ +public enum UserStatus +{ + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java new file mode 100644 index 0000000..f6ad2ab --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java @@ -0,0 +1,15 @@ +package com.ruoyi.common.exception; + +/** + * 演示模式异常 + * + * @author ruoyi + */ +public class DemoModeException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public DemoModeException() + { + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java new file mode 100644 index 0000000..81a71b5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java @@ -0,0 +1,58 @@ +package com.ruoyi.common.exception; + +/** + * 全局异常 + * + * @author ruoyi + */ +public class GlobalException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException() + { + } + + public GlobalException(String message) + { + this.message = message; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public GlobalException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage() + { + return message; + } + + public GlobalException setMessage(String message) + { + this.message = message; + return this; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java new file mode 100644 index 0000000..fcc7ab6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java @@ -0,0 +1,74 @@ +package com.ruoyi.common.exception; + +/** + * 业务异常 + * + * @author ruoyi + */ +public final class ServiceException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() + { + } + + public ServiceException(String message) + { + this.message = message; + } + + public ServiceException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public String getDetailMessage() + { + return detailMessage; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java new file mode 100644 index 0000000..980fa46 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.exception; + +/** + * 工具类异常 + * + * @author ruoyi + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) + { + super(e.getMessage(), e); + } + + public UtilException(String message) + { + super(message); + } + + public UtilException(String message, Throwable throwable) + { + super(message, throwable); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java new file mode 100644 index 0000000..b55d72e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java @@ -0,0 +1,97 @@ +package com.ruoyi.common.exception.base; + +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 基础异常 + * + * @author ruoyi + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() + { + String message = null; + if (!StringUtils.isEmpty(code)) + { + message = MessageUtils.message(code, args); + } + if (message == null) + { + message = defaultMessage; + } + return message; + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java new file mode 100644 index 0000000..871f09b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.exception.file; + +import com.ruoyi.common.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author ruoyi + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args, null); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..70e0ec9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author ruoyi + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..ec6ab05 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author ruoyi + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java new file mode 100644 index 0000000..f45e7ef --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java @@ -0,0 +1,61 @@ +package com.ruoyi.common.exception.file; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * 文件上传异常类 + * + * @author ruoyi + */ +public class FileUploadException extends Exception +{ + + private static final long serialVersionUID = 1L; + + private final Throwable cause; + + public FileUploadException() + { + this(null, null); + } + + public FileUploadException(final String msg) + { + this(msg, null); + } + + public FileUploadException(String msg, Throwable cause) + { + super(msg); + this.cause = cause; + } + + @Override + public void printStackTrace(PrintStream stream) + { + super.printStackTrace(stream); + if (cause != null) + { + stream.println("Caused by:"); + cause.printStackTrace(stream); + } + } + + @Override + public void printStackTrace(PrintWriter writer) + { + super.printStackTrace(writer); + if (cause != null) + { + writer.println("Caused by:"); + cause.printStackTrace(writer); + } + } + + @Override + public Throwable getCause() + { + return cause; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java new file mode 100644 index 0000000..011f308 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java @@ -0,0 +1,80 @@ +package com.ruoyi.common.exception.file; + +import java.util.Arrays; + +/** + * 文件上传 误异常类 + * + * @author ruoyi + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java new file mode 100644 index 0000000..a567b40 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java @@ -0,0 +1,34 @@ +package com.ruoyi.common.exception.job; + +/** + * 计划策略异常 + * + * @author ruoyi + */ +public class TaskException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Code code; + + public TaskException(String msg, Code code) + { + this(msg, code, null); + } + + public TaskException(String msg, Code code, Exception nestedEx) + { + super(msg, nestedEx); + this.code = code; + } + + public Code getCode() + { + return code; + } + + public enum Code + { + TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java new file mode 100644 index 0000000..2bf5038 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 黑名单IP异常类 + * + * @author ruoyi + */ +public class BlackListException extends UserException +{ + private static final long serialVersionUID = 1L; + + public BlackListException() + { + super("login.blocked", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java new file mode 100644 index 0000000..389dbc7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 验证码错误异常类 + * + * @author ruoyi + */ +public class CaptchaException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException() + { + super("user.jcaptcha.error", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..85f9486 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 验证码失效异常类 + * + * @author ruoyi + */ +public class CaptchaExpireException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() + { + super("user.jcaptcha.expire", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java new file mode 100644 index 0000000..c292d70 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.exception.user; + +import com.ruoyi.common.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author ruoyi + */ +public class UserException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) + { + super("user", code, args, null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java new file mode 100644 index 0000000..eff8181 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户不存在异常类 + * + * @author ruoyi + */ +public class UserNotExistsException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserNotExistsException() + { + super("user.not.exists", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 0000000..a7f3e5f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author ruoyi + */ +public class UserPasswordNotMatchException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() + { + super("user.password.not.match", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java new file mode 100644 index 0000000..c887cf1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户错误最大次数异常类 + * + * @author ruoyi + */ +public class UserPasswordRetryLimitExceedException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) + { + super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java new file mode 100644 index 0000000..e1e431b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.filter; + +import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; + +/** + * 排除JSON敏感属性 + * + * @author ruoyi + */ +public class PropertyPreExcludeFilter extends SimplePropertyPreFilter +{ + public PropertyPreExcludeFilter() + { + } + + public PropertyPreExcludeFilter addExcludes(String... filters) + { + for (int i = 0; i < filters.length; i++) + { + this.getExcludes().add(filters[i]); + } + return this; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java new file mode 100644 index 0000000..efeded9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java @@ -0,0 +1,52 @@ +package com.ruoyi.common.filter; + +import java.io.IOException; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.http.MediaType; +import com.ruoyi.common.utils.StringUtils; + +/** + * Repeatable 过滤器 + * + * @author ruoyi + */ +public class RepeatableFilter implements Filter +{ + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest + && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) + { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) + { + chain.doFilter(request, response); + } + else + { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() + { + + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 0000000..5c6ee08 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,76 @@ +package com.ruoyi.common.filter; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import com.ruoyi.common.utils.http.HttpHelper; +import com.ruoyi.common.constant.Constants; + +/** + * 构建可重复读取inputStream的request + * + * @author ruoyi + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper +{ + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException + { + super(request); + request.setCharacterEncoding(Constants.UTF8); + response.setCharacterEncoding(Constants.UTF8); + + body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8); + } + + @Override + public BufferedReader getReader() throws IOException + { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() + { + @Override + public int read() throws IOException + { + return bais.read(); + } + + @Override + public int available() throws IOException + { + return body.length; + } + + @Override + public boolean isFinished() + { + return false; + } + + @Override + public boolean isReady() + { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) + { + + } + }; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java new file mode 100644 index 0000000..da76913 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java @@ -0,0 +1,75 @@ +package com.ruoyi.common.filter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.enums.HttpMethod; + +/** + * 防止XSS攻击的过滤器 + * + * @author ruoyi + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(tempExcludes)) + { + String[] url = tempExcludes.split(","); + for (int i = 0; url != null && i < url.length; i++) + { + excludes.add(url[i]); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) + { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() + { + + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..bf7837b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,111 @@ +package com.ruoyi.common.filter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import org.apache.commons.io.IOUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.html.EscapeUtil; + +/** + * XSS过滤处理 + * + * @author ruoyi + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapesValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapesValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return escapesValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = EscapeUtil.clean(json).trim(); + byte[] jsonBytes = json.getBytes("utf-8"); + final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public int available() throws IOException + { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + * @param request + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/rabbitmq/config/DelayedQueueConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/rabbitmq/config/DelayedQueueConfig.java new file mode 100644 index 0000000..c5830cd --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/rabbitmq/config/DelayedQueueConfig.java @@ -0,0 +1,78 @@ +package com.ruoyi.common.rabbitmq.config; + +import org.springframework.amqp.core.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import jakarta.annotation.Resource; +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class DelayedQueueConfig { + + //死信交换机,队列,路由相关配置 + public static final String DLK_EXCHANGE = "dlk_exchange"; + public static final String DLK_ROUTEKEY = "dlk_routeKey"; + public static final String DLK_QUEUE = "dlk_queue"; + + // 业务交换机... + public static final String OPEN_ROLL_EXCHANGE = "open_roll_exchange"; + public static final String OPEN_ROLL_QUEUE = "open_roll_queue"; + public static final String OPEN_ROLL_KEY = "open_roll_key"; + + // 业务交换机... + public static final String DELIVERY_QUEUE = "delivery_queue"; + + + // roll开奖倒计时 + @Bean + public CustomExchange openRollExchange() { + return new CustomExchange(OPEN_ROLL_EXCHANGE,"direct", true, false); + } + @Bean + public Queue openRollQueue() { + + //只需要在声明业务队列时添加x-dead-letter-exchange,值为死信交换机 + Map map = new HashMap<>(1); + map.put("x-dead-letter-exchange",DLK_EXCHANGE); + //该参数x-dead-letter-routing-key可以修改该死信的路由key,不设置则使用原消息的路由key + map.put("x-dead-letter-routing-key",DLK_ROUTEKEY); + + return new Queue(OPEN_ROLL_QUEUE,true,false,false,map); + } + @Bean + public Binding openRollBind(Queue openRollQueue, CustomExchange openRollExchange) { + return BindingBuilder + .bind(openRollQueue) + .to(openRollExchange) + .with(OPEN_ROLL_KEY) + .noargs(); + } + + // 死信交换机 + @Bean + public DirectExchange dlkExchange(){ + return new DirectExchange(DLK_EXCHANGE,true,false); + } + @Bean + public Queue dlkQueue(){ + return new Queue(DLK_QUEUE,true,false,false); + } + @Bean + public Binding dlkBind(){ + return BindingBuilder.bind(dlkQueue()).to(dlkExchange()).with(DLK_ROUTEKEY); + } + + + + // 自动提货业务消息队列 + //@Bean + public Queue deliveryQueue() { + return new Queue(DELIVERY_QUEUE); + } + + +} + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/rabbitmq/config/FightQueueConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/rabbitmq/config/FightQueueConfig.java new file mode 100644 index 0000000..afa9068 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/rabbitmq/config/FightQueueConfig.java @@ -0,0 +1,41 @@ +package com.ruoyi.common.rabbitmq.config; + +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.BindingBuilder; +import org.springframework.amqp.core.CustomExchange; +import org.springframework.amqp.core.Queue; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.HashMap; + +// 弃用 +//@Configuration +public class FightQueueConfig { + + // public static final String FIGHT_EXCHANGE = "fight_exchange"; + // public static final String FIGHT_QUEUE = "fight_queue"; + // public static final String FIGHT_ROUTING_KEY = "fight_routingKey"; + // + // @Bean + // public Queue fightQueue() { + // return new Queue(FIGHT_QUEUE); + // } + // + // @Bean + // public CustomExchange fightExchange() { + // HashMap arguments = new HashMap<>(); + // arguments.put("x-fight-type", "direct"); + // // arguments.put("x-fight-type", "direct"); + // return new CustomExchange(FIGHT_EXCHANGE, "x-delayed-message", true, false, arguments); + // // return new CustomExchange(FIGHT_EXCHANGE, "x-fight-message", true, false, arguments); + // } + // + // + // + // @Bean + // public Binding fightQueueBindingFightExchange(Queue fightQueue, CustomExchange fightExchange) { + // return BindingBuilder.bind(fightQueue).to(fightExchange).with(FIGHT_ROUTING_KEY).noargs(); + // } +} + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/rabbitmq/config/RabbitMQConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/rabbitmq/config/RabbitMQConfig.java new file mode 100644 index 0000000..bb91893 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/rabbitmq/config/RabbitMQConfig.java @@ -0,0 +1,15 @@ +package com.ruoyi.common.rabbitmq.config; + +import org.springframework.amqp.support.converter.JacksonJsonMessageConverter; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RabbitMQConfig { + + @Bean + public MessageConverter jsonMessageConverter() { + return new JacksonJsonMessageConverter(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/redis/config/RedisLock.java b/ruoyi-common/src/main/java/com/ruoyi/common/redis/config/RedisLock.java new file mode 100644 index 0000000..8a64b9d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/redis/config/RedisLock.java @@ -0,0 +1,39 @@ +package com.ruoyi.common.redis.config; + +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +@Component +public class RedisLock { + @Autowired + private RedissonClient redissonClient; + public RLock getRLock(String lockKey) { + return redissonClient.getLock(lockKey); + } + + public Boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) { + RLock rLock = getRLock(lockKey); + boolean tryLock; + try { + tryLock = rLock.tryLock(waitTime, leaseTime, unit); + } catch (InterruptedException e) { + return false; + } + return tryLock; + } + + public void unlock(String lockKey) { + RLock lock = getRLock(lockKey); + + if(lock.isLocked()){ // 是否还是锁定状态 + if(lock.isHeldByCurrentThread()){ // 时候是当前执行线程的锁 + lock.unlock(); // 释放锁 + } + } + // lock.unlock(); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/redis/config/RedissonConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/redis/config/RedissonConfig.java new file mode 100644 index 0000000..d881e61 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/redis/config/RedissonConfig.java @@ -0,0 +1,35 @@ +package com.ruoyi.common.redis.config; + +import com.ruoyi.common.utils.StringUtils; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.SingleServerConfig; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RedissonConfig { + @Value("${spring.redis.host}") + private String host; + + @Value("${spring.redis.port}") + private String port; + + @Value("${spring.redis.password}") + private String password; + + @Value("${spring.redis.database}") + private String database; + + @Bean(destroyMethod = "shutdown") + @ConditionalOnMissingBean(RedissonClient.class) + public RedissonClient redissonClient() { + Config config = new Config(); + config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password); + config.useSingleServer().setDatabase(Integer.parseInt(database)); // 设置Redis索引 + return Redisson.create(config); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/typehandler/JsonObjectHandler.java b/ruoyi-common/src/main/java/com/ruoyi/common/typehandler/JsonObjectHandler.java new file mode 100644 index 0000000..e580d50 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/typehandler/JsonObjectHandler.java @@ -0,0 +1,64 @@ +package com.ruoyi.common.typehandler; + +import com.ruoyi.common.utils.json.SnakeCaseJsonUtils; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + + +public class JsonObjectHandler extends BaseTypeHandler { + + private final Type actualType; + + public JsonObjectHandler() { + actualType = actualType(); + } + + protected Type actualType() { + Type t = getClass().getGenericSuperclass(); + // Cglib代理可能会继承子类,需要取父类名称 + if (!(t instanceof ParameterizedType)) { + t = getClass().getSuperclass().getGenericSuperclass(); + } + ParameterizedType p = (ParameterizedType) t; + return p.getActualTypeArguments()[0]; + } + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { + String json = SnakeCaseJsonUtils.toSnakeCaseJson(parameter); + if (jdbcType == null) { + ps.setObject(i, json); + } else { + ps.setObject(i, json, jdbcType.TYPE_CODE); + } + } + + @Override + public T getNullableResult(ResultSet rs, String columnName) throws SQLException { + return parseJson(rs.getString(columnName)); + } + + @Override + public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + return parseJson(rs.getString(columnIndex)); + } + + @Override + public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + return parseJson(cs.getString(columnIndex)); + } + + private T parseJson(String json) { + if (json == null || json.isEmpty()) { + return null; + } + return SnakeCaseJsonUtils.fromSnakeCaseJson(json, actualType); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java new file mode 100644 index 0000000..b6326c2 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java @@ -0,0 +1,114 @@ +package com.ruoyi.common.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 精确的浮点数运算 + * + * @author ruoyi + */ +public class Arith +{ + + /** 默认除法运算精度 */ + private static final int DEF_DIV_SCALE = 10; + + /** 这个类不能实例化 */ + private Arith() + { + } + + /** + * 提供精确的加法运算。 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.add(b2).doubleValue(); + } + + /** + * 提供精确的减法运算。 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double sub(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.subtract(b2).doubleValue(); + } + + /** + * 提供精确的乘法运算。 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double mul(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.multiply(b2).doubleValue(); + } + + /** + * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 + * 小数点以后10位,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double div(double v1, double v2) + { + return div(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 + * 定精度,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示表示需要精确到小数点以后几位。 + * @return 两个参数的商 + */ + public static double div(double v1, double v2, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (b1.compareTo(BigDecimal.ZERO) == 0) + { + return BigDecimal.ZERO.doubleValue(); + } + return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue(); + } + + /** + * 提供精确的小数位四舍五入处理。 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(double v, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = BigDecimal.ONE; + return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateTimeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateTimeUtils.java new file mode 100644 index 0000000..6543bd8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateTimeUtils.java @@ -0,0 +1,259 @@ +package com.ruoyi.common.utils; + +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 时间工具类 + * + * @author ruoyi + */ +public class DateTimeUtils extends org.apache.commons.lang3.time.DateUtils { + // 定义系统默认时区常量,避免重复调用,保证线程安全 + public static final ZoneId ZONE_ID = ZoneId.of("Asia/Shanghai"); + public static final ZoneOffset ZONE_OFFSET = ZoneOffset.ofHours(8); // 东八区固定偏移 + + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + // 时间戳一套API + public static Long toSeconds(LocalDateTime localDateTime) { + return localDateTime + .atZone(ZONE_ID) // 假设是本地时区时间 + .withZoneSameInstant(ZoneOffset.UTC) // 转到 UTC + .toEpochSecond(); + } + + // LocalDateTime一套API + // 时区转换 + public static LocalDateTime utcToZone8(LocalDateTime utcDateTime) { + return utcDateTime.atZone(ZoneOffset.UTC).withZoneSameInstant(ZONE_ID).toLocalDateTime(); + } + + // 时间戳-LocalDateTime转换API + + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String date(final LocalDateTime date) { + return date.format(DateTimeFormatter.ofPattern(YYYY_MM_DD)); + } + + public static final String dateTime(final LocalDateTime date) { + return date.format(DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS)); + } + public static final String dateTime(final LocalDateTime date, String format) { + return date.format(DateTimeFormatter.ofPattern(format)); + } + + public static final String parseDateToStr(final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static final LocalDateTime localDateTime(final String ts, String format) { + return LocalDateTime.parse(ts, DateTimeFormatter.ofPattern(format)); + } + + public static final Date dateTime(final String format, final String ts) { + try { + return new SimpleDateFormat(format).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), parsePatterns); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算时间差 + * + * @param endDate 最后时间 + * @param startTime 开始时间 + * @return 时间差(天/小时/分钟) + */ + public static String timeDistance(Date endDate, Date startTime) { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - startTime.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 Date ==> LocalDateTime + */ + public static LocalDateTime toLocalDateTime(Date date) { + return date.toInstant() + .atZone(ZONE_ID) + .toLocalDateTime(); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + public static Long now() { + return System.currentTimeMillis() / 1000; + } + + public static Long getTodayAtZero() { + return LocalDate.now().atStartOfDay().atZone(ZONE_ID).toEpochSecond(); + } + + public static Long getTomorrowAtZero() { + return LocalDate.now().plusDays(1).atStartOfDay(ZONE_ID).toEpochSecond(); + } + + + /** + * Generate daily date strings between beginTime and endTime in yyyy-MM-dd format + * @param beginTime start date string in yyyy-MM-dd format + * @param endTime end date string in yyyy-MM-dd format + * @return list of date strings in yyyy-MM-dd format + */ + public static List generateDailyDates(String beginTime, String endTime) { + List dates = new ArrayList<>(); + try { + // Convert string dates to LocalDate + LocalDate startDate = LocalDate.parse(beginTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + LocalDate endDate = LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + + // Calculate days between start and end + long daysBetween = ChronoUnit.DAYS.between(startDate, endDate); + + // Generate each date in between + for (long i = 0; i <= daysBetween; i++) { + LocalDate currentDate = startDate.plusDays(i); + dates.add(currentDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + } + } catch (Exception e) { + // If parsing fails, return empty list + e.printStackTrace(); + } + return dates; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java new file mode 100644 index 0000000..7ada2bf --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java @@ -0,0 +1,200 @@ +package com.ruoyi.common.utils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import org.apache.commons.lang3.time.DateFormatUtils; + +/** + * 时间工具类 + * + * @author ruoyi + */ +@Deprecated +public class DateUtils extends org.apache.commons.lang3.time.DateUtils +{ + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() + { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() + { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() + { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() + { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) + { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) + { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) + { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) + { + try + { + return new SimpleDateFormat(format).parse(ts); + } + catch (ParseException e) + { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) + { + if (str == null) + { + return null; + } + try + { + return parseDate(str.toString(), parsePatterns); + } + catch (ParseException e) + { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() + { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) + { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算时间差 + * + * @param endDate 最后时间 + * @param startTime 开始时间 + * @return 时间差(天/小时/分钟) + */ + public static String timeDistance(Date endDate, Date startTime) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - startTime.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) + { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) + { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + public static LocalDateTime getNow() { + return LocalDateTime.now(); + } + + public static LocalDateTime getTodayAtZero() { + return LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0, 0)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java new file mode 100644 index 0000000..cc5eab2 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java @@ -0,0 +1,186 @@ +package com.ruoyi.common.utils; + +import java.util.Collection; +import java.util.List; +import com.alibaba.fastjson2.JSONArray; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 字典工具类 + * + * @author ruoyi + */ +public class DictUtils +{ + /** + * 分隔符 + */ + public static final String SEPARATOR = ","; + + /** + * 设置字典缓存 + * + * @param key 参数键 + * @param dictDatas 字典数据列表 + */ + public static void setDictCache(String key, List dictDatas) + { + SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas); + } + + /** + * 获取字典缓存 + * + * @param key 参数键 + * @return dictDatas 字典数据列表 + */ + public static List getDictCache(String key) + { + JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key)); + if (StringUtils.isNotNull(arrayCache)) + { + return arrayCache.toList(SysDictData.class); + } + return null; + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue) + { + return getDictLabel(dictType, dictValue, SEPARATOR); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel) + { + return getDictValue(dictType, dictLabel, SEPARATOR); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue, String separator) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + + if (StringUtils.isNotNull(datas)) + { + if (StringUtils.containsAny(separator, dictValue)) + { + for (SysDictData dict : datas) + { + for (String value : dictValue.split(separator)) + { + if (value.equals(dict.getDictValue())) + { + propertyString.append(dict.getDictLabel()).append(separator); + break; + } + } + } + } + else + { + for (SysDictData dict : datas) + { + if (dictValue.equals(dict.getDictValue())) + { + return dict.getDictLabel(); + } + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel, String separator) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + + if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas)) + { + for (SysDictData dict : datas) + { + for (String label : dictLabel.split(separator)) + { + if (label.equals(dict.getDictLabel())) + { + propertyString.append(dict.getDictValue()).append(separator); + break; + } + } + } + } + else + { + for (SysDictData dict : datas) + { + if (dictLabel.equals(dict.getDictLabel())) + { + return dict.getDictValue(); + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 删除指定字典缓存 + * + * @param key 字典键 + */ + public static void removeDictCache(String key) + { + SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key)); + } + + /** + * 清空字典缓存 + */ + public static void clearDictCache() + { + Collection keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*"); + SpringUtils.getBean(RedisCache.class).deleteObject(keys); + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + public static String getCacheKey(String configKey) + { + return CacheConstants.SYS_DICT_KEY + configKey; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java new file mode 100644 index 0000000..214e4a0 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java @@ -0,0 +1,39 @@ +package com.ruoyi.common.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; +import org.apache.commons.lang3.exception.ExceptionUtils; + +/** + * 错误信息处理类。 + * + * @author ruoyi + */ +public class ExceptionUtil +{ + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage(Throwable e) + { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); + } + + public static String getRootErrorMessage(Exception e) + { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) + { + return ""; + } + String msg = root.getMessage(); + if (msg == null) + { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java new file mode 100644 index 0000000..0de30c6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.utils; + +/** + * 处理并记录日志文件 + * + * @author ruoyi + */ +public class LogUtils +{ + public static String getBlock(Object msg) + { + if (msg == null) + { + msg = ""; + } + return "[" + msg.toString() + "]"; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java new file mode 100644 index 0000000..7dac75a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.utils; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 获取i18n资源文件 + * + * @author ruoyi + */ +public class MessageUtils +{ + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/MoneyUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MoneyUtil.java new file mode 100644 index 0000000..5eb9769 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MoneyUtil.java @@ -0,0 +1,11 @@ +package com.ruoyi.common.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class MoneyUtil { + public static String toStr(BigDecimal money) { + if (money == null) return "0.00"; + return money.setScale(2, RoundingMode.HALF_UP).toPlainString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java new file mode 100644 index 0000000..70e9b08 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java @@ -0,0 +1,35 @@ +package com.ruoyi.common.utils; + +import com.github.pagehelper.PageHelper; +import com.ruoyi.common.core.page.PageDomain; +import com.ruoyi.common.core.page.TableSupport; +import com.ruoyi.common.utils.sql.SqlUtil; + +/** + * 分页工具类 + * + * @author ruoyi + */ +public class PageUtils extends PageHelper +{ + /** + * 设置请求分页数据 + */ + public static void startPage() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + Boolean reasonable = pageDomain.getReasonable(); + PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); + } + + /** + * 清理分页的线程变量 + */ + public static void clearPage() + { + PageHelper.clearPage(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/RandomUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/RandomUtil.java new file mode 100644 index 0000000..199e549 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/RandomUtil.java @@ -0,0 +1,138 @@ +package com.ruoyi.common.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class RandomUtil { + // 返回[0,1)范围内的一个随机数 + public static double random() { + return ThreadLocalRandom.current().nextDouble(); + } + + // 返回[0,max)范围内的一个整数 + public static int rand(int max) { + int min = 0; + if (min > max) { + throw new IllegalArgumentException("min cannot be greater than max"); + } + if (min == max) { + return min; + } + return ThreadLocalRandom.current().nextInt(min, max); + } + + // 返回[min,max)范围内的一个整数 + public static int rand(int min, int max) { + if (min > max) { + throw new IllegalArgumentException("min cannot be greater than max"); + } + if (min == max) { + return min; + } + return ThreadLocalRandom.current().nextInt(min, max); + } + + // 返回[min,max]范围内的一个整数 + public static int randInt(int min, int max) { + if (min > max) { + throw new IllegalArgumentException("min cannot be greater than max"); + } + if (min == max) { + return min; + } + return ThreadLocalRandom.current().nextInt(min, max + 1); + } + + public static T choice(List collection) { + return choices(collection, 1).get(0); + } + + public static List choices(List list, int k) { + if (list == null || list.isEmpty() || k <= 0) { + return new ArrayList<>(); + } + double[] weights = new double[list.size()]; + for (int i = 0; i < list.size(); i++) { + weights[i] = 1; + } + return choices(list, weights, k); + } + + public static List choices(List list, List weights, int k) { + return choices(list, weights.stream().mapToDouble(Double::doubleValue).toArray(), k); + } + + public static List choices(T[] list, double[] weights, int k) { + return choices(Arrays.asList(list), weights, k); + } + + /** + * 随机获取集合中的元素,支持加权随机 + * + * @param list 候选集合 + * @param weights 对应集合中每个元素的权重数组 + * @param k 选取的元素个数 + * @param 泛型类型 + * @return 包含 k 个随机元素的列表 + */ + public static List choices(List list, double[] weights, int k) { + if (list == null || list.isEmpty()) { + throw new IllegalArgumentException("Collection cannot be null or empty"); + } + if (weights == null || weights.length != list.size()) { + throw new IllegalArgumentException("Weights array cannot be null and must match collection size"); + } + if (k <= 0) { + return new ArrayList<>(); + } + + + List result = new ArrayList<>(k); + + // 1. 构建前缀和数组(累积权重) + // 例如 weights: [1, 2, 3] -> prefixSum: [1, 3, 6] + double[] prefixSum = new double[weights.length]; + prefixSum[0] = weights[0]; + for (int i = 1; i < weights.length; i++) { + prefixSum[i] = prefixSum[i - 1] + weights[i]; + } + double totalWeight = prefixSum[weights.length - 1]; + + if (totalWeight <= 0) { + throw new IllegalArgumentException("Total weight must be positive"); + } + + // 2. 进行 k 次随机选择 + for (int i = 0; i < k; i++) { + // 生成 [0, totalWeight) 之间的随机数 + double r = ThreadLocalRandom.current().nextDouble() * totalWeight; + System.out.println(r); + // 3. 使用二分查找在累积权重数组中找到对应的索引 + int index = binarySearchPrefix(prefixSum, r); + + result.add(list.get(index)); + } + + return result; + } + + /** + * 在前缀和数组中二分查找第一个大于 target 的索引 + */ + private static int binarySearchPrefix(double[] prefixSum, double target) { + int low = 0; + int high = prefixSum.length - 1; + + while (low < high) { + int mid = (low + high) >>> 1; + if (prefixSum[mid] <= target) { + low = mid + 1; + } else { + high = mid; + } + } + return low; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java new file mode 100644 index 0000000..22119c7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java @@ -0,0 +1,177 @@ +package com.ruoyi.common.utils; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.util.PatternMatchUtils; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.exception.ServiceException; + +/** + * 安全服务工具类 + * + * @author ruoyi + */ +public class SecurityUtils +{ + /** + * 用户ID + **/ + public static Long getUserId() + { + try + { + return getLoginUser().getUserId(); + } + catch (Exception e) + { + throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取部门ID + **/ + public static Long getDeptId() + { + try + { + return getLoginUser().getDeptId(); + } + catch (Exception e) + { + throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户账户 + **/ + public static String getUsername() + { + try + { + return getLoginUser().getUsername(); + } + catch (Exception e) + { + throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户 + **/ + public static LoginUser getLoginUser() + { + try + { + return (LoginUser) getAuthentication().getPrincipal(); + } + catch (Exception e) + { + throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取Authentication + */ + public static Authentication getAuthentication() + { + return SecurityContextHolder.getContext().getAuthentication(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public static boolean hasPermi(String permission) + { + return hasPermi(getLoginUser().getPermissions(), permission); + } + + /** + * 判断是否包含权限 + * + * @param authorities 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public static boolean hasPermi(Collection authorities, String permission) + { + return authorities.stream().filter(StringUtils::hasText) + .anyMatch(x -> Constants.ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(x, permission)); + } + + /** + * 验证用户是否拥有某个角色 + * + * @param role 角色标识 + * @return 用户是否具备某角色 + */ + public static boolean hasRole(String role) + { + List roleList = getLoginUser().getUser().getRoles(); + Collection roles = roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toSet()); + return hasRole(roles, role); + } + + /** + * 判断是否包含角色 + * + * @param roles 角色列表 + * @param role 角色 + * @return 用户是否具备某角色权限 + */ + public static boolean hasRole(Collection roles, String role) + { + return roles.stream().filter(StringUtils::hasText) + .anyMatch(x -> Constants.SUPER_ADMIN.contains(x) || PatternMatchUtils.simpleMatch(x, role)); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java new file mode 100644 index 0000000..5635db7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java @@ -0,0 +1,218 @@ +package com.ruoyi.common.utils; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.text.Convert; + +/** + * 客户端工具类 + * + * @author ruoyi + */ +public class ServletUtils +{ + /** + * 获取String参数 + */ + public static String getParameter(String name) + { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) + { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) + { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) + { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) + { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) + { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map getParams(ServletRequest request) + { + final Map map = request.getParameterMap(); + return Collections.unmodifiableMap(map); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map getParamMap(ServletRequest request) + { + Map params = new HashMap<>(); + for (Map.Entry entry : getParams(request).entrySet()) + { + params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); + } + return params; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() + { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() + { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() + { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() + { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) + { + try + { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) + { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) + { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) + { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) + { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) + { + try + { + return URLEncoder.encode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) + { + try + { + return URLDecoder.decode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java new file mode 100644 index 0000000..1e52ac2 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java @@ -0,0 +1,666 @@ +package com.ruoyi.common.utils; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.springframework.util.AntPathMatcher; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.text.StrFormatter; + +/** + * 字符串工具类 + * + * @author ruoyi + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection coll) + { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + ** @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) + { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map map) + { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) + { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) + { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) + { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) + { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) + { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 判断是否为空,并且不是空白字符 + * + * @param str 要判断的value + * @return 结果 + */ + public static boolean hasText(String str) + { + return (str != null && !str.isEmpty() && containsText(str)); + } + + private static boolean containsText(CharSequence str) + { + int strLen = str.length(); + for (int i = 0; i < strLen; i++) + { + if (!Character.isWhitespace(str.charAt(i))) + { + return true; + } + } + return false; + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) + { + if (isEmpty(params) || isEmpty(template)) + { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) + { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static final Set str2Set(String str, String sep) + { + return new HashSet(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static final List str2List(String str, String sep, boolean filterBlank, boolean trim) + { + List list = new ArrayList(); + if (StringUtils.isEmpty(str)) + { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) + { + return list; + } + String[] split = str.split(sep); + for (String string : split) + { + if (filterBlank && StringUtils.isBlank(string)) + { + continue; + } + if (trim) + { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param collection 给定的集合 + * @param array 给定的数组 + * @return boolean 结果 + */ + public static boolean containsAny(Collection collection, String... array) + { + if (isEmpty(collection) || isEmpty(array)) + { + return false; + } + else + { + for (String str : array) + { + if (collection.contains(str)) + { + return true; + } + } + return false; + } + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) + { + if (isEmpty(cs) || isEmpty(searchCharSequences)) + { + return false; + } + for (CharSequence testStr : searchCharSequences) + { + if (containsIgnoreCase(cs, testStr)) + { + return true; + } + } + return false; + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 + * 例如:user_name->userName + */ + public static String toCamelCase(String s) + { + if (s == null) + { + return null; + } + if (s.indexOf(SEPARATOR) == -1) + { + return s; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + + if (c == SEPARATOR) + { + upperCase = true; + } + else if (upperCase) + { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else + { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String pattern : strs) + { + if (isMatch(pattern, str)) + { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) + { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) + { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) + { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } + + public static Long convertToLong(String str) { + try { + // 获取字符串的字节数组 + byte[] bytes = str.getBytes(); + + // 使用 MD5 获取摘要 + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] hash = md.digest(bytes); + + // 将字节数组转换为 BigInteger + BigInteger bigInteger = new BigInteger(1, hash); + + // 获取 BigInteger 的绝对值并转换为 Long + Long result = bigInteger.abs().longValue(); + + // 确保返回值是非负数 + return result >= 0 ? result : -result; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return -1L; + } + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java new file mode 100644 index 0000000..71fe6d5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java @@ -0,0 +1,99 @@ +package com.ruoyi.common.utils; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 线程相关工具类. + * + * @author ruoyi + */ +public class Threads +{ + private static final Logger logger = LoggerFactory.getLogger(Threads.class); + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) + { + try + { + Thread.sleep(milliseconds); + } + catch (InterruptedException e) + { + return; + } + } + + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) + { + if (pool != null && !pool.isShutdown()) + { + pool.shutdown(); + try + { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + logger.info("Pool did not terminate"); + } + } + } + catch (InterruptedException ie) + { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) + { + if (t == null && r instanceof Future) + { + try + { + Future future = (Future) r; + if (future.isDone()) + { + future.get(); + } + } + catch (CancellationException ce) + { + t = ce; + } + catch (ExecutionException ee) + { + t = ee.getCause(); + } + catch (InterruptedException ie) + { + Thread.currentThread().interrupt(); + } + } + if (t != null) + { + logger.error(t.getMessage(), t); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java new file mode 100644 index 0000000..4463662 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java @@ -0,0 +1,110 @@ +package com.ruoyi.common.utils.bean; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Bean 工具类 + * + * @author ruoyi + */ +public class BeanUtils extends org.springframework.beans.BeanUtils +{ + /** Bean方法名中属性名开始的下标 */ + private static final int BEAN_METHOD_PROP_INDEX = 3; + + /** * 匹配getter方法的正则表达式 */ + private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); + + /** * 匹配setter方法的正则表达式 */ + private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); + + /** + * Bean属性复制工具方法。 + * + * @param dest 目标对象 + * @param src 源对象 + */ + public static void copyBeanProp(Object dest, Object src) + { + try + { + copyProperties(src, dest); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * 获取对象的setter方法。 + * + * @param obj 对象 + * @return 对象的setter方法列表 + */ + public static List getSetterMethods(Object obj) + { + // setter方法列表 + List setterMethods = new ArrayList(); + + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + + // 查找setter方法 + + for (Method method : methods) + { + Matcher m = SET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 1)) + { + setterMethods.add(method); + } + } + // 返回setter方法列表 + return setterMethods; + } + + /** + * 获取对象的getter方法。 + * + * @param obj 对象 + * @return 对象的getter方法列表 + */ + + public static List getGetterMethods(Object obj) + { + // getter方法列表 + List getterMethods = new ArrayList(); + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + // 查找getter方法 + for (Method method : methods) + { + Matcher m = GET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 0)) + { + getterMethods.add(method); + } + } + // 返回getter方法列表 + return getterMethods; + } + + /** + * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 + * + * @param m1 方法名1 + * @param m2 方法名2 + * @return 属性名一样返回true,否则返回false + */ + + public static boolean isMethodPropEquals(String m1, String m2) + { + return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java new file mode 100644 index 0000000..898aca5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.utils.bean; + +import java.util.Set; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Validator; + +/** + * bean对象属性验证 + * + * @author ruoyi + */ +public class BeanValidators +{ + public static void validateWithException(Validator validator, Object object, Class... groups) + throws ConstraintViolationException + { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) + { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ApiStringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ApiStringUtils.java new file mode 100644 index 0000000..4fefad0 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ApiStringUtils.java @@ -0,0 +1,32 @@ +package com.ruoyi.common.utils.file; + +import com.ruoyi.common.config.RuoYiConfig; + +import java.io.File; + +public class ApiStringUtils { + + public static int findNthMatchIndex(String str, String matchStr, int n) { + return findNthMatchIndexHelper(str, matchStr, n, 0); + } + + private static int findNthMatchIndexHelper(String str, String matchStr, int n, int startIndex) { + int index = str.indexOf(matchStr, startIndex); + if (index == -1) { + return -1; + } + if (n == 1) { + return index; + } + return findNthMatchIndexHelper(str, matchStr, n - 1, index + matchStr.length()); + } + + public static String delAvatar(String filePath) { + String domainName = RuoYiConfig.getDomainName(); + int numberOfSlashes = domainName.length() - domainName.replaceAll("/", "").length(); + int nthMatchIndex = ApiStringUtils.findNthMatchIndex(filePath, "/", numberOfSlashes + 2); + File delFile = new File(RuoYiConfig.getProfile() + filePath.substring(nthMatchIndex)); + if (delFile.delete()) return "删除成功!"; + return ""; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java new file mode 100644 index 0000000..68130b9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java @@ -0,0 +1,76 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import org.apache.commons.lang3.StringUtils; + +/** + * 文件类型工具类 + * + * @author ruoyi + */ +public class FileTypeUtils +{ + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java new file mode 100644 index 0000000..d9f2b13 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java @@ -0,0 +1,232 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Objects; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException; +import com.ruoyi.common.exception.file.FileSizeLimitExceededException; +import com.ruoyi.common.exception.file.InvalidExtensionException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.Seq; + +/** + * 文件上传工具类 + * + * @author ruoyi + */ +public class FileUploadUtils +{ + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 默认上传的地址 + */ + private static String defaultBaseDir = RuoYiConfig.getProfile(); + + public static void setDefaultBaseDir(String defaultBaseDir) + { + FileUploadUtils.defaultBaseDir = defaultBaseDir; + } + + public static String getDefaultBaseDir() + { + return defaultBaseDir; + } + + /** + * 以默认配置进行文件上传 + * + * @param file 上传的文件 + * @return 文件名称 + * @throws Exception + */ + public static final String upload(MultipartFile file) throws IOException + { + try + { + return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException + { + try + { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + file.transferTo(Paths.get(absPath)); + return getPathFileName(baseDir, fileName); + } + + /** + * 编码文件名 + */ + public static final String extractFilename(MultipartFile file) + { + return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), + FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); + } + + public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException + { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) + { + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + } + return desc; + } + + public static final String getPathFileName(String uploadDir, String fileName) throws IOException + { + int dirLastIndex = RuoYiConfig.getProfile().length() + 1; + String currentDir = StringUtils.substring(uploadDir, dirLastIndex); + return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @return + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException + { + long size = file.getSize(); + if (size > DEFAULT_MAX_SIZE) + { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) + { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) + { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) + { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) + { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) + { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } + else + { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension + * @param allowedExtension + * @return + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) + { + for (String str : allowedExtension) + { + if (str.equalsIgnoreCase(extension)) + { + return true; + } + } + return false; + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) + { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) + { + extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); + } + return extension; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java new file mode 100644 index 0000000..2062b75 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -0,0 +1,291 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import org.apache.commons.io.FilenameUtils; + +/** + * 文件处理工具类 + * + * @author ruoyi + */ +public class FileUtils +{ + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + /** + * 输出指定文件的byte数组 + * + * @param filePath 文件路径 + * @param os 输出流 + * @return + */ + public static void writeBytes(String filePath, OutputStream os) throws IOException + { + FileInputStream fis = null; + try + { + File file = new File(filePath); + if (!file.exists()) + { + throw new FileNotFoundException(filePath); + } + fis = new FileInputStream(file); + byte[] b = new byte[1024]; + int length; + while ((length = fis.read(b)) > 0) + { + os.write(b, 0, length); + } + } + catch (IOException e) + { + throw e; + } + finally + { + IOUtils.close(os); + IOUtils.close(fis); + } + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeImportBytes(byte[] data) throws IOException + { + return writeBytes(data, RuoYiConfig.getImportPath()); + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @param uploadDir 目标文件 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeBytes(byte[] data, String uploadDir) throws IOException + { + FileOutputStream fos = null; + String pathName = ""; + try + { + String extension = getFileExtendName(data); + pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; + File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName); + fos = new FileOutputStream(file); + fos.write(data); + } + finally + { + IOUtils.close(fos); + } + return FileUploadUtils.getPathFileName(uploadDir, pathName); + } + + /** + * 删除文件 + * + * @param filePath 文件 + * @return + */ + public static boolean deleteFile(String filePath) + { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) + { + flag = file.delete(); + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) + { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload(String resource) + { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) + { + return false; + } + + // 检查允许下载的文件规则 + if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) + { + return true; + } + + // 不在允许下载的文件规则 + return false; + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException + { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) + { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } + else if (agent.contains("Firefox")) + { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } + else if (agent.contains("Chrome")) + { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + else + { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException + { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException + { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } + + /** + * 获取图像后缀 + * + * @param photoByte 图像数据 + * @return 后缀名 + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "jpg"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "gif"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "jpg"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "bmp"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "png"; + } + return strFileExtendName; + } + + /** + * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png + * + * @param fileName 路径名称 + * @return 没有文件路径的名称 + */ + public static String getName(String fileName) + { + if (fileName == null) + { + return null; + } + int lastUnixPos = fileName.lastIndexOf('/'); + int lastWindowsPos = fileName.lastIndexOf('\\'); + int index = Math.max(lastUnixPos, lastWindowsPos); + return fileName.substring(index + 1); + } + + /** + * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi + * + * @param fileName 路径名称 + * @return 没有文件路径和后缀的名称 + */ + public static String getNameNotSuffix(String fileName) + { + if (fileName == null) + { + return null; + } + String baseName = FilenameUtils.getBaseName(fileName); + return baseName; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java new file mode 100644 index 0000000..432dfda --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java @@ -0,0 +1,98 @@ +package com.ruoyi.common.utils.file; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; + +/** + * 图片处理工具类 + * + * @author ruoyi + */ +public class ImageUtils +{ + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); + + public static byte[] getImage(String imagePath) + { + InputStream is = getFile(imagePath); + try + { + return IOUtils.toByteArray(is); + } + catch (Exception e) + { + log.error("图片加载异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(is); + } + } + + public static InputStream getFile(String imagePath) + { + try + { + byte[] result = readFile(imagePath); + result = Arrays.copyOf(result, result.length); + return new ByteArrayInputStream(result); + } + catch (Exception e) + { + log.error("获取图片异常 {}", e); + } + return null; + } + + /** + * 读取文件为字节数据 + * + * @param url 地址 + * @return 字节数据 + */ + public static byte[] readFile(String url) + { + InputStream in = null; + try + { + if (url.startsWith("http")) + { + // 网络地址 + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + urlConnection.setConnectTimeout(30 * 1000); + urlConnection.setReadTimeout(60 * 1000); + urlConnection.setDoInput(true); + in = urlConnection.getInputStream(); + } + else + { + // 本机地址 + String localPath = RuoYiConfig.getProfile(); + String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX); + in = new FileInputStream(downloadPath); + } + return IOUtils.toByteArray(in); + } + catch (Exception e) + { + log.error("获取文件路径异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(in); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..f968f1a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.utils.file; + +/** + * 媒体类型工具类 + * + * @author ruoyi + */ +public class MimeTypeUtils +{ + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + public static String getExtension(String prefix) + { + switch (prefix) + { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java new file mode 100644 index 0000000..f52e83e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java @@ -0,0 +1,167 @@ +package com.ruoyi.common.utils.html; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author ruoyi + */ +public class EscapeUtil +{ + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static + { + for (int i = 0; i < 64; i++) + { + TEXT[i] = new char[] { (char) i }; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) + { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) + { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) + { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) + { + if (StringUtils.isEmpty(text)) + { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0; i < text.length(); i++) + { + c = text.charAt(i); + if (c < 256) + { + tmp.append("%"); + if (c < 16) + { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + else + { + tmp.append("%u"); + if (c <= 0xfff) + { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) + { + if (StringUtils.isEmpty(content)) + { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) + { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) + { + if (content.charAt(pos + 1) == 'u') + { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } + else + { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } + else + { + if (pos == -1) + { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } + else + { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } + + public static void main(String[] args) + { + String html = ""; + String escape = EscapeUtil.escape(html); + // String html = "ipt>alert(\"XSS\")ipt>"; + // String html = "<123"; + // String html = "123>"; + System.out.println("clean: " + EscapeUtil.clean(html)); + System.out.println("escape: " + escape); + System.out.println("unescape: " + EscapeUtil.unescape(escape)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java new file mode 100644 index 0000000..ebff3fd --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java @@ -0,0 +1,570 @@ +package com.ruoyi.common.utils.html; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author ruoyi + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (!inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java new file mode 100644 index 0000000..401f25a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java @@ -0,0 +1,55 @@ +package com.ruoyi.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import jakarta.servlet.ServletRequest; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 通用http工具封装 + * + * @author ruoyi + */ +public class HttpHelper +{ + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) + { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try (InputStream inputStream = request.getInputStream()) + { + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + } + catch (IOException e) + { + LOGGER.warn("getBodyString出现问题!"); + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException e) + { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java new file mode 100644 index 0000000..02bd060 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java @@ -0,0 +1,272 @@ +package com.ruoyi.common.utils.http; + +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.*; +import java.io.*; +import java.net.*; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import java.util.Map; + +public class HttpUtils { + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + public static String sendGet(String url) { + return sendGet(url, StringUtils.EMPTY); + } + + public static String sendGet(String url, String param) { + return sendGet(url, param, Constants.UTF8); + } + + public static String sendGet(String url, String param, String contentType) { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try { + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + //log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + //log.info("recv - {}", result); + } catch (ConnectException e) { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } catch (SocketTimeoutException e) { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } catch (IOException e) { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } catch (Exception e) { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (Exception ex) { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendPost(String url, String param) { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try { + // log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + } catch (ConnectException e) { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } catch (SocketTimeoutException e) { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } catch (IOException e) { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } catch (Exception e) { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } finally { + try { + if (out != null) { + out.close(); + } + if (in != null) { + in.close(); + } + } catch (IOException ex) { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendPostJSONString(String url, String JSONString) { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try { + // log.info("sendPostJSONString - {}", url); + URL realUrl = new URL(url); + HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(JSONString); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + } catch (ConnectException e) { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",JSONString=" + JSONString, e); + } catch (SocketTimeoutException e) { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",JSONString=" + JSONString, e); + } catch (IOException e) { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",JSONString=" + JSONString, e); + } catch (Exception e) { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",JSONString=" + JSONString, e); + } finally { + try { + if (out != null) { + out.close(); + } + if (in != null) { + in.close(); + } + } catch (IOException ex) { + log.error("调用in.close Exception, url=" + url + ",JSONString=" + JSONString, ex); + } + } + return result.toString(); + } + + public static String sendPostJSONString(String url, String jsonString, Map headers) { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try { + URL realUrl = new URL(url); + HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); + + // 设置请求方法 + conn.setRequestMethod("POST"); + + // 设置默认请求头 + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Content-Type", "application/json"); + + // 添加自定义请求头 + for (Map.Entry entry : headers.entrySet()) { + conn.setRequestProperty(entry.getKey(), entry.getValue()); + } + + // 设置连接属性 + conn.setDoOutput(true); + conn.setDoInput(true); + + out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream())); + out.print(jsonString); + out.flush(); + + in = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + } catch (IOException e) { + log.error("调用HttpUtils.sendPostJSONString IOException, url=" + url + ",jsonString=" + jsonString, e); + } finally { + try { + if (out != null) { + out.close(); + } + if (in != null) { + in.close(); + } + } catch (IOException ex) { + log.error("调用in.close Exception, url=" + url + ",jsonString=" + jsonString, ex); + } + } + return result.toString(); + } + + public static String sendSSLPost(String url, String param) { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) { + if (!ret.trim().isEmpty()) { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + conn.disconnect(); + br.close(); + } catch (ConnectException e) { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } catch (SocketTimeoutException e) { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } catch (IOException e) { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } catch (Exception e) { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java new file mode 100644 index 0000000..edfe419 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java @@ -0,0 +1,56 @@ +package com.ruoyi.common.utils.ip; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpUtils; + +/** + * 获取地址类 + * + * @author ruoyi + */ +public class AddressUtils +{ + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) + { + // 内网不查询 + if (IpUtils.internalIp(ip)) + { + return "内网IP"; + } + if (RuoYiConfig.isAddressEnabled()) + { + try + { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); + if (StringUtils.isEmpty(rspStr)) + { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + JSONObject obj = JSON.parseObject(rspStr); + String region = obj.getString("pro"); + String city = obj.getString("city"); + return String.format("%s %s", region, city); + } + catch (Exception e) + { + log.error("获取地理位置异常 {}", ip); + } + } + return UNKNOWN; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java new file mode 100644 index 0000000..a376416 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java @@ -0,0 +1,382 @@ +package com.ruoyi.common.utils.ip; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import jakarta.servlet.http.HttpServletRequest; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 获取IP方法 + * + * @author ruoyi + */ +public class IpUtils +{ + public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"; + // 匹配 ip + public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")"; + public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; + // 匹配网段 + public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; + + /** + * 获取客户端IP + * + * @return IP地址 + */ + public static String getIpAddr() + { + return getIpAddr(ServletUtils.getRequest()); + } + + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + public static String getIpAddr(HttpServletRequest request) + { + if (request == null) + { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * @return 结果 + */ + public static boolean internalIp(String ip) + { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * @return 结果 + */ + private static boolean internalIp(byte[] addr) + { + if (StringUtils.isNull(addr) || addr.length < 2) + { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) + { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) + { + return true; + } + case SECTION_5: + switch (b1) + { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) + { + if (text.length() == 0) + { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try + { + long l; + int i; + switch (elements.length) + { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) + { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) + { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) + { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } + catch (NumberFormatException e) + { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp() + { + try + { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) + { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName() + { + try + { + return InetAddress.getLocalHost().getHostName(); + } + catch (UnknownHostException e) + { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) + { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) + { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) + { + if (false == isUnknown(subIp)) + { + ip = subIp; + break; + } + } + } + return StringUtils.substring(ip, 0, 255); + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) + { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } + + /** + * 是否为IP + */ + public static boolean isIP(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP); + } + + /** + * 是否为IP,或 *为间隔的通配符地址 + */ + public static boolean isIpWildCard(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD); + } + + /** + * 检测参数是否在ip通配符里 + */ + public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip) + { + String[] s1 = ipWildCard.split("\\."); + String[] s2 = ip.split("\\."); + boolean isMatchedSeg = true; + for (int i = 0; i < s1.length && !s1[i].equals("*"); i++) + { + if (!s1[i].equals(s2[i])) + { + isMatchedSeg = false; + break; + } + } + return isMatchedSeg; + } + + /** + * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串 + */ + public static boolean isIPSegment(String ipSeg) + { + return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG); + } + + /** + * 判断ip是否在指定网段中 + */ + public static boolean ipIsInNetNoCheck(String iparea, String ip) + { + int idx = iparea.indexOf('-'); + String[] sips = iparea.substring(0, idx).split("\\."); + String[] sipe = iparea.substring(idx + 1).split("\\."); + String[] sipt = ip.split("\\."); + long ips = 0L, ipe = 0L, ipt = 0L; + for (int i = 0; i < 4; ++i) + { + ips = ips << 8 | Integer.parseInt(sips[i]); + ipe = ipe << 8 | Integer.parseInt(sipe[i]); + ipt = ipt << 8 | Integer.parseInt(sipt[i]); + } + if (ips > ipe) + { + long t = ips; + ips = ipe; + ipe = t; + } + return ips <= ipt && ipt <= ipe; + } + + /** + * 校验ip是否符合过滤串规则 + * + * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99` + * @param ip 校验IP地址 + * @return boolean 结果 + */ + public static boolean isMatchedIp(String filter, String ip) + { + if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) + { + return false; + } + String[] ips = filter.split(";"); + for (String iStr : ips) + { + if (isIP(iStr) && iStr.equals(ip)) + { + return true; + } + else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) + { + return true; + } + else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) + { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/json/SnakeCaseJsonUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/json/SnakeCaseJsonUtils.java new file mode 100644 index 0000000..edad410 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/json/SnakeCaseJsonUtils.java @@ -0,0 +1,247 @@ +package com.ruoyi.common.utils.json; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.NameFilter; + +import java.lang.reflect.Type; +import java.util.*; + +/** + * 基于 FastJSON2 的通用蛇形命名 JSON 工具类 + * 特性:性能更高、安全性更好、API 更现代 + * + * 使用方式: + * // 标准蛇形 JSON + * String snakeJson = SnakeCaseJsonUtils.toSnakeCaseJson(user); + * // 格式化输出 + * String pretty = SnakeCaseJsonUtils2.toSnakeCaseJsonPretty(user); + * + * // 自动映射蛇形到驼峰 + * UserProfile parsed = SnakeCaseJsonUtils.fromSnakeCaseJson(inputJson, UserProfile.class); + * // 泛型集合 + * List userList = SnakeCaseJsonUtils2.parseSnakeCaseList(jsonArrayString, UserProfile.class); + * + * // ============ Map 转换示例 ============ + * Map snakeMap = SnakeCaseJsonUtils2.toSnakeCaseMap(user); + * + * UserProfile fromMap = SnakeCaseJsonUtils2.fromSnakeCaseMap(snakeMap, UserProfile.class); + */ +public class SnakeCaseJsonUtils { + + // FastJSON2 内置的命名策略过滤器(性能最优) + private static final NameFilter SNAKE_CASE_FILTER = (object, name, value) -> + camelToSnake(name); + + // 序列化特性:使用蛇形命名 + 标准特性 + private static final JSONWriter.Feature[] SERIALIZE_FEATURES = { + JSONWriter.Feature.WriteMapNullValue, // 输出 null 值 + JSONWriter.Feature.WriteLongAsString, // Long 转为 String(避免前端精度丢失) + JSONWriter.Feature.WriteEnumsUsingName // 枚举使用 name + }; + + // 反序列化特性:支持蛇形命名自动映射 + private static final JSONReader.Feature[] DESERIALIZE_FEATURES = { + JSONReader.Feature.SupportSmartMatch // 智能匹配(自动处理命名差异) + }; + + // ==================== 核心序列化方法 ==================== + + /** + * 将对象序列化为蛇形命名的 JSON 字符串 + * 使用 FastJSON2 的 NameFilter 实现高性能转换 + */ + public static String toSnakeCaseJson(Object obj) { + if (obj == null) { + return "{}"; + } + return JSON.toJSONString(obj, SNAKE_CASE_FILTER, SERIALIZE_FEATURES); + } + + /** + * 格式化输出(Pretty Format) + */ + public static String toSnakeCaseJsonPretty(Object obj) { + if (obj == null) { + return "{}"; + } + return JSON.toJSONString(obj, SNAKE_CASE_FILTER, + JSONWriter.Feature.PrettyFormat, + JSONWriter.Feature.WriteMapNullValue + ); + } + + /** + * 序列化为字节数组(使用 JSONB 格式,性能更高) + */ + public static byte[] toSnakeCaseJsonb(Object obj) { + if (obj == null) { + return new byte[0]; + } + // JSONB 也支持 NameFilter + return JSON.toJSONBytes(obj, SNAKE_CASE_FILTER); + } + + // ==================== 核心反序列化方法 ==================== + + /** + * 将蛇形命名的 JSON 字符串反序列化为指定类型的对象 + * FastJSON2 的 SupportSmartMatch 自动处理命名映射 + */ + public static T fromSnakeCaseJson(String json, Class clazz) { + if (json == null || json.trim().isEmpty()) { + return null; + } + // SupportSmartMatch 特性会自动将 snake_case 映射到 camelCase + return JSON.parseObject(json, clazz, DESERIALIZE_FEATURES); + } + + /** + * 支持泛型的反序列化(如 List、Map 等) + */ + public static T fromSnakeCaseJson(String json, Type type) { + if (json == null || json.trim().isEmpty()) { + return null; + } + return JSON.parseObject(json, type, DESERIALIZE_FEATURES); + } + + /** + * 从 JSONB 字节数组反序列化 + */ + public static T fromSnakeCaseJsonb(byte[] jsonbBytes, Class clazz) { + if (jsonbBytes == null || jsonbBytes.length == 0) { + return null; + } + return JSON.parseObject(jsonbBytes, clazz, DESERIALIZE_FEATURES); + } + + // ==================== Map/JSONObject 转换 ==================== + + /** + * 将对象转换为蛇形命名的 JSONObject + */ + public static JSONObject toSnakeCaseJSONObject(Object obj) { + if (obj == null) { + return new JSONObject(); + } + String json = toSnakeCaseJson(obj); + return JSON.parseObject(json); + } + + /** + * 将对象转换为蛇形命名的 Map + */ + public static Map toSnakeCaseMap(Object obj) { + if (obj == null) { + return new HashMap<>(); + } + JSONObject jsonObject = toSnakeCaseJSONObject(obj); + return jsonObject.toJavaObject(Map.class); + } + + /** + * 将蛇形命名的 Map 转换为指定类型的对象 + */ + public static T fromSnakeCaseMap(Map map, Class clazz) { + if (map == null) { + return null; + } + // FastJSON2 的 toJSONString 直接支持 Map + String json = JSON.toJSONString(map); + return fromSnakeCaseJson(json, clazz); + } + + // ==================== 数组/集合处理 ==================== + + /** + * 将 List 序列化为蛇形命名的 JSON 数组字符串 + */ + public static String toSnakeCaseJsonList(List list) { + if (list == null) { + return "[]"; + } + return JSON.toJSONString(list, SNAKE_CASE_FILTER, SERIALIZE_FEATURES); + } + + /** + * 将蛇形命名的 JSON 数组反序列化为 List + */ + public static List parseSnakeCaseList(String json, Class clazz) { + if (json == null || json.trim().isEmpty()) { + return new ArrayList<>(); + } + return JSON.parseArray(json, clazz, DESERIALIZE_FEATURES); + } + + /** + * 将蛇形命名的 JSON 数组反序列化为 JSONArray + */ + public static JSONArray parseSnakeCaseArray(String json) { + if (json == null || json.trim().isEmpty()) { + return new JSONArray(); + } + return JSON.parseArray(json); + } + + // ==================== 命名转换工具方法 ==================== + + /** + * 将 camelCase 转换为 snake_case + * 优化版:处理连续大写字母(如 HTTPRequest -> http_request) + */ + public static String camelToSnake(String camel) { + if (camel == null || camel.isEmpty()) { + return camel; + } + + StringBuilder snake = new StringBuilder(camel.length() + 10); + char[] chars = camel.toCharArray(); + + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + + if (Character.isUpperCase(c)) { + // 处理首字母大写或连续大写的情况 + if (i > 0 && + (Character.isLowerCase(chars[i - 1]) || + (i + 1 < chars.length && Character.isLowerCase(chars[i + 1])))) { + snake.append('_'); + } + snake.append(Character.toLowerCase(c)); + } else { + snake.append(c); + } + } + + return snake.toString(); + } + + /** + * 将 snake_case 转换为 camelCase + */ + public static String snakeToCamel(String snake) { + if (snake == null || !snake.contains("_")) { + return snake; + } + + StringBuilder camel = new StringBuilder(snake.length()); + boolean nextUpper = false; + + for (char c : snake.toCharArray()) { + if (c == '_') { + nextUpper = true; + } else if (nextUpper) { + camel.append(Character.toUpperCase(c)); + nextUpper = false; + } else { + camel.append(c); + } + } + + return camel.toString(); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java new file mode 100644 index 0000000..ccab288 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.utils.poi; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * Excel数据格式处理适配器 + * + * @author ruoyi + */ +public interface ExcelHandlerAdapter +{ + /** + * 格式化 + * + * @param value 单元格数据值 + * @param args excel注解args参数组 + * @param cell 单元格对象 + * @param wb 工作簿对象 + * + * @return 处理后的值 + */ + Object format(Object value, String[] args, Cell cell, Workbook wb); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java new file mode 100644 index 0000000..2cdca67 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -0,0 +1,1715 @@ +package com.ruoyi.common.utils.poi; + +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.annotation.Excel.Type; +import com.ruoyi.common.annotation.Excels; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.UtilException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileTypeUtils; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.common.utils.file.ImageUtils; +import com.ruoyi.common.utils.reflect.ReflectUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.poi.hssf.usermodel.*; +import org.apache.poi.ooxml.POIXMLDocumentPart; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.*; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Excel相关处理 + * + * @author ruoyi + */ +public class ExcelUtil +{ + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + + public static final String[] FORMULA_STR = { "=", "-", "+", "@" }; + + /** + * 用于dictType属性数据存储,避免重复查缓存 + */ + public Map sysDictMap = new HashMap(); + + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + + /** + * 工作表名称 + */ + private String sheetName; + + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 样式列表 + */ + private Map styles; + + /** + * 导入导出数据列表 + */ + private List list; + + /** + * 注解列表 + */ + private List fields; + + /** + * 当前行号 + */ + private int rownum; + + /** + * 标题 + */ + private String title; + + /** + * 最大高度 + */ + private short maxHeight; + + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + + /** + * 对象的子列表方法 + */ + private Method subMethod; + + /** + * 对象的子列表属性 + */ + private List subFields; + + /** + * 统计列表 + */ + private Map statistics = new HashMap(); + + /** + * 数字格式 + */ + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + + /** + * 实体对象 + */ + public Class clazz; + + /** + * 需要排除列属性 + */ + public String[] excludeFields; + + public ExcelUtil(Class clazz) + { + this.clazz = clazz; + } + + /** + * 隐藏Excel中列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + * @throws Exception + */ + public void hideColumn(String... fields) + { + this.excludeFields = fields; + } + + public void init(List list, String sheetName, String title, Type type) + { + if (list == null) + { + list = new ArrayList(); + } + this.list = list; + this.sheetName = sheetName; + this.type = type; + this.title = title; + createExcelField(); + createWorkbook(); + createTitle(); + createSubHead(); + } + + /** + * 创建excel第一行标题 + */ + public void createTitle() + { + if (StringUtils.isNotEmpty(title)) + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + int titleLastCol = this.fields.size() - 1; + if (isSubList()) + { + titleLastCol = titleLastCol + subFields.size() - 1; + } + Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol)); + } + } + + /** + * 创建对象的子列表名称 + */ + public void createSubHead() + { + if (isSubList()) + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + Row subRow = sheet.createRow(rownum); + int excelNum = 0; + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Cell headCell1 = subRow.createCell(excelNum); + headCell1.setCellValue(attr.name()); + headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + excelNum++; + } + int headFirstRow = excelNum - 1; + int headLastRow = headFirstRow + subFields.size() - 1; + if (headLastRow > headFirstRow) + { + sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow)); + } + rownum++; + } + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(InputStream is) + { + List list = null; + try + { + list = importExcel(is, 0); + } + catch (Exception e) + { + log.error("导入Excel异常{}", e.getMessage()); + throw new UtilException(e.getMessage()); + } + finally + { + IOUtils.closeQuietly(is); + } + return list; + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @param titleNum 标题占用行数 + * @return 转换后集合 + */ + public List importExcel(InputStream is, int titleNum) throws Exception + { + return importExcel(StringUtils.EMPTY, is, titleNum); + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param titleNum 标题占用行数 + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(String sheetName, InputStream is, int titleNum) throws Exception + { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet + Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); + if (sheet == null) + { + throw new IOException("文件sheet不存在"); + } + boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook); + Map pictures; + if (isXSSFWorkbook) + { + pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb); + } + else + { + pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb); + } + // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 + int rows = sheet.getLastRowNum(); + if (rows > 0) + { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + Row heard = sheet.getRow(titleNum); + for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) + { + Cell cell = heard.getCell(i); + if (StringUtils.isNotNull(cell)) + { + String value = this.getCellValue(heard, i).toString(); + cellMap.put(value, i); + } + else + { + cellMap.put(null, i); + } + } + // 有数据时才处理 得到类的所有field. + List fields = this.getFields(); + Map fieldsMap = new HashMap(); + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Integer column = cellMap.get(attr.name()); + if (column != null) + { + fieldsMap.put(column, objects); + } + } + for (int i = titleNum + 1; i <= rows; i++) + { + // 从第2行开始取数据,默认第一行是表头. + Row row = sheet.getRow(i); + // 判断当前行是否是空行 + if (isRowEmpty(row)) + { + continue; + } + T entity = null; + for (Map.Entry entry : fieldsMap.entrySet()) + { + Object val = this.getCellValue(row, entry.getKey()); + + // 如果不存在实例则新建. + entity = (entity == null ? clazz.newInstance() : entity); + // 从map中得到对应列的field. + Field field = (Field) entry.getValue()[0]; + Excel attr = (Excel) entry.getValue()[1]; + // 取得类型,并根据对象类型设置值. + Class fieldType = field.getType(); + if (String.class == fieldType) + { + String s = Convert.toStr(val); + if (StringUtils.endsWith(s, ".0")) + { + val = StringUtils.substringBefore(s, ".0"); + } + else + { + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (StringUtils.isNotEmpty(dateFormat)) + { + val = parseDateToStr(dateFormat, val); + } + else + { + val = Convert.toStr(val); + } + } + } + else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toInt(val); + } + else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toLong(val); + } + else if (Double.TYPE == fieldType || Double.class == fieldType) + { + val = Convert.toDouble(val); + } + else if (Float.TYPE == fieldType || Float.class == fieldType) + { + val = Convert.toFloat(val); + } + else if (BigDecimal.class == fieldType) + { + val = Convert.toBigDecimal(val); + } + else if (Date.class == fieldType) + { + if (val instanceof String) + { + val = DateUtils.parseDate(val); + } + else if (val instanceof Double) + { + val = DateUtil.getJavaDate((Double) val); + } + } + else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) + { + val = Convert.toBool(val, false); + } + if (StringUtils.isNotNull(fieldType)) + { + String propertyName = field.getName(); + if (StringUtils.isNotEmpty(attr.targetAttr())) + { + propertyName = field.getName() + "." + attr.targetAttr(); + } + if (StringUtils.isNotEmpty(attr.readConverterExp())) + { + val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); + } + else if (StringUtils.isNotEmpty(attr.dictType())) + { + val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + val = dataFormatHandlerAdapter(val, attr, null); + } + else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)) + { + PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey()); + if (image == null) + { + val = ""; + } + else + { + byte[] data = image.getData(); + val = FileUtils.writeImportBytes(data); + } + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName) + { + return exportExcel(list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName, String title) + { + this.init(list, sheetName, title, Type.EXPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName) + { + exportExcel(response, list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(list, sheetName, title, Type.EXPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName) + { + return importTemplateExcel(sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName, String title) + { + this.init(null, sheetName, title, Type.IMPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName) + { + importTemplateExcel(response, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(null, sheetName, title, Type.IMPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public void exportExcel(HttpServletResponse response) + { + try + { + writeSheet(); + wb.write(response.getOutputStream()); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + } + finally + { + IOUtils.closeQuietly(wb); + } + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public AjaxResult exportExcel() + { + OutputStream out = null; + try + { + writeSheet(); + String filename = encodingFilename(sheetName); + out = new FileOutputStream(getAbsoluteFile(filename)); + wb.write(out); + return AjaxResult.success(filename); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + throw new UtilException("导出Excel失败,请联系网站管理员!"); + } + finally + { + IOUtils.closeQuietly(wb); + IOUtils.closeQuietly(out); + } + } + + /** + * 创建写入数据到Sheet + */ + public void writeSheet() + { + // 取出一共有多少个sheet. + int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); + for (int index = 0; index < sheetNo; index++) + { + createSheet(sheetNo, index); + + // 产生一行 + Row row = sheet.createRow(rownum); + int column = 0; + // 写入各个字段的列头名称 + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + this.createHeadCell(subExcel, row, column++); + } + } + else + { + this.createHeadCell(excel, row, column++); + } + } + if (Type.EXPORT.equals(type)) + { + fillExcelData(index, row); + addStatisticsRow(); + } + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + * @param row 单元格行 + */ + @SuppressWarnings("unchecked") + public void fillExcelData(int index, Row row) + { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + int rowNo = (1 + rownum) - startNo; + for (int i = startNo; i < endNo; i++) + { + rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo; + row = sheet.createRow(rowNo); + // 得到导出对象. + T vo = (T) list.get(i); + Collection subList = null; + if (isSubList()) + { + if (isSubListValue(vo)) + { + subList = getListCellValue(vo); + subMergedLastRowNum = subMergedLastRowNum + subList.size(); + } + else + { + subMergedFirstRowNum++; + subMergedLastRowNum++; + } + } + int column = 0; + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)) + { + boolean subFirst = false; + for (Object obj : subList) + { + if (subFirst) + { + rowNo++; + row = sheet.createRow(rowNo); + } + List subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class); + int subIndex = 0; + for (Field subField : subFields) + { + if (subField.isAnnotationPresent(Excel.class)) + { + subField.setAccessible(true); + Excel attr = subField.getAnnotation(Excel.class); + this.addCell(attr, row, (T) obj, subField, column + subIndex); + } + subIndex++; + } + subFirst = true; + } + this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size(); + } + else + { + this.addCell(excel, row, vo, field, column++); + } + } + } + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) + { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("Arial"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + style.setFont(titleFont); + styles.put("title", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font totalFont = wb.createFont(); + totalFont.setFontName("Arial"); + totalFont.setFontHeightInPoints((short) 10); + style.setFont(totalFont); + styles.put("total", style); + + styles.putAll(annotationHeaderStyles(wb, styles)); + + styles.putAll(annotationDataStyles(wb)); + + return styles; + } + + /** + * 根据Excel注解创建表格头样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationHeaderStyles(Workbook wb, Map styles) + { + Map headerStyles = new HashMap(); + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); + if (!headerStyles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(excel.headerBackgroundColor().index); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(excel.headerColor().index); + style.setFont(headerFont); + headerStyles.put(key, style); + } + } + return headerStyles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationDataStyles(Workbook wb) + { + Map styles = new HashMap(); + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor()); + if (!styles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.setAlignment(excel.align()); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(excel.backgroundColor().getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + dataFont.setColor(excel.color().index); + style.setFont(dataFont); + styles.put(key, style); + } + } + return styles; + } + + /** + * 创建单元格 + */ + public Cell createHeadCell(Excel attr, Row row, int column) + { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (isSubList()) + { + // 填充默认样式,防止合并单元格样式失效 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + if (attr.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); + } + } + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo(Object value, Excel attr, Cell cell) + { + if (ColumnType.STRING == attr.cellType()) + { + String cellValue = Convert.toStr(value); + // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 + if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) + { + cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); + } + if (value instanceof Collection && StringUtils.equals("[]", cellValue)) + { + cellValue = StringUtils.EMPTY; + } + cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); + } + else if (ColumnType.NUMERIC == attr.cellType()) + { + if (StringUtils.isNotNull(value)) + { + cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } + } + else if (ColumnType.IMAGE == attr.cellType()) + { + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); + String imagePath = Convert.toStr(value); + if (StringUtils.isNotEmpty(imagePath)) + { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, + cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + } + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch(Sheet sheet) + { + if (sheet.getDrawingPatriarch() == null) + { + sheet.createDrawingPatriarch(); + } + return sheet.getDrawingPatriarch(); + } + + /** + * 获取图片类型,设置图片插入类型 + */ + public int getImageType(byte[] value) + { + String type = FileTypeUtils.getFileExtendName(value); + if ("JPG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_JPEG; + } + else if ("PNG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_PNG; + } + return Workbook.PICTURE_TYPE_JPEG; + } + + /** + * 创建表格样式 + */ + public void setDataValidation(Excel attr, Row row, int column) + { + if (attr.name().indexOf("注:") >= 0) + { + sheet.setColumnWidth(column, 6000); + } + else + { + // 设置列宽 + sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); + } + if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0) + { + if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255) + { + // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 + setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + } + else + { + // 提示信息或只能选择不能输入的列内容. + setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + } + } + } + + /** + * 添加单元格 + */ + public Cell addCell(Excel attr, Row row, T vo, Field field, int column) + { + Cell cell = null; + try + { + // 设置行高 + row.setHeight(maxHeight); + // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. + if (attr.isExport()) + { + // 创建cell + cell = row.createCell(column); + if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) + { + CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); + sheet.addMergedRegion(cellAddress); + } + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); + + // 用于读取对象中的属性 + Object value = getTargetValue(vo, field, attr); + String dateFormat = attr.dateFormat(); + String readConverterExp = attr.readConverterExp(); + String separator = attr.separator(); + String dictType = attr.dictType(); + if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) + { + cell.setCellValue(parseDateToStr(dateFormat, value)); + } + else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) + { + cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); + } + else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)) + { + if (!sysDictMap.containsKey(dictType + value)) + { + String lable = convertDictByExp(Convert.toStr(value), dictType, separator); + sysDictMap.put(dictType + value, lable); + } + cell.setCellValue(sysDictMap.get(dictType + value)); + } + else if (value instanceof BigDecimal && -1 != attr.scale()) + { + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell)); + } + else + { + // 设置列类型 + setCellVo(value, attr, cell); + } + addStatisticsData(column, Convert.toStr(value), attr); + } + } + catch (Exception e) + { + log.error("导出Excel失败{}", e); + } + return cell; + } + + /** + * 设置 POI XSSFSheet 单元格提示或选择框 + * + * @param sheet 表单 + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, + int firstCol, int endCol) + { + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + } + + /** + * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). + * + * @param sheet 要设置的sheet. + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol) + { + String hideSheetName = "combo_" + firstCol + "_" + endCol; + Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据 + for (int i = 0; i < textlist.length; i++) + { + hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]); + } + // 创建名称,可被其他单元格引用 + Name name = wb.createName(); + name.setNameName(hideSheetName + "_data"); + name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length); + DataValidationHelper helper = sheet.getDataValidationHelper(); + // 加载下拉列表内容 + DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data"); + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + // 数据有效性对象 + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + + sheet.addValidationData(dataValidation); + // 设置hiddenSheet隐藏 + wb.setSheetHidden(wb.getSheetIndex(hideSheet), true); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[0].equals(value)) + { + propertyString.append(itemArray[1] + separator); + break; + } + } + } + else + { + if (itemArray[0].equals(propertyValue)) + { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(","); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[1].equals(value)) + { + propertyString.append(itemArray[0] + separator); + break; + } + } + } + else + { + if (itemArray[1].equals(propertyValue)) + { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 解析字典值 + * + * @param dictValue 字典值 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String convertDictByExp(String dictValue, String dictType, String separator) + { + return DictUtils.getDictLabel(dictType, dictValue, separator); + } + + /** + * 反向解析值字典值 + * + * @param dictLabel 字典标签 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典值 + */ + public static String reverseDictByExp(String dictLabel, String dictType, String separator) + { + return DictUtils.getDictValue(dictType, dictLabel, separator); + } + + /** + * 数据处理器 + * + * @param value 数据值 + * @param excel 数据注解 + * @return + */ + public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell) + { + try + { + Object instance = excel.handler().newInstance(); + Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class }); + value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb); + } + catch (Exception e) + { + log.error("不能格式化数据 " + excel.handler(), e.getMessage()); + } + return Convert.toStr(value); + } + + /** + * 合计统计信息 + */ + private void addStatisticsData(Integer index, String text, Excel entity) + { + if (entity != null && entity.isStatistics()) + { + Double temp = 0D; + if (!statistics.containsKey(index)) + { + statistics.put(index, temp); + } + try + { + temp = Double.valueOf(text); + } + catch (NumberFormatException e) + { + } + statistics.put(index, statistics.get(index) + temp); + } + } + + /** + * 创建统计行 + */ + public void addStatisticsRow() + { + if (statistics.size() > 0) + { + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + Cell cell = row.createCell(0); + cell.setCellStyle(styles.get("total")); + cell.setCellValue("合计"); + + for (Integer key : keys) + { + cell = row.createCell(key); + cell.setCellStyle(styles.get("total")); + cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key))); + } + statistics.clear(); + } + } + + /** + * 编码文件名 + */ + public String encodingFilename(String filename) + { + filename = UUID.randomUUID() + "_" + filename + ".xlsx"; + return filename; + } + + /** + * 获取下载路径 + * + * @param filename 文件名称 + */ + public String getAbsoluteFile(String filename) + { + String downloadPath = RuoYiConfig.getDownloadPath() + "/" + filename; + File desc = new File(downloadPath); + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + return downloadPath; + } + + /** + * 获取bean中的属性值 + * + * @param vo 实体对象 + * @param field 字段 + * @param excel 注解 + * @return 最终的属性值 + * @throws Exception + */ + private Object getTargetValue(T vo, Field field, Excel excel) throws Exception + { + Object o = field.get(vo); + if (StringUtils.isNotEmpty(excel.targetAttr())) + { + String target = excel.targetAttr(); + if (target.contains(".")) + { + String[] targets = target.split("[.]"); + for (String name : targets) + { + o = getValue(o, name); + } + } + else + { + o = getValue(o, target); + } + } + return o; + } + + /** + * 以类的属性的get方法方法形式获取值 + * + * @param o + * @param name + * @return value + * @throws Exception + */ + private Object getValue(Object o, String name) throws Exception + { + if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) + { + Class clazz = o.getClass(); + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + o = field.get(o); + } + return o; + } + + /** + * 得到所有定义字段 + */ + private void createExcelField() + { + this.fields = getFields(); + this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); + this.maxHeight = getRowHeight(); + } + + /** + * 获取字段注解信息 + */ + public List getFields() + { + List fields = new ArrayList(); + List tempFields = new ArrayList<>(); + tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); + tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); + for (Field field : tempFields) + { + if (!ArrayUtils.contains(this.excludeFields, field.getName())) + { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) + { + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + { + field.setAccessible(true); + fields.add(new Object[] { field, attr }); + } + if (Collection.class.isAssignableFrom(field.getType())) + { + subMethod = getSubMethod(field.getName(), clazz); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + } + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) + { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) + { + if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) + { + field.setAccessible(true); + fields.add(new Object[] { field, attr }); + } + } + } + } + } + return fields; + } + + /** + * 根据注解获取最大行高 + */ + public short getRowHeight() + { + double maxHeight = 0; + for (Object[] os : this.fields) + { + Excel excel = (Excel) os[1]; + maxHeight = Math.max(maxHeight, excel.height()); + } + return (short) (maxHeight * 20); + } + + /** + * 创建一个工作簿 + */ + public void createWorkbook() + { + this.wb = new SXSSFWorkbook(500); + this.sheet = wb.createSheet(); + wb.setSheetName(0, sheetName); + this.styles = createStyles(wb); + } + + /** + * 创建工作表 + * + * @param sheetNo sheet数量 + * @param index 序号 + */ + public void createSheet(int sheetNo, int index) + { + // 设置工作表的名称. + if (sheetNo > 1 && index > 0) + { + this.sheet = wb.createSheet(); + this.createTitle(); + wb.setSheetName(index, sheetName + index); + } + } + + /** + * 获取单元格值 + * + * @param row 获取的行 + * @param column 获取单元格列号 + * @return 单元格值 + */ + public Object getCellValue(Row row, int column) + { + if (row == null) + { + return row; + } + Object val = ""; + try + { + Cell cell = row.getCell(column); + if (StringUtils.isNotNull(cell)) + { + if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) + { + val = cell.getNumericCellValue(); + if (DateUtil.isCellDateFormatted(cell)) + { + val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 + } + else + { + if ((Double) val % 1 != 0) + { + val = new BigDecimal(val.toString()); + } + else + { + val = new DecimalFormat("0").format(val); + } + } + } + else if (cell.getCellType() == CellType.STRING) + { + val = cell.getStringCellValue(); + } + else if (cell.getCellType() == CellType.BOOLEAN) + { + val = cell.getBooleanCellValue(); + } + else if (cell.getCellType() == CellType.ERROR) + { + val = cell.getErrorCellValue(); + } + + } + } + catch (Exception e) + { + return val; + } + return val; + } + + /** + * 判断是否是空行 + * + * @param row 判断的行 + * @return + */ + private 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; + } + + /** + * 获取Excel2003图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook) + { + Map sheetIndexPicMap = new HashMap(); + List pictures = workbook.getAllPictures(); + if (!pictures.isEmpty()) + { + for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) + { + HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor(); + if (shape instanceof HSSFPicture) + { + HSSFPicture pic = (HSSFPicture) shape; + int pictureIndex = pic.getPictureIndex() - 1; + HSSFPictureData picData = pictures.get(pictureIndex); + String picIndex = anchor.getRow1() + "_" + anchor.getCol1(); + sheetIndexPicMap.put(picIndex, picData); + } + } + return sheetIndexPicMap; + } + else + { + return sheetIndexPicMap; + } + } + + /** + * 获取Excel2007图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook) + { + Map sheetIndexPicMap = new HashMap(); + for (POIXMLDocumentPart dr : sheet.getRelations()) + { + if (dr instanceof XSSFDrawing) + { + XSSFDrawing drawing = (XSSFDrawing) dr; + List shapes = drawing.getShapes(); + for (XSSFShape shape : shapes) + { + if (shape instanceof XSSFPicture) + { + XSSFPicture pic = (XSSFPicture) shape; + XSSFClientAnchor anchor = pic.getPreferredSize(); + CTMarker ctMarker = anchor.getFrom(); + String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol(); + sheetIndexPicMap.put(picIndex, pic.getPictureData()); + } + } + } + } + return sheetIndexPicMap; + } + + /** + * 格式化不同类型的日期对象 + * + * @param dateFormat 日期格式 + * @param val 被格式化的日期对象 + * @return 格式化后的日期字符 + */ + public String parseDateToStr(String dateFormat, Object val) + { + if (val == null) + { + return ""; + } + String str; + if (val instanceof Date) + { + str = DateUtils.parseDateToStr(dateFormat, (Date) val); + } + else if (val instanceof LocalDateTime) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); + } + else if (val instanceof LocalDate) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); + } + else + { + str = val.toString(); + } + return str; + } + + /** + * 是否有对象的子列表 + */ + public boolean isSubList() + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0; + } + + /** + * 是否有对象的子列表,集合不为空 + */ + public boolean isSubListValue(T vo) + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0; + } + + /** + * 获取集合的值 + */ + public Collection getListCellValue(Object obj) + { + Object value; + try + { + value = subMethod.invoke(obj, new Object[] {}); + } + catch (Exception e) + { + return new ArrayList(); + } + return (Collection) value; + } + + /** + * 获取对象的子列表方法 + * + * @param name 名称 + * @param pojoClass 类对象 + * @return 子列表方法 + */ + public Method getSubMethod(String name, Class pojoClass) + { + StringBuffer getMethodName = new StringBuffer("get"); + getMethodName.append(name.substring(0, 1).toUpperCase()); + getMethodName.append(name.substring(1)); + Method method = null; + try + { + method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); + } + catch (Exception e) + { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..b19953e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java @@ -0,0 +1,410 @@ +package com.ruoyi.common.utils.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Date; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.DateUtils; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author ruoyi + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils +{ + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) + { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) + { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) + { + if (i < names.length - 1) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + else + { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[] { value }); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + @SuppressWarnings("unchecked") + public static E getFieldValue(final Object obj, final String fieldName) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try + { + result = (E) field.get(obj); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue(final Object obj, final String fieldName, final E value) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try + { + field.set(obj, value); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + @SuppressWarnings("unchecked") + public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) + { + if (obj == null || methodName == null) + { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) + { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) + { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0; i < cs.length; i++) + { + if (args[i] != null && !args[i].getClass().equals(cs[i])) + { + if (cs[i] == String.class) + { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) + { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } + else if (cs[i] == Integer.class) + { + args[i] = Convert.toInt(args[i]); + } + else if (cs[i] == Long.class) + { + args[i] = Convert.toLong(args[i]); + } + else if (cs[i] == Double.class) + { + args[i] = Convert.toDouble(args[i]); + } + else if (cs[i] == Float.class) + { + args[i] = Convert.toFloat(args[i]); + } + else if (cs[i] == Date.class) + { + if (args[i] instanceof String) + { + args[i] = DateUtils.parseDate(args[i]); + } + else + { + args[i] = DateUtil.getJavaDate((Double) args[i]); + } + } + else if (cs[i] == boolean.class || cs[i] == Boolean.class) + { + args[i] = Convert.toBool(args[i]); + } + } + } + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, final String fieldName) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) + { + try + { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } + catch (NoSuchFieldException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + try + { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } + catch (NoSuchMethodException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) + { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) + { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Method method) + { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) + { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Field field) + { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) + { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) + { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + */ + public static Class getClassGenricType(final Class clazz, final int index) + { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) + { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) + { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) + { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) + { + if (instance == null) + { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) + { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) + { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) + { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) + { + return new IllegalArgumentException(msg, e); + } + else if (e instanceof InvocationTargetException) + { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java new file mode 100644 index 0000000..ca1cd92 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java @@ -0,0 +1,291 @@ +package com.ruoyi.common.utils.sign; + +/** + * Base64工具类 + * + * @author ruoyi + */ +public final class Base64 +{ + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static + { + for (int i = 0; i < BASELENGTH; ++i) + { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace(char octect) + { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) + { + return (octect == PAD); + } + + private static boolean isData(char octect) + { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) + { + if (binaryData == null) + { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) + { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) + { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) + { + if (encoded == null) + { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) + { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) + { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) + { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) + { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) + { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) + {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) + { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } + else if (!isPad(d3) && isPad(d4)) + { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } + else + { + return null; + } + } + else + { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) + { + if (data == null) + { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) + { + if (!isWhiteSpace(data[i])) + { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java new file mode 100644 index 0000000..c1c58db --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java @@ -0,0 +1,67 @@ +package com.ruoyi.common.utils.sign; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Md5加密方法 + * + * @author ruoyi + */ +public class Md5Utils +{ + private static final Logger log = LoggerFactory.getLogger(Md5Utils.class); + + private static byte[] md5(String s) + { + MessageDigest algorithm; + try + { + algorithm = MessageDigest.getInstance("MD5"); + algorithm.reset(); + algorithm.update(s.getBytes("UTF-8")); + byte[] messageDigest = algorithm.digest(); + return messageDigest; + } + catch (Exception e) + { + log.error("MD5 Error...", e); + } + return null; + } + + private static final String toHex(byte hash[]) + { + if (hash == null) + { + return null; + } + StringBuffer buf = new StringBuffer(hash.length * 2); + int i; + + for (i = 0; i < hash.length; i++) + { + if ((hash[i] & 0xff) < 0x10) + { + buf.append("0"); + } + buf.append(Long.toString(hash[i] & 0xff, 16)); + } + return buf.toString(); + } + + public static String hash(String s) + { + try + { + return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + } + catch (Exception e) + { + log.error("not supported charset...{}", e); + return s; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java new file mode 100644 index 0000000..f290ec3 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java @@ -0,0 +1,158 @@ +package com.ruoyi.common.utils.spring; + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author ruoyi + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws org.springframework.beans.BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws org.springframework.beans.BeansException + * + */ + public static T getBean(Class clz) throws BeansException + { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) + { + return (T) AopContext.currentProxy(); + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() + { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取当前的环境配置,当有多个环境配置时,只获取第一个 + * + * @return 当前的环境配置 + */ + public static String getActiveProfile() + { + final String[] activeProfiles = getActiveProfiles(); + return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } + + /** + * 获取配置文件中的值 + * + * @param key 配置文件的key + * @return 当前的配置文件的值 + * + */ + public static String getRequiredProperty(String key) + { + return applicationContext.getEnvironment().getRequiredProperty(key); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java new file mode 100644 index 0000000..93b0347 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java @@ -0,0 +1,70 @@ +package com.ruoyi.common.utils.sql; + +import com.ruoyi.common.exception.UtilException; +import com.ruoyi.common.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author ruoyi + */ +public class SqlUtil +{ + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()"; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 限制orderBy最大长度 + */ + private static final int ORDER_BY_MAX_LENGTH = 500; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + throw new UtilException("参数不符合规范,不能进行查询"); + } + if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) + { + throw new UtilException("参数已超过最大限制,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) + { + if (StringUtils.isEmpty(value)) + { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) + { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) + { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java new file mode 100644 index 0000000..2c84427 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java @@ -0,0 +1,49 @@ +package com.ruoyi.common.utils.uuid; + +/** + * ID生成器工具类 + * + * @author ruoyi + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java new file mode 100644 index 0000000..bf99611 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java @@ -0,0 +1,86 @@ +package com.ruoyi.common.utils.uuid; + +import java.util.concurrent.atomic.AtomicInteger; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * @author ruoyi 序列生成类 + */ +public class Seq +{ + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + // 机器标识 + private static final String machineCode = "A"; + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId() + { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId(String type) + { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) + { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * @return 序列值 + */ + public static String getId(AtomicInteger atomicInt, int length) + { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq(AtomicInteger atomicInt, int length) + { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) + { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java new file mode 100644 index 0000000..a5585d6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java @@ -0,0 +1,484 @@ +package com.ruoyi.common.utils.uuid; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import com.ruoyi.common.exception.UtilException; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author ruoyi + */ +public final class UUID implements java.io.Serializable, Comparable +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + *

+ * 版本号具有以下含意: + *

    + *
  • 1 基于时间的 UUID + *
  • 2 DCE 安全 UUID + *
  • 3 基于名称的 UUID + *
  • 4 随机生成的 UUID + *
+ * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + *

+ * 变体号具有以下含意: + *

    + *
  • 0 为 NCS 向后兼容保留 + *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 + *
  • 6 保留,微软向后兼容 + *
  • 7 保留供以后定义使用 + *
+ * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + *

+ * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
+ * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + *

+ * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + *

+ * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + *

+ * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + *

+ * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + *

+ * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (!isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (!isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (!isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (!isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + *

+ * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java b/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java new file mode 100644 index 0000000..cb4b408 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java @@ -0,0 +1,27 @@ +package com.ruoyi.common.xss; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) +@Constraint(validatedBy = { XssValidator.class }) +public @interface Xss +{ + String message() + + default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java b/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java new file mode 100644 index 0000000..cc282f3 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java @@ -0,0 +1,34 @@ +package com.ruoyi.common.xss; + +import com.ruoyi.common.utils.StringUtils; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author ruoyi + */ +public class XssValidator implements ConstraintValidator +{ + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) + { + if (StringUtils.isBlank(value)) + { + return true; + } + return !containsHtml(value); + } + + public static boolean containsHtml(String value) + { + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + return matcher.matches(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/.DS_Store b/ruoyi-framework/.DS_Store new file mode 100644 index 0000000..a5f0d39 Binary files /dev/null and b/ruoyi-framework/.DS_Store differ diff --git a/ruoyi-framework/pom.xml b/ruoyi-framework/pom.xml new file mode 100644 index 0000000..083f9d5 --- /dev/null +++ b/ruoyi-framework/pom.xml @@ -0,0 +1,95 @@ + + + + ruoyi + com.ruoyi + 4.8.2 + + 4.0.0 + + ruoyi-framework + + + framework框架核心 + + + + + + + org.springframework.boot + spring-boot-starter-webmvc + + + + + org.springframework.boot + spring-boot-starter-aspectj + + + + + com.alibaba + druid-spring-boot-4-starter + + + + + pro.fessional + kaptcha + + + servlet-api + javax.servlet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.github.oshi + oshi-core + + + + + com.ruoyi + ruoyi-system + + + + com.ruoyi + service-admin + 4.8.2 + + + + \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java new file mode 100644 index 0000000..1bc2f69 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java @@ -0,0 +1,174 @@ +package com.ruoyi.framework.aspectj; + +import java.util.ArrayList; +import java.util.List; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.context.PermissionContextHolder; + +/** + * 数据过滤处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class DataScopeAspect +{ + /** + * 全部数据权限 + */ + public static final String DATA_SCOPE_ALL = "1"; + + /** + * 自定数据权限 + */ + public static final String DATA_SCOPE_CUSTOM = "2"; + + /** + * 部门数据权限 + */ + public static final String DATA_SCOPE_DEPT = "3"; + + /** + * 部门及以下数据权限 + */ + public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; + + /** + * 仅本人数据权限 + */ + public static final String DATA_SCOPE_SELF = "5"; + + /** + * 数据权限过滤关键字 + */ + public static final String DATA_SCOPE = "dataScope"; + + @Before("@annotation(controllerDataScope)") + public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable + { + clearDataScope(point); + handleDataScope(point, controllerDataScope); + } + + protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNotNull(loginUser)) + { + SysUser currentUser = loginUser.getUser(); + // 如果是超级管理员,则不过滤数据 + if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) + { + String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext()); + dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), + controllerDataScope.userAlias(), permission); + } + } + } + + /** + * 数据范围过滤 + * + * @param joinPoint 切点 + * @param user 用户 + * @param deptAlias 部门别名 + * @param userAlias 用户别名 + * @param permission 权限字符 + */ + public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) + { + StringBuilder sqlString = new StringBuilder(); + List conditions = new ArrayList(); + + for (SysRole role : user.getRoles()) + { + String dataScope = role.getDataScope(); + if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) + { + continue; + } + if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions()) + && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) + { + continue; + } + if (DATA_SCOPE_ALL.equals(dataScope)) + { + sqlString = new StringBuilder(); + conditions.add(dataScope); + break; + } + else if (DATA_SCOPE_CUSTOM.equals(dataScope)) + { + sqlString.append(StringUtils.format( + " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, + role.getRoleId())); + } + else if (DATA_SCOPE_DEPT.equals(dataScope)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); + } + else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) + { + sqlString.append(StringUtils.format( + " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", + deptAlias, user.getDeptId(), user.getDeptId())); + } + else if (DATA_SCOPE_SELF.equals(dataScope)) + { + if (StringUtils.isNotBlank(userAlias)) + { + sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); + } + else + { + // 数据权限为仅本人且没有userAlias别名不查询任何数据 + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + } + conditions.add(dataScope); + } + + // 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据 + if (StringUtils.isEmpty(conditions)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + + if (StringUtils.isNotBlank(sqlString.toString())) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); + } + } + } + + /** + * 拼接权限sql前先清空params.dataScope参数防止注入 + */ + private void clearDataScope(final JoinPoint joinPoint) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, ""); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java new file mode 100644 index 0000000..8c2c9f4 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java @@ -0,0 +1,72 @@ +package com.ruoyi.framework.aspectj; + +import java.util.Objects; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; + +/** + * 多数据源处理 + * + * @author ruoyi + */ +@Aspect +@Order(1) +@Component +public class DataSourceAspect +{ + protected Logger logger = LoggerFactory.getLogger(getClass()); + + @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" + + "|| @within(com.ruoyi.common.annotation.DataSource)") + public void dsPointCut() + { + + } + + @Around("dsPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable + { + DataSource dataSource = getDataSource(point); + + if (StringUtils.isNotNull(dataSource)) + { + DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); + } + + try + { + return point.proceed(); + } + finally + { + // 销毁数据源 在执行方法之后 + DynamicDataSourceContextHolder.clearDataSourceType(); + } + } + + /** + * 获取需要切换的数据源 + */ + public DataSource getDataSource(ProceedingJoinPoint point) + { + MethodSignature signature = (MethodSignature) point.getSignature(); + DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); + if (Objects.nonNull(dataSource)) + { + return dataSource; + } + + return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java new file mode 100644 index 0000000..f4c0719 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -0,0 +1,249 @@ +package com.ruoyi.framework.aspectj; + +import java.util.Collection; +import java.util.Map; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.NamedThreadLocal; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.BusinessStatus; +import com.ruoyi.common.enums.HttpMethod; +import com.ruoyi.common.filter.PropertyPreExcludeFilter; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.system.domain.SysOperLog; + +/** + * 操作日志记录处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class LogAspect +{ + private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + + /** 排除敏感属性字段 */ + public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; + + /** 计算操作消耗时间 */ + private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time"); + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(controllerLog)") + public void boBefore(JoinPoint joinPoint, Log controllerLog) + { + TIME_THREADLOCAL.set(System.currentTimeMillis()); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) + { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) + { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) + { + try + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + + // *========数据库日志=========*// + SysOperLog operLog = new SysOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = IpUtils.getIpAddr(); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + if (loginUser != null) + { + operLog.setOperName(loginUser.getUsername()); + } + + if (e != null) + { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 设置消耗时间 + operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get()); + // 保存数据库 + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } + catch (Exception exp) + { + // 记录本地异常日志 + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } + finally + { + TIME_THREADLOCAL.remove(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception + { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) + { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog, log.excludeParamNames()); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) + { + operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception + { + Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + String requestMethod = operLog.getRequestMethod(); + if (StringUtils.isEmpty(paramsMap) + && (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))) + { + String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } + else + { + operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) + { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) + { + for (Object o : paramsArray) + { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) + { + try + { + String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); + params += jsonObj.toString() + " "; + } + catch (Exception e) + { + } + } + } + } + return params.trim(); + } + + /** + * 忽略敏感属性 + */ + public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) + { + return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames)); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) + { + Class clazz = o.getClass(); + if (clazz.isArray()) + { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } + else if (Collection.class.isAssignableFrom(clazz)) + { + Collection collection = (Collection) o; + for (Object value : collection) + { + return value instanceof MultipartFile; + } + } + else if (Map.class.isAssignableFrom(clazz)) + { + Map map = (Map) o; + for (Object value : map.entrySet()) + { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java new file mode 100644 index 0000000..b720bc1 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java @@ -0,0 +1,89 @@ +package com.ruoyi.framework.aspectj; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; +import com.ruoyi.common.annotation.RateLimiter; +import com.ruoyi.common.enums.LimitType; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; + +/** + * 限流处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class RateLimiterAspect +{ + private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); + + private RedisTemplate redisTemplate; + + private RedisScript limitScript; + + @Autowired + public void setRedisTemplate1(RedisTemplate redisTemplate) + { + this.redisTemplate = redisTemplate; + } + + @Autowired + public void setLimitScript(RedisScript limitScript) + { + this.limitScript = limitScript; + } + + @Before("@annotation(rateLimiter)") + public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable + { + int time = rateLimiter.time(); + int count = rateLimiter.count(); + + String combineKey = getCombineKey(rateLimiter, point); + List keys = Collections.singletonList(combineKey); + try + { + Long number = redisTemplate.execute(limitScript, keys, count, time); + if (StringUtils.isNull(number) || number.intValue() > count) + { + throw new ServiceException("访问过于频繁,请稍候再试"); + } + log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey); + } + catch (ServiceException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException("服务器限流异常,请稍候再试"); + } + } + + public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) + { + StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); + if (rateLimiter.limitType() == LimitType.IP) + { + stringBuffer.append(IpUtils.getIpAddr()).append("-"); + } + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class targetClass = method.getDeclaringClass(); + stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); + return stringBuffer.toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RequestLogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RequestLogAspect.java new file mode 100644 index 0000000..3d624c3 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RequestLogAspect.java @@ -0,0 +1,169 @@ +package com.ruoyi.framework.aspectj; + +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.enums.HttpMethod; +import com.ruoyi.common.filter.PropertyPreExcludeFilter; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.NamedThreadLocal; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Collection; +import java.util.Map; + +/** + * 操作日志记录处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class RequestLogAspect { + private static final boolean close = false; + private static final Logger log = LoggerFactory.getLogger(RequestLogAspect.class); + + /** + * 排除敏感属性字段 + */ + public static final String[] EXCLUDE_PROPERTIES = {"password", "oldPassword", "newPassword", "confirmPassword"}; + + /** + * 计算操作消耗时间 + */ + private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time"); + + /** + * 处理请求前执行 + */ + @Before(value = "execution(public * com.ruoyi..*.controller..*.*(..))") + public void boBefore(JoinPoint joinPoint) { + TIME_THREADLOCAL.set(System.currentTimeMillis()); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "execution(public * com.ruoyi.*.controller..*.*(..))", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) { + handleLog(joinPoint, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "execution(public * com.ruoyi.*.controller..*.*(..))", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Exception e) { + handleLog(joinPoint, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) { + if (close) { + return; + } + try { + String className = joinPoint.getTarget().getClass().getSimpleName(); + String methodName = joinPoint.getSignature().getName(); + String call = className + "." + methodName; + var speed = System.currentTimeMillis() - TIME_THREADLOCAL.get(); + var request = getRequestValue(joinPoint, ServletUtils.getRequest().getMethod()); + if (e != null) { + var err = StringUtils.substring(e.getMessage(), 0, 2000); + log.info("Call={} Speed={}ms request={}\n\nerror={}\n", call, speed, request, err); + } else { + var result = JSON.toJSONString(jsonResult); + log.info("Call={} Speed={}ms request={}\n\nresponse={}\n", call, speed, request, result); + } + } catch (Exception exp) { + // 记录本地异常日志 + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } finally { + TIME_THREADLOCAL.remove(); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @throws Exception 异常 + */ + private String getRequestValue(JoinPoint joinPoint, String requestMethod) throws Exception { + Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + if (StringUtils.isEmpty(paramsMap) + && (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))) { + return argsArrayToString(joinPoint.getArgs()); + } else { + return StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter()), 0, 2000); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) { + for (Object o : paramsArray) { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) { + try { + String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter()); + params += jsonObj.toString() + " "; + } catch (Exception e) { + } + } + } + } + return params.trim(); + } + + /** + * 忽略敏感属性 + */ + public PropertyPreExcludeFilter excludePropertyPreFilter() { + return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES)); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.entrySet()) { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/UpdateUserCacheAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/UpdateUserCacheAspect.java new file mode 100644 index 0000000..9be08b9 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/UpdateUserCacheAspect.java @@ -0,0 +1,119 @@ +package com.ruoyi.framework.aspectj; + +import cn.hutool.core.util.ObjectUtil; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.entity.UserData; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.framework.web.service.TokenService; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Aspect +@Component +@Order(1) +@Slf4j +public class UpdateUserCacheAspect { + + private final TtUserService userService; + private final TokenService tokenService; + + public UpdateUserCacheAspect(TtUserService userService, + TokenService tokenService) { + this.userService = userService; + this.tokenService = tokenService; + } + + @Pointcut("@annotation(com.ruoyi.common.annotation.NewUserInfo)") + private void NewUserInfo() { + } + + @Pointcut("@annotation(com.ruoyi.common.annotation.UpdateUserCache)") + private void permissionCheck() { + } + + @Before("NewUserInfo()") + public void NewUserInfo(JoinPoint joinPoint) throws IOException { + // 获取request对象 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (StringUtils.isNull(attributes)) return; + HttpServletRequest request = attributes.getRequest(); + HttpServletResponse response = attributes.getResponse(); + + // 获取登录用户信息 + LoginUser loginUser = tokenService.getLoginUser(request); + if (ObjectUtil.isEmpty(loginUser)){ + response.sendError(401,"登录过期,请重新登录。"); + log.warn("登录过期,请重新登录。"); + return; + } + TtUser user = userService.getById(loginUser.getUserId()); + + // TODO: 2024/4/18 响应401 + if (ObjectUtil.isEmpty(user)) return; + + // 设置更新后的属性 + UserData userData = loginUser.getUserData(); + // 对象拷贝 + BeanUtils.copyBeanProp(userData, user); + // 获取上级用户 + TtUser ParentUser = userService.getById(user.getParentId()); + if (StringUtils.isNotNull(ParentUser)) + userData.setParentInvitationCode(ParentUser.getInvitationCode()); // 上级邀请码 + loginUser.setUserData(userData); + + // 刷新token + tokenService.refreshToken(loginUser); + //log.info("用户数据已提前刷新"); + } + + @After("permissionCheck()") + public void afterAdvice(JoinPoint joinPoint) throws IOException { + + // 获取request对象 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (StringUtils.isNull(attributes)) return; + HttpServletRequest request = attributes.getRequest(); + HttpServletResponse response = attributes.getResponse(); + + // 获取登录用户信息 + LoginUser loginUser = tokenService.getLoginUser(request); + if (ObjectUtil.isEmpty(loginUser)){ + response.sendError(401,"登录过期,请重新登录。"); + log.warn("登录过期,请重新登录。"); + return; + } + TtUser user = userService.getById(loginUser.getUserId()); + + // 设置更新后的属性 + UserData userData = loginUser.getUserData(); + if (userData == null) { + userData = UserData.builder().build(); + } + // 对象拷贝 + BeanUtils.copyBeanProp(userData, user); + // 获取上级用户 + TtUser ParentUser = userService.getById(user.getParentId()); + if (StringUtils.isNotNull(ParentUser)) + userData.setParentInvitationCode(ParentUser.getInvitationCode()); // 上级邀请码 + loginUser.setUserData(userData); + + // 刷新token + tokenService.refreshToken(loginUser); + //log.info("用户数据缓存刷新完毕"); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java new file mode 100644 index 0000000..26451c6 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java @@ -0,0 +1,19 @@ +package com.ruoyi.framework.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * 程序注解配置 + * + * @author ruoyi + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("com.ruoyi.**.mapper") +public class ApplicationConfig +{ +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java new file mode 100644 index 0000000..43e78ae --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java @@ -0,0 +1,83 @@ +package com.ruoyi.framework.config; + +import java.util.Properties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import static com.google.code.kaptcha.Constants.*; + +/** + * 验证码配置 + * + * @author ruoyi + */ +@Configuration +public class CaptchaConfig +{ + @Bean(name = "captchaProducer") + public DefaultKaptcha getKaptchaBean() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + + @Bean(name = "captchaProducerMath") + public DefaultKaptcha getKaptchaBeanMath() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 边框颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); + // 验证码文本生成器 + properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.framework.config.KaptchaTextCreator"); + // 验证码文本字符间距 默认为2 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 验证码噪点颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); + // 干扰实现类 + properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java new file mode 100644 index 0000000..53f1491 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java @@ -0,0 +1,126 @@ +package com.ruoyi.framework.config; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import javax.sql.DataSource; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.spring.boot4.autoconfigure.DruidDataSourceBuilder; +import com.alibaba.druid.spring.boot4.autoconfigure.properties.DruidStatProperties; +import com.alibaba.druid.util.Utils; +import com.ruoyi.common.enums.DataSourceType; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.config.properties.DruidProperties; +import com.ruoyi.framework.datasource.DynamicDataSource; + +/** + * druid 配置多数据源 + * + * @author ruoyi + */ +@Configuration +public class DruidConfig +{ + @Bean + @ConfigurationProperties("spring.datasource.druid.master") + public DataSource masterDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean + @ConfigurationProperties("spring.datasource.druid.slave") + @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") + public DataSource slaveDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean(name = "dynamicDataSource") + @Primary + public DynamicDataSource dataSource(DataSource masterDataSource) + { + Map targetDataSources = new HashMap<>(); + targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); + setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); + return new DynamicDataSource(masterDataSource, targetDataSources); + } + + /** + * 设置数据源 + * + * @param targetDataSources 备选数据源集合 + * @param sourceName 数据源名称 + * @param beanName bean名称 + */ + public void setDataSource(Map targetDataSources, String sourceName, String beanName) + { + try + { + DataSource dataSource = SpringUtils.getBean(beanName); + targetDataSources.put(sourceName, dataSource); + } + catch (Exception e) + { + } + } + + /** + * 去除监控页面底部的广告 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) + { + // 获取web监控页面的参数 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + // 提取common.js的配置路径 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + final String filePath = "support/http/resources/js/common.js"; + // 创建filter进行过滤 + Filter filter = new Filter() + { + @Override + public void init(jakarta.servlet.FilterConfig filterConfig) throws ServletException + { + } + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 + response.resetBuffer(); + // 获取common.js + String text = Utils.readFromResource(filePath); + // 正则替换banner, 除去底部的广告信息 + text = text.replaceAll("
", ""); + text = text.replaceAll("powered.*?shrek.wang", ""); + response.getWriter().write(text); + } + @Override + public void destroy() + { + } + }; + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(filter); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..4adbb7f --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,52 @@ +package com.ruoyi.framework.config; + +import java.nio.charset.Charset; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.Filter; +import com.ruoyi.common.constant.Constants; + +/** + * Redis使用FastJson序列化 + * + * @author ruoyi + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer +{ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR); + + private Class clazz; + + public FastJson2JsonRedisSerializer(Class clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java new file mode 100644 index 0000000..fad6bb8 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java @@ -0,0 +1,58 @@ +package com.ruoyi.framework.config; + +import java.util.HashMap; +import java.util.Map; +import jakarta.servlet.DispatcherType; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.ruoyi.common.filter.RepeatableFilter; +import com.ruoyi.common.filter.XssFilter; +import com.ruoyi.common.utils.StringUtils; + +/** + * Filter配置 + * + * @author ruoyi + */ +@Configuration +public class FilterConfig +{ + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", excludes); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean someFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java new file mode 100644 index 0000000..7f8e1d5 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java @@ -0,0 +1,68 @@ +package com.ruoyi.framework.config; + +import java.util.Random; +import com.google.code.kaptcha.text.impl.DefaultTextCreator; + +/** + * 验证码文本生成器 + * + * @author ruoyi + */ +public class KaptchaTextCreator extends DefaultTextCreator +{ + private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); + + @Override + public String getText() + { + Integer result = 0; + Random random = new Random(); + int x = random.nextInt(10); + int y = random.nextInt(10); + StringBuilder suChinese = new StringBuilder(); + int randomoperands = random.nextInt(3); + if (randomoperands == 0) + { + result = x * y; + suChinese.append(CNUMBERS[x]); + suChinese.append("*"); + suChinese.append(CNUMBERS[y]); + } + else if (randomoperands == 1) + { + if ((x != 0) && y % x == 0) + { + result = y / x; + suChinese.append(CNUMBERS[y]); + suChinese.append("/"); + suChinese.append(CNUMBERS[x]); + } + else + { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + } + else + { + if (x >= y) + { + result = x - y; + suChinese.append(CNUMBERS[x]); + suChinese.append("-"); + suChinese.append(CNUMBERS[y]); + } + else + { + result = y - x; + suChinese.append(CNUMBERS[y]); + suChinese.append("-"); + suChinese.append(CNUMBERS[x]); + } + } + suChinese.append("=?@" + result); + return suChinese.toString(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java new file mode 100644 index 0000000..7eff4bb --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java @@ -0,0 +1,62 @@ +package com.ruoyi.framework.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.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * Mybatis Plus 配置 + * + * @author ruoyi + */ +@EnableTransactionManagement(proxyTargetClass = true) +@Configuration +public class MybatisPlusConfig +{ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() + { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 分页插件 + interceptor.addInnerInterceptor(paginationInnerInterceptor()); + // 乐观锁插件 + interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); + // 阻断插件 + interceptor.addInnerInterceptor(blockAttackInnerInterceptor()); + return interceptor; + } + + /** + * 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html + */ + public PaginationInnerInterceptor paginationInnerInterceptor() + { + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + // 设置数据库类型为mysql + paginationInnerInterceptor.setDbType(DbType.MYSQL); + // 设置最大单页限制数量,默认 500 条,-1 不受限制 + paginationInnerInterceptor.setMaxLimit(-1L); + return paginationInnerInterceptor; + } + + /** + * 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html + */ + public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() + { + return new OptimisticLockerInnerInterceptor(); + } + + /** + * 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html + */ + public BlockAttackInnerInterceptor blockAttackInnerInterceptor() + { + return new BlockAttackInnerInterceptor(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java new file mode 100644 index 0000000..ce4df41 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -0,0 +1,110 @@ +package com.ruoyi.framework.config; + +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.Duration; + +/** + * redis配置 + * + * @author ruoyi + */ +@Configuration +@EnableCaching +public class RedisConfig { + + @Bean + public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) { + // 初始化 RedisCacheWriter + RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory); + + // 获取默认的缓存配置(可选:这里可以自定义序列化方式) + RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig() + // 设置 key 的序列化方式(使用 String 序列化) + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) + // 设置 value 的序列化方式(这里使用 JSON 序列化,需确保 FastJson2JsonRedisSerializer 存在) + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new FastJson2JsonRedisSerializer<>(Object.class))) + // 默认过期时间(可选,例如 30 分钟) + .entryTtl(Duration.ofMinutes(30)); + + // 返回 RedisCacheManager 实例 + return new RedisCacheManager(redisCacheWriter, defaultCacheConfig); + } + + @Bean + @SuppressWarnings(value = {"unchecked", "rawtypes"}) + public RedisTemplate longRedisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + template.afterPropertiesSet(); + return template; + } + + @Bean + @SuppressWarnings(value = {"unchecked", "rawtypes"}) + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + template.afterPropertiesSet(); + return template; + } + + @Bean + public DefaultRedisScript limitScript() + { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(limitScriptText()); + redisScript.setResultType(Long.class); + return redisScript; + } + + /** + * 限流脚本 + */ + private String limitScriptText() + { + return "local key = KEYS[1]\n" + + "local count = tonumber(ARGV[1])\n" + + "local time = tonumber(ARGV[2])\n" + + "local current = redis.call('get', key);\n" + + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + + "end\n" + + "current = redis.call('incr', key)\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java new file mode 100644 index 0000000..6182916 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java @@ -0,0 +1,79 @@ +package com.ruoyi.framework.config; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 通用配置 + * + * @author ruoyi + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer +{ + @Autowired + private RepeatSubmitInterceptor repeatSubmitInterceptor; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + /** 本地文件上传路径 */ + registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**") + .addResourceLocations("file:" + RuoYiConfig.getProfile() + "/"); + + registry.addResourceHandler("/uploads/**") + .addResourceLocations("file:" + RuoYiConfig.getProfile() + "/uploads/"); + + registry.addResourceHandler("/upload/**") + .addResourceLocations("file:" + RuoYiConfig.getProfile() + "/upload/"); + + /** swagger配置 */ + registry.addResourceHandler("/swagger-ui/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/") + .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic()); + } + + /** + * 自定义拦截规则 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + } + + /** + * 跨域配置 + */ + @Bean + public CorsFilter corsFilter() + { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + // 设置访问源地址 + config.addAllowedOriginPattern("*"); + // 设置访问源请求头 + config.addAllowedHeader("*"); + // 设置访问源请求方法 + config.addAllowedMethod("*"); + // 有效期 1800秒 + config.setMaxAge(1800L); + // 添加映射路径,拦截一切请求 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + // 返回新的CorsFilter + return new CorsFilter(source); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java new file mode 100644 index 0000000..7169d19 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -0,0 +1,123 @@ +package com.ruoyi.framework.config; + +import com.ruoyi.framework.config.properties.PermitAllUrlProperties; +import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; +import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl; +import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.filter.CorsFilter; + +@Configuration +@EnableWebSecurity +@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SecurityConfig { + @Qualifier("apiUserDetailsServiceImpl") + @Autowired + private UserDetailsService apiUserDetailsService; + + @Autowired + private AuthenticationEntryPointImpl unauthorizedHandler; + + @Autowired + private LogoutSuccessHandlerImpl logoutSuccessHandler; + + @Autowired + private JwtAuthenticationTokenFilter authenticationTokenFilter; + + @Autowired + private CorsFilter corsFilter; + + @Autowired + private PermitAllUrlProperties permitAllUrl; + + /** + * 配置 AuthenticationManager + * 替代原 configure(AuthenticationManagerBuilder auth) 方法 + */ + @Bean + public AuthenticationManager authenticationManager() { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(apiUserDetailsService); + provider.setPasswordEncoder(bCryptPasswordEncoder()); + return new ProviderManager(provider); + } + + /** + * 配置 SecurityFilterChain + * 替代原 configure(HttpSecurity http) 方法 + */ + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + // CSRF 禁用 + .csrf(AbstractHttpConfigurer::disable) +// 禁用缓存 + .headers(headers -> headers.cacheControl(HeadersConfigurer.CacheControlConfig::disable).frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) + // 异常处理 + .exceptionHandling(exception -> exception + .authenticationEntryPoint(unauthorizedHandler) + ) + // Session 管理:无状态 + .sessionManagement(session -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + // 请求授权配置 + .authorizeHttpRequests(auth -> { + // 1. 动态配置允许匿名访问的 URL (来自 PermitAllUrlProperties) + permitAllUrl.getUrls().forEach(url -> auth.requestMatchers(url).permitAll()); + + // 2. 固定允许匿名访问的 URL + auth.requestMatchers("/login", "/register", "/captchaImage").permitAll() + // 静态资源 + .requestMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", + "/**/*.css", "/**/*.js", "/profile/**", "/uploads/**", "/upload/**").permitAll() + // Swagger & Druid + .requestMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() + // 业务接口 + .requestMatchers("/api/**", "/admin/**", "/api/websocket/skins").permitAll() + // WebSocket + .requestMatchers("/ws/**").permitAll() + // 其他所有请求都需要认证 + .anyRequest().authenticated(); + }) + // 登出配置 + .logout(logout -> logout + .logoutUrl("/logout") + .logoutSuccessHandler(logoutSuccessHandler) + ); + + // 添加 JWT 过滤器 (在 UsernamePasswordAuthenticationFilter 之前) + http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + + // 添加 CORS 过滤器 + // 注意:在 Spring Security 6 中,通常推荐使用 http.cors(Customizer.withDefaults()) 集成 CorsConfigurationSource + // 这里为了保持原有逻辑,继续手动添加 Filter + http.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); + http.addFilterBefore(corsFilter, LogoutFilter.class); + + return http.build(); + } + + + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java new file mode 100644 index 0000000..cd8fb09 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java @@ -0,0 +1,32 @@ +package com.ruoyi.framework.config; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 服务相关配置 + * + * @author ruoyi + */ +@Component +public class ServerConfig +{ + /** + * 获取完整的请求路径,包括:域名,端口,上下文访问路径 + * + * @return 服务地址 + */ + public String getUrl() + { + HttpServletRequest request = ServletUtils.getRequest(); + return getDomain(request); + } + + public static String getDomain(HttpServletRequest request) + { + StringBuffer url = request.getRequestURL(); + String contextPath = request.getServletContext().getContextPath(); + return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java new file mode 100644 index 0000000..2b99fb0 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java @@ -0,0 +1,116 @@ +package com.ruoyi.framework.config; + +import com.ruoyi.common.utils.Threads; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.Semaphore; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author ruoyi + **/ +@Configuration +public class ThreadPoolConfig { + // 核心线程池大小 + private int corePoolSize = 50; + + // 最大可创建的线程数 + private int maxPoolSize = 200; + + // 队列最大长度 + private int queueCapacity = 1000; + + // 线程池维护线程所允许的空闲时间 + private int keepAliveSeconds = 300; + + + @Bean(name = "threadPoolTaskExecutor") + public Executor threadPoolTaskExecutor() { + // 这是一个包装类,用于限制并发量 + return new Executor() { + // 核心限制:允许同时进行的最大并发数 + // 相当于传统线程池的 maxPoolSize,但用的是虚拟线程 + private final Semaphore semaphore = new Semaphore(100000); + private final Executor delegate = Executors.newVirtualThreadPerTaskExecutor(); + + @Override + public void execute(Runnable command) { + // 尝试获取许可(非阻塞或超时可配置,这里演示阻塞获取) + // 如果不想阻塞,可以使用 semaphore.tryAcquire() 并处理失败情况 + try { + semaphore.acquire(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("线程获取被中断", e); + } + + delegate.execute(() -> { + try { + command.run(); + } finally { + // 任务完成后释放许可 + semaphore.release(); + } + }); + } + }; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() { + return new ScheduledThreadPoolExecutor(corePoolSize, + Thread.ofVirtual().name("schedule-pool-", 0).factory(), + new ThreadPoolExecutor.CallerRunsPolicy()) { + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } + + /** + * 自定义线程池 + */ + @Bean(name = "customThreadPoolExecutor") + public Executor customThreadPoolExecutor() { + return new Executor() { + // 核心限制:允许同时进行的最大并发数 + // 相当于传统线程池的 maxPoolSize,但用的是虚拟线程 + private final Semaphore semaphore = new Semaphore(100000); + private final Executor delegate = Executors.newVirtualThreadPerTaskExecutor(); + + @Override + public void execute(Runnable command) { + // 尝试获取许可(非阻塞或超时可配置,这里演示阻塞获取) + // 如果不想阻塞,可以使用 semaphore.tryAcquire() 并处理失败情况 + try { + semaphore.acquire(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("线程获取被中断", e); + } + + delegate.execute(() -> { + try { + command.run(); + } finally { + // 任务完成后释放许可 + semaphore.release(); + } + }); + } + }; + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java new file mode 100644 index 0000000..c8a5c8a --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java @@ -0,0 +1,89 @@ +package com.ruoyi.framework.config.properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import com.alibaba.druid.pool.DruidDataSource; + +/** + * druid 配置属性 + * + * @author ruoyi + */ +@Configuration +public class DruidProperties +{ + @Value("${spring.datasource.druid.initialSize}") + private int initialSize; + + @Value("${spring.datasource.druid.minIdle}") + private int minIdle; + + @Value("${spring.datasource.druid.maxActive}") + private int maxActive; + + @Value("${spring.datasource.druid.maxWait}") + private int maxWait; + + @Value("${spring.datasource.druid.connectTimeout}") + private int connectTimeout; + + @Value("${spring.datasource.druid.socketTimeout}") + private int socketTimeout; + + @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") + private int timeBetweenEvictionRunsMillis; + + @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}") + private int minEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}") + private int maxEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.validationQuery}") + private String validationQuery; + + @Value("${spring.datasource.druid.testWhileIdle}") + private boolean testWhileIdle; + + @Value("${spring.datasource.druid.testOnBorrow}") + private boolean testOnBorrow; + + @Value("${spring.datasource.druid.testOnReturn}") + private boolean testOnReturn; + + public DruidDataSource dataSource(DruidDataSource datasource) + { + /** 配置初始化大小、最小、最大 */ + datasource.setInitialSize(initialSize); + datasource.setMaxActive(maxActive); + datasource.setMinIdle(minIdle); + + /** 配置获取连接等待超时的时间 */ + datasource.setMaxWait(maxWait); + + /** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */ + datasource.setConnectTimeout(connectTimeout); + + /** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */ + datasource.setSocketTimeout(socketTimeout); + + /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + + /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */ + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); + + /** + * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + */ + datasource.setValidationQuery(validationQuery); + /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ + datasource.setTestWhileIdle(testWhileIdle); + /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnBorrow(testOnBorrow); + /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnReturn(testOnReturn); + return datasource; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java new file mode 100644 index 0000000..00b3d74 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java @@ -0,0 +1,69 @@ +package com.ruoyi.framework.config.properties; + +import com.ruoyi.common.annotation.Anonymous; +import org.apache.commons.lang3.RegExUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Pattern; + +/** + * 设置Anonymous注解允许匿名访问的url + * + * @author ruoyi + */ +@Configuration +public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware { + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + + private ApplicationContext applicationContext; + + private List urls = new ArrayList<>(); + + public String ASTERISK = "*"; + + @Override + public void afterPropertiesSet() { + RequestMappingHandlerMapping mapping = applicationContext.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class); + Map map = mapping.getHandlerMethods(); + + map.keySet().forEach(info -> { + HandlerMethod handlerMethod = map.get(info); + + // 获取方法上边的注解 替代path variable 为 * + Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); + Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatterns()) + .forEach(url -> urls.add(RegExUtils.replaceAll(url.getPatternString(), PATTERN.pattern(), ASTERISK)))); + + // 获取类上边的注解, 替代path variable 为 * + Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); + Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPathPatternsCondition().getPatterns()) + .forEach(url -> urls.add(RegExUtils.replaceAll(url.getPatternString(), PATTERN.pattern(), ASTERISK)))); + }); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.applicationContext = context; + } + + public List getUrls() { + return urls; + } + + public void setUrls(List urls) { + this.urls = urls; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java new file mode 100644 index 0000000..e70b8cf --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java @@ -0,0 +1,26 @@ +package com.ruoyi.framework.datasource; + +import java.util.Map; +import javax.sql.DataSource; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * 动态数据源 + * + * @author ruoyi + */ +public class DynamicDataSource extends AbstractRoutingDataSource +{ + public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) + { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() + { + return DynamicDataSourceContextHolder.getDataSourceType(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java new file mode 100644 index 0000000..9770af6 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java @@ -0,0 +1,45 @@ +package com.ruoyi.framework.datasource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 数据源切换处理 + * + * @author ruoyi + */ +public class DynamicDataSourceContextHolder +{ + public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); + + /** + * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, + * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 + */ + private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); + + /** + * 设置数据源的变量 + */ + public static void setDataSourceType(String dsType) + { + log.info("切换到{}数据源", dsType); + CONTEXT_HOLDER.set(dsType); + } + + /** + * 获得数据源的变量 + */ + public static String getDataSourceType() + { + return CONTEXT_HOLDER.get(); + } + + /** + * 清空数据源变量 + */ + public static void clearDataSourceType() + { + CONTEXT_HOLDER.remove(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java new file mode 100644 index 0000000..4e8d20f --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java @@ -0,0 +1,56 @@ +package com.ruoyi.framework.interceptor; + +import java.lang.reflect.Method; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 防止重复提交拦截器 + * + * @author ruoyi + */ +@Component +public abstract class RepeatSubmitInterceptor implements HandlerInterceptor +{ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception + { + if (handler instanceof HandlerMethod) + { + HandlerMethod handlerMethod = (HandlerMethod) handler; + Method method = handlerMethod.getMethod(); + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); + if (annotation != null) + { + if (this.isRepeatSubmit(request, annotation)) + { + AjaxResult ajaxResult = AjaxResult.error(annotation.message()); + ServletUtils.renderString(response, JSON.toJSONString(ajaxResult)); + return false; + } + } + return true; + } + else + { + return true; + } + } + + /** + * 验证是否重复提交由子类实现具体的防重复提交的规则 + * + * @param request 请求信息 + * @param annotation 防重复注解参数 + * @return 结果 + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java new file mode 100644 index 0000000..c93df50 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -0,0 +1,110 @@ +package com.ruoyi.framework.interceptor.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.filter.RepeatedlyRequestWrapper; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpHelper; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 判断请求url和数据是否和上一次相同, + * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 + * + * @author ruoyi + */ +@Component +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor +{ + public final String REPEAT_PARAMS = "repeatParams"; + + public final String REPEAT_TIME = "repeatTime"; + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + @Autowired + private RedisCache redisCache; + + @SuppressWarnings("unchecked") + @Override + public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) + { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) + { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + nowParams = HttpHelper.getBodyString(repeatedlyRequest); + } + + // body参数为空,获取Parameter的数据 + if (StringUtils.isEmpty(nowParams)) + { + nowParams = JSON.toJSONString(request.getParameterMap()); + } + Map nowDataMap = new HashMap(); + nowDataMap.put(REPEAT_PARAMS, nowParams); + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = StringUtils.trimToEmpty(request.getHeader(header)); + + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey; + + Object sessionObj = redisCache.getCacheObject(cacheRepeatKey); + if (sessionObj != null) + { + Map sessionMap = (Map) sessionObj; + if (sessionMap.containsKey(url)) + { + Map preDataMap = (Map) sessionMap.get(url); + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) + { + return true; + } + } + } + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS); + return false; + } + + /** + * 判断参数是否相同 + */ + private boolean compareParams(Map nowMap, Map preMap) + { + String nowParams = (String) nowMap.get(REPEAT_PARAMS); + String preParams = (String) preMap.get(REPEAT_PARAMS); + return nowParams.equals(preParams); + } + + /** + * 判断两次间隔时间 + */ + private boolean compareTime(Map nowMap, Map preMap, int interval) + { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < interval) + { + return true; + } + return false; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java new file mode 100644 index 0000000..3f5e478 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java @@ -0,0 +1,82 @@ +package com.ruoyi.framework.manager; + +import com.ruoyi.common.utils.Threads; +import com.ruoyi.common.utils.spring.SpringUtils; + +import java.util.TimerTask; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Supplier; + +/** + * 异步任务管理器 + * + * @author ruoyi + */ +public class AsyncManager { + /** + * 操作延迟10毫秒 + */ + private final int OPERATE_DELAY_TIME = 10; + + /** + * 异步操作任务调度线程池 + */ + private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); + + /** + * 虚拟线程池 + */ + private final Executor virtualThreadExecutor = SpringUtils.getBean("threadPoolTaskExecutor"); + + /** + * 单例模式 + */ + private AsyncManager() { + } + + private static AsyncManager me = new AsyncManager(); + + public static AsyncManager me() { + return me; + } + + /** + * 执行任务 + * + * @param task 任务 + */ + public void execute(TimerTask task) { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + public void timeout(TimerTask task, long delay, TimeUnit unit) { + executor.schedule(task, delay, unit); + } + + public void run(Runnable runnable) { + CompletableFuture.runAsync(runnable, virtualThreadExecutor); + } + + public CompletableFuture supply(Supplier supplier) { + return CompletableFuture.supplyAsync(supplier, virtualThreadExecutor); + } + + public static U getMs(CompletableFuture cf, long timeout) { + try { + return cf.get(timeout, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + return null; + } + } + + /** + * 停止任务线程池 + */ + public void shutdown() { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java new file mode 100644 index 0000000..095b865 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java @@ -0,0 +1,39 @@ +package com.ruoyi.framework.manager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import jakarta.annotation.PreDestroy; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author ruoyi + */ +@Component +public class ShutdownManager +{ + private static final Logger logger = LoggerFactory.getLogger("sys-user"); + + @PreDestroy + public void destroy() + { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() + { + try + { + logger.info("====关闭后台任务任务线程池===="); + AsyncManager.me().shutdown(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java new file mode 100644 index 0000000..267e305 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java @@ -0,0 +1,102 @@ +package com.ruoyi.framework.manager.factory; + +import java.util.TimerTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.LogUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.service.ISysLogininforService; +import com.ruoyi.system.service.ISysOperLogService; +import eu.bitwalker.useragentutils.UserAgent; + +/** + * 异步工厂(产生任务用) + * + * @author ruoyi + */ +public class AsyncFactory +{ + private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + * @return 任务task + */ + public static TimerTask recordLogininfor(final String username, final String status, final String message, + final Object... args) + { + final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + final String ip = IpUtils.getIpAddr(); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOperatingSystem().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) + { + logininfor.setStatus(Constants.SUCCESS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor); + } + }; + } + + /** + * 操作日志记录 + * + * @param operLog 操作日志信息 + * @return 任务task + */ + public static TimerTask recordOper(final SysOperLog operLog) + { + return new TimerTask() + { + @Override + public void run() + { + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); + } + }; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java new file mode 100644 index 0000000..6c776ce --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java @@ -0,0 +1,28 @@ +package com.ruoyi.framework.security.context; + +import org.springframework.security.core.Authentication; + +/** + * 身份验证信息 + * + * @author ruoyi + */ +public class AuthenticationContextHolder +{ + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static Authentication getContext() + { + return contextHolder.get(); + } + + public static void setContext(Authentication context) + { + contextHolder.set(context); + } + + public static void clearContext() + { + contextHolder.remove(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java new file mode 100644 index 0000000..5472f3d --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java @@ -0,0 +1,27 @@ +package com.ruoyi.framework.security.context; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import com.ruoyi.common.core.text.Convert; + +/** + * 权限信息 + * + * @author ruoyi + */ +public class PermissionContextHolder +{ + private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; + + public static void setContext(String permission) + { + RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, + RequestAttributes.SCOPE_REQUEST); + } + + public static String getContext() + { + return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, + RequestAttributes.SCOPE_REQUEST)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000..b8a411d --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -0,0 +1,48 @@ +package com.ruoyi.framework.security.filter; + +import java.io.IOException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.TokenService; + +/** + * token过滤器 验证token有效性 + * + * @author ruoyi + */ +@Slf4j +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter +{ + @Autowired + private TokenService tokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException + { + log.debug("请求信息:{}:{}{}",request.getRemoteAddr(),request.getLocalPort(),request.getRequestURI()); + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) + { + tokenService.verifyToken(loginUser); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + chain.doFilter(request, response); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java new file mode 100644 index 0000000..e1789f8 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java @@ -0,0 +1,34 @@ +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import java.io.Serializable; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 认证失败处理类 返回未授权 + * + * @author ruoyi + */ +@Component +public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable +{ + private static final long serialVersionUID = -8970718410437077606L; + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException + { + int code = HttpStatus.UNAUTHORIZED; + String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java new file mode 100644 index 0000000..9e4380e --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -0,0 +1,52 @@ +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.web.service.TokenService; + +/** + * 自定义退出处理类 返回成功 + * + * @author ruoyi + */ +@Configuration +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler +{ + @Autowired + private TokenService tokenService; + + /** + * 退出处理 + * + * @return + */ + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) + { + String userName = loginUser.getUsername(); + // 删除用户缓存记录 + tokenService.delLoginUser(loginUser.getToken()); + // 记录用户退出日志 + AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); + } + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success("退出成功"))); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java new file mode 100644 index 0000000..63b03da --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java @@ -0,0 +1,240 @@ +package com.ruoyi.framework.web.domain; + +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.web.domain.server.Cpu; +import com.ruoyi.framework.web.domain.server.Jvm; +import com.ruoyi.framework.web.domain.server.Mem; +import com.ruoyi.framework.web.domain.server.Sys; +import com.ruoyi.framework.web.domain.server.SysFile; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.CentralProcessor.TickType; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.Util; + +/** + * 服务器相关信息 + * + * @author ruoyi + */ +public class Server +{ + private static final int OSHI_WAIT_SECOND = 1000; + + /** + * CPU相关信息 + */ + private Cpu cpu = new Cpu(); + + /** + * 內存相关信息 + */ + private Mem mem = new Mem(); + + /** + * JVM相关信息 + */ + private Jvm jvm = new Jvm(); + + /** + * 服务器相关信息 + */ + private Sys sys = new Sys(); + + /** + * 磁盘相关信息 + */ + private List sysFiles = new LinkedList(); + + public Cpu getCpu() + { + return cpu; + } + + public void setCpu(Cpu cpu) + { + this.cpu = cpu; + } + + public Mem getMem() + { + return mem; + } + + public void setMem(Mem mem) + { + this.mem = mem; + } + + public Jvm getJvm() + { + return jvm; + } + + public void setJvm(Jvm jvm) + { + this.jvm = jvm; + } + + public Sys getSys() + { + return sys; + } + + public void setSys(Sys sys) + { + this.sys = sys; + } + + public List getSysFiles() + { + return sysFiles; + } + + public void setSysFiles(List sysFiles) + { + this.sysFiles = sysFiles; + } + + public void copyTo() throws Exception + { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + + setCpuInfo(hal.getProcessor()); + + setMemInfo(hal.getMemory()); + + setSysInfo(); + + setJvmInfo(); + + setSysFiles(si.getOperatingSystem()); + } + + /** + * 设置CPU信息 + */ + private void setCpuInfo(CentralProcessor processor) + { + // CPU信息 + long[] prevTicks = processor.getSystemCpuLoadTicks(); + Util.sleep(OSHI_WAIT_SECOND); + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; + long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; + long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; + long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; + long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; + long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; + long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; + long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + cpu.setCpuNum(processor.getLogicalProcessorCount()); + cpu.setTotal(totalCpu); + cpu.setSys(cSys); + cpu.setUsed(user); + cpu.setWait(iowait); + cpu.setFree(idle); + } + + /** + * 设置内存信息 + */ + private void setMemInfo(GlobalMemory memory) + { + mem.setTotal(memory.getTotal()); + mem.setUsed(memory.getTotal() - memory.getAvailable()); + mem.setFree(memory.getAvailable()); + } + + /** + * 设置服务器信息 + */ + private void setSysInfo() + { + Properties props = System.getProperties(); + sys.setComputerName(IpUtils.getHostName()); + sys.setComputerIp(IpUtils.getHostIp()); + sys.setOsName(props.getProperty("os.name")); + sys.setOsArch(props.getProperty("os.arch")); + sys.setUserDir(props.getProperty("user.dir")); + } + + /** + * 设置Java虚拟机 + */ + private void setJvmInfo() throws UnknownHostException + { + Properties props = System.getProperties(); + jvm.setTotal(Runtime.getRuntime().totalMemory()); + jvm.setMax(Runtime.getRuntime().maxMemory()); + jvm.setFree(Runtime.getRuntime().freeMemory()); + jvm.setVersion(props.getProperty("java.version")); + jvm.setHome(props.getProperty("java.home")); + } + + /** + * 设置磁盘信息 + */ + private void setSysFiles(OperatingSystem os) + { + FileSystem fileSystem = os.getFileSystem(); + List fsArray = fileSystem.getFileStores(); + for (OSFileStore fs : fsArray) + { + long free = fs.getUsableSpace(); + long total = fs.getTotalSpace(); + long used = total - free; + SysFile sysFile = new SysFile(); + sysFile.setDirName(fs.getMount()); + sysFile.setSysTypeName(fs.getType()); + sysFile.setTypeName(fs.getName()); + sysFile.setTotal(convertFileSize(total)); + sysFile.setFree(convertFileSize(free)); + sysFile.setUsed(convertFileSize(used)); + sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); + sysFiles.add(sysFile); + } + } + + /** + * 字节转换 + * + * @param size 字节大小 + * @return 转换后值 + */ + public String convertFileSize(long size) + { + long kb = 1024; + long mb = kb * 1024; + long gb = mb * 1024; + if (size >= gb) + { + return String.format("%.1f GB", (float) size / gb); + } + else if (size >= mb) + { + float f = (float) size / mb; + return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f); + } + else if (size >= kb) + { + float f = (float) size / kb; + return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f); + } + else + { + return String.format("%d B", size); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java new file mode 100644 index 0000000..a13a66c --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java @@ -0,0 +1,101 @@ +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * CPU相关信息 + * + * @author ruoyi + */ +public class Cpu +{ + /** + * 核心数 + */ + private int cpuNum; + + /** + * CPU总的使用率 + */ + private double total; + + /** + * CPU系统使用率 + */ + private double sys; + + /** + * CPU用户使用率 + */ + private double used; + + /** + * CPU当前等待率 + */ + private double wait; + + /** + * CPU当前空闲率 + */ + private double free; + + public int getCpuNum() + { + return cpuNum; + } + + public void setCpuNum(int cpuNum) + { + this.cpuNum = cpuNum; + } + + public double getTotal() + { + return Arith.round(Arith.mul(total, 100), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getSys() + { + return Arith.round(Arith.mul(sys / total, 100), 2); + } + + public void setSys(double sys) + { + this.sys = sys; + } + + public double getUsed() + { + return Arith.round(Arith.mul(used / total, 100), 2); + } + + public void setUsed(double used) + { + this.used = used; + } + + public double getWait() + { + return Arith.round(Arith.mul(wait / total, 100), 2); + } + + public void setWait(double wait) + { + this.wait = wait; + } + + public double getFree() + { + return Arith.round(Arith.mul(free / total, 100), 2); + } + + public void setFree(double free) + { + this.free = free; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java new file mode 100644 index 0000000..1fdc6ac --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java @@ -0,0 +1,130 @@ +package com.ruoyi.framework.web.domain.server; + +import java.lang.management.ManagementFactory; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.DateUtils; + +/** + * JVM相关信息 + * + * @author ruoyi + */ +public class Jvm +{ + /** + * 当前JVM占用的内存总数(M) + */ + private double total; + + /** + * JVM最大可用内存总数(M) + */ + private double max; + + /** + * JVM空闲内存(M) + */ + private double free; + + /** + * JDK版本 + */ + private String version; + + /** + * JDK路径 + */ + private String home; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getMax() + { + return Arith.div(max, (1024 * 1024), 2); + } + + public void setMax(double max) + { + this.max = max; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024), 2); + } + + public void setFree(double free) + { + this.free = free; + } + + public double getUsed() + { + return Arith.div(total - free, (1024 * 1024), 2); + } + + public double getUsage() + { + return Arith.mul(Arith.div(total - free, total, 4), 100); + } + + /** + * 获取JDK名称 + */ + public String getName() + { + return ManagementFactory.getRuntimeMXBean().getVmName(); + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getHome() + { + return home; + } + + public void setHome(String home) + { + this.home = home; + } + + /** + * JDK启动时间 + */ + public String getStartTime() + { + return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()); + } + + /** + * JDK运行时间 + */ + public String getRunTime() + { + return DateUtils.timeDistance(DateUtils.getNowDate(), DateUtils.getServerStartDate()); + } + + /** + * 运行参数 + */ + public String getInputArgs() + { + return ManagementFactory.getRuntimeMXBean().getInputArguments().toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java new file mode 100644 index 0000000..13eec52 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java @@ -0,0 +1,61 @@ +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * 內存相关信息 + * + * @author ruoyi + */ +public class Mem +{ + /** + * 内存总量 + */ + private double total; + + /** + * 已用内存 + */ + private double used; + + /** + * 剩余内存 + */ + private double free; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024 * 1024), 2); + } + + public void setTotal(long total) + { + this.total = total; + } + + public double getUsed() + { + return Arith.div(used, (1024 * 1024 * 1024), 2); + } + + public void setUsed(long used) + { + this.used = used; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024 * 1024), 2); + } + + public void setFree(long free) + { + this.free = free; + } + + public double getUsage() + { + return Arith.mul(Arith.div(used, total, 4), 100); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java new file mode 100644 index 0000000..45d64d9 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java @@ -0,0 +1,84 @@ +package com.ruoyi.framework.web.domain.server; + +/** + * 系统相关信息 + * + * @author ruoyi + */ +public class Sys +{ + /** + * 服务器名称 + */ + private String computerName; + + /** + * 服务器Ip + */ + private String computerIp; + + /** + * 项目路径 + */ + private String userDir; + + /** + * 操作系统 + */ + private String osName; + + /** + * 系统架构 + */ + private String osArch; + + public String getComputerName() + { + return computerName; + } + + public void setComputerName(String computerName) + { + this.computerName = computerName; + } + + public String getComputerIp() + { + return computerIp; + } + + public void setComputerIp(String computerIp) + { + this.computerIp = computerIp; + } + + public String getUserDir() + { + return userDir; + } + + public void setUserDir(String userDir) + { + this.userDir = userDir; + } + + public String getOsName() + { + return osName; + } + + public void setOsName(String osName) + { + this.osName = osName; + } + + public String getOsArch() + { + return osArch; + } + + public void setOsArch(String osArch) + { + this.osArch = osArch; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java new file mode 100644 index 0000000..1320cde --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java @@ -0,0 +1,114 @@ +package com.ruoyi.framework.web.domain.server; + +/** + * 系统文件相关信息 + * + * @author ruoyi + */ +public class SysFile +{ + /** + * 盘符路径 + */ + private String dirName; + + /** + * 盘符类型 + */ + private String sysTypeName; + + /** + * 文件类型 + */ + private String typeName; + + /** + * 总大小 + */ + private String total; + + /** + * 剩余大小 + */ + private String free; + + /** + * 已经使用量 + */ + private String used; + + /** + * 资源的使用率 + */ + private double usage; + + public String getDirName() + { + return dirName; + } + + public void setDirName(String dirName) + { + this.dirName = dirName; + } + + public String getSysTypeName() + { + return sysTypeName; + } + + public void setSysTypeName(String sysTypeName) + { + this.sysTypeName = sysTypeName; + } + + public String getTypeName() + { + return typeName; + } + + public void setTypeName(String typeName) + { + this.typeName = typeName; + } + + public String getTotal() + { + return total; + } + + public void setTotal(String total) + { + this.total = total; + } + + public String getFree() + { + return free; + } + + public void setFree(String free) + { + this.free = free; + } + + public String getUsed() + { + return used; + } + + public void setUsed(String used) + { + this.used = used; + } + + public double getUsage() + { + return usage; + } + + public void setUsage(double usage) + { + this.usage = usage; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..86374db --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,165 @@ +package com.ruoyi.framework.web.exception; + +import jakarta.servlet.http.HttpServletRequest; + +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingPathVariableException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.exception.DemoModeException; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; + +/** + * 全局异常处理器 + * + * @author ruoyi + */ +@RestControllerAdvice +public class GlobalExceptionHandler +{ + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限校验异常 + */ + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); + } + + /** + * 密码错误异常 + */ + @ExceptionHandler(UserPasswordNotMatchException.class) + public AjaxResult handleUserPasswordNotMatchException(UserPasswordNotMatchException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',错误信息:'{}'", requestURI, e.getMessage()); + return AjaxResult.error(500, "账号/密码错误。"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return AjaxResult.error(e.getMessage()); + } + + /** + * 请求参数不支持 + */ + @ExceptionHandler(HttpMessageNotReadableException.class) + public AjaxResult handleHttpMessageNotReadableException(HttpMessageNotReadableException e, + HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',检查请求参数是否正确", requestURI); + return AjaxResult.error(e.getMessage()); + } + + + /** + * 业务异常 + */ + @ExceptionHandler(ServiceException.class) + public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) + { + log.error(e.getMessage()); + Integer code = e.getCode(); + return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); + } + + /** + * 请求路径中缺少必需的路径变量 + */ + @ExceptionHandler(MissingPathVariableException.class) + public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); + } + + /** + * 请求参数类型不匹配 + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求参数{}类型不匹配'{}',发生系统异常.",request.getQueryString(), requestURI); + return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getParameter(), e.getRequiredType().getName(), e.getValue())); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'%s',发生未知异常. e is %s", requestURI, e.getMessage(), e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public AjaxResult handleBindException(BindException e) + { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) + { + log.error(e.getMessage()); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public AjaxResult handleDemoModeException(DemoModeException e) + { + return AjaxResult.error("演示模式,不允许操作"); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/ApiUserDetailsServiceImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/ApiUserDetailsServiceImpl.java new file mode 100644 index 0000000..3c752d6 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/ApiUserDetailsServiceImpl.java @@ -0,0 +1,127 @@ +package com.ruoyi.framework.web.service; + +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.entity.UserData; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.UserStatus; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.system.service.ISysUserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +/** + * 用户验证处理 + */ +@Service +public class ApiUserDetailsServiceImpl implements UserDetailsService { + + private static final Logger log = LoggerFactory.getLogger(ApiUserDetailsServiceImpl.class); + private final TtUserService ttUserService; + private final ISysUserService userService; + private final SysPasswordService passwordService; + private final SysPermissionService permissionService; + + public ApiUserDetailsServiceImpl(TtUserService ttUserService, + ISysUserService userService, + SysPasswordService passwordService, + SysPermissionService permissionService) { + this.ttUserService = ttUserService; + this.userService = userService; + this.passwordService = passwordService; + this.permissionService = permissionService; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + + if (username.startsWith("api_")) { + username = username.substring(4); + TtUser user = new LambdaQueryChainWrapper<>(ttUserService.getBaseMapper()).eq(TtUser::getUserName, username).one(); + if (StringUtils.isNull(user)) { + log.info("登录用户:{} 不存在.", username); + throw new ServiceException(MessageUtils.message("user.not.exists")); + } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException(MessageUtils.message("user.password.delete")); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException(MessageUtils.message("user.blocked")); + } + // 获取上级用户 + TtUser ParentUser = new LambdaQueryChainWrapper<>(ttUserService.getBaseMapper()).eq(TtUser::getUserId, user.getParentId()).one(); + + UserData userData = UserData.builder().build(); + // 对象拷贝 + BeanUtils.copyBeanProp(userData, user); + + if (ObjectUtils.isEmpty(ParentUser)) { + userData.setParentInvitationCode(""); + } else { + userData.setParentInvitationCode(ParentUser.getInvitationCode()); + } + return createLoginUser(userData); + } else if (username.startsWith("promo_")) { + username = username.substring(6); // 去掉前缀 + TtUser user = new LambdaQueryChainWrapper<>(ttUserService.getBaseMapper()) + .eq(TtUser::getUserName, username) +// .eq(TtUser::getUserType, "01") // 01主播 + .one(); + if (StringUtils.isNull(user)) { + log.info("登录用户:{} 不存在.", username); + throw new ServiceException(MessageUtils.message("user.not.exists")); + } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException(MessageUtils.message("user.password.delete")); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException(MessageUtils.message("user.blocked")); + } + if (!UserType.ANCHOR.getCode().equals(user.getUserType()) + && !UserType.BUSINESS_USER.getCode().equals(user.getUserType())) { + log.info("登录用户:{} 身份错误.", username); + throw new ServiceException(MessageUtils.message("user.not.exists")); + } + UserData userData = UserData.builder().build(); + // 对象拷贝 + BeanUtils.copyBeanProp(userData, user); + + return createLoginUser(userData); + } else { + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)) { + log.info("登录用户:{} 不存在.", username); + throw new ServiceException(MessageUtils.message("user.not.exists")); + } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException(MessageUtils.message("user.password.delete")); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException(MessageUtils.message("user.blocked")); + } + + passwordService.validate(user); + + return createLoginUser(user); + } + } + + private UserDetails createLoginUser(UserData userData) { + return new LoginUser(userData.getUserId().longValue(), userData); + } + + private UserDetails createLoginUser(SysUser user) { + return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java new file mode 100644 index 0000000..07d259a --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java @@ -0,0 +1,159 @@ +package com.ruoyi.framework.web.service; + +import java.util.Set; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.context.PermissionContextHolder; + +/** + * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 + * + * @author ruoyi + */ +@Service("ss") +public class PermissionService +{ + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public boolean hasPermi(String permission) + { + if (StringUtils.isEmpty(permission)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permission); + return hasPermissions(loginUser.getPermissions(), permission); + } + + /** + * 验证用户是否不具备某权限,与 hasPermi逻辑相反 + * + * @param permission 权限字符串 + * @return 用户是否不具备某权限 + */ + public boolean lacksPermi(String permission) + { + return hasPermi(permission) != true; + } + + /** + * 验证用户是否具有以下任意一个权限 + * + * @param permissions 以 PERMISSION_DELIMETER 为分隔符的权限列表 + * @return 用户是否具有以下任意一个权限 + */ + public boolean hasAnyPermi(String permissions) + { + if (StringUtils.isEmpty(permissions)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permissions); + Set authorities = loginUser.getPermissions(); + for (String permission : permissions.split(Constants.PERMISSION_DELIMETER)) + { + if (permission != null && hasPermissions(authorities, permission)) + { + return true; + } + } + return false; + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色字符串 + * @return 用户是否具备某角色 + */ + public boolean hasRole(String role) + { + if (StringUtils.isEmpty(role)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (SysRole sysRole : loginUser.getUser().getRoles()) + { + String roleKey = sysRole.getRoleKey(); + if (Constants.SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) + { + return true; + } + } + return false; + } + + /** + * 验证用户是否不具备某角色,与 isRole逻辑相反。 + * + * @param role 角色名称 + * @return 用户是否不具备某角色 + */ + public boolean lacksRole(String role) + { + return hasRole(role) != true; + } + + /** + * 验证用户是否具有以下任意一个角色 + * + * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 + * @return 用户是否具有以下任意一个角色 + */ + public boolean hasAnyRoles(String roles) + { + if (StringUtils.isEmpty(roles)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (String role : roles.split(Constants.ROLE_DELIMETER)) + { + if (hasRole(role)) + { + return true; + } + } + return false; + } + + /** + * 判断是否包含权限 + * + * @param permissions 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + private boolean hasPermissions(Set permissions, String permission) + { + return permissions.contains(Constants.ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java new file mode 100644 index 0000000..542db17 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java @@ -0,0 +1,187 @@ +package com.ruoyi.framework.web.service; + +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.exception.user.*; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + +/** + * 登录校验方法 + * + * @author ruoyi + */ +@Slf4j +@Component +public class SysLoginService +{ + @Autowired + private TokenService tokenService; + + @Resource + private AuthenticationManager authenticationManager; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + /** + * 登录验证 + * + * @param username 用户名 + * @param password 密码 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public String login(String username, String password, String code, String uuid) + { + // 验证码校验 + validateCaptcha(username, code, uuid); + // 登录前置校验 + loginPreCheck(username, password); + // 生成登录用户信息 + LoginUser loginUser = createLoginUser(username, password); + // 记录登录信息 + recordLoginInfo(loginUser.getUserId()); + // 生成token + return tokenService.createToken(loginUser); + } + + /** + * 生成登录用户信息 + * + * @param username 用户名 + * @param password 密码 + * @return 结果 + */ + public LoginUser createLoginUser(String username, String password) { + // 用户验证 + Authentication authentication = null; + try { + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); + AuthenticationContextHolder.setContext(authenticationToken); + // 该方法会去调用ApiUserDetailsServiceImpl.loadUserByUsername + authentication = authenticationManager.authenticate(authenticationToken); + } catch (Exception e) { + log.error("验证错误", e); + if (e instanceof BadCredentialsException) { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } else { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); + throw new ServiceException(e.getMessage()); + } + } + finally { + AuthenticationContextHolder.clearContext(); + } + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); + return (LoginUser) authentication.getPrincipal(); + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + boolean captchaEnabled = configService.selectCaptchaEnabled(); + if (captchaEnabled) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + String captcha = redisCache.getCacheObject(verifyKey); + redisCache.deleteObject(verifyKey); + if (captcha == null) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); + throw new CaptchaException(); + } + } + } + + /** + * 登录前置校验 + * @param username 用户名 + * @param password 用户密码 + */ + public void loginPreCheck(String username, String password) + { + // 用户名或密码为空 错误 + if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null"))); + throw new UserNotExistsException(); + } + // 密码如果不在指定范围内 错误 + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + // 用户名不在指定范围内 错误 + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + // IP黑名单校验 + String blackStr = configService.selectConfigByKey("sys.login.blackIPList"); + if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked"))); + throw new BlackListException(); + } + } + + /** + * 记录登录信息 + * + * @param userId 用户ID + */ + public void recordLoginInfo(Long userId) + { + SysUser sysUser = new SysUser(); + sysUser.setUserId(userId); + sysUser.setLoginIp(IpUtils.getIpAddr()); + sysUser.setLoginDate(DateUtils.getNowDate()); + userService.updateUserProfile(sysUser); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java new file mode 100644 index 0000000..6ad91b0 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java @@ -0,0 +1,94 @@ +package com.ruoyi.framework.web.service; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; + +/** + * 登录密码方法 + * + * @author ruoyi + */ +@Component +public class SysPasswordService +{ + @Autowired + private RedisCache redisCache; + + @Value(value = "${user.password.maxRetryCount}") + private int maxRetryCount; + + @Value(value = "${user.password.lockTime}") + private int lockTime; + + /** + * 登录账户密码错误次数缓存键名 + * + * @param username 用户名 + * @return 缓存键key + */ + private String getCacheKey(String username) + { + return CacheConstants.PWD_ERR_CNT_KEY + username; + } + + public void validate(SysUser user) + { + Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); + String username = usernamePasswordAuthenticationToken.getName(); + String password = usernamePasswordAuthenticationToken.getCredentials().toString(); + + Integer retryCount = redisCache.getCacheObject(getCacheKey(username)); + + if (retryCount == null) + { + retryCount = 0; + } + + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, + MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime))); + throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime); + } + + if (!matches(user, password)) + { + retryCount = retryCount + 1; + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, + MessageUtils.message("user.password.retry.limit.count", retryCount))); + redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); + throw new UserPasswordNotMatchException(); + } + else + { + clearLoginRecordCache(username); + } + } + + public boolean matches(SysUser user, String rawPassword) + { + return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); + } + + public void clearLoginRecordCache(String loginName) + { + if (redisCache.hasKey(getCacheKey(loginName))) + { + redisCache.deleteObject(getCacheKey(loginName)); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java new file mode 100644 index 0000000..d1fb4ed --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java @@ -0,0 +1,83 @@ +package com.ruoyi.framework.web.service; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.system.service.ISysMenuService; +import com.ruoyi.system.service.ISysRoleService; + +/** + * 用户权限处理 + * + * @author ruoyi + */ +@Component +public class SysPermissionService +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param user 用户信息 + * @return 角色权限信息 + */ + public Set getRolePermission(SysUser user) + { + Set roles = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + roles.add("admin"); + } + else + { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param user 用户信息 + * @return 菜单权限信息 + */ + public Set getMenuPermission(SysUser user) + { + Set perms = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + perms.add("*:*:*"); + } + else + { + List roles = user.getRoles(); + if (!CollectionUtils.isEmpty(roles)) + { + // 多角色设置permissions属性,以便数据权限匹配权限 + for (SysRole role : roles) + { + Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); + role.setPermissions(rolePerms); + perms.addAll(rolePerms); + } + } + else + { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + } + return perms; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java new file mode 100644 index 0000000..f2afe31 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java @@ -0,0 +1,115 @@ +package com.ruoyi.framework.web.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.RegisterBody; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.user.CaptchaException; +import com.ruoyi.common.exception.user.CaptchaExpireException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 注册校验方法 + * + * @author ruoyi + */ +@Component +public class SysRegisterService +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + @Autowired + private RedisCache redisCache; + + /** + * 注册 + */ + public String register(RegisterBody registerBody) + { + String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + + // 验证码开关 + boolean captchaEnabled = configService.selectCaptchaEnabled(); + if (captchaEnabled) + { + validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); + } + + if (StringUtils.isEmpty(username)) + { + msg = "用户名不能为空"; + } + else if (StringUtils.isEmpty(password)) + { + msg = "用户密码不能为空"; + } + else if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + msg = "账户长度必须在2到20个字符之间"; + } + else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + msg = "密码长度必须在5到20个字符之间"; + } + else if (!userService.checkUserNameUnique(sysUser)) + { + msg = "保存用户'" + username + "'失败,注册账号已存在"; + } + else + { + sysUser.setNickName(username); + sysUser.setPassword(SecurityUtils.encryptPassword(password)); + boolean regFlag = userService.registerUser(sysUser); + if (!regFlag) + { + msg = "注册失败,请联系系统管理人员"; + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"))); + } + } + return msg; + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + String captcha = redisCache.getCacheObject(verifyKey); + redisCache.deleteObject(verifyKey); + if (captcha == null) + { + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) + { + throw new CaptchaException(); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java new file mode 100644 index 0000000..a03ebad --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java @@ -0,0 +1,231 @@ +package com.ruoyi.framework.web.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import eu.bitwalker.useragentutils.UserAgent; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +/** + * token验证处理 + * + * @author ruoyi + */ +@Component +public class TokenService +{ + private static final Logger log = LoggerFactory.getLogger(TokenService.class); + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + // 令牌秘钥 + @Value("${token.secret}") + private String secret; + + // 令牌有效期(默认30分钟) + @Value("${token.expireTime}") + private int expireTime; + + protected static final long MILLIS_SECOND = 1000; + + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; + + @Autowired + private RedisCache redisCache; + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = getToken(request); + if (StringUtils.isNotEmpty(token)) + { + try + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + LoginUser user = redisCache.getCacheObject(userKey); + return user; + } + catch (Exception e) + { + log.error("获取用户信息异常'{}'", e.getMessage()); + } + } + return null; + } + + /** + * 设置用户身份信息 + */ + public void setLoginUser(LoginUser loginUser) + { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) + { + refreshToken(loginUser); + } + } + + /** + * 删除用户身份信息 + */ + public void delLoginUser(String token) + { + if (StringUtils.isNotEmpty(token)) + { + String userKey = getTokenKey(token); + redisCache.deleteObject(userKey); + } + } + + /** + * 创建令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + public String createToken(LoginUser loginUser) + { + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + setUserAgent(loginUser); + refreshToken(loginUser); + + Map claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_KEY, token); + return createToken(claims); + } + + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param loginUser + * @return 令牌 + */ + public void verifyToken(LoginUser loginUser) + { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) + { + refreshToken(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken(LoginUser loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + public void setUserAgent(LoginUser loginUser) + { + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = IpUtils.getIpAddr(); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + loginUser.setBrowser(userAgent.getBrowser().getName()); + loginUser.setOs(userAgent.getOperatingSystem().getName()); + } + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createToken(Map claims) + { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + private Claims parseToken(String token) + { + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token) + .getBody(); + } + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public String getUsernameFromToken(String token) + { + Claims claims = parseToken(token); + return claims.getSubject(); + } + + /** + * 获取请求token + * + * @param request + * @return token + */ + private String getToken(HttpServletRequest request) + { + String token = request.getHeader(header); + if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) + { + token = token.replace(Constants.TOKEN_PREFIX, ""); + } + return token; + } + + private String getTokenKey(String uuid) + { + return CacheConstants.LOGIN_TOKEN_KEY + uuid; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java new file mode 100644 index 0000000..5dcdf90 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java @@ -0,0 +1,66 @@ +package com.ruoyi.framework.web.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.UserStatus; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysUserService; + +/** + * 用户验证处理 + * + * @author ruoyi + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService +{ + private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); + + @Autowired + private ISysUserService userService; + + @Autowired + private SysPasswordService passwordService; + + @Autowired + private SysPermissionService permissionService; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException + { + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)) + { + log.info("登录用户:{} 不存在.", username); + throw new ServiceException(MessageUtils.message("user.not.exists")); + } + else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) + { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException(MessageUtils.message("user.password.delete")); + } + else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) + { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException(MessageUtils.message("user.blocked")); + } + + passwordService.validate(user); + + return createLoginUser(user); + } + + public UserDetails createLoginUser(SysUser user) + { + return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketServer.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketServer.java new file mode 100644 index 0000000..543e125 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketServer.java @@ -0,0 +1,131 @@ +package com.ruoyi.framework.websocket; + +import com.alibaba.fastjson2.JSON; +import com.ruoyi.framework.websocket.pojo.ResultData; +import com.ruoyi.framework.websocket.pojo.SessionData; +import com.ruoyi.framework.websocket.util.SemaphoreUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import jakarta.websocket.*; +import jakarta.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Semaphore; +import java.util.stream.Collectors; + +// @Component +// @ServerEndpoint("/api/websocket/skins") +public class WebSocketServer { + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class); + + public static int socketMaxOnlineCount = 100; + + private static Semaphore socketSemaphore = new Semaphore(socketMaxOnlineCount); + + @OnOpen + public void onOpen(Session session) throws Exception { + SessionData sessionData = new SessionData(); + boolean semaphoreFlag = false; + // 尝试获取信号量 + semaphoreFlag = SemaphoreUtils.tryAcquire(socketSemaphore); + if (!semaphoreFlag) { + // 未获取到信号量 + LOGGER.error("\n 当前在线人数超过限制数- {}", socketMaxOnlineCount); + try { + ResultData resultData = new ResultData<>(); + resultData.setCode(500); + resultData.setTypeName("WebSocket异常提示"); + resultData.setData("当前在线人数超过限制数:" + socketMaxOnlineCount); + session.getBasicRemote().sendText(JSON.toJSONString(resultData)); + } catch (IOException e) { + LOGGER.error("\n[发送消息异常]", e); + } + session.close(); + } else { + // 添加用户 + sessionData.setSession(session); + WebSocketUsers.put(session.getId(), sessionData); + LOGGER.info("\n 建立连接 - {}", session); + LOGGER.info("\n sessionId - {}", session.getId()); + LOGGER.info("\n 当前人数 - {}", WebSocketUsers.getUsers().size()); + try { + ResultData resultData = new ResultData<>(); + resultData.setCode(0); + resultData.setTypeName("WebSocket连接成功"); + resultData.setData("连接成功"); + session.getBasicRemote().sendText(JSON.toJSONString(resultData)); + } catch (IOException e) { + LOGGER.error("\n[发送消息异常]", e); + } + } + } + + @OnClose + public void onClose(Session session) { + LOGGER.info("\n 关闭连接 - {}", session); + // 移除用户 + WebSocketUsers.remove(session.getId()); + // 获取到信号量则需释放 + SemaphoreUtils.release(socketSemaphore); + } + + @OnError + public void onError(Session session, Throwable exception) throws Exception { + if (session.isOpen()) { + // 关闭连接 + session.close(); + } + String sessionId = session.getId(); + LOGGER.info("\n 连接异常 - {}", sessionId); + LOGGER.info("\n 异常信息 - {}", exception); + // 移出用户 + WebSocketUsers.remove(sessionId); + // 获取到信号量则需释放 + SemaphoreUtils.release(socketSemaphore); + } + + @OnMessage + public void onMessage(String message, Session session) { + if (message.startsWith("login_userId_")) { + Integer userId = Integer.valueOf(message.replace("login_userId_", "")); + Map users = WebSocketUsers.getUsers().entrySet().stream() + .filter(entry -> Objects.equals(entry.getValue().getUserId(), userId)) + .peek(entry -> { + entry.getValue().setUserId(0); + LOGGER.info("用户{}重新登录!", userId); + }) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + SessionData sessionData = new SessionData(); + sessionData.setUserId(userId); + sessionData.setSession(session); + WebSocketUsers.put(session.getId(), sessionData); + LOGGER.info("用户{}登录成功!", userId); + } + if (message.startsWith("logout_userId_")) { + Integer userId = Integer.valueOf(message.replace("logout_userId_", "")); + // 遍历所有用户集 + Map users = WebSocketUsers.getUsers().entrySet().stream() + .filter(entry -> Objects.equals(entry.getValue().getUserId(), userId)) + .peek(entry -> { + entry.getValue().setUserId(0); + LOGGER.info("用户{}退出登录!", userId); + }) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + if ("ping".equals(message)) { + try { + ResultData resultData = new ResultData<>(); + resultData.setCode(200); + resultData.setTypeName("心跳检测"); + resultData.setData("pong"); + session.getBasicRemote().sendText(JSON.toJSONString(resultData)); + } catch (IOException e) { + LOGGER.error("\n[发送消息异常]", e); + } + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketUsers.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketUsers.java new file mode 100644 index 0000000..989fba7 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/WebSocketUsers.java @@ -0,0 +1,96 @@ +package com.ruoyi.framework.websocket; + +import com.ruoyi.framework.websocket.pojo.SessionData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class WebSocketUsers { + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUsers.class); + + private static Map USERS = new ConcurrentHashMap(); + + public static void put(String key, SessionData sessionData) { + USERS.put(key, sessionData); + } + + public static boolean remove(SessionData sessionData) { + String key = null; + boolean flag = USERS.containsValue(sessionData); + if (flag) { + Set> entries = USERS.entrySet(); + for (Map.Entry entry : entries) { + SessionData value = entry.getValue(); + if (value.getSession().equals(sessionData.getSession())) { + key = entry.getKey(); + break; + } + } + } else { + return true; + } + return remove(key); + } + + public static boolean remove(String key) { + LOGGER.info("\n 正在移出用户 - {}", key); + SessionData remove = USERS.remove(key); + if (remove.getSession() != null) { + boolean containsValue = USERS.containsValue(remove); + LOGGER.info("\n 移出结果 - {}", containsValue ? "失败" : "成功"); + LOGGER.info("\n 当前人数 - {}", WebSocketUsers.getUsers().size()); + return containsValue; + } else { + return true; + } + } + + public static Map getUsers() { + return USERS; + } + + public static void sendMessageToUsersByText(String message) { + Collection values = USERS.values(); + for (SessionData value : values) { + try { + value.getSession().getBasicRemote().sendText(message); + } catch (IOException e) { + //LOGGER.error("\n[发送消息异常]", e); + LOGGER.error("\n[发送消息异常]"); + } + } + } + + public static void sendMessageToUserByText(Integer userId, String message) { + Collection values = USERS.values(); + if (userId == 0) { + for (SessionData value : values) { + if (value.getSession() != null && Objects.equals(value.getUserId(), userId)) { + try { + value.getSession().getBasicRemote().sendText(message); + } catch (IOException e) { + LOGGER.error("\n[发送消息异常]", e); + } + } + } + } else { + for (SessionData value : values) { + if (value.getSession() != null && Objects.equals(value.getUserId(), userId)) { + try { + value.getSession().getBasicRemote().sendText(message); + return; + } catch (IOException e) { + LOGGER.error("\n[发送消息异常]", e); + return; + } + } + } + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/config/WebSocketConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/config/WebSocketConfig.java new file mode 100644 index 0000000..7074a79 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/config/WebSocketConfig.java @@ -0,0 +1,13 @@ +package com.ruoyi.framework.websocket.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +// @Configuration +public class WebSocketConfig { + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/pojo/ResultData.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/pojo/ResultData.java new file mode 100644 index 0000000..117ae94 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/pojo/ResultData.java @@ -0,0 +1,17 @@ +package com.ruoyi.framework.websocket.pojo; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ResultData implements Serializable { + + private static final long serialVersionUID = 1L; + + private int code; + + private String typeName; + + private T data; +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/pojo/SessionData.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/pojo/SessionData.java new file mode 100644 index 0000000..3185919 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/pojo/SessionData.java @@ -0,0 +1,13 @@ +package com.ruoyi.framework.websocket.pojo; + +import lombok.Data; + +import jakarta.websocket.Session; + +@Data +public class SessionData { + + private Integer userId = 0; + private Session session; + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/util/SemaphoreUtils.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/util/SemaphoreUtils.java new file mode 100644 index 0000000..0e42189 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/websocket/util/SemaphoreUtils.java @@ -0,0 +1,31 @@ +package com.ruoyi.framework.websocket.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.Semaphore; + +public class SemaphoreUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(SemaphoreUtils.class); + + public static boolean tryAcquire(Semaphore semaphore) { + boolean flag = false; + + try { + flag = semaphore.tryAcquire(); + } catch (Exception e) { + LOGGER.error("获取信号量异常", e); + } + + return flag; + } + + public static void release(Semaphore semaphore) { + + try { + semaphore.release(); + } catch (Exception e) { + LOGGER.error("释放信号量异常", e); + } + } +} diff --git a/ruoyi-generator/pom.xml b/ruoyi-generator/pom.xml new file mode 100644 index 0000000..7e8e067 --- /dev/null +++ b/ruoyi-generator/pom.xml @@ -0,0 +1,46 @@ + + + + ruoyi + com.ruoyi + 4.8.2 + + 4.0.0 + + ruoyi-generator + + + generator代码生成 + + + + + + + org.apache.velocity + velocity-engine-core + + + + + commons-collections + commons-collections + + + + + com.ruoyi + ruoyi-common + + + + + com.alibaba + druid-spring-boot-4-starter + + + + + \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java new file mode 100644 index 0000000..cc4cd14 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java @@ -0,0 +1,73 @@ +package com.ruoyi.generator.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * 读取代码生成相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "gen") +@PropertySource(value = { "classpath:generator.yml" }) +public class GenConfig +{ + /** 作者 */ + public static String author; + + /** 生成包路径 */ + public static String packageName; + + /** 自动去除表前缀,默认是false */ + public static boolean autoRemovePre; + + /** 表前缀(类名不会包含表前缀) */ + public static String tablePrefix; + + public static String getAuthor() + { + return author; + } + + @Value("${author}") + public void setAuthor(String author) + { + GenConfig.author = author; + } + + public static String getPackageName() + { + return packageName; + } + + @Value("${packageName}") + public void setPackageName(String packageName) + { + GenConfig.packageName = packageName; + } + + public static boolean getAutoRemovePre() + { + return autoRemovePre; + } + + @Value("${autoRemovePre}") + public void setAutoRemovePre(boolean autoRemovePre) + { + GenConfig.autoRemovePre = autoRemovePre; + } + + public static String getTablePrefix() + { + return tablePrefix; + } + + @Value("${tablePrefix}") + public void setTablePrefix(String tablePrefix) + { + GenConfig.tablePrefix = tablePrefix; + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java new file mode 100644 index 0000000..4ec1333 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java @@ -0,0 +1,214 @@ +package com.ruoyi.generator.controller; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.service.IGenTableColumnService; +import com.ruoyi.generator.service.IGenTableService; + +/** + * 代码生成 操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/tool/gen") +public class GenController extends BaseController +{ + @Autowired + private IGenTableService genTableService; + + @Autowired + private IGenTableColumnService genTableColumnService; + + /** + * 查询代码生成列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/list") + public TableDataInfo genList(GenTable genTable) + { + startPage(); + List list = genTableService.selectGenTableList(genTable); + return getDataTable(list); + } + + /** + * 修改代码生成业务 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:query')") + @GetMapping(value = "/{tableId}") + public AjaxResult getInfo(@PathVariable Long tableId) + { + GenTable table = genTableService.selectGenTableById(tableId); + List tables = genTableService.selectGenTableAll(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + Map map = new HashMap(); + map.put("info", table); + map.put("rows", list); + map.put("tables", tables); + return success(map); + } + + /** + * 查询数据库列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/db/list") + public TableDataInfo dataList(GenTable genTable) + { + startPage(); + List list = genTableService.selectDbTableList(genTable); + return getDataTable(list); + } + + /** + * 查询数据表字段列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping(value = "/column/{tableId}") + public TableDataInfo columnList(Long tableId) + { + TableDataInfo dataInfo = new TableDataInfo(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + dataInfo.setRows(list); + dataInfo.setTotal(list.size()); + return dataInfo; + } + + /** + * 导入表结构(保存) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:import')") + @Log(title = "代码生成", businessType = BusinessType.IMPORT) + @PostMapping("/importTable") + public AjaxResult importTableSave(String tables) + { + String[] tableNames = Convert.toStrArray(tables); + // 查询表信息 + List tableList = genTableService.selectDbTableListByNames(tableNames); + genTableService.importGenTable(tableList); + return success(); + } + + /** + * 修改保存代码生成业务 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult editSave(@Validated @RequestBody GenTable genTable) + { + genTableService.validateEdit(genTable); + genTableService.updateGenTable(genTable); + return success(); + } + + /** + * 删除代码生成 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:remove')") + @Log(title = "代码生成", businessType = BusinessType.DELETE) + @DeleteMapping("/{tableIds}") + public AjaxResult remove(@PathVariable Long[] tableIds) + { + genTableService.deleteGenTableByIds(tableIds); + return success(); + } + + /** + * 预览代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:preview')") + @GetMapping("/preview/{tableId}") + public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException + { + Map dataMap = genTableService.previewCode(tableId); + return success(dataMap); + } + + /** + * 生成代码(下载方式) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/download/{tableName}") + public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException + { + byte[] data = genTableService.downloadCode(tableName); + genCode(response, data); + } + + /** + * 生成代码(自定义路径) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableName}") + public AjaxResult genCode(@PathVariable("tableName") String tableName) + { + genTableService.generatorCode(tableName); + return success(); + } + + /** + * 同步数据库 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableName}") + public AjaxResult synchDb(@PathVariable("tableName") String tableName) + { + genTableService.synchDb(tableName); + return success(); + } + + /** + * 批量生成代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/batchGenCode") + public void batchGenCode(HttpServletResponse response, String tables) throws IOException + { + String[] tableNames = Convert.toStrArray(tables); + byte[] data = genTableService.downloadCode(tableNames); + genCode(response, data); + } + + /** + * 生成zip文件 + */ + private void genCode(HttpServletResponse response, byte[] data) throws IOException + { + response.reset(); + response.addHeader("Access-Control-Allow-Origin", "*"); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); + response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\""); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IOUtils.write(data, response.getOutputStream()); + } +} \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java new file mode 100644 index 0000000..53279dc --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java @@ -0,0 +1,372 @@ +package com.ruoyi.generator.domain; + +import java.util.List; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; + +/** + * 业务表 gen_table + * + * @author ruoyi + */ +public class GenTable extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 编号 */ + private Long tableId; + + /** 表名称 */ + @NotBlank(message = "表名称不能为空") + private String tableName; + + /** 表描述 */ + @NotBlank(message = "表描述不能为空") + private String tableComment; + + /** 关联父表的表名 */ + private String subTableName; + + /** 本表关联父表的外键名 */ + private String subTableFkName; + + /** 实体类名称(首字母大写) */ + @NotBlank(message = "实体类名称不能为空") + private String className; + + /** 使用的模板(crud单表操作 tree树表操作 sub主子表操作) */ + private String tplCategory; + + /** 生成包路径 */ + @NotBlank(message = "生成包路径不能为空") + private String packageName; + + /** 生成模块名 */ + @NotBlank(message = "生成模块名不能为空") + private String moduleName; + + /** 生成业务名 */ + @NotBlank(message = "生成业务名不能为空") + private String businessName; + + /** 生成功能名 */ + @NotBlank(message = "生成功能名不能为空") + private String functionName; + + /** 生成作者 */ + @NotBlank(message = "作者不能为空") + private String functionAuthor; + + /** 生成代码方式(0zip压缩包 1自定义路径) */ + private String genType; + + /** 生成路径(不填默认项目路径) */ + private String genPath; + + /** 主键信息 */ + private GenTableColumn pkColumn; + + /** 子表信息 */ + private GenTable subTable; + + /** 表列信息 */ + @Valid + private List columns; + + /** 其它生成选项 */ + private String options; + + /** 树编码字段 */ + private String treeCode; + + /** 树父编码字段 */ + private String treeParentCode; + + /** 树名称字段 */ + private String treeName; + + /** 上级菜单ID字段 */ + private String parentMenuId; + + /** 上级菜单名称字段 */ + private String parentMenuName; + + public Long getTableId() + { + return tableId; + } + + public void setTableId(Long tableId) + { + this.tableId = tableId; + } + + public String getTableName() + { + return tableName; + } + + public void setTableName(String tableName) + { + this.tableName = tableName; + } + + public String getTableComment() + { + return tableComment; + } + + public void setTableComment(String tableComment) + { + this.tableComment = tableComment; + } + + public String getSubTableName() + { + return subTableName; + } + + public void setSubTableName(String subTableName) + { + this.subTableName = subTableName; + } + + public String getSubTableFkName() + { + return subTableFkName; + } + + public void setSubTableFkName(String subTableFkName) + { + this.subTableFkName = subTableFkName; + } + + public String getClassName() + { + return className; + } + + public void setClassName(String className) + { + this.className = className; + } + + public String getTplCategory() + { + return tplCategory; + } + + public void setTplCategory(String tplCategory) + { + this.tplCategory = tplCategory; + } + + public String getPackageName() + { + return packageName; + } + + public void setPackageName(String packageName) + { + this.packageName = packageName; + } + + public String getModuleName() + { + return moduleName; + } + + public void setModuleName(String moduleName) + { + this.moduleName = moduleName; + } + + public String getBusinessName() + { + return businessName; + } + + public void setBusinessName(String businessName) + { + this.businessName = businessName; + } + + public String getFunctionName() + { + return functionName; + } + + public void setFunctionName(String functionName) + { + this.functionName = functionName; + } + + public String getFunctionAuthor() + { + return functionAuthor; + } + + public void setFunctionAuthor(String functionAuthor) + { + this.functionAuthor = functionAuthor; + } + + public String getGenType() + { + return genType; + } + + public void setGenType(String genType) + { + this.genType = genType; + } + + public String getGenPath() + { + return genPath; + } + + public void setGenPath(String genPath) + { + this.genPath = genPath; + } + + public GenTableColumn getPkColumn() + { + return pkColumn; + } + + public void setPkColumn(GenTableColumn pkColumn) + { + this.pkColumn = pkColumn; + } + + public GenTable getSubTable() + { + return subTable; + } + + public void setSubTable(GenTable subTable) + { + this.subTable = subTable; + } + + public List getColumns() + { + return columns; + } + + public void setColumns(List columns) + { + this.columns = columns; + } + + public String getOptions() + { + return options; + } + + public void setOptions(String options) + { + this.options = options; + } + + public String getTreeCode() + { + return treeCode; + } + + public void setTreeCode(String treeCode) + { + this.treeCode = treeCode; + } + + public String getTreeParentCode() + { + return treeParentCode; + } + + public void setTreeParentCode(String treeParentCode) + { + this.treeParentCode = treeParentCode; + } + + public String getTreeName() + { + return treeName; + } + + public void setTreeName(String treeName) + { + this.treeName = treeName; + } + + public String getParentMenuId() + { + return parentMenuId; + } + + public void setParentMenuId(String parentMenuId) + { + this.parentMenuId = parentMenuId; + } + + public String getParentMenuName() + { + return parentMenuName; + } + + public void setParentMenuName(String parentMenuName) + { + this.parentMenuName = parentMenuName; + } + + public boolean isSub() + { + return isSub(this.tplCategory); + } + + public static boolean isSub(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); + } + + public boolean isTree() + { + return isTree(this.tplCategory); + } + + public static boolean isTree(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); + } + + public boolean isCrud() + { + return isCrud(this.tplCategory); + } + + public static boolean isCrud(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); + } + + public boolean isSuperColumn(String javaField) + { + return isSuperColumn(this.tplCategory, javaField); + } + + public static boolean isSuperColumn(String tplCategory, String javaField) + { + if (isTree(tplCategory)) + { + return StringUtils.equalsAnyIgnoreCase(javaField, + ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY)); + } + return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); + } +} \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java new file mode 100644 index 0000000..3dc6d62 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java @@ -0,0 +1,373 @@ +package com.ruoyi.generator.domain; + +import jakarta.validation.constraints.NotBlank; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; + +/** + * 代码生成业务字段表 gen_table_column + * + * @author ruoyi + */ +public class GenTableColumn extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 编号 */ + private Long columnId; + + /** 归属表编号 */ + private Long tableId; + + /** 列名称 */ + private String columnName; + + /** 列描述 */ + private String columnComment; + + /** 列类型 */ + private String columnType; + + /** JAVA类型 */ + private String javaType; + + /** JAVA字段名 */ + @NotBlank(message = "Java属性不能为空") + private String javaField; + + /** 是否主键(1是) */ + private String isPk; + + /** 是否自增(1是) */ + private String isIncrement; + + /** 是否必填(1是) */ + private String isRequired; + + /** 是否为插入字段(1是) */ + private String isInsert; + + /** 是否编辑字段(1是) */ + private String isEdit; + + /** 是否列表字段(1是) */ + private String isList; + + /** 是否查询字段(1是) */ + private String isQuery; + + /** 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */ + private String queryType; + + /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) */ + private String htmlType; + + /** 字典类型 */ + private String dictType; + + /** 排序 */ + private Integer sort; + + public void setColumnId(Long columnId) + { + this.columnId = columnId; + } + + public Long getColumnId() + { + return columnId; + } + + public void setTableId(Long tableId) + { + this.tableId = tableId; + } + + public Long getTableId() + { + return tableId; + } + + public void setColumnName(String columnName) + { + this.columnName = columnName; + } + + public String getColumnName() + { + return columnName; + } + + public void setColumnComment(String columnComment) + { + this.columnComment = columnComment; + } + + public String getColumnComment() + { + return columnComment; + } + + public void setColumnType(String columnType) + { + this.columnType = columnType; + } + + public String getColumnType() + { + return columnType; + } + + public void setJavaType(String javaType) + { + this.javaType = javaType; + } + + public String getJavaType() + { + return javaType; + } + + public void setJavaField(String javaField) + { + this.javaField = javaField; + } + + public String getJavaField() + { + return javaField; + } + + public String getCapJavaField() + { + return StringUtils.capitalize(javaField); + } + + public void setIsPk(String isPk) + { + this.isPk = isPk; + } + + public String getIsPk() + { + return isPk; + } + + public boolean isPk() + { + return isPk(this.isPk); + } + + public boolean isPk(String isPk) + { + return isPk != null && StringUtils.equals("1", isPk); + } + + public String getIsIncrement() + { + return isIncrement; + } + + public void setIsIncrement(String isIncrement) + { + this.isIncrement = isIncrement; + } + + public boolean isIncrement() + { + return isIncrement(this.isIncrement); + } + + public boolean isIncrement(String isIncrement) + { + return isIncrement != null && StringUtils.equals("1", isIncrement); + } + + public void setIsRequired(String isRequired) + { + this.isRequired = isRequired; + } + + public String getIsRequired() + { + return isRequired; + } + + public boolean isRequired() + { + return isRequired(this.isRequired); + } + + public boolean isRequired(String isRequired) + { + return isRequired != null && StringUtils.equals("1", isRequired); + } + + public void setIsInsert(String isInsert) + { + this.isInsert = isInsert; + } + + public String getIsInsert() + { + return isInsert; + } + + public boolean isInsert() + { + return isInsert(this.isInsert); + } + + public boolean isInsert(String isInsert) + { + return isInsert != null && StringUtils.equals("1", isInsert); + } + + public void setIsEdit(String isEdit) + { + this.isEdit = isEdit; + } + + public String getIsEdit() + { + return isEdit; + } + + public boolean isEdit() + { + return isInsert(this.isEdit); + } + + public boolean isEdit(String isEdit) + { + return isEdit != null && StringUtils.equals("1", isEdit); + } + + public void setIsList(String isList) + { + this.isList = isList; + } + + public String getIsList() + { + return isList; + } + + public boolean isList() + { + return isList(this.isList); + } + + public boolean isList(String isList) + { + return isList != null && StringUtils.equals("1", isList); + } + + public void setIsQuery(String isQuery) + { + this.isQuery = isQuery; + } + + public String getIsQuery() + { + return isQuery; + } + + public boolean isQuery() + { + return isQuery(this.isQuery); + } + + public boolean isQuery(String isQuery) + { + return isQuery != null && StringUtils.equals("1", isQuery); + } + + public void setQueryType(String queryType) + { + this.queryType = queryType; + } + + public String getQueryType() + { + return queryType; + } + + public String getHtmlType() + { + return htmlType; + } + + public void setHtmlType(String htmlType) + { + this.htmlType = htmlType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getDictType() + { + return dictType; + } + + public void setSort(Integer sort) + { + this.sort = sort; + } + + public Integer getSort() + { + return sort; + } + + public boolean isSuperColumn() + { + return isSuperColumn(this.javaField); + } + + public static boolean isSuperColumn(String javaField) + { + return StringUtils.equalsAnyIgnoreCase(javaField, + // BaseEntity + "createBy", "createTime", "updateBy", "updateTime", "remark", + // TreeEntity + "parentName", "parentId", "orderNum", "ancestors"); + } + + public boolean isUsableColumn() + { + return isUsableColumn(javaField); + } + + public static boolean isUsableColumn(String javaField) + { + // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 + return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); + } + + public String readConverterExp() + { + String remarks = StringUtils.substringBetween(this.columnComment, "(", ")"); + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotEmpty(remarks)) + { + for (String value : remarks.split(" ")) + { + if (StringUtils.isNotEmpty(value)) + { + Object startStr = value.subSequence(0, 1); + String endStr = value.substring(1); + sb.append("").append(startStr).append("=").append(endStr).append(","); + } + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } + else + { + return this.columnComment; + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java new file mode 100644 index 0000000..951e166 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java @@ -0,0 +1,60 @@ +package com.ruoyi.generator.mapper; + +import java.util.List; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 业务字段 数据层 + * + * @author ruoyi + */ +public interface GenTableColumnMapper +{ + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @return 列信息 + */ + public List selectDbTableColumnsByName(String tableName); + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段 + * + * @param genTableColumns 列数据 + * @return 结果 + */ + public int deleteGenTableColumns(List genTableColumns); + + /** + * 批量删除业务字段 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(Long[] ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java new file mode 100644 index 0000000..9b330df --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java @@ -0,0 +1,83 @@ +package com.ruoyi.generator.mapper; + +import java.util.List; +import com.ruoyi.generator.domain.GenTable; + +/** + * 业务 数据层 + * + * @author ruoyi + */ +public interface GenTableMapper +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + + /** + * 查询表ID业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 查询表名称业务信息 + * + * @param tableName 表名称 + * @return 业务信息 + */ + public GenTable selectGenTableByName(String tableName); + + /** + * 新增业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public int insertGenTable(GenTable genTable); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public int updateGenTable(GenTable genTable); + + /** + * 批量删除业务 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableByIds(Long[] ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java new file mode 100644 index 0000000..0679689 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java @@ -0,0 +1,68 @@ +package com.ruoyi.generator.service; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.mapper.GenTableColumnMapper; + +/** + * 业务字段 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableColumnServiceImpl implements IGenTableColumnService +{ + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + @Override + public List selectGenTableColumnListByTableId(Long tableId) + { + return genTableColumnMapper.selectGenTableColumnListByTableId(tableId); + } + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int insertGenTableColumn(GenTableColumn genTableColumn) + { + return genTableColumnMapper.insertGenTableColumn(genTableColumn); + } + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int updateGenTableColumn(GenTableColumn genTableColumn) + { + return genTableColumnMapper.updateGenTableColumn(genTableColumn); + } + + /** + * 删除业务字段对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteGenTableColumnByIds(String ids) + { + return genTableColumnMapper.deleteGenTableColumnByIds(Convert.toLongArray(ids)); + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java new file mode 100644 index 0000000..4889f81 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java @@ -0,0 +1,521 @@ +package com.ruoyi.generator.service; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.core.text.CharsetKit; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.mapper.GenTableColumnMapper; +import com.ruoyi.generator.mapper.GenTableMapper; +import com.ruoyi.generator.util.GenUtils; +import com.ruoyi.generator.util.VelocityInitializer; +import com.ruoyi.generator.util.VelocityUtils; + +/** + * 业务 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableServiceImpl implements IGenTableService +{ + private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class); + + @Autowired + private GenTableMapper genTableMapper; + + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + @Override + public GenTable selectGenTableById(Long id) + { + GenTable genTable = genTableMapper.selectGenTableById(id); + setTableFromOptions(genTable); + return genTable; + } + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + @Override + public List selectGenTableList(GenTable genTable) + { + return genTableMapper.selectGenTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + @Override + public List selectDbTableList(GenTable genTable) + { + return genTableMapper.selectDbTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + @Override + public List selectDbTableListByNames(String[] tableNames) + { + return genTableMapper.selectDbTableListByNames(tableNames); + } + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List selectGenTableAll() + { + return genTableMapper.selectGenTableAll(); + } + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + @Override + @Transactional + public void updateGenTable(GenTable genTable) + { + String options = JSON.toJSONString(genTable.getParams()); + genTable.setOptions(options); + int row = genTableMapper.updateGenTable(genTable); + if (row > 0) + { + for (GenTableColumn cenTableColumn : genTable.getColumns()) + { + genTableColumnMapper.updateGenTableColumn(cenTableColumn); + } + } + } + + /** + * 删除业务对象 + * + * @param tableIds 需要删除的数据ID + * @return 结果 + */ + @Override + @Transactional + public void deleteGenTableByIds(Long[] tableIds) + { + genTableMapper.deleteGenTableByIds(tableIds); + genTableColumnMapper.deleteGenTableColumnByIds(tableIds); + } + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + @Override + @Transactional + public void importGenTable(List tableList) + { + String operName = SecurityUtils.getUsername(); + try + { + for (GenTable table : tableList) + { + String tableName = table.getTableName(); + GenUtils.initTable(table, operName); + int row = genTableMapper.insertGenTable(table); + if (row > 0) + { + // 保存列信息 + List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + for (GenTableColumn column : genTableColumns) + { + GenUtils.initColumnField(column, table); + genTableColumnMapper.insertGenTableColumn(column); + } + } + } + } + catch (Exception e) + { + throw new ServiceException("导入失败:" + e.getMessage()); + } + } + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + @Override + public Map previewCode(Long tableId) + { + Map dataMap = new LinkedHashMap<>(); + // 查询表信息 + GenTable table = genTableMapper.selectGenTableById(tableId); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + dataMap.put(template, sw.toString()); + } + return dataMap; + } + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + @Override + public byte[] downloadCode(String tableName) + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + generatorCode(tableName, zip); + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + */ + @Override + public void generatorCode(String tableName) + { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) + { + if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try + { + String path = getGenPath(table, template); + FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8); + } + catch (IOException e) + { + throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); + } + } + } + } + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + @Override + @Transactional + public void synchDb(String tableName) + { + GenTable table = genTableMapper.selectGenTableByName(tableName); + List tableColumns = table.getColumns(); + Map tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity())); + + List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + if (StringUtils.isEmpty(dbTableColumns)) + { + throw new ServiceException("同步数据失败,原表结构不存在"); + } + List dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); + + dbTableColumns.forEach(column -> { + GenUtils.initColumnField(column, table); + if (tableColumnMap.containsKey(column.getColumnName())) + { + GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName()); + column.setColumnId(prevColumn.getColumnId()); + if (column.isList()) + { + // 如果是列表,继续保留查询方式/字典类型选项 + column.setDictType(prevColumn.getDictType()); + column.setQueryType(prevColumn.getQueryType()); + } + if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk() + && (column.isInsert() || column.isEdit()) + && ((column.isUsableColumn()) || (!column.isSuperColumn()))) + { + // 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项 + column.setIsRequired(prevColumn.getIsRequired()); + column.setHtmlType(prevColumn.getHtmlType()); + } + genTableColumnMapper.updateGenTableColumn(column); + } + else + { + genTableColumnMapper.insertGenTableColumn(column); + } + }); + + List delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList()); + if (StringUtils.isNotEmpty(delColumns)) + { + genTableColumnMapper.deleteGenTableColumns(delColumns); + } + } + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + @Override + public byte[] downloadCode(String[] tableNames) + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + for (String tableName : tableNames) + { + generatorCode(tableName, zip); + } + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 查询表信息并生成代码 + */ + private void generatorCode(String tableName, ZipOutputStream zip) + { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try + { + // 添加到zip + zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table))); + IOUtils.write(sw.toString(), zip, Constants.UTF8); + IOUtils.closeQuietly(sw); + zip.flush(); + zip.closeEntry(); + } + catch (IOException e) + { + log.error("渲染模板失败,表名:" + table.getTableName(), e); + } + } + } + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + @Override + public void validateEdit(GenTable genTable) + { + if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) + { + String options = JSON.toJSONString(genTable.getParams()); + JSONObject paramsObj = JSON.parseObject(options); + if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) + { + throw new ServiceException("树编码字段不能为空"); + } + else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) + { + throw new ServiceException("树父编码字段不能为空"); + } + else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) + { + throw new ServiceException("树名称字段不能为空"); + } + else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) + { + if (StringUtils.isEmpty(genTable.getSubTableName())) + { + throw new ServiceException("关联子表的表名不能为空"); + } + else if (StringUtils.isEmpty(genTable.getSubTableFkName())) + { + throw new ServiceException("子表关联的外键名不能为空"); + } + } + } + } + + /** + * 设置主键列信息 + * + * @param table 业务表信息 + */ + public void setPkColumn(GenTable table) + { + for (GenTableColumn column : table.getColumns()) + { + if (column.isPk()) + { + table.setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getPkColumn())) + { + table.setPkColumn(table.getColumns().get(0)); + } + if (GenConstants.TPL_SUB.equals(table.getTplCategory())) + { + for (GenTableColumn column : table.getSubTable().getColumns()) + { + if (column.isPk()) + { + table.getSubTable().setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getSubTable().getPkColumn())) + { + table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); + } + } + } + + /** + * 设置主子表信息 + * + * @param table 业务表信息 + */ + public void setSubTable(GenTable table) + { + String subTableName = table.getSubTableName(); + if (StringUtils.isNotEmpty(subTableName)) + { + table.setSubTable(genTableMapper.selectGenTableByName(subTableName)); + } + } + + /** + * 设置代码生成其他选项值 + * + * @param genTable 设置后的生成对象 + */ + public void setTableFromOptions(GenTable genTable) + { + JSONObject paramsObj = JSON.parseObject(genTable.getOptions()); + if (StringUtils.isNotNull(paramsObj)) + { + String treeCode = paramsObj.getString(GenConstants.TREE_CODE); + String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID); + String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME); + + genTable.setTreeCode(treeCode); + genTable.setTreeParentCode(treeParentCode); + genTable.setTreeName(treeName); + genTable.setParentMenuId(parentMenuId); + genTable.setParentMenuName(parentMenuName); + } + } + + /** + * 获取代码生成地址 + * + * @param table 业务表信息 + * @param template 模板文件路径 + * @return 生成地址 + */ + public static String getGenPath(GenTable table, String template) + { + String genPath = table.getGenPath(); + if (StringUtils.equals(genPath, "/")) + { + return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); + } + return genPath + File.separator + VelocityUtils.getFileName(template, table); + } +} \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java new file mode 100644 index 0000000..3037f70 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java @@ -0,0 +1,44 @@ +package com.ruoyi.generator.service; + +import java.util.List; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 业务字段 服务层 + * + * @author ruoyi + */ +public interface IGenTableColumnService +{ + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(String ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java new file mode 100644 index 0000000..9d53f95 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java @@ -0,0 +1,121 @@ +package com.ruoyi.generator.service; + +import java.util.List; +import java.util.Map; +import com.ruoyi.generator.domain.GenTable; + +/** + * 业务 服务层 + * + * @author ruoyi + */ +public interface IGenTableService +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public void updateGenTable(GenTable genTable); + + /** + * 删除业务信息 + * + * @param tableIds 需要删除的表数据ID + * @return 结果 + */ + public void deleteGenTableByIds(Long[] tableIds); + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + public void importGenTable(List tableList); + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + public Map previewCode(Long tableId); + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + public byte[] downloadCode(String tableName); + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + * @return 数据 + */ + public void generatorCode(String tableName); + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + public void synchDb(String tableName); + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + public byte[] downloadCode(String[] tableNames); + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + public void validateEdit(GenTable genTable); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java new file mode 100644 index 0000000..e7ebc20 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java @@ -0,0 +1,257 @@ +package com.ruoyi.generator.util; + +import java.util.Arrays; +import org.apache.commons.lang3.RegExUtils; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.config.GenConfig; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 代码生成器 工具类 + * + * @author ruoyi + */ +public class GenUtils +{ + /** + * 初始化表信息 + */ + public static void initTable(GenTable genTable, String operName) + { + genTable.setClassName(convertClassName(genTable.getTableName())); + genTable.setPackageName(GenConfig.getPackageName()); + genTable.setModuleName(getModuleName(GenConfig.getPackageName())); + genTable.setBusinessName(getBusinessName(genTable.getTableName())); + genTable.setFunctionName(replaceText(genTable.getTableComment())); + genTable.setFunctionAuthor(GenConfig.getAuthor()); + genTable.setCreateBy(operName); + } + + /** + * 初始化列属性字段 + */ + public static void initColumnField(GenTableColumn column, GenTable table) + { + String dataType = getDbType(column.getColumnType()); + String columnName = column.getColumnName(); + column.setTableId(table.getTableId()); + column.setCreateBy(table.getCreateBy()); + // 设置java字段名 + column.setJavaField(StringUtils.toCamelCase(columnName)); + // 设置默认类型 + column.setJavaType(GenConstants.TYPE_STRING); + column.setQueryType(GenConstants.QUERY_EQ); + + if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) + { + // 字符串长度超过500设置为文本域 + Integer columnLength = getColumnLength(column.getColumnType()); + String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; + column.setHtmlType(htmlType); + } + else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) + { + column.setJavaType(GenConstants.TYPE_DATE); + column.setHtmlType(GenConstants.HTML_DATETIME); + } + else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) + { + column.setHtmlType(GenConstants.HTML_INPUT); + + // 如果是浮点型 统一用BigDecimal + String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); + if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) + { + column.setJavaType(GenConstants.TYPE_BIGDECIMAL); + } + // 如果是整形 + else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) + { + column.setJavaType(GenConstants.TYPE_INTEGER); + } + // 长整形 + else + { + column.setJavaType(GenConstants.TYPE_LONG); + } + } + + // 插入字段(默认所有字段都需要插入) + column.setIsInsert(GenConstants.REQUIRE); + + // 编辑字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) + { + column.setIsEdit(GenConstants.REQUIRE); + } + // 列表字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) + { + column.setIsList(GenConstants.REQUIRE); + } + // 查询字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) + { + column.setIsQuery(GenConstants.REQUIRE); + } + + // 查询字段类型 + if (StringUtils.endsWithIgnoreCase(columnName, "name")) + { + column.setQueryType(GenConstants.QUERY_LIKE); + } + // 状态字段设置单选框 + if (StringUtils.endsWithIgnoreCase(columnName, "status")) + { + column.setHtmlType(GenConstants.HTML_RADIO); + } + // 类型&性别字段设置下拉框 + else if (StringUtils.endsWithIgnoreCase(columnName, "type") + || StringUtils.endsWithIgnoreCase(columnName, "sex")) + { + column.setHtmlType(GenConstants.HTML_SELECT); + } + // 图片字段设置图片上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "image")) + { + column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); + } + // 文件字段设置文件上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "file")) + { + column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); + } + // 内容字段设置富文本控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "content")) + { + column.setHtmlType(GenConstants.HTML_EDITOR); + } + } + + /** + * 校验数组是否包含指定值 + * + * @param arr 数组 + * @param targetValue 值 + * @return 是否包含 + */ + public static boolean arraysContains(String[] arr, String targetValue) + { + return Arrays.asList(arr).contains(targetValue); + } + + /** + * 获取模块名 + * + * @param packageName 包名 + * @return 模块名 + */ + public static String getModuleName(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + int nameLength = packageName.length(); + return StringUtils.substring(packageName, lastIndex + 1, nameLength); + } + + /** + * 获取业务名 + * + * @param tableName 表名 + * @return 业务名 + */ + public static String getBusinessName(String tableName) + { + int lastIndex = tableName.lastIndexOf("_"); + int nameLength = tableName.length(); + return StringUtils.substring(tableName, lastIndex + 1, nameLength); + } + + /** + * 表名转换成Java类名 + * + * @param tableName 表名称 + * @return 类名 + */ + public static String convertClassName(String tableName) + { + boolean autoRemovePre = GenConfig.getAutoRemovePre(); + String tablePrefix = GenConfig.getTablePrefix(); + if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) + { + String[] searchList = StringUtils.split(tablePrefix, ","); + tableName = replaceFirst(tableName, searchList); + } + return StringUtils.convertToCamelCase(tableName); + } + + /** + * 批量替换前缀 + * + * @param replacementm 替换值 + * @param searchList 替换列表 + * @return + */ + public static String replaceFirst(String replacementm, String[] searchList) + { + String text = replacementm; + for (String searchString : searchList) + { + if (replacementm.startsWith(searchString)) + { + text = replacementm.replaceFirst(searchString, ""); + break; + } + } + return text; + } + + /** + * 关键字替换 + * + * @param text 需要被替换的名字 + * @return 替换后的名字 + */ + public static String replaceText(String text) + { + return RegExUtils.replaceAll(text, "(?:表|若依)", ""); + } + + /** + * 获取数据库类型字段 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static String getDbType(String columnType) + { + if (StringUtils.indexOf(columnType, "(") > 0) + { + return StringUtils.substringBefore(columnType, "("); + } + else + { + return columnType; + } + } + + /** + * 获取字段长度 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static Integer getColumnLength(String columnType) + { + if (StringUtils.indexOf(columnType, "(") > 0) + { + String length = StringUtils.substringBetween(columnType, "(", ")"); + return Integer.valueOf(length); + } + else + { + return 0; + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java new file mode 100644 index 0000000..9f69403 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java @@ -0,0 +1,34 @@ +package com.ruoyi.generator.util; + +import java.util.Properties; +import org.apache.velocity.app.Velocity; +import com.ruoyi.common.constant.Constants; + +/** + * VelocityEngine工厂 + * + * @author ruoyi + */ +public class VelocityInitializer +{ + /** + * 初始化vm方法 + */ + public static void initVelocity() + { + Properties p = new Properties(); + try + { + // 加载classpath目录下的vm文件 + p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + // 定义字符集 + p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); + // 初始化Velocity引擎,指定配置Properties + Velocity.init(p); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java new file mode 100644 index 0000000..7ede02d --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java @@ -0,0 +1,402 @@ +package com.ruoyi.generator.util; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.velocity.VelocityContext; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 模板处理工具类 + * + * @author ruoyi + */ +public class VelocityUtils +{ + /** 项目空间路径 */ + private static final String PROJECT_PATH = "main/java"; + + /** mybatis空间路径 */ + private static final String MYBATIS_PATH = "main/resources/mapper"; + + /** 默认上级菜单,系统工具 */ + private static final String DEFAULT_PARENT_MENU_ID = "3"; + + /** + * 设置模板变量信息 + * + * @return 模板列表 + */ + public static VelocityContext prepareContext(GenTable genTable) + { + String moduleName = genTable.getModuleName(); + String businessName = genTable.getBusinessName(); + String packageName = genTable.getPackageName(); + String tplCategory = genTable.getTplCategory(); + String functionName = genTable.getFunctionName(); + + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("tplCategory", genTable.getTplCategory()); + velocityContext.put("tableName", genTable.getTableName()); + velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); + velocityContext.put("ClassName", genTable.getClassName()); + velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); + velocityContext.put("moduleName", genTable.getModuleName()); + velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); + velocityContext.put("businessName", genTable.getBusinessName()); + velocityContext.put("basePackage", getPackagePrefix(packageName)); + velocityContext.put("packageName", packageName); + velocityContext.put("author", genTable.getFunctionAuthor()); + velocityContext.put("datetime", DateUtils.getDate()); + velocityContext.put("pkColumn", genTable.getPkColumn()); + velocityContext.put("importList", getImportList(genTable)); + velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); + velocityContext.put("columns", genTable.getColumns()); + velocityContext.put("table", genTable); + velocityContext.put("dicts", getDicts(genTable)); + setMenuVelocityContext(velocityContext, genTable); + if (GenConstants.TPL_TREE.equals(tplCategory)) + { + setTreeVelocityContext(velocityContext, genTable); + } + if (GenConstants.TPL_SUB.equals(tplCategory)) + { + setSubVelocityContext(velocityContext, genTable); + } + return velocityContext; + } + + public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + + public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeCode = getTreecode(paramsObj); + String treeParentCode = getTreeParentCode(paramsObj); + String treeName = getTreeName(paramsObj); + + context.put("treeCode", treeCode); + context.put("treeParentCode", treeParentCode); + context.put("treeName", treeName); + context.put("expandColumn", getExpandColumn(genTable)); + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME)); + } + } + + public static void setSubVelocityContext(VelocityContext context, GenTable genTable) + { + GenTable subTable = genTable.getSubTable(); + String subTableName = genTable.getSubTableName(); + String subTableFkName = genTable.getSubTableFkName(); + String subClassName = genTable.getSubTable().getClassName(); + String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName); + + context.put("subTable", subTable); + context.put("subTableName", subTableName); + context.put("subTableFkName", subTableFkName); + context.put("subTableFkClassName", subTableFkClassName); + context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName)); + context.put("subClassName", subClassName); + context.put("subclassName", StringUtils.uncapitalize(subClassName)); + context.put("subImportList", getImportList(genTable.getSubTable())); + } + + /** + * 获取模板信息 + * + * @return 模板列表 + */ + public static List getTemplateList(String tplCategory) + { + List templates = new ArrayList(); + templates.add("vm/java/domain.java.vm"); + templates.add("vm/java/mapper.java.vm"); + templates.add("vm/java/service.java.vm"); + templates.add("vm/java/serviceImpl.java.vm"); + templates.add("vm/java/controller.java.vm"); + templates.add("vm/xml/mapper.xml.vm"); + templates.add("vm/sql/sql.vm"); + templates.add("vm/js/api.js.vm"); + if (GenConstants.TPL_CRUD.equals(tplCategory)) + { + templates.add("vm/vue/index.vue.vm"); + } + else if (GenConstants.TPL_TREE.equals(tplCategory)) + { + templates.add("vm/vue/index-tree.vue.vm"); + } + else if (GenConstants.TPL_SUB.equals(tplCategory)) + { + templates.add("vm/vue/index.vue.vm"); + templates.add("vm/java/sub-domain.java.vm"); + } + return templates; + } + + /** + * 获取文件名 + */ + public static String getFileName(String template, GenTable genTable) + { + // 文件名称 + String fileName = ""; + // 包路径 + String packageName = genTable.getPackageName(); + // 模块名 + String moduleName = genTable.getModuleName(); + // 大写类名 + String className = genTable.getClassName(); + // 业务名称 + String businessName = genTable.getBusinessName(); + + String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); + String mybatisPath = MYBATIS_PATH + "/" + moduleName; + String vuePath = "vue"; + + if (template.contains("domain.java.vm")) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); + } + if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); + } + else if (template.contains("mapper.java.vm")) + { + fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); + } + else if (template.contains("service.java.vm")) + { + fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); + } + else if (template.contains("serviceImpl.java.vm")) + { + fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); + } + else if (template.contains("controller.java.vm")) + { + fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); + } + else if (template.contains("mapper.xml.vm")) + { + fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); + } + else if (template.contains("sql.vm")) + { + fileName = businessName + "Menu.sql"; + } + else if (template.contains("api.js.vm")) + { + fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName); + } + else if (template.contains("index.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + else if (template.contains("index-tree.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + return fileName; + } + + /** + * 获取包前缀 + * + * @param packageName 包名称 + * @return 包前缀名称 + */ + public static String getPackagePrefix(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + return StringUtils.substring(packageName, 0, lastIndex); + } + + /** + * 根据列类型获取导入包 + * + * @param genTable 业务表对象 + * @return 返回需要导入的包列表 + */ + public static HashSet getImportList(GenTable genTable) + { + List columns = genTable.getColumns(); + GenTable subGenTable = genTable.getSubTable(); + HashSet importList = new HashSet(); + if (StringUtils.isNotNull(subGenTable)) + { + importList.add("java.util.List"); + } + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) + { + importList.add("java.util.Date"); + importList.add("com.fasterxml.jackson.annotation.JsonFormat"); + } + else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) + { + importList.add("java.math.BigDecimal"); + } + } + return importList; + } + + /** + * 根据列类型获取字典组 + * + * @param genTable 业务表对象 + * @return 返回字典组 + */ + public static String getDicts(GenTable genTable) + { + List columns = genTable.getColumns(); + Set dicts = new HashSet(); + addDicts(dicts, columns); + if (StringUtils.isNotNull(genTable.getSubTable())) + { + List subColumns = genTable.getSubTable().getColumns(); + addDicts(dicts, subColumns); + } + return StringUtils.join(dicts, ", "); + } + + /** + * 添加字典列表 + * + * @param dicts 字典列表 + * @param columns 列集合 + */ + public static void addDicts(Set dicts, List columns) + { + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( + column.getHtmlType(), + new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) + { + dicts.add("'" + column.getDictType() + "'"); + } + } + } + + /** + * 获取权限前缀 + * + * @param moduleName 模块名称 + * @param businessName 业务名称 + * @return 返回权限前缀 + */ + public static String getPermissionPrefix(String moduleName, String businessName) + { + return StringUtils.format("{}:{}", moduleName, businessName); + } + + /** + * 获取上级菜单ID字段 + * + * @param paramsObj 生成其他选项 + * @return 上级菜单ID字段 + */ + public static String getParentMenuId(JSONObject paramsObj) + { + if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) + && StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID))) + { + return paramsObj.getString(GenConstants.PARENT_MENU_ID); + } + return DEFAULT_PARENT_MENU_ID; + } + + /** + * 获取树编码 + * + * @param paramsObj 生成其他选项 + * @return 树编码 + */ + public static String getTreecode(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_CODE)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树父编码 + * + * @param paramsObj 生成其他选项 + * @return 树父编码 + */ + public static String getTreeParentCode(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树名称 + * + * @param paramsObj 生成其他选项 + * @return 树名称 + */ + public static String getTreeName(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME)); + } + return StringUtils.EMPTY; + } + + /** + * 获取需要在哪一列上面显示展开按钮 + * + * @param genTable 业务表对象 + * @return 展开按钮列序号 + */ + public static int getExpandColumn(GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + int num = 0; + for (GenTableColumn column : genTable.getColumns()) + { + if (column.isList()) + { + num++; + String columnName = column.getColumnName(); + if (columnName.equals(treeName)) + { + break; + } + } + } + return num; + } +} diff --git a/ruoyi-generator/src/main/resources/generator.yml b/ruoyi-generator/src/main/resources/generator.yml new file mode 100644 index 0000000..7eae68e --- /dev/null +++ b/ruoyi-generator/src/main/resources/generator.yml @@ -0,0 +1,10 @@ +# 代码生成 +gen: + # 作者 + author: ruoyi + # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + packageName: com.ruoyi.system + # 自动去除表前缀,默认是false + autoRemovePre: false + # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + tablePrefix: sys_ \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml new file mode 100644 index 0000000..66109de --- /dev/null +++ b/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select column_id, table_id, column_name, column_comment, column_type, java_type, java_field, is_pk, is_increment, is_required, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, create_by, create_time, update_by, update_time from gen_table_column + + + + + + + + insert into gen_table_column ( + table_id, + column_name, + column_comment, + column_type, + java_type, + java_field, + is_pk, + is_increment, + is_required, + is_insert, + is_edit, + is_list, + is_query, + query_type, + html_type, + dict_type, + sort, + create_by, + create_time + )values( + #{tableId}, + #{columnName}, + #{columnComment}, + #{columnType}, + #{javaType}, + #{javaField}, + #{isPk}, + #{isIncrement}, + #{isRequired}, + #{isInsert}, + #{isEdit}, + #{isList}, + #{isQuery}, + #{queryType}, + #{htmlType}, + #{dictType}, + #{sort}, + #{createBy}, + sysdate() + ) + + + + update gen_table_column + + column_comment = #{columnComment}, + java_type = #{javaType}, + java_field = #{javaField}, + is_insert = #{isInsert}, + is_edit = #{isEdit}, + is_list = #{isList}, + is_query = #{isQuery}, + is_required = #{isRequired}, + query_type = #{queryType}, + html_type = #{htmlType}, + dict_type = #{dictType}, + sort = #{sort}, + update_by = #{updateBy}, + update_time = sysdate() + + where column_id = #{columnId} + + + + delete from gen_table_column where table_id in + + #{tableId} + + + + + delete from gen_table_column where column_id in + + #{item.columnId} + + + + \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml new file mode 100644 index 0000000..b605e90 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table + + + + + + + + + + + + + + + + + + insert into gen_table ( + table_name, + table_comment, + class_name, + tpl_category, + package_name, + module_name, + business_name, + function_name, + function_author, + gen_type, + gen_path, + remark, + create_by, + create_time + )values( + #{tableName}, + #{tableComment}, + #{className}, + #{tplCategory}, + #{packageName}, + #{moduleName}, + #{businessName}, + #{functionName}, + #{functionAuthor}, + #{genType}, + #{genPath}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update gen_table + + table_name = #{tableName}, + table_comment = #{tableComment}, + sub_table_name = #{subTableName}, + sub_table_fk_name = #{subTableFkName}, + class_name = #{className}, + function_author = #{functionAuthor}, + gen_type = #{genType}, + gen_path = #{genPath}, + tpl_category = #{tplCategory}, + package_name = #{packageName}, + module_name = #{moduleName}, + business_name = #{businessName}, + function_name = #{functionName}, + options = #{options}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where table_id = #{tableId} + + + + delete from gen_table where table_id in + + #{tableId} + + + + \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/vm/java/controller.java.vm b/ruoyi-generator/src/main/resources/vm/java/controller.java.vm new file mode 100644 index 0000000..bf88988 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/controller.java.vm @@ -0,0 +1,115 @@ +package ${packageName}.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import com.ruoyi.common.utils.poi.ExcelUtil; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.page.TableDataInfo; +#elseif($table.tree) +#end + +/** + * ${functionName}Controller + * + * @author ${author} + * @date ${datetime} + */ +@RestController +@RequestMapping("/${moduleName}/${businessName}") +public class ${ClassName}Controller extends BaseController +{ + @Autowired + private I${ClassName}Service ${className}Service; + + /** + * 查询${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") + @GetMapping("/list") +#if($table.crud || $table.sub) + public TableDataInfo list(${ClassName} ${className}) + { + startPage(); + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return getDataTable(list); + } +#elseif($table.tree) + public AjaxResult list(${ClassName} ${className}) + { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return success(list); + } +#end + + /** + * 导出${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, ${ClassName} ${className}) + { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class); + util.exportExcel(response, list, "${functionName}数据"); + } + + /** + * 获取${functionName}详细信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") + @GetMapping(value = "/{${pkColumn.javaField}}") + public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) + { + return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody ${ClassName} ${className}) + { + return toAjax(${className}Service.insert${ClassName}(${className})); + } + + /** + * 修改${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody ${ClassName} ${className}) + { + return toAjax(${className}Service.update${ClassName}(${className})); + } + + /** + * 删除${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") + @Log(title = "${functionName}", businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) + { + return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s)); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/java/domain.java.vm b/ruoyi-generator/src/main/resources/vm/java/domain.java.vm new file mode 100644 index 0000000..bd51c17 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/domain.java.vm @@ -0,0 +1,105 @@ +package ${packageName}.domain; + +#foreach ($import in $importList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.domain.BaseEntity; +#elseif($table.tree) +import com.ruoyi.common.core.domain.TreeEntity; +#end + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($table.crud || $table.sub) +#set($Entity="BaseEntity") +#elseif($table.tree) +#set($Entity="TreeEntity") +#end +public class ${ClassName} extends ${Entity} +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#if($table.sub) + /** $table.subTable.functionName信息 */ + private List<${subClassName}> ${subclassName}List; + +#end +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + +#if($table.sub) + public List<${subClassName}> get${subClassName}List() + { + return ${subclassName}List; + } + + public void set${subClassName}List(List<${subClassName}> ${subclassName}List) + { + this.${subclassName}List = ${subclassName}List; + } + +#end + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end +#if($table.sub) + .append("${subclassName}List", get${subClassName}List()) +#end + .toString(); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm b/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm new file mode 100644 index 0000000..7e7d7c2 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm @@ -0,0 +1,91 @@ +package ${packageName}.mapper; + +import java.util.List; +import ${packageName}.domain.${ClassName}; +#if($table.sub) +import ${packageName}.domain.${subClassName}; +#end + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 删除${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的数据主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); +#if($table.sub) + + /** + * 批量删除${subTable.functionName} + * + * @param ${pkColumn.javaField}s 需要删除的数据主键集合 + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 批量新增${subTable.functionName} + * + * @param ${subclassName}List ${subTable.functionName}列表 + * @return 结果 + */ + public int batch${subClassName}(List<${subClassName}> ${subclassName}List); + + + /** + * 通过${functionName}主键删除${subTable.functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField}); +#end +} diff --git a/ruoyi-generator/src/main/resources/vm/java/service.java.vm b/ruoyi-generator/src/main/resources/vm/java/service.java.vm new file mode 100644 index 0000000..264882b --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/service.java.vm @@ -0,0 +1,61 @@ +package ${packageName}.service; + +import java.util.List; +import ${packageName}.domain.${ClassName}; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); +} diff --git a/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm new file mode 100644 index 0000000..14746e1 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -0,0 +1,169 @@ +package ${packageName}.service.impl; + +import java.util.List; +#foreach ($column in $columns) +#if($column.javaField == 'createTime' || $column.javaField == 'updateTime') +import com.ruoyi.common.utils.DateUtils; +#break +#end +#end +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +#if($table.sub) +import java.util.ArrayList; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; +import ${packageName}.domain.${subClassName}; +#end +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl implements I${ClassName}Service +{ + @Autowired + private ${ClassName}Mapper ${className}Mapper; + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + @Override + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { + return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) + { + return ${className}Mapper.select${ClassName}List(${className}); + } + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int insert${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'createTime') + ${className}.setCreateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + int rows = ${className}Mapper.insert${ClassName}(${className}); + insert${subClassName}(${className}); + return rows; +#else + return ${className}Mapper.insert${ClassName}(${className}); +#end + } + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int update${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'updateTime') + ${className}.setUpdateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}()); + insert${subClassName}(${className}); +#end + return ${className}Mapper.update${ClassName}(${className}); + } + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s); + } + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField}); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } +#if($table.sub) + + /** + * 新增${subTable.functionName}信息 + * + * @param ${className} ${functionName}对象 + */ + public void insert${subClassName}(${ClassName} ${className}) + { + List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List(); + ${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}(); + if (StringUtils.isNotNull(${subclassName}List)) + { + List<${subClassName}> list = new ArrayList<${subClassName}>(); + for (${subClassName} ${subclassName} : ${subclassName}List) + { + ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField}); + list.add(${subclassName}); + } + if (list.size() > 0) + { + ${className}Mapper.batch${subClassName}(list); + } + } + } +#end +} diff --git a/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm b/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm new file mode 100644 index 0000000..a3f53eb --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm @@ -0,0 +1,76 @@ +package ${packageName}.domain; + +#foreach ($import in $subImportList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $subTable.columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end + .toString(); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/js/api.js.vm b/ruoyi-generator/src/main/resources/vm/js/api.js.vm new file mode 100644 index 0000000..9295524 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/js/api.js.vm @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}(query) { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }) +} diff --git a/ruoyi-generator/src/main/resources/vm/sql/sql.vm b/ruoyi-generator/src/main/resources/vm/sql/sql.vm new file mode 100644 index 0000000..0575583 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/sql/sql.vm @@ -0,0 +1,22 @@ +-- 菜单 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm new file mode 100644 index 0000000..4819c2a --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -0,0 +1,505 @@ + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm new file mode 100644 index 0000000..6296014 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -0,0 +1,602 @@ + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm new file mode 100644 index 0000000..c54d62b --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm @@ -0,0 +1,474 @@ + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm new file mode 100644 index 0000000..8b25665 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm @@ -0,0 +1,590 @@ + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt b/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt new file mode 100644 index 0000000..99239bb --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt @@ -0,0 +1 @@ +ʹõRuoYi-Vue3ǰˣôҪһ´Ŀ¼ģindex.vue.vmindex-tree.vue.vmļϼvueĿ¼ \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm b/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm new file mode 100644 index 0000000..0ceb3d8 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm @@ -0,0 +1,135 @@ + + + + + +#foreach ($column in $columns) + +#end + +#if($table.sub) + + + + + + +#foreach ($column in $subTable.columns) + +#end + +#end + + + select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end from ${tableName} + + + + + + + + insert into ${tableName} + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + $column.columnName, +#end +#end + + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + #{$column.javaField}, +#end +#end + + + + + update ${tableName} + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName) + $column.columnName = #{$column.javaField}, +#end +#end + + where ${pkColumn.columnName} = #{${pkColumn.javaField}} + + + + delete from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}} + + + + delete from ${tableName} where ${pkColumn.columnName} in + + #{${pkColumn.javaField}} + + +#if($table.sub) + + + delete from ${subTableName} where ${subTableFkName} in + + #{${subTableFkclassName}} + + + + + delete from ${subTableName} where ${subTableFkName} = #{${subTableFkclassName}} + + + + insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end) values + + (#foreach($column in $subTable.columns) #{item.$column.javaField}#if($foreach.count != $subTable.columns.size()),#end#end) + + +#end + \ No newline at end of file diff --git a/ruoyi-quartz/pom.xml b/ruoyi-quartz/pom.xml new file mode 100644 index 0000000..db0441f --- /dev/null +++ b/ruoyi-quartz/pom.xml @@ -0,0 +1,46 @@ + + + + ruoyi + com.ruoyi + 4.8.2 + + 4.0.0 + + ruoyi-quartz + + + quartz定时任务 + + + + + + + org.springframework.boot + spring-boot-starter-quartz + + + + + org.quartz-scheduler + quartz + + + com.mchange + c3p0 + + + + + + + com.ruoyi + ruoyi-common + + + + + \ No newline at end of file diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java new file mode 100644 index 0000000..a558170 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java @@ -0,0 +1,57 @@ +//package com.ruoyi.quartz.config; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.scheduling.quartz.SchedulerFactoryBean; +//import javax.sql.DataSource; +//import java.util.Properties; +// +///** +// * 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效) +// * +// * @author ruoyi +// */ +//@Configuration +//public class ScheduleConfig +//{ +// @Bean +// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) +// { +// SchedulerFactoryBean factory = new SchedulerFactoryBean(); +// factory.setDataSource(dataSource); +// +// // quartz参数 +// Properties prop = new Properties(); +// prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler"); +// prop.put("org.quartz.scheduler.instanceId", "AUTO"); +// // 线程池配置 +// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); +// prop.put("org.quartz.threadPool.threadCount", "20"); +// prop.put("org.quartz.threadPool.threadPriority", "5"); +// // JobStore配置 +// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); +// // 集群配置 +// prop.put("org.quartz.jobStore.isClustered", "true"); +// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); +// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1"); +// prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true"); +// +// // sqlserver 启用 +// // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); +// prop.put("org.quartz.jobStore.misfireThreshold", "12000"); +// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); +// factory.setQuartzProperties(prop); +// +// factory.setSchedulerName("RuoyiScheduler"); +// // 延时启动 +// factory.setStartupDelay(1); +// factory.setApplicationContextSchedulerContextKey("applicationContextKey"); +// // 可选,QuartzScheduler +// // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 +// factory.setOverwriteExistingJobs(true); +// // 设置自动启动,默认为true +// factory.setAutoStartup(true); +// +// return factory; +// } +//} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java new file mode 100644 index 0000000..e826dfd --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java @@ -0,0 +1,185 @@ +package com.ruoyi.quartz.controller; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.quartz.domain.SysJob; +import com.ruoyi.quartz.service.ISysJobService; +import com.ruoyi.quartz.util.CronUtils; +import com.ruoyi.quartz.util.ScheduleUtils; + +/** + * 调度任务信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/job") +public class SysJobController extends BaseController +{ + @Autowired + private ISysJobService jobService; + + /** + * 查询定时任务列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:list')") + @GetMapping("/list") + public TableDataInfo list(SysJob sysJob) + { + startPage(); + List list = jobService.selectJobList(sysJob); + return getDataTable(list); + } + + /** + * 导出定时任务列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:export')") + @Log(title = "定时任务", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, SysJob sysJob) + { + List list = jobService.selectJobList(sysJob); + ExcelUtil util = new ExcelUtil(SysJob.class); + util.exportExcel(response, list, "定时任务"); + } + + /** + * 获取定时任务详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{jobId}") + public AjaxResult getInfo(@PathVariable("jobId") Long jobId) + { + return success(jobService.selectJobById(jobId)); + } + + /** + * 新增定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:add')") + @Log(title = "定时任务", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException + { + if (!CronUtils.isValid(job.getCronExpression())) + { + return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确"); + } + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规"); + } + else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); + } + job.setCreateBy(getUsername()); + return toAjax(jobService.insertJob(job)); + } + + /** + * 修改定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:edit')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException + { + if (!CronUtils.isValid(job.getCronExpression())) + { + return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确"); + } + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规"); + } + else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); + } + job.setUpdateBy(getUsername()); + return toAjax(jobService.updateJob(job)); + } + + /** + * 定时任务状态修改 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException + { + SysJob newJob = jobService.selectJobById(job.getJobId()); + newJob.setStatus(job.getStatus()); + return toAjax(jobService.changeStatus(newJob)); + } + + /** + * 定时任务立即执行一次 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping("/run") + public AjaxResult run(@RequestBody SysJob job) throws SchedulerException + { + boolean result = jobService.run(job); + return result ? success() : error("任务不存在或已过期!"); + } + + /** + * 删除定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "定时任务", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobIds}") + public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException + { + jobService.deleteJobByIds(jobIds); + return success(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java new file mode 100644 index 0000000..233361b --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java @@ -0,0 +1,92 @@ +package com.ruoyi.quartz.controller; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.service.ISysJobLogService; + +/** + * 调度日志操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/jobLog") +public class SysJobLogController extends BaseController +{ + @Autowired + private ISysJobLogService jobLogService; + + /** + * 查询定时任务调度日志列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:list')") + @GetMapping("/list") + public TableDataInfo list(SysJobLog sysJobLog) + { + startPage(); + List list = jobLogService.selectJobLogList(sysJobLog); + return getDataTable(list); + } + + /** + * 导出定时任务调度日志列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:export')") + @Log(title = "任务调度日志", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, SysJobLog sysJobLog) + { + List list = jobLogService.selectJobLogList(sysJobLog); + ExcelUtil util = new ExcelUtil(SysJobLog.class); + util.exportExcel(response, list, "调度日志"); + } + + /** + * 根据调度编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{jobLogId}") + public AjaxResult getInfo(@PathVariable Long jobLogId) + { + return success(jobLogService.selectJobLogById(jobLogId)); + } + + + /** + * 删除定时任务调度日志 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobLogIds}") + public AjaxResult remove(@PathVariable Long[] jobLogIds) + { + return toAjax(jobLogService.deleteJobLogByIds(jobLogIds)); + } + + /** + * 清空定时任务调度日志 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "调度日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + jobLogService.cleanJobLog(); + return success(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java new file mode 100644 index 0000000..cea12dc --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java @@ -0,0 +1,171 @@ +package com.ruoyi.quartz.domain; + +import java.util.Date; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.quartz.util.CronUtils; + +/** + * 定时任务调度表 sys_job + * + * @author ruoyi + */ +public class SysJob extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 任务ID */ + @Excel(name = "任务序号", cellType = ColumnType.NUMERIC) + private Long jobId; + + /** 任务名称 */ + @Excel(name = "任务名称") + private String jobName; + + /** 任务组名 */ + @Excel(name = "任务组名") + private String jobGroup; + + /** 调用目标字符串 */ + @Excel(name = "调用目标字符串") + private String invokeTarget; + + /** cron执行表达式 */ + @Excel(name = "执行表达式 ") + private String cronExpression; + + /** cron计划策略 */ + @Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行") + private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT; + + /** 是否并发执行(0允许 1禁止) */ + @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止") + private String concurrent; + + /** 任务状态(0正常 1暂停) */ + @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停") + private String status; + + public Long getJobId() + { + return jobId; + } + + public void setJobId(Long jobId) + { + this.jobId = jobId; + } + + @NotBlank(message = "任务名称不能为空") + @Size(min = 0, max = 64, message = "任务名称不能超过64个字符") + public String getJobName() + { + return jobName; + } + + public void setJobName(String jobName) + { + this.jobName = jobName; + } + + public String getJobGroup() + { + return jobGroup; + } + + public void setJobGroup(String jobGroup) + { + this.jobGroup = jobGroup; + } + + @NotBlank(message = "调用目标字符串不能为空") + @Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符") + public String getInvokeTarget() + { + return invokeTarget; + } + + public void setInvokeTarget(String invokeTarget) + { + this.invokeTarget = invokeTarget; + } + + @NotBlank(message = "Cron执行表达式不能为空") + @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符") + public String getCronExpression() + { + return cronExpression; + } + + public void setCronExpression(String cronExpression) + { + this.cronExpression = cronExpression; + } + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + public Date getNextValidTime() + { + if (StringUtils.isNotEmpty(cronExpression)) + { + return CronUtils.getNextExecution(cronExpression); + } + return null; + } + + public String getMisfirePolicy() + { + return misfirePolicy; + } + + public void setMisfirePolicy(String misfirePolicy) + { + this.misfirePolicy = misfirePolicy; + } + + public String getConcurrent() + { + return concurrent; + } + + public void setConcurrent(String concurrent) + { + this.concurrent = concurrent; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("jobId", getJobId()) + .append("jobName", getJobName()) + .append("jobGroup", getJobGroup()) + .append("cronExpression", getCronExpression()) + .append("nextValidTime", getNextValidTime()) + .append("misfirePolicy", getMisfirePolicy()) + .append("concurrent", getConcurrent()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java new file mode 100644 index 0000000..121c035 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java @@ -0,0 +1,155 @@ +package com.ruoyi.quartz.domain; + +import java.util.Date; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 定时任务调度日志表 sys_job_log + * + * @author ruoyi + */ +public class SysJobLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + @Excel(name = "日志序号") + private Long jobLogId; + + /** 任务名称 */ + @Excel(name = "任务名称") + private String jobName; + + /** 任务组名 */ + @Excel(name = "任务组名") + private String jobGroup; + + /** 调用目标字符串 */ + @Excel(name = "调用目标字符串") + private String invokeTarget; + + /** 日志信息 */ + @Excel(name = "日志信息") + private String jobMessage; + + /** 执行状态(0正常 1失败) */ + @Excel(name = "执行状态", readConverterExp = "0=正常,1=失败") + private String status; + + /** 异常信息 */ + @Excel(name = "异常信息") + private String exceptionInfo; + + /** 开始时间 */ + private Date startTime; + + /** 停止时间 */ + private Date stopTime; + + public Long getJobLogId() + { + return jobLogId; + } + + public void setJobLogId(Long jobLogId) + { + this.jobLogId = jobLogId; + } + + public String getJobName() + { + return jobName; + } + + public void setJobName(String jobName) + { + this.jobName = jobName; + } + + public String getJobGroup() + { + return jobGroup; + } + + public void setJobGroup(String jobGroup) + { + this.jobGroup = jobGroup; + } + + public String getInvokeTarget() + { + return invokeTarget; + } + + public void setInvokeTarget(String invokeTarget) + { + this.invokeTarget = invokeTarget; + } + + public String getJobMessage() + { + return jobMessage; + } + + public void setJobMessage(String jobMessage) + { + this.jobMessage = jobMessage; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getExceptionInfo() + { + return exceptionInfo; + } + + public void setExceptionInfo(String exceptionInfo) + { + this.exceptionInfo = exceptionInfo; + } + + public Date getStartTime() + { + return startTime; + } + + public void setStartTime(Date startTime) + { + this.startTime = startTime; + } + + public Date getStopTime() + { + return stopTime; + } + + public void setStopTime(Date stopTime) + { + this.stopTime = stopTime; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("jobLogId", getJobLogId()) + .append("jobName", getJobName()) + .append("jobGroup", getJobGroup()) + .append("jobMessage", getJobMessage()) + .append("status", getStatus()) + .append("exceptionInfo", getExceptionInfo()) + .append("startTime", getStartTime()) + .append("stopTime", getStopTime()) + .toString(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java new file mode 100644 index 0000000..727d916 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java @@ -0,0 +1,64 @@ +package com.ruoyi.quartz.mapper; + +import java.util.List; +import com.ruoyi.quartz.domain.SysJobLog; + +/** + * 调度任务日志信息 数据层 + * + * @author ruoyi + */ +public interface SysJobLogMapper +{ + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + public List selectJobLogList(SysJobLog jobLog); + + /** + * 查询所有调度任务日志 + * + * @return 调度任务日志列表 + */ + public List selectJobLogAll(); + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + public SysJobLog selectJobLogById(Long jobLogId); + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + * @return 结果 + */ + public int insertJobLog(SysJobLog jobLog); + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的数据ID + * @return 结果 + */ + public int deleteJobLogByIds(Long[] logIds); + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + * @return 结果 + */ + public int deleteJobLogById(Long jobId); + + /** + * 清空任务日志 + */ + public void cleanJobLog(); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java new file mode 100644 index 0000000..20f45db --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java @@ -0,0 +1,67 @@ +package com.ruoyi.quartz.mapper; + +import java.util.List; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 调度任务信息 数据层 + * + * @author ruoyi + */ +public interface SysJobMapper +{ + /** + * 查询调度任务日志集合 + * + * @param job 调度信息 + * @return 操作日志集合 + */ + public List selectJobList(SysJob job); + + /** + * 查询所有调度任务 + * + * @return 调度任务列表 + */ + public List selectJobAll(); + + /** + * 通过调度ID查询调度任务信息 + * + * @param jobId 调度ID + * @return 角色对象信息 + */ + public SysJob selectJobById(Long jobId); + + /** + * 通过调度ID删除调度任务信息 + * + * @param jobId 调度ID + * @return 结果 + */ + public int deleteJobById(Long jobId); + + /** + * 批量删除调度任务信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteJobByIds(Long[] ids); + + /** + * 修改调度任务信息 + * + * @param job 调度任务信息 + * @return 结果 + */ + public int updateJob(SysJob job); + + /** + * 新增调度任务信息 + * + * @param job 调度任务信息 + * @return 结果 + */ + public int insertJob(SysJob job); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java new file mode 100644 index 0000000..8546792 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java @@ -0,0 +1,56 @@ +package com.ruoyi.quartz.service; + +import java.util.List; +import com.ruoyi.quartz.domain.SysJobLog; + +/** + * 定时任务调度日志信息信息 服务层 + * + * @author ruoyi + */ +public interface ISysJobLogService +{ + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + public List selectJobLogList(SysJobLog jobLog); + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + public SysJobLog selectJobLogById(Long jobLogId); + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + */ + public void addJobLog(SysJobLog jobLog); + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的日志ID + * @return 结果 + */ + public int deleteJobLogByIds(Long[] logIds); + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + * @return 结果 + */ + public int deleteJobLogById(Long jobId); + + /** + * 清空任务日志 + */ + public void cleanJobLog(); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java new file mode 100644 index 0000000..437ade8 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java @@ -0,0 +1,102 @@ +package com.ruoyi.quartz.service; + +import java.util.List; +import org.quartz.SchedulerException; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务调度信息信息 服务层 + * + * @author ruoyi + */ +public interface ISysJobService +{ + /** + * 获取quartz调度器的计划任务 + * + * @param job 调度信息 + * @return 调度任务集合 + */ + public List selectJobList(SysJob job); + + /** + * 通过调度任务ID查询调度信息 + * + * @param jobId 调度任务ID + * @return 调度任务对象信息 + */ + public SysJob selectJobById(Long jobId); + + /** + * 暂停任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int pauseJob(SysJob job) throws SchedulerException; + + /** + * 恢复任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int resumeJob(SysJob job) throws SchedulerException; + + /** + * 删除任务后,所对应的trigger也将被删除 + * + * @param job 调度信息 + * @return 结果 + */ + public int deleteJob(SysJob job) throws SchedulerException; + + /** + * 批量删除调度信息 + * + * @param jobIds 需要删除的任务ID + * @return 结果 + */ + public void deleteJobByIds(Long[] jobIds) throws SchedulerException; + + /** + * 任务调度状态修改 + * + * @param job 调度信息 + * @return 结果 + */ + public int changeStatus(SysJob job) throws SchedulerException; + + /** + * 立即运行任务 + * + * @param job 调度信息 + * @return 结果 + */ + public boolean run(SysJob job) throws SchedulerException; + + /** + * 新增任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int insertJob(SysJob job) throws SchedulerException, TaskException; + + /** + * 更新任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int updateJob(SysJob job) throws SchedulerException, TaskException; + + /** + * 校验cron表达式是否有效 + * + * @param cronExpression 表达式 + * @return 结果 + */ + public boolean checkCronExpressionIsValid(String cronExpression); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java new file mode 100644 index 0000000..812eed7 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java @@ -0,0 +1,87 @@ +package com.ruoyi.quartz.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.mapper.SysJobLogMapper; +import com.ruoyi.quartz.service.ISysJobLogService; + +/** + * 定时任务调度日志信息 服务层 + * + * @author ruoyi + */ +@Service +public class SysJobLogServiceImpl implements ISysJobLogService +{ + @Autowired + private SysJobLogMapper jobLogMapper; + + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + @Override + public List selectJobLogList(SysJobLog jobLog) + { + return jobLogMapper.selectJobLogList(jobLog); + } + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + @Override + public SysJobLog selectJobLogById(Long jobLogId) + { + return jobLogMapper.selectJobLogById(jobLogId); + } + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + */ + @Override + public void addJobLog(SysJobLog jobLog) + { + jobLogMapper.insertJobLog(jobLog); + } + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteJobLogByIds(Long[] logIds) + { + return jobLogMapper.deleteJobLogByIds(logIds); + } + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + */ + @Override + public int deleteJobLogById(Long jobId) + { + return jobLogMapper.deleteJobLogById(jobId); + } + + /** + * 清空任务日志 + */ + @Override + public void cleanJobLog() + { + jobLogMapper.cleanJobLog(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java new file mode 100644 index 0000000..78ebef8 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java @@ -0,0 +1,261 @@ +package com.ruoyi.quartz.service.impl; + +import java.util.List; +import jakarta.annotation.PostConstruct; +import org.quartz.JobDataMap; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.quartz.domain.SysJob; +import com.ruoyi.quartz.mapper.SysJobMapper; +import com.ruoyi.quartz.service.ISysJobService; +import com.ruoyi.quartz.util.CronUtils; +import com.ruoyi.quartz.util.ScheduleUtils; + +/** + * 定时任务调度信息 服务层 + * + * @author ruoyi + */ +@Service +public class SysJobServiceImpl implements ISysJobService +{ + @Autowired + private Scheduler scheduler; + + @Autowired + private SysJobMapper jobMapper; + + /** + * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据) + */ + @PostConstruct + public void init() throws SchedulerException, TaskException + { + scheduler.clear(); + List jobList = jobMapper.selectJobAll(); + for (SysJob job : jobList) + { + ScheduleUtils.createScheduleJob(scheduler, job); + } + } + + /** + * 获取quartz调度器的计划任务列表 + * + * @param job 调度信息 + * @return + */ + @Override + public List selectJobList(SysJob job) + { + return jobMapper.selectJobList(job); + } + + /** + * 通过调度任务ID查询调度信息 + * + * @param jobId 调度任务ID + * @return 调度任务对象信息 + */ + @Override + public SysJob selectJobById(Long jobId) + { + return jobMapper.selectJobById(jobId); + } + + /** + * 暂停任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int pauseJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 恢复任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int resumeJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + job.setStatus(ScheduleConstants.Status.NORMAL.getValue()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 删除任务后,所对应的trigger也将被删除 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + int rows = jobMapper.deleteJobById(jobId); + if (rows > 0) + { + scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 批量删除调度信息 + * + * @param jobIds 需要删除的任务ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteJobByIds(Long[] jobIds) throws SchedulerException + { + for (Long jobId : jobIds) + { + SysJob job = jobMapper.selectJobById(jobId); + deleteJob(job); + } + } + + /** + * 任务调度状态修改 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int changeStatus(SysJob job) throws SchedulerException + { + int rows = 0; + String status = job.getStatus(); + if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) + { + rows = resumeJob(job); + } + else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) + { + rows = pauseJob(job); + } + return rows; + } + + /** + * 立即运行任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean run(SysJob job) throws SchedulerException + { + boolean result = false; + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + SysJob properties = selectJobById(job.getJobId()); + // 参数 + JobDataMap dataMap = new JobDataMap(); + dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties); + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); + if (scheduler.checkExists(jobKey)) + { + result = true; + scheduler.triggerJob(jobKey, dataMap); + } + return result; + } + + /** + * 新增任务 + * + * @param job 调度信息 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertJob(SysJob job) throws SchedulerException, TaskException + { + job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); + int rows = jobMapper.insertJob(job); + if (rows > 0) + { + ScheduleUtils.createScheduleJob(scheduler, job); + } + return rows; + } + + /** + * 更新任务的时间表达式 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateJob(SysJob job) throws SchedulerException, TaskException + { + SysJob properties = selectJobById(job.getJobId()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + updateSchedulerJob(job, properties.getJobGroup()); + } + return rows; + } + + /** + * 更新任务 + * + * @param job 任务对象 + * @param jobGroup 任务组名 + */ + public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException + { + Long jobId = job.getJobId(); + // 判断是否存在 + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); + if (scheduler.checkExists(jobKey)) + { + // 防止创建时存在数据问题 先移除,然后在执行创建操作 + scheduler.deleteJob(jobKey); + } + ScheduleUtils.createScheduleJob(scheduler, job); + } + + /** + * 校验cron表达式是否有效 + * + * @param cronExpression 表达式 + * @return 结果 + */ + @Override + public boolean checkCronExpressionIsValid(String cronExpression) + { + return CronUtils.isValid(cronExpression); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java new file mode 100644 index 0000000..853243b --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java @@ -0,0 +1,28 @@ +package com.ruoyi.quartz.task; + +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; + +/** + * 定时任务调度测试 + * + * @author ruoyi + */ +@Component("ryTask") +public class RyTask +{ + public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) + { + System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i)); + } + + public void ryParams(String params) + { + System.out.println("执行有参方法:" + params); + } + + public void ryNoParams() + { + System.out.println("执行无参方法"); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java new file mode 100644 index 0000000..731a5eb --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java @@ -0,0 +1,107 @@ +package com.ruoyi.quartz.util; + +import java.util.Date; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.utils.ExceptionUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.service.ISysJobLogService; + +/** + * 抽象quartz调用 + * + * @author ruoyi + */ +public abstract class AbstractQuartzJob implements Job +{ + private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class); + + /** + * 线程本地变量 + */ + private static ThreadLocal threadLocal = new ThreadLocal<>(); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException + { + SysJob sysJob = new SysJob(); + BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES)); + try + { + before(context, sysJob); + if (sysJob != null) + { + doExecute(context, sysJob); + } + after(context, sysJob, null); + } + catch (Exception e) + { + log.error("任务执行异常 - :", e); + after(context, sysJob, e); + } + } + + /** + * 执行前 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + */ + protected void before(JobExecutionContext context, SysJob sysJob) + { + threadLocal.set(new Date()); + } + + /** + * 执行后 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + */ + protected void after(JobExecutionContext context, SysJob sysJob, Exception e) + { + Date startTime = threadLocal.get(); + threadLocal.remove(); + + final SysJobLog sysJobLog = new SysJobLog(); + sysJobLog.setJobName(sysJob.getJobName()); + sysJobLog.setJobGroup(sysJob.getJobGroup()); + sysJobLog.setInvokeTarget(sysJob.getInvokeTarget()); + sysJobLog.setStartTime(startTime); + sysJobLog.setStopTime(new Date()); + long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime(); + sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒"); + if (e != null) + { + sysJobLog.setStatus(Constants.FAIL); + String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000); + sysJobLog.setExceptionInfo(errorMsg); + } + else + { + sysJobLog.setStatus(Constants.SUCCESS); + } + + // 写入数据库当中 + SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog); + } + + /** + * 执行方法,由子类重载 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + * @throws Exception 执行过程中的异常 + */ + protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception; +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java new file mode 100644 index 0000000..dd53839 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java @@ -0,0 +1,63 @@ +package com.ruoyi.quartz.util; + +import java.text.ParseException; +import java.util.Date; +import org.quartz.CronExpression; + +/** + * cron表达式工具类 + * + * @author ruoyi + * + */ +public class CronUtils +{ + /** + * 返回一个布尔值代表一个给定的Cron表达式的有效性 + * + * @param cronExpression Cron表达式 + * @return boolean 表达式是否有效 + */ + public static boolean isValid(String cronExpression) + { + return CronExpression.isValidExpression(cronExpression); + } + + /** + * 返回一个字符串值,表示该消息无效Cron表达式给出有效性 + * + * @param cronExpression Cron表达式 + * @return String 无效时返回表达式错误描述,如果有效返回null + */ + public static String getInvalidMessage(String cronExpression) + { + try + { + new CronExpression(cronExpression); + return null; + } + catch (ParseException pe) + { + return pe.getMessage(); + } + } + + /** + * 返回下一个执行时间根据给定的Cron表达式 + * + * @param cronExpression Cron表达式 + * @return Date 下次Cron表达式执行时间 + */ + public static Date getNextExecution(String cronExpression) + { + try + { + CronExpression cron = new CronExpression(cronExpression); + return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis())); + } + catch (ParseException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java new file mode 100644 index 0000000..e3dc62c --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java @@ -0,0 +1,182 @@ +package com.ruoyi.quartz.util; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.LinkedList; +import java.util.List; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 任务执行工具 + * + * @author ruoyi + */ +public class JobInvokeUtil +{ + /** + * 执行方法 + * + * @param sysJob 系统任务 + */ + public static void invokeMethod(SysJob sysJob) throws Exception + { + String invokeTarget = sysJob.getInvokeTarget(); + String beanName = getBeanName(invokeTarget); + String methodName = getMethodName(invokeTarget); + List methodParams = getMethodParams(invokeTarget); + + if (!isValidClassName(beanName)) + { + Object bean = SpringUtils.getBean(beanName); + invokeMethod(bean, methodName, methodParams); + } + else + { + Object bean = Class.forName(beanName).getDeclaredConstructor().newInstance(); + invokeMethod(bean, methodName, methodParams); + } + } + + /** + * 调用任务方法 + * + * @param bean 目标对象 + * @param methodName 方法名称 + * @param methodParams 方法参数 + */ + private static void invokeMethod(Object bean, String methodName, List methodParams) + throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException + { + if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0) + { + Method method = bean.getClass().getMethod(methodName, getMethodParamsType(methodParams)); + method.invoke(bean, getMethodParamsValue(methodParams)); + } + else + { + Method method = bean.getClass().getMethod(methodName); + method.invoke(bean); + } + } + + /** + * 校验是否为为class包名 + * + * @param invokeTarget 名称 + * @return true是 false否 + */ + public static boolean isValidClassName(String invokeTarget) + { + return StringUtils.countMatches(invokeTarget, ".") > 1; + } + + /** + * 获取bean名称 + * + * @param invokeTarget 目标字符串 + * @return bean名称 + */ + public static String getBeanName(String invokeTarget) + { + String beanName = StringUtils.substringBefore(invokeTarget, "("); + return StringUtils.substringBeforeLast(beanName, "."); + } + + /** + * 获取bean方法 + * + * @param invokeTarget 目标字符串 + * @return method方法 + */ + public static String getMethodName(String invokeTarget) + { + String methodName = StringUtils.substringBefore(invokeTarget, "("); + return StringUtils.substringAfterLast(methodName, "."); + } + + /** + * 获取method方法参数相关列表 + * + * @param invokeTarget 目标字符串 + * @return method方法相关参数列表 + */ + public static List getMethodParams(String invokeTarget) + { + String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")"); + if (StringUtils.isEmpty(methodStr)) + { + return null; + } + String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)"); + List classs = new LinkedList<>(); + for (int i = 0; i < methodParams.length; i++) + { + String str = StringUtils.trimToEmpty(methodParams[i]); + // String字符串类型,以'或"开头 + if (StringUtils.startsWithAny(str, "'", "\"")) + { + classs.add(new Object[] { StringUtils.substring(str, 1, str.length() - 1), String.class }); + } + // boolean布尔类型,等于true或者false + else if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)) + { + classs.add(new Object[] { Boolean.valueOf(str), Boolean.class }); + } + // long长整形,以L结尾 + else if (StringUtils.endsWith(str, "L")) + { + classs.add(new Object[] { Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class }); + } + // double浮点类型,以D结尾 + else if (StringUtils.endsWith(str, "D")) + { + classs.add(new Object[] { Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class }); + } + // 其他类型归类为整形 + else + { + classs.add(new Object[] { Integer.valueOf(str), Integer.class }); + } + } + return classs; + } + + /** + * 获取参数类型 + * + * @param methodParams 参数相关列表 + * @return 参数类型列表 + */ + public static Class[] getMethodParamsType(List methodParams) + { + Class[] classs = new Class[methodParams.size()]; + int index = 0; + for (Object[] os : methodParams) + { + classs[index] = (Class) os[1]; + index++; + } + return classs; + } + + /** + * 获取参数值 + * + * @param methodParams 参数相关列表 + * @return 参数值列表 + */ + public static Object[] getMethodParamsValue(List methodParams) + { + Object[] classs = new Object[methodParams.size()]; + int index = 0; + for (Object[] os : methodParams) + { + classs[index] = (Object) os[0]; + index++; + } + return classs; + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java new file mode 100644 index 0000000..5e13558 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java @@ -0,0 +1,21 @@ +package com.ruoyi.quartz.util; + +import org.quartz.DisallowConcurrentExecution; +import org.quartz.JobExecutionContext; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务处理(禁止并发执行) + * + * @author ruoyi + * + */ +@DisallowConcurrentExecution +public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java new file mode 100644 index 0000000..e975326 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java @@ -0,0 +1,19 @@ +package com.ruoyi.quartz.util; + +import org.quartz.JobExecutionContext; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务处理(允许并发执行) + * + * @author ruoyi + * + */ +public class QuartzJobExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java new file mode 100644 index 0000000..21fedae --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java @@ -0,0 +1,141 @@ +package com.ruoyi.quartz.util; + +import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; +import org.quartz.Job; +import org.quartz.JobBuilder; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.common.exception.job.TaskException.Code; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务工具类 + * + * @author ruoyi + * + */ +public class ScheduleUtils +{ + /** + * 得到quartz任务类 + * + * @param sysJob 执行计划 + * @return 具体执行任务类 + */ + private static Class getQuartzJobClass(SysJob sysJob) + { + boolean isConcurrent = "0".equals(sysJob.getConcurrent()); + return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class; + } + + /** + * 构建任务触发对象 + */ + public static TriggerKey getTriggerKey(Long jobId, String jobGroup) + { + return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup); + } + + /** + * 构建任务键对象 + */ + public static JobKey getJobKey(Long jobId, String jobGroup) + { + return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup); + } + + /** + * 创建定时任务 + */ + public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException + { + Class jobClass = getQuartzJobClass(job); + // 构建job信息 + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build(); + + // 表达式调度构建器 + CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); + cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder); + + // 按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup)) + .withSchedule(cronScheduleBuilder).build(); + + // 放入参数,运行时的方法可以获取 + jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); + + // 判断是否存在 + if (scheduler.checkExists(getJobKey(jobId, jobGroup))) + { + // 防止创建时存在数据问题 先移除,然后在执行创建操作 + scheduler.deleteJob(getJobKey(jobId, jobGroup)); + } + + // 判断任务是否过期 + if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))) + { + // 执行调度任务 + scheduler.scheduleJob(jobDetail, trigger); + } + + // 暂停任务 + if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) + { + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + } + + /** + * 设置定时任务策略 + */ + public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb) + throws TaskException + { + switch (job.getMisfirePolicy()) + { + case ScheduleConstants.MISFIRE_DEFAULT: + return cb; + case ScheduleConstants.MISFIRE_IGNORE_MISFIRES: + return cb.withMisfireHandlingInstructionIgnoreMisfires(); + case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED: + return cb.withMisfireHandlingInstructionFireAndProceed(); + case ScheduleConstants.MISFIRE_DO_NOTHING: + return cb.withMisfireHandlingInstructionDoNothing(); + default: + throw new TaskException("The task misfire policy '" + job.getMisfirePolicy() + + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR); + } + } + + /** + * 检查包名是否为白名单配置 + * + * @param invokeTarget 目标字符串 + * @return 结果 + */ + public static boolean whiteList(String invokeTarget) + { + String packageName = StringUtils.substringBefore(invokeTarget, "("); + int count = StringUtils.countMatches(packageName, "."); + if (count > 1) + { + return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR); + } + Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]); + String beanPackageName = obj.getClass().getPackage().getName(); + return StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_WHITELIST_STR) + && !StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_ERROR_STR); + } +} diff --git a/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml new file mode 100644 index 0000000..e608e42 --- /dev/null +++ b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + select job_log_id, job_name, job_group, invoke_target, job_message, status, exception_info, create_time + from sys_job_log + + + + + + + + + + delete from sys_job_log where job_log_id = #{jobLogId} + + + + delete from sys_job_log where job_log_id in + + #{jobLogId} + + + + + truncate table sys_job_log + + + + insert into sys_job_log( + job_log_id, + job_name, + job_group, + invoke_target, + job_message, + status, + exception_info, + create_time + )values( + #{jobLogId}, + #{jobName}, + #{jobGroup}, + #{invokeTarget}, + #{jobMessage}, + #{status}, + #{exceptionInfo}, + sysdate() + ) + + + \ No newline at end of file diff --git a/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml new file mode 100644 index 0000000..5605c44 --- /dev/null +++ b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark + from sys_job + + + + + + + + + + delete from sys_job where job_id = #{jobId} + + + + delete from sys_job where job_id in + + #{jobId} + + + + + update sys_job + + job_name = #{jobName}, + job_group = #{jobGroup}, + invoke_target = #{invokeTarget}, + cron_expression = #{cronExpression}, + misfire_policy = #{misfirePolicy}, + concurrent = #{concurrent}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where job_id = #{jobId} + + + + insert into sys_job( + job_id, + job_name, + job_group, + invoke_target, + cron_expression, + misfire_policy, + concurrent, + status, + remark, + create_by, + create_time + )values( + #{jobId}, + #{jobName}, + #{jobGroup}, + #{invokeTarget}, + #{cronExpression}, + #{misfirePolicy}, + #{concurrent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml new file mode 100644 index 0000000..8c61a01 --- /dev/null +++ b/ruoyi-system/pom.xml @@ -0,0 +1,28 @@ + + + + ruoyi + com.ruoyi + 4.8.2 + + 4.0.0 + + ruoyi-system + + + system系统模块 + + + + + + + com.ruoyi + ruoyi-common + + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java new file mode 100644 index 0000000..83f0703 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java @@ -0,0 +1,81 @@ +package com.ruoyi.system.domain; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 缓存信息 + * + * @author ruoyi + */ +public class SysCache +{ + /** 缓存名称 */ + private String cacheName = ""; + + /** 缓存键名 */ + private String cacheKey = ""; + + /** 缓存内容 */ + private String cacheValue = ""; + + /** 备注 */ + private String remark = ""; + + public SysCache() + { + + } + + public SysCache(String cacheName, String remark) + { + this.cacheName = cacheName; + this.remark = remark; + } + + public SysCache(String cacheName, String cacheKey, String cacheValue) + { + this.cacheName = StringUtils.replace(cacheName, ":", ""); + this.cacheKey = StringUtils.replace(cacheKey, cacheName, ""); + this.cacheValue = cacheValue; + } + + public String getCacheName() + { + return cacheName; + } + + public void setCacheName(String cacheName) + { + this.cacheName = cacheName; + } + + public String getCacheKey() + { + return cacheKey; + } + + public void setCacheKey(String cacheKey) + { + this.cacheKey = cacheKey; + } + + public String getCacheValue() + { + return cacheValue; + } + + public void setCacheValue(String cacheValue) + { + this.cacheValue = cacheValue; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java new file mode 100644 index 0000000..cae07be --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java @@ -0,0 +1,111 @@ +package com.ruoyi.system.domain; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 参数配置表 sys_config + * + * @author ruoyi + */ +public class SysConfig extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 参数主键 */ + @Excel(name = "参数主键", cellType = ColumnType.NUMERIC) + private Long configId; + + /** 参数名称 */ + @Excel(name = "参数名称") + private String configName; + + /** 参数键名 */ + @Excel(name = "参数键名") + private String configKey; + + /** 参数键值 */ + @Excel(name = "参数键值") + private String configValue; + + /** 系统内置(Y是 N否) */ + @Excel(name = "系统内置", readConverterExp = "Y=是,N=否") + private String configType; + + public Long getConfigId() + { + return configId; + } + + public void setConfigId(Long configId) + { + this.configId = configId; + } + + @NotBlank(message = "参数名称不能为空") + @Size(min = 0, max = 100, message = "参数名称不能超过100个字符") + public String getConfigName() + { + return configName; + } + + public void setConfigName(String configName) + { + this.configName = configName; + } + + @NotBlank(message = "参数键名长度不能为空") + @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符") + public String getConfigKey() + { + return configKey; + } + + public void setConfigKey(String configKey) + { + this.configKey = configKey; + } + + @NotBlank(message = "参数键值不能为空") + @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符") + public String getConfigValue() + { + return configValue; + } + + public void setConfigValue(String configValue) + { + this.configValue = configValue; + } + + public String getConfigType() + { + return configType; + } + + public void setConfigType(String configType) + { + this.configType = configType; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("configId", getConfigId()) + .append("configName", getConfigName()) + .append("configKey", getConfigKey()) + .append("configValue", getConfigValue()) + .append("configType", getConfigType()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java new file mode 100644 index 0000000..7fdea30 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java @@ -0,0 +1,144 @@ +package com.ruoyi.system.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 系统访问记录表 sys_logininfor + * + * @author ruoyi + */ +public class SysLogininfor extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + @Excel(name = "序号", cellType = ColumnType.NUMERIC) + private Long infoId; + + /** 用户账号 */ + @Excel(name = "用户账号") + private String userName; + + /** 登录状态 0成功 1失败 */ + @Excel(name = "登录状态", readConverterExp = "0=成功,1=失败") + private String status; + + /** 登录IP地址 */ + @Excel(name = "登录地址") + private String ipaddr; + + /** 登录地点 */ + @Excel(name = "登录地点") + private String loginLocation; + + /** 浏览器类型 */ + @Excel(name = "浏览器") + private String browser; + + /** 操作系统 */ + @Excel(name = "操作系统") + private String os; + + /** 提示消息 */ + @Excel(name = "提示消息") + private String msg; + + /** 访问时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date loginTime; + + public Long getInfoId() + { + return infoId; + } + + public void setInfoId(Long infoId) + { + this.infoId = infoId; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public Date getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Date loginTime) + { + this.loginTime = loginTime; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java new file mode 100644 index 0000000..8603ad8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java @@ -0,0 +1,102 @@ +package com.ruoyi.system.domain; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.xss.Xss; + +/** + * 通知公告表 sys_notice + * + * @author ruoyi + */ +public class SysNotice extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 公告ID */ + private Long noticeId; + + /** 公告标题 */ + private String noticeTitle; + + /** 公告类型(1通知 2公告) */ + private String noticeType; + + /** 公告内容 */ + private String noticeContent; + + /** 公告状态(0正常 1关闭) */ + private String status; + + public Long getNoticeId() + { + return noticeId; + } + + public void setNoticeId(Long noticeId) + { + this.noticeId = noticeId; + } + + public void setNoticeTitle(String noticeTitle) + { + this.noticeTitle = noticeTitle; + } + + @Xss(message = "公告标题不能包含脚本字符") + @NotBlank(message = "公告标题不能为空") + @Size(min = 0, max = 50, message = "公告标题不能超过50个字符") + public String getNoticeTitle() + { + return noticeTitle; + } + + public void setNoticeType(String noticeType) + { + this.noticeType = noticeType; + } + + public String getNoticeType() + { + return noticeType; + } + + public void setNoticeContent(String noticeContent) + { + this.noticeContent = noticeContent; + } + + public String getNoticeContent() + { + return noticeContent; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("noticeId", getNoticeId()) + .append("noticeTitle", getNoticeTitle()) + .append("noticeType", getNoticeType()) + .append("noticeContent", getNoticeContent()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java new file mode 100644 index 0000000..f6761df --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java @@ -0,0 +1,269 @@ +package com.ruoyi.system.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 操作日志记录表 oper_log + * + * @author ruoyi + */ +public class SysOperLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 日志主键 */ + @Excel(name = "操作序号", cellType = ColumnType.NUMERIC) + private Long operId; + + /** 操作模块 */ + @Excel(name = "操作模块") + private String title; + + /** 业务类型(0其它 1新增 2修改 3删除) */ + @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据") + private Integer businessType; + + /** 业务类型数组 */ + private Integer[] businessTypes; + + /** 请求方法 */ + @Excel(name = "请求方法") + private String method; + + /** 请求方式 */ + @Excel(name = "请求方式") + private String requestMethod; + + /** 操作类别(0其它 1后台用户 2手机端用户) */ + @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户") + private Integer operatorType; + + /** 操作人员 */ + @Excel(name = "操作人员") + private String operName; + + /** 部门名称 */ + @Excel(name = "部门名称") + private String deptName; + + /** 请求url */ + @Excel(name = "请求地址") + private String operUrl; + + /** 操作地址 */ + @Excel(name = "操作地址") + private String operIp; + + /** 操作地点 */ + @Excel(name = "操作地点") + private String operLocation; + + /** 请求参数 */ + @Excel(name = "请求参数") + private String operParam; + + /** 返回参数 */ + @Excel(name = "返回参数") + private String jsonResult; + + /** 操作状态(0正常 1异常) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=异常") + private Integer status; + + /** 错误消息 */ + @Excel(name = "错误消息") + private String errorMsg; + + /** 操作时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date operTime; + + /** 消耗时间 */ + @Excel(name = "消耗时间", suffix = "毫秒") + private Long costTime; + + public Long getOperId() + { + return operId; + } + + public void setOperId(Long operId) + { + this.operId = operId; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public Integer getBusinessType() + { + return businessType; + } + + public void setBusinessType(Integer businessType) + { + this.businessType = businessType; + } + + public Integer[] getBusinessTypes() + { + return businessTypes; + } + + public void setBusinessTypes(Integer[] businessTypes) + { + this.businessTypes = businessTypes; + } + + public String getMethod() + { + return method; + } + + public void setMethod(String method) + { + this.method = method; + } + + public String getRequestMethod() + { + return requestMethod; + } + + public void setRequestMethod(String requestMethod) + { + this.requestMethod = requestMethod; + } + + public Integer getOperatorType() + { + return operatorType; + } + + public void setOperatorType(Integer operatorType) + { + this.operatorType = operatorType; + } + + public String getOperName() + { + return operName; + } + + public void setOperName(String operName) + { + this.operName = operName; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getOperUrl() + { + return operUrl; + } + + public void setOperUrl(String operUrl) + { + this.operUrl = operUrl; + } + + public String getOperIp() + { + return operIp; + } + + public void setOperIp(String operIp) + { + this.operIp = operIp; + } + + public String getOperLocation() + { + return operLocation; + } + + public void setOperLocation(String operLocation) + { + this.operLocation = operLocation; + } + + public String getOperParam() + { + return operParam; + } + + public void setOperParam(String operParam) + { + this.operParam = operParam; + } + + public String getJsonResult() + { + return jsonResult; + } + + public void setJsonResult(String jsonResult) + { + this.jsonResult = jsonResult; + } + + public Integer getStatus() + { + return status; + } + + public void setStatus(Integer status) + { + this.status = status; + } + + public String getErrorMsg() + { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) + { + this.errorMsg = errorMsg; + } + + public Date getOperTime() + { + return operTime; + } + + public void setOperTime(Date operTime) + { + this.operTime = operTime; + } + + public Long getCostTime() + { + return costTime; + } + + public void setCostTime(Long costTime) + { + this.costTime = costTime; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java new file mode 100644 index 0000000..62dbd47 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java @@ -0,0 +1,124 @@ +package com.ruoyi.system.domain; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 岗位表 sys_post + * + * @author ruoyi + */ +public class SysPost extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 岗位序号 */ + @Excel(name = "岗位序号", cellType = ColumnType.NUMERIC) + private Long postId; + + /** 岗位编码 */ + @Excel(name = "岗位编码") + private String postCode; + + /** 岗位名称 */ + @Excel(name = "岗位名称") + private String postName; + + /** 岗位排序 */ + @Excel(name = "岗位排序") + private Integer postSort; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 用户是否存在此岗位标识 默认不存在 */ + private boolean flag = false; + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @NotBlank(message = "岗位编码不能为空") + @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符") + public String getPostCode() + { + return postCode; + } + + public void setPostCode(String postCode) + { + this.postCode = postCode; + } + + @NotBlank(message = "岗位名称不能为空") + @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符") + public String getPostName() + { + return postName; + } + + public void setPostName(String postName) + { + this.postName = postName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getPostSort() + { + return postSort; + } + + public void setPostSort(Integer postSort) + { + this.postSort = postSort; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("postId", getPostId()) + .append("postCode", getPostCode()) + .append("postName", getPostName()) + .append("postSort", getPostSort()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java new file mode 100644 index 0000000..47b21bf --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和部门关联 sys_role_dept + * + * @author ruoyi + */ +public class SysRoleDept +{ + /** 角色ID */ + private Long roleId; + + /** 部门ID */ + private Long deptId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("deptId", getDeptId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java new file mode 100644 index 0000000..de10a74 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author ruoyi + */ +public class SysRoleMenu +{ + /** 角色ID */ + private Long roleId; + + /** 菜单ID */ + private Long menuId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("menuId", getMenuId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java new file mode 100644 index 0000000..2bbd318 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java @@ -0,0 +1,113 @@ +package com.ruoyi.system.domain; + +/** + * 当前在线会话 + * + * @author ruoyi + */ +public class SysUserOnline +{ + /** 会话编号 */ + private String tokenId; + + /** 部门名称 */ + private String deptName; + + /** 用户名称 */ + private String userName; + + /** 登录IP地址 */ + private String ipaddr; + + /** 登录地址 */ + private String loginLocation; + + /** 浏览器类型 */ + private String browser; + + /** 操作系统 */ + private String os; + + /** 登录时间 */ + private Long loginTime; + + public String getTokenId() + { + return tokenId; + } + + public void setTokenId(String tokenId) + { + this.tokenId = tokenId; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java new file mode 100644 index 0000000..6e8c416 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和岗位关联 sys_user_post + * + * @author ruoyi + */ +public class SysUserPost +{ + /** 用户ID */ + private Long userId; + + /** 岗位ID */ + private Long postId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("postId", getPostId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java new file mode 100644 index 0000000..4d15810 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和角色关联 sys_user_role + * + * @author ruoyi + */ +public class SysUserRole +{ + /** 用户ID */ + private Long userId; + + /** 角色ID */ + private Long roleId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("roleId", getRoleId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java new file mode 100644 index 0000000..a5d5fdc --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java @@ -0,0 +1,106 @@ +package com.ruoyi.system.domain.vo; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 路由显示信息 + * + * @author ruoyi + */ +public class MetaVo +{ + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 缓存 + */ + private boolean noCache; + + /** + * 内链地址(http(s)://开头) + */ + private String link; + + public MetaVo() + { + } + + public MetaVo(String title, String icon) + { + this.title = title; + this.icon = icon; + } + + public MetaVo(String title, String icon, boolean noCache) + { + this.title = title; + this.icon = icon; + this.noCache = noCache; + } + + public MetaVo(String title, String icon, String link) + { + this.title = title; + this.icon = icon; + this.link = link; + } + + public MetaVo(String title, String icon, boolean noCache, String link) + { + this.title = title; + this.icon = icon; + this.noCache = noCache; + if (StringUtils.ishttp(link)) + { + this.link = link; + } + } + + public boolean isNoCache() + { + return noCache; + } + + public void setNoCache(boolean noCache) + { + this.noCache = noCache; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public String getLink() + { + return link; + } + + public void setLink(String link) + { + this.link = link; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java new file mode 100644 index 0000000..afff8c9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java @@ -0,0 +1,148 @@ +package com.ruoyi.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.List; + +/** + * 路由配置信息 + * + * @author ruoyi + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo +{ + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private boolean hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 路由参数:如 {"id": 1, "name": "ry"} + */ + private String query; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVo meta; + + /** + * 子路由 + */ + private List children; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + public boolean getHidden() + { + return hidden; + } + + public void setHidden(boolean hidden) + { + this.hidden = hidden; + } + + public String getRedirect() + { + return redirect; + } + + public void setRedirect(String redirect) + { + this.redirect = redirect; + } + + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public Boolean getAlwaysShow() + { + return alwaysShow; + } + + public void setAlwaysShow(Boolean alwaysShow) + { + this.alwaysShow = alwaysShow; + } + + public MetaVo getMeta() + { + return meta; + } + + public void setMeta(MetaVo meta) + { + this.meta = meta; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..13d49d6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java @@ -0,0 +1,76 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysConfig; + +/** + * 参数配置 数据层 + * + * @author ruoyi + */ +public interface SysConfigMapper +{ + /** + * 查询参数配置信息 + * + * @param config 参数配置信息 + * @return 参数配置信息 + */ + public SysConfig selectConfig(SysConfig config); + + /** + * 通过ID查询配置 + * + * @param configId 参数ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List selectConfigList(SysConfig config); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数配置信息 + */ + public SysConfig checkConfigKeyUnique(String configKey); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 删除参数配置 + * + * @param configId 参数ID + * @return 结果 + */ + public int deleteConfigById(Long configId); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + * @return 结果 + */ + public int deleteConfigByIds(Long[] configIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java new file mode 100644 index 0000000..384a9b6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java @@ -0,0 +1,118 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysDept; + +/** + * 部门管理 数据层 + * + * @author ruoyi + */ +public interface SysDeptMapper +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List selectDeptList(SysDept dept); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @param deptCheckStrictly 部门树选择项是否关联显示 + * @return 选中部门列表 + */ + public List selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门 + * + * @param deptId 部门ID + * @return 部门列表 + */ + public List selectChildrenDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public int hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 + */ + public int checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param deptName 部门名称 + * @param parentId 父部门ID + * @return 结果 + */ + public SysDept checkDeptNameUnique(@Param("deptName") String deptName, @Param("parentId") Long parentId); + + /** + * 新增部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 修改所在部门正常状态 + * + * @param deptIds 部门ID组 + */ + public void updateDeptStatusNormal(Long[] deptIds); + + /** + * 修改子元素关系 + * + * @param depts 子元素 + * @return 结果 + */ + public int updateDeptChildren(@Param("depts") List depts); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java new file mode 100644 index 0000000..25e7848 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java @@ -0,0 +1,106 @@ +package com.ruoyi.system.mapper; + +import java.util.List; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysDictData; + +/** + * 字典表 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysDictDataMapper +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List selectDictDataByType(String dictType); + + /** + * 根据字典类型批量查询字典数据 + * + * @param dictTypes 字典类型集合 + * @return 字典数据集合信息 + */ + public List selectDictDataByTypes(@Param("dictTypes") List dictTypes); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(@Param("dictType") String dictType, @Param("dictValue") String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据 + */ + public int countDictDataByType(String dictType); + + /** + * 通过字典ID删除字典数据信息 + * + * @param dictCode 字典数据ID + * @return 结果 + */ + public int deleteDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + * @return 结果 + */ + public int deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); + + /** + * 同步修改字典类型 + * + * @param oldDictType 旧字典类型 + * @param newDictType 新旧字典类型 + * @return 结果 + */ + public int updateDictDataType(@Param("oldDictType") String oldDictType, @Param("newDictType") String newDictType); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java new file mode 100644 index 0000000..5fb48fb --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,83 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysDictType; + +/** + * 字典表 数据层 + * + * @author ruoyi + */ +public interface SysDictTypeMapper +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll(); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 通过字典ID删除字典信息 + * + * @param dictId 字典ID + * @return 结果 + */ + public int deleteDictTypeById(Long dictId); + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + * @return 结果 + */ + public int deleteDictTypeByIds(Long[] dictIds); + + /** + * 新增字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public SysDictType checkDictTypeUnique(String dictType); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java new file mode 100644 index 0000000..629866f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java @@ -0,0 +1,42 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 数据层 + * + * @author ruoyi + */ +public interface SysLogininforMapper +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + * + * @return 结果 + */ + public int cleanLogininfor(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..99c0c50 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java @@ -0,0 +1,125 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysMenu; + +/** + * 菜单表 数据层 + * + * @author ruoyi + */ +public interface SysMenuMapper +{ + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List selectMenuList(SysMenu menu); + + /** + * 根据用户所有权限 + * + * @return 权限列表 + */ + public List selectMenuPerms(); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List selectMenuListByUserId(SysMenu menu); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public List selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public List selectMenuPermsByUserId(Long userId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + public List selectMenuTreeAll(); + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @param menuCheckStrictly 菜单树选择项是否关联显示 + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int hasChildByMenuId(Long menuId); + + /** + * 新增菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menuName 菜单名称 + * @param parentId 父菜单ID + * @return 结果 + */ + public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java new file mode 100644 index 0000000..c34f0a2 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java @@ -0,0 +1,60 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysNotice; + +/** + * 通知公告表 数据层 + * + * @author ruoyi + */ +public interface SysNoticeMapper +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 批量删除公告 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..2ae6457 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysOperLog; + +/** + * 操作日志 数据层 + * + * @author ruoyi + */ +public interface SysOperLogMapper +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java new file mode 100644 index 0000000..19be227 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java @@ -0,0 +1,99 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysPost; + +/** + * 岗位信息 数据层 + * + * @author ruoyi + */ +public interface SysPostMapper +{ + /** + * 查询岗位数据集合 + * + * @param post 岗位信息 + * @return 岗位数据集合 + */ + public List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId(Long userId); + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public List selectPostsByUserName(String userName); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 修改岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); + + /** + * 新增岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 校验岗位名称 + * + * @param postName 岗位名称 + * @return 结果 + */ + public SysPost checkPostNameUnique(String postName); + + /** + * 校验岗位编码 + * + * @param postCode 岗位编码 + * @return 结果 + */ + public SysPost checkPostCodeUnique(String postCode); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 0000000..f9d3a2f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,44 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysRoleDept; + +/** + * 角色与部门关联表 数据层 + * + * @author ruoyi + */ +public interface SysRoleDeptMapper +{ + /** + * 通过角色ID删除角色和部门关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleDeptByRoleId(Long roleId); + + /** + * 批量删除角色部门关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleDept(Long[] ids); + + /** + * 查询部门使用数量 + * + * @param deptId 部门ID + * @return 结果 + */ + public int selectCountRoleDeptByDeptId(Long deptId); + + /** + * 批量新增角色部门信息 + * + * @param roleDeptList 角色部门列表 + * @return 结果 + */ + public int batchRoleDept(List roleDeptList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..cf2bd8c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java @@ -0,0 +1,107 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysRole; + +/** + * 角色表 数据层 + * + * @author ruoyi + */ +public interface SysRoleMapper +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 根据用户ID查询角色 + * + * @param userName 用户名 + * @return 角色列表 + */ + public List selectRolesByUserName(String userName); + + /** + * 校验角色名称是否唯一 + * + * @param roleName 角色名称 + * @return 角色信息 + */ + public SysRole checkRoleNameUnique(String roleName); + + /** + * 校验角色权限是否唯一 + * + * @param roleKey 角色权限 + * @return 角色信息 + */ + public SysRole checkRoleKeyUnique(String roleKey); + + /** + * 修改角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 新增角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(Long[] roleIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..6602bee --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,44 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysRoleMenu; + +/** + * 角色与菜单关联表 数据层 + * + * @author ruoyi + */ +public interface SysRoleMenuMapper +{ + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int checkMenuExistRole(Long menuId); + + /** + * 通过角色ID删除角色和菜单关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleMenuByRoleId(Long roleId); + + /** + * 批量删除角色菜单关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleMenu(Long[] ids); + + /** + * 批量新增角色菜单信息 + * + * @param roleMenuList 角色菜单列表 + * @return 结果 + */ + public int batchRoleMenu(List roleMenuList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..76e1c79 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java @@ -0,0 +1,127 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysUser; + +/** + * 用户表 数据层 + * + * @author ruoyi + */ +public interface SysUserMapper +{ + /** + * 根据条件分页查询用户列表 + * + * @param sysUser 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser sysUser); + + /** + * 根据条件分页查询已配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(@Param("userName") String userName, @Param("password") String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + public int deleteUserByIds(Long[] userIds); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + public SysUser checkUserNameUnique(String userName); + + /** + * 校验手机号码是否唯一 + * + * @param phonenumber 手机号码 + * @return 结果 + */ + public SysUser checkPhoneUnique(String phonenumber); + + /** + * 校验email是否唯一 + * + * @param email 用户邮箱 + * @return 结果 + */ + public SysUser checkEmailUnique(String email); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java new file mode 100644 index 0000000..e08991d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java @@ -0,0 +1,44 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysUserPost; + +/** + * 用户与岗位关联表 数据层 + * + * @author ruoyi + */ +public interface SysUserPostMapper +{ + /** + * 通过用户ID删除用户和岗位关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserPostByUserId(Long userId); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 批量删除用户和岗位关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserPost(Long[] ids); + + /** + * 批量新增用户岗位信息 + * + * @param userPostList 用户角色列表 + * @return 结果 + */ + public int batchUserPost(List userPostList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..3143ec8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,62 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.system.domain.SysUserRole; + +/** + * 用户与角色关联表 数据层 + * + * @author ruoyi + */ +public interface SysUserRoleMapper +{ + /** + * 通过用户ID删除用户和角色关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserRoleByUserId(Long userId); + + /** + * 批量删除用户和角色关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserRole(Long[] ids); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 批量新增用户角色信息 + * + * @param userRoleList 用户角色列表 + * @return 结果 + */ + public int batchUserRole(List userRoleList); + + /** + * 删除用户和角色关联信息 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteUserRoleInfo(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java new file mode 100644 index 0000000..b307776 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java @@ -0,0 +1,89 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysConfig; + +/** + * 参数配置 服务层 + * + * @author ruoyi + */ +public interface ISysConfigService +{ + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数键值 + */ + public String selectConfigByKey(String configKey); + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + public boolean selectCaptchaEnabled(); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List selectConfigList(SysConfig config); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + public void deleteConfigByIds(Long[] configIds); + + /** + * 加载参数缓存数据 + */ + public void loadingConfigCache(); + + /** + * 清空参数缓存数据 + */ + public void clearConfigCache(); + + /** + * 重置参数缓存数据 + */ + public void resetConfigCache(); + + /** + * 校验参数键名是否唯一 + * + * @param config 参数信息 + * @return 结果 + */ + public boolean checkConfigKeyUnique(SysConfig config); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java new file mode 100644 index 0000000..f228208 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java @@ -0,0 +1,124 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysDept; + +/** + * 部门管理 服务层 + * + * @author ruoyi + */ +public interface ISysDeptService +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List selectDeptList(SysDept dept); + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + public List selectDeptTreeList(SysDept dept); + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + public List buildDeptTree(List depts); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + public List buildDeptTreeSelect(List depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + public List selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public boolean hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + public boolean checkDeptNameUnique(SysDept dept); + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + public void checkDeptDataScope(Long deptId); + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java new file mode 100644 index 0000000..9bc4f13 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java @@ -0,0 +1,60 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysDictData; + +/** + * 字典 业务层 + * + * @author ruoyi + */ +public interface ISysDictDataService +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + public void deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java new file mode 100644 index 0000000..01c1c1d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java @@ -0,0 +1,98 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.domain.entity.SysDictType; + +/** + * 字典 业务层 + * + * @author ruoyi + */ +public interface ISysDictTypeService +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll(); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List selectDictDataByType(String dictType); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 批量删除字典信息 + * + * @param dictIds 需要删除的字典ID + */ + public void deleteDictTypeByIds(Long[] dictIds); + + /** + * 加载字典缓存数据 + */ + public void loadingDictCache(); + + /** + * 清空字典缓存数据 + */ + public void clearDictCache(); + + /** + * 重置字典缓存数据 + */ + public void resetDictCache(); + + /** + * 新增保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public boolean checkDictTypeUnique(SysDictType dictType); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java new file mode 100644 index 0000000..ce3151d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java @@ -0,0 +1,40 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 服务层 + * + * @author ruoyi + */ +public interface ISysLogininforService +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + */ + public void cleanLogininfor(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java new file mode 100644 index 0000000..7d60696 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java @@ -0,0 +1,144 @@ +package com.ruoyi.system.service; + +import java.util.List; +import java.util.Set; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.system.domain.vo.RouterVo; + +/** + * 菜单 业务层 + * + * @author ruoyi + */ +public interface ISysMenuService +{ + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuList(Long userId); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuList(SysMenu menu, Long userId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public Set selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId(Long roleId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + public List buildMenus(List menus); + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + public List buildMenuTree(List menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + public List buildMenuTreeSelect(List menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean hasChildByMenuId(Long menuId); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkMenuExistRole(Long menuId); + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean checkMenuNameUnique(SysMenu menu); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java new file mode 100644 index 0000000..47ce1b7 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java @@ -0,0 +1,60 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysNotice; + +/** + * 公告 服务层 + * + * @author ruoyi + */ +public interface ISysNoticeService +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java new file mode 100644 index 0000000..4fd8e5a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysOperLog; + +/** + * 操作日志 服务层 + * + * @author ruoyi + */ +public interface ISysOperLogService +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java new file mode 100644 index 0000000..84779bf --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java @@ -0,0 +1,99 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysPost; + +/** + * 岗位信息 服务层 + * + * @author ruoyi + */ +public interface ISysPostService +{ + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位列表 + */ + public List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId(Long userId); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * @return 结果 + */ + public boolean checkPostNameUnique(SysPost post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * @return 结果 + */ + public boolean checkPostCodeUnique(SysPost post); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java new file mode 100644 index 0000000..6c29f09 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java @@ -0,0 +1,173 @@ +package com.ruoyi.system.service; + +import java.util.List; +import java.util.Set; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.system.domain.SysUserRole; + +/** + * 角色业务层 + * + * @author ruoyi + */ +public interface ISysRoleService +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色列表 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolesByUserId(Long userId); + + /** + * 根据用户ID查询角色权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public boolean checkRoleNameUnique(SysRole role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public boolean checkRoleKeyUnique(SysRole role); + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + public void checkRoleAllowed(SysRole role); + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + public void checkRoleDataScope(Long roleId); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRoleStatus(SysRole role); + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int authDataScope(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(Long[] roleIds); + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteAuthUser(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + public int deleteAuthUsers(Long roleId, Long[] userIds); + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int insertAuthUsers(Long roleId, Long[] userIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java new file mode 100644 index 0000000..8eb5448 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.service; + +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.system.domain.SysUserOnline; + +/** + * 在线用户 服务层 + * + * @author ruoyi + */ +public interface ISysUserOnlineService +{ + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user); + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user); + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user); + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + public SysUserOnline loginUserToUserOnline(LoginUser user); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java new file mode 100644 index 0000000..10bc2ab --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java @@ -0,0 +1,206 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysUser; + +/** + * 用户 业务层 + * + * @author ruoyi + */ +public interface ISysUserService +{ + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser user); + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserRoleGroup(String userName); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserPostGroup(String userName); + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkUserNameUnique(SysUser user); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkPhoneUnique(SysUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkEmailUnique(SysUser user); + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + public void checkUserAllowed(SysUser user); + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + public void checkUserDataScope(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean registerUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserAuth(Long userId, Long[] roleIds); + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserStatus(SysUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserProfile(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public boolean updateUserAvatar(String userName, String avatar); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + public int resetPwd(SysUser user); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(String userName, String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + public int deleteUserByIds(Long[] userIds); + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + public String importUser(List userList, Boolean isUpdateSupport, String operName); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..83750e7 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,232 @@ +package com.ruoyi.system.service.impl; + +import java.util.Collection; +import java.util.List; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.enums.DataSourceType; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.mapper.SysConfigMapper; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 参数配置 服务层实现 + * + * @author ruoyi + */ +@Service +public class SysConfigServiceImpl implements ISysConfigService +{ + @Autowired + private SysConfigMapper configMapper; + + @Autowired + private RedisCache redisCache; + + /** + * 项目启动时,初始化参数到缓存 + */ + @PostConstruct + public void init() + { + loadingConfigCache(); + } + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + @Override + @DataSource(DataSourceType.MASTER) + public SysConfig selectConfigById(Long configId) + { + SysConfig config = new SysConfig(); + config.setConfigId(configId); + return configMapper.selectConfig(config); + } + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数key + * @return 参数键值 + */ + @Override + public String selectConfigByKey(String configKey) + { + String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey))); + if (StringUtils.isNotEmpty(configValue)) + { + return configValue; + } + SysConfig config = new SysConfig(); + config.setConfigKey(configKey); + SysConfig retConfig = configMapper.selectConfig(config); + if (StringUtils.isNotNull(retConfig)) + { + redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue()); + return retConfig.getConfigValue(); + } + return StringUtils.EMPTY; + } + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + @Override + public boolean selectCaptchaEnabled() + { + String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled"); + if (StringUtils.isEmpty(captchaEnabled)) + { + return true; + } + return Convert.toBool(captchaEnabled); + } + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + @Override + public List selectConfigList(SysConfig config) + { + return configMapper.selectConfigList(config); + } + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int insertConfig(SysConfig config) + { + int row = configMapper.insertConfig(config); + if (row > 0) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int updateConfig(SysConfig config) + { + SysConfig temp = configMapper.selectConfigById(config.getConfigId()); + if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) + { + redisCache.deleteObject(getCacheKey(temp.getConfigKey())); + } + + int row = configMapper.updateConfig(config); + if (row > 0) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + @Override + public void deleteConfigByIds(Long[] configIds) + { + for (Long configId : configIds) + { + SysConfig config = selectConfigById(configId); + if (StringUtils.equals(UserConstants.YES, config.getConfigType())) + { + throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey())); + } + configMapper.deleteConfigById(configId); + redisCache.deleteObject(getCacheKey(config.getConfigKey())); + } + } + + /** + * 加载参数缓存数据 + */ + @Override + public void loadingConfigCache() + { + List configsList = configMapper.selectConfigList(new SysConfig()); + for (SysConfig config : configsList) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + } + + /** + * 清空参数缓存数据 + */ + @Override + public void clearConfigCache() + { + Collection keys = redisCache.keys(CacheConstants.SYS_CONFIG_KEY + "*"); + redisCache.deleteObject(keys); + } + + /** + * 重置参数缓存数据 + */ + @Override + public void resetConfigCache() + { + clearConfigCache(); + loadingConfigCache(); + } + + /** + * 校验参数键名是否唯一 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public boolean checkConfigKeyUnique(SysConfig config) + { + Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId(); + SysConfig info = configMapper.checkConfigKeyUnique(config.getConfigKey()); + if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + private String getCacheKey(String configKey) + { + return CacheConstants.SYS_CONFIG_KEY + configKey; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..f7fb088 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,338 @@ +package com.ruoyi.system.service.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.mapper.SysDeptMapper; +import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.service.ISysDeptService; + +/** + * 部门管理 服务实现 + * + * @author ruoyi + */ +@Service +public class SysDeptServiceImpl implements ISysDeptService +{ + @Autowired + private SysDeptMapper deptMapper; + + @Autowired + private SysRoleMapper roleMapper; + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + @Override + @DataScope(deptAlias = "d") + public List selectDeptList(SysDept dept) + { + return deptMapper.selectDeptList(dept); + } + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + @Override + public List selectDeptTreeList(SysDept dept) + { + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + return buildDeptTreeSelect(depts); + } + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + @Override + public List buildDeptTree(List depts) + { + List returnList = new ArrayList(); + List tempList = depts.stream().map(SysDept::getDeptId).collect(Collectors.toList()); + for (SysDept dept : depts) + { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(dept.getParentId())) + { + recursionFn(depts, dept); + returnList.add(dept); + } + } + if (returnList.isEmpty()) + { + returnList = depts; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + @Override + public List buildDeptTreeSelect(List depts) + { + List deptTrees = buildDeptTree(depts); + return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + @Override + public List selectDeptListByRoleId(Long roleId) + { + SysRole role = roleMapper.selectRoleById(roleId); + return deptMapper.selectDeptListByRoleId(roleId, role.isDeptCheckStrictly()); + } + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + @Override + public SysDept selectDeptById(Long deptId) + { + return deptMapper.selectDeptById(deptId); + } + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + @Override + public int selectNormalChildrenDeptById(Long deptId) + { + return deptMapper.selectNormalChildrenDeptById(deptId); + } + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public boolean hasChildByDeptId(Long deptId) + { + int result = deptMapper.hasChildByDeptId(deptId); + return result > 0; + } + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkDeptExistUser(Long deptId) + { + int result = deptMapper.checkDeptExistUser(deptId); + return result > 0; + } + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public boolean checkDeptNameUnique(SysDept dept) + { + Long deptId = StringUtils.isNull(dept.getDeptId()) ? -1L : dept.getDeptId(); + SysDept info = deptMapper.checkDeptNameUnique(dept.getDeptName(), dept.getParentId()); + if (StringUtils.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + @Override + public void checkDeptDataScope(Long deptId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + SysDept dept = new SysDept(); + dept.setDeptId(deptId); + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + if (StringUtils.isEmpty(depts)) + { + throw new ServiceException("没有权限访问部门数据!"); + } + } + } + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int insertDept(SysDept dept) + { + SysDept info = deptMapper.selectDeptById(dept.getParentId()); + // 如果父节点不为正常状态,则不允许新增子节点 + if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) + { + throw new ServiceException("部门停用,不允许新增"); + } + dept.setAncestors(info.getAncestors() + "," + dept.getParentId()); + return deptMapper.insertDept(dept); + } + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int updateDept(SysDept dept) + { + SysDept newParentDept = deptMapper.selectDeptById(dept.getParentId()); + SysDept oldDept = deptMapper.selectDeptById(dept.getDeptId()); + if (StringUtils.isNotNull(newParentDept) && StringUtils.isNotNull(oldDept)) + { + String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId(); + String oldAncestors = oldDept.getAncestors(); + dept.setAncestors(newAncestors); + updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors); + } + int result = deptMapper.updateDept(dept); + if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors()) + && !StringUtils.equals("0", dept.getAncestors())) + { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + updateParentDeptStatusNormal(dept); + } + return result; + } + + /** + * 修改该部门的父级部门状态 + * + * @param dept 当前部门 + */ + private void updateParentDeptStatusNormal(SysDept dept) + { + String ancestors = dept.getAncestors(); + Long[] deptIds = Convert.toLongArray(ancestors); + deptMapper.updateDeptStatusNormal(deptIds); + } + + /** + * 修改子元素关系 + * + * @param deptId 被修改的部门ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) + { + List children = deptMapper.selectChildrenDeptById(deptId); + for (SysDept child : children) + { + child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + } + if (children.size() > 0) + { + deptMapper.updateDeptChildren(children); + } + } + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public int deleteDeptById(Long deptId) + { + return deptMapper.deleteDeptById(deptId); + } + + /** + * 递归列表 + */ + private void recursionFn(List list, SysDept t) + { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysDept tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysDept t) + { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) + { + SysDept n = (SysDept) it.next(); + if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysDept t) + { + return getChildList(list, t).size() > 0; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..fced569 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,111 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.system.mapper.SysDictDataMapper; +import com.ruoyi.system.service.ISysDictDataService; + +/** + * 字典 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysDictDataServiceImpl implements ISysDictDataService +{ + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataList(SysDictData dictData) + { + return dictDataMapper.selectDictDataList(dictData); + } + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + @Override + public String selectDictLabel(String dictType, String dictValue) + { + return dictDataMapper.selectDictLabel(dictType, dictValue); + } + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + @Override + public SysDictData selectDictDataById(Long dictCode) + { + return dictDataMapper.selectDictDataById(dictCode); + } + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + @Override + public void deleteDictDataByIds(Long[] dictCodes) + { + for (Long dictCode : dictCodes) + { + SysDictData data = selectDictDataById(dictCode); + dictDataMapper.deleteDictDataById(dictCode); + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + } + + /** + * 新增保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int insertDictData(SysDictData data) + { + int row = dictDataMapper.insertDictData(data); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } + + /** + * 修改保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int updateDictData(SysDictData data) + { + int row = dictDataMapper.updateDictData(data); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..37928c4 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,223 @@ +package com.ruoyi.system.service.impl; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.domain.entity.SysDictType; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.mapper.SysDictDataMapper; +import com.ruoyi.system.mapper.SysDictTypeMapper; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 字典 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysDictTypeServiceImpl implements ISysDictTypeService +{ + @Autowired + private SysDictTypeMapper dictTypeMapper; + + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 项目启动时,初始化字典到缓存 + */ + @PostConstruct + public void init() + { + loadingDictCache(); + } + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeList(SysDictType dictType) + { + return dictTypeMapper.selectDictTypeList(dictType); + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeAll() + { + return dictTypeMapper.selectDictTypeAll(); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataByType(String dictType) + { + List dictDatas = DictUtils.getDictCache(dictType); + if (StringUtils.isNotEmpty(dictDatas)) + { + return dictDatas; + } + dictDatas = dictDataMapper.selectDictDataByType(dictType); + if (StringUtils.isNotEmpty(dictDatas)) + { + DictUtils.setDictCache(dictType, dictDatas); + return dictDatas; + } + return null; + } + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeById(Long dictId) + { + return dictTypeMapper.selectDictTypeById(dictId); + } + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeByType(String dictType) + { + return dictTypeMapper.selectDictTypeByType(dictType); + } + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + */ + @Override + public void deleteDictTypeByIds(Long[] dictIds) + { + for (Long dictId : dictIds) + { + SysDictType dictType = selectDictTypeById(dictId); + if (dictDataMapper.countDictDataByType(dictType.getDictType()) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName())); + } + dictTypeMapper.deleteDictTypeById(dictId); + DictUtils.removeDictCache(dictType.getDictType()); + } + } + + /** + * 加载字典缓存数据 + */ + @Override + public void loadingDictCache() + { + SysDictData dictData = new SysDictData(); + dictData.setStatus("0"); + Map> dictDataMap = dictDataMapper.selectDictDataList(dictData).stream().collect(Collectors.groupingBy(SysDictData::getDictType)); + for (Map.Entry> entry : dictDataMap.entrySet()) + { + DictUtils.setDictCache(entry.getKey(), entry.getValue().stream().sorted(Comparator.comparing(SysDictData::getDictSort)).collect(Collectors.toList())); + } + } + + /** + * 清空字典缓存数据 + */ + @Override + public void clearDictCache() + { + DictUtils.clearDictCache(); + } + + /** + * 重置字典缓存数据 + */ + @Override + public void resetDictCache() + { + clearDictCache(); + loadingDictCache(); + } + + /** + * 新增保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + public int insertDictType(SysDictType dict) + { + int row = dictTypeMapper.insertDictType(dict); + if (row > 0) + { + DictUtils.setDictCache(dict.getDictType(), null); + } + return row; + } + + /** + * 修改保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + @Transactional + public int updateDictType(SysDictType dict) + { + SysDictType oldDict = dictTypeMapper.selectDictTypeById(dict.getDictId()); + dictDataMapper.updateDictDataType(oldDict.getDictType(), dict.getDictType()); + int row = dictTypeMapper.updateDictType(dict); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(dict.getDictType()); + DictUtils.setDictCache(dict.getDictType(), dictDatas); + } + return row; + } + + /** + * 校验字典类型称是否唯一 + * + * @param dict 字典类型 + * @return 结果 + */ + @Override + public boolean checkDictTypeUnique(SysDictType dict) + { + Long dictId = StringUtils.isNull(dict.getDictId()) ? -1L : dict.getDictId(); + SysDictType dictType = dictTypeMapper.checkDictTypeUnique(dict.getDictType()); + if (StringUtils.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 0000000..216aecb --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,65 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.mapper.SysLogininforMapper; +import com.ruoyi.system.service.ISysLogininforService; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysLogininforServiceImpl implements ISysLogininforService +{ + + @Autowired + private SysLogininforMapper logininforMapper; + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public void insertLogininfor(SysLogininfor logininfor) + { + logininforMapper.insertLogininfor(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + @Override + public List selectLogininforList(SysLogininfor logininfor) + { + return logininforMapper.selectLogininforList(logininfor); + } + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + @Override + public int deleteLogininforByIds(Long[] infoIds) + { + return logininforMapper.deleteLogininforByIds(infoIds); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor() + { + logininforMapper.cleanLogininfor(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..225c280 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,531 @@ +package com.ruoyi.system.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.vo.MetaVo; +import com.ruoyi.system.domain.vo.RouterVo; +import com.ruoyi.system.mapper.SysMenuMapper; +import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.mapper.SysRoleMenuMapper; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 菜单 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysMenuServiceImpl implements ISysMenuService +{ + public static final String PREMISSION_STRING = "perms[\"{0}\"]"; + + @Autowired + private SysMenuMapper menuMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + @Override + public List selectMenuList(Long userId) + { + return selectMenuList(new SysMenu(), userId); + } + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + @Override + public List selectMenuList(SysMenu menu, Long userId) + { + List menuList = null; + // 管理员显示所有菜单信息 + if (SysUser.isAdmin(userId)) + { + menuList = menuMapper.selectMenuList(menu); + } + else + { + menu.getParams().put("userId", userId); + menuList = menuMapper.selectMenuListByUserId(menu); + } + return menuList; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByUserId(Long userId) + { + List perms = menuMapper.selectMenuPermsByUserId(userId); + Set permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByRoleId(Long roleId) + { + List perms = menuMapper.selectMenuPermsByRoleId(roleId); + Set permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户名称 + * @return 菜单列表 + */ + @Override + public List selectMenuTreeByUserId(Long userId) + { + List menus = null; + if (SecurityUtils.isAdmin(userId)) + { + menus = menuMapper.selectMenuTreeAll(); + } + else + { + menus = menuMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, 0); + } + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + @Override + public List selectMenuListByRoleId(Long roleId) + { + SysRole role = roleMapper.selectRoleById(roleId); + return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly()); + } + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + @Override + public List buildMenus(List menus) + { + List routers = new LinkedList(); + for (SysMenu menu : menus) + { + RouterVo router = new RouterVo(); + router.setHidden("1".equals(menu.getVisible())); + router.setName(getRouteName(menu)); + router.setPath(getRouterPath(menu)); + router.setComponent(getComponent(menu)); + router.setQuery(menu.getQuery()); + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + List cMenus = menu.getChildren(); + if (StringUtils.isNotEmpty(cMenus) && UserConstants.TYPE_DIR.equals(menu.getMenuType())) + { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } + else if (isMenuFrame(menu)) + { + router.setMeta(null); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(StringUtils.capitalize(menu.getPath())); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + children.setQuery(menu.getQuery()); + childrenList.add(children); + router.setChildren(childrenList); + } + else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) + { + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); + router.setPath("/"); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + String routerPath = innerLinkReplaceEach(menu.getPath()); + children.setPath(routerPath); + children.setComponent(UserConstants.INNER_LINK); + children.setName(StringUtils.capitalize(routerPath)); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); + childrenList.add(children); + router.setChildren(childrenList); + } + routers.add(router); + } + return routers; + } + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + @Override + public List buildMenuTree(List menus) + { + List returnList = new ArrayList(); + List tempList = menus.stream().map(SysMenu::getMenuId).collect(Collectors.toList()); + for (Iterator iterator = menus.iterator(); iterator.hasNext();) + { + SysMenu menu = (SysMenu) iterator.next(); + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(menu.getParentId())) + { + recursionFn(menus, menu); + returnList.add(menu); + } + } + if (returnList.isEmpty()) + { + returnList = menus; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + @Override + public List buildMenuTreeSelect(List menus) + { + List menuTrees = buildMenuTree(menus); + return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + @Override + public SysMenu selectMenuById(Long menuId) + { + return menuMapper.selectMenuById(menuId); + } + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(Long menuId) + { + int result = menuMapper.hasChildByMenuId(menuId); + return result > 0; + } + + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean checkMenuExistRole(Long menuId) + { + int result = roleMenuMapper.checkMenuExistRole(menuId); + return result > 0; + } + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int insertMenu(SysMenu menu) + { + return menuMapper.insertMenu(menu); + } + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int updateMenu(SysMenu menu) + { + return menuMapper.updateMenu(menu); + } + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public int deleteMenuById(Long menuId) + { + return menuMapper.deleteMenuById(menuId); + } + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public boolean checkMenuNameUnique(SysMenu menu) + { + Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId(); + SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId()); + if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 获取路由名称 + * + * @param menu 菜单信息 + * @return 路由名称 + */ + public String getRouteName(SysMenu menu) + { + String routerName = StringUtils.capitalize(menu.getPath()); + // 非外链并且是一级目录(类型为目录) + if (isMenuFrame(menu)) + { + routerName = StringUtils.EMPTY; + } + return routerName; + } + + /** + * 获取路由地址 + * + * @param menu 菜单信息 + * @return 路由地址 + */ + public String getRouterPath(SysMenu menu) + { + String routerPath = menu.getPath(); + // 内链打开外网方式 + if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) + { + routerPath = innerLinkReplaceEach(routerPath); + } + // 非外链并且是一级目录(类型为目录) + if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType()) + && UserConstants.NO_FRAME.equals(menu.getIsFrame())) + { + routerPath = "/" + menu.getPath(); + } + // 非外链并且是一级目录(类型为菜单) + else if (isMenuFrame(menu)) + { + routerPath = "/"; + } + return routerPath; + } + + /** + * 获取组件信息 + * + * @param menu 菜单信息 + * @return 组件信息 + */ + public String getComponent(SysMenu menu) + { + String component = UserConstants.LAYOUT; + if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) + { + component = menu.getComponent(); + } + else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) + { + component = UserConstants.INNER_LINK; + } + else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) + { + component = UserConstants.PARENT_VIEW; + } + return component; + } + + /** + * 是否为菜单内部跳转 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isMenuFrame(SysMenu menu) + { + return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType()) + && menu.getIsFrame().equals(UserConstants.NO_FRAME); + } + + /** + * 是否为内链组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isInnerLink(SysMenu menu) + { + return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath()); + } + + /** + * 是否为parent_view组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isParentView(SysMenu menu) + { + return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType()); + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + public List getChildPerms(List list, int parentId) + { + List returnList = new ArrayList(); + for (Iterator iterator = list.iterator(); iterator.hasNext();) + { + SysMenu t = (SysMenu) iterator.next(); + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) + { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + * + * @param list 分类表 + * @param t 子节点 + */ + private void recursionFn(List list, SysMenu t) + { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysMenu tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysMenu t) + { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) + { + SysMenu n = (SysMenu) it.next(); + if (n.getParentId().longValue() == t.getMenuId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysMenu t) + { + return getChildList(list, t).size() > 0; + } + + /** + * 内链域名特殊字符替换 + * + * @return 替换后的内链域名 + */ + public String innerLinkReplaceEach(String path) + { + return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS, Constants.WWW, "." }, + new String[] { "", "", "", "/" }); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 0000000..765438b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,92 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.domain.SysNotice; +import com.ruoyi.system.mapper.SysNoticeMapper; +import com.ruoyi.system.service.ISysNoticeService; + +/** + * 公告 服务层实现 + * + * @author ruoyi + */ +@Service +public class SysNoticeServiceImpl implements ISysNoticeService +{ + @Autowired + private SysNoticeMapper noticeMapper; + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + @Override + public SysNotice selectNoticeById(Long noticeId) + { + return noticeMapper.selectNoticeById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + @Override + public List selectNoticeList(SysNotice notice) + { + return noticeMapper.selectNoticeList(notice); + } + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int insertNotice(SysNotice notice) + { + return noticeMapper.insertNotice(notice); + } + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int updateNotice(SysNotice notice) + { + return noticeMapper.updateNotice(notice); + } + + /** + * 删除公告对象 + * + * @param noticeId 公告ID + * @return 结果 + */ + @Override + public int deleteNoticeById(Long noticeId) + { + return noticeMapper.deleteNoticeById(noticeId); + } + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + @Override + public int deleteNoticeByIds(Long[] noticeIds) + { + return noticeMapper.deleteNoticeByIds(noticeIds); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..5489815 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,76 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.mapper.SysOperLogMapper; +import com.ruoyi.system.service.ISysOperLogService; + +/** + * 操作日志 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysOperLogServiceImpl implements ISysOperLogService +{ + @Autowired + private SysOperLogMapper operLogMapper; + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + @Override + public void insertOperlog(SysOperLog operLog) + { + operLogMapper.insertOperlog(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + @Override + public List selectOperLogList(SysOperLog operLog) + { + return operLogMapper.selectOperLogList(operLog); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + @Override + public int deleteOperLogByIds(Long[] operIds) + { + return operLogMapper.deleteOperLogByIds(operIds); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + @Override + public SysOperLog selectOperLogById(Long operId) + { + return operLogMapper.selectOperLogById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog() + { + operLogMapper.cleanOperLog(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..5e5fe06 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,178 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.mapper.SysPostMapper; +import com.ruoyi.system.mapper.SysUserPostMapper; +import com.ruoyi.system.service.ISysPostService; + +/** + * 岗位信息 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysPostServiceImpl implements ISysPostService +{ + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位信息集合 + */ + @Override + public List selectPostList(SysPost post) + { + return postMapper.selectPostList(post); + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List selectPostAll() + { + return postMapper.selectPostAll(); + } + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + @Override + public SysPost selectPostById(Long postId) + { + return postMapper.selectPostById(postId); + } + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + @Override + public List selectPostListByUserId(Long userId) + { + return postMapper.selectPostListByUserId(userId); + } + + /** + * 校验岗位名称是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostNameUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostNameUnique(post.getPostName()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验岗位编码是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostCodeUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostCodeUnique(post.getPostCode()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int countUserPostById(Long postId) + { + return userPostMapper.countUserPostById(postId); + } + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int deletePostById(Long postId) + { + return postMapper.deletePostById(postId); + } + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + @Override + public int deletePostByIds(Long[] postIds) + { + for (Long postId : postIds) + { + SysPost post = selectPostById(postId); + if (countUserPostById(postId) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName())); + } + } + return postMapper.deletePostByIds(postIds); + } + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int insertPost(SysPost post) + { + return postMapper.insertPost(post); + } + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int updatePost(SysPost post) + { + return postMapper.updatePost(post); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..d6cee80 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,424 @@ +package com.ruoyi.system.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.domain.SysRoleDept; +import com.ruoyi.system.domain.SysRoleMenu; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.mapper.SysRoleDeptMapper; +import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.mapper.SysRoleMenuMapper; +import com.ruoyi.system.mapper.SysUserRoleMapper; +import com.ruoyi.system.service.ISysRoleService; + +/** + * 角色 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysRoleServiceImpl implements ISysRoleService +{ + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysRoleDeptMapper roleDeptMapper; + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + @Override + @DataScope(deptAlias = "d") + public List selectRoleList(SysRole role) + { + return roleMapper.selectRoleList(role); + } + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesByUserId(Long userId) + { + List userRoles = roleMapper.selectRolePermissionByUserId(userId); + List roles = selectRoleAll(); + for (SysRole role : roles) + { + for (SysRole userRole : userRoles) + { + if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) + { + role.setFlag(true); + break; + } + } + } + return roles; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectRolePermissionByUserId(Long userId) + { + List perms = roleMapper.selectRolePermissionByUserId(userId); + Set permsSet = new HashSet<>(); + for (SysRole perm : perms) + { + if (StringUtils.isNotNull(perm)) + { + permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); + } + } + return permsSet; + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + @Override + public List selectRoleAll() + { + return SpringUtils.getAopProxy(this).selectRoleList(new SysRole()); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + @Override + public List selectRoleListByUserId(Long userId) + { + return roleMapper.selectRoleListByUserId(userId); + } + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + @Override + public SysRole selectRoleById(Long roleId) + { + return roleMapper.selectRoleById(roleId); + } + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleNameUnique(SysRole role) + { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleNameUnique(role.getRoleName()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleKeyUnique(SysRole role) + { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleKeyUnique(role.getRoleKey()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + @Override + public void checkRoleAllowed(SysRole role) + { + if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin()) + { + throw new ServiceException("不允许操作超级管理员角色"); + } + } + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + @Override + public void checkRoleDataScope(Long roleId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + SysRole role = new SysRole(); + role.setRoleId(roleId); + List roles = SpringUtils.getAopProxy(this).selectRoleList(role); + if (StringUtils.isEmpty(roles)) + { + throw new ServiceException("没有权限访问角色数据!"); + } + } + } + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + public int countUserRoleByRoleId(Long roleId) + { + return userRoleMapper.countUserRoleByRoleId(roleId); + } + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int insertRole(SysRole role) + { + // 新增角色信息 + roleMapper.insertRole(role); + return insertRoleMenu(role); + } + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int updateRole(SysRole role) + { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(role.getRoleId()); + return insertRoleMenu(role); + } + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public int updateRoleStatus(SysRole role) + { + return roleMapper.updateRole(role); + } + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int authDataScope(SysRole role) + { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(role.getRoleId()); + // 新增角色和部门信息(数据权限) + return insertRoleDept(role); + } + + /** + * 新增角色菜单信息 + * + * @param role 角色对象 + */ + public int insertRoleMenu(SysRole role) + { + int rows = 1; + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long menuId : role.getMenuIds()) + { + SysRoleMenu rm = new SysRoleMenu(); + rm.setRoleId(role.getRoleId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (list.size() > 0) + { + rows = roleMenuMapper.batchRoleMenu(list); + } + return rows; + } + + /** + * 新增角色部门信息(数据权限) + * + * @param role 角色对象 + */ + public int insertRoleDept(SysRole role) + { + int rows = 1; + // 新增角色与部门(数据权限)管理 + List list = new ArrayList(); + for (Long deptId : role.getDeptIds()) + { + SysRoleDept rd = new SysRoleDept(); + rd.setRoleId(role.getRoleId()); + rd.setDeptId(deptId); + list.add(rd); + } + if (list.size() > 0) + { + rows = roleDeptMapper.batchRoleDept(list); + } + return rows; + } + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleById(Long roleId) + { + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(roleId); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(roleId); + return roleMapper.deleteRoleById(roleId); + } + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleByIds(Long[] roleIds) + { + for (Long roleId : roleIds) + { + checkRoleAllowed(new SysRole(roleId)); + checkRoleDataScope(roleId); + SysRole role = selectRoleById(roleId); + if (countUserRoleByRoleId(roleId) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName())); + } + } + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenu(roleIds); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDept(roleIds); + return roleMapper.deleteRoleByIds(roleIds); + } + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + @Override + public int deleteAuthUser(SysUserRole userRole) + { + return userRoleMapper.deleteUserRoleInfo(userRole); + } + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + @Override + public int deleteAuthUsers(Long roleId, Long[] userIds) + { + return userRoleMapper.deleteUserRoleInfos(roleId, userIds); + } + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要授权的用户数据ID + * @return 结果 + */ + @Override + public int insertAuthUsers(Long roleId, Long[] userIds) + { + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long userId : userIds) + { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + return userRoleMapper.batchUserRole(list); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java new file mode 100644 index 0000000..f80a877 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java @@ -0,0 +1,96 @@ +package com.ruoyi.system.service.impl; + +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysUserOnline; +import com.ruoyi.system.service.ISysUserOnlineService; + +/** + * 在线用户 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysUserOnlineServiceImpl implements ISysUserOnlineService +{ + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user) + { + if (StringUtils.equals(ipaddr, user.getIpaddr())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user) + { + if (StringUtils.equals(userName, user.getUsername())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user) + { + if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + @Override + public SysUserOnline loginUserToUserOnline(LoginUser user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUser())) + { + return null; + } + SysUserOnline sysUserOnline = new SysUserOnline(); + sysUserOnline.setTokenId(user.getToken()); + sysUserOnline.setUserName(user.getUsername()); + sysUserOnline.setIpaddr(user.getIpaddr()); + sysUserOnline.setLoginLocation(user.getLoginLocation()); + sysUserOnline.setBrowser(user.getBrowser()); + sysUserOnline.setOs(user.getOs()); + sysUserOnline.setLoginTime(user.getLoginTime()); + if (StringUtils.isNotNull(user.getUser().getDept())) + { + sysUserOnline.setDeptName(user.getUser().getDept().getDeptName()); + } + return sysUserOnline; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..1d22c23 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,544 @@ +package com.ruoyi.system.service.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import jakarta.validation.Validator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanValidators; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.domain.SysUserPost; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.mapper.SysPostMapper; +import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.mapper.SysUserMapper; +import com.ruoyi.system.mapper.SysUserPostMapper; +import com.ruoyi.system.mapper.SysUserRoleMapper; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 用户 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysUserServiceImpl implements ISysUserService +{ + private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class); + + @Autowired + private SysUserMapper userMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + @Autowired + private ISysConfigService configService; + + @Autowired + protected Validator validator; + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUserList(SysUser user) + { + return userMapper.selectUserList(user); + } + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectAllocatedList(SysUser user) + { + return userMapper.selectAllocatedList(user); + } + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUnallocatedList(SysUser user) + { + return userMapper.selectUnallocatedList(user); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByUserName(String userName) + { + return userMapper.selectUserByUserName(userName); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public SysUser selectUserById(Long userId) + { + return userMapper.selectUserById(userId); + } + + /** + * 查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserRoleGroup(String userName) + { + List list = roleMapper.selectRolesByUserName(userName); + if (CollectionUtils.isEmpty(list)) + { + return StringUtils.EMPTY; + } + return list.stream().map(SysRole::getRoleName).collect(Collectors.joining(",")); + } + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserPostGroup(String userName) + { + List list = postMapper.selectPostsByUserName(userName); + if (CollectionUtils.isEmpty(list)) + { + return StringUtils.EMPTY; + } + return list.stream().map(SysPost::getPostName).collect(Collectors.joining(",")); + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean checkUserNameUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkUserNameUnique(user.getUserName()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public boolean checkPhoneUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkPhoneUnique(user.getPhonenumber()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public boolean checkEmailUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkEmailUnique(user.getEmail()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + @Override + public void checkUserAllowed(SysUser user) + { + if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) + { + throw new ServiceException("不允许操作超级管理员用户"); + } + } + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + @Override + public void checkUserDataScope(Long userId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + SysUser user = new SysUser(); + user.setUserId(userId); + List users = SpringUtils.getAopProxy(this).selectUserList(user); + if (StringUtils.isEmpty(users)) + { + throw new ServiceException("没有权限访问用户数据!"); + } + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int insertUser(SysUser user) + { + // 新增用户信息 + int rows = userMapper.insertUser(user); + // 新增用户岗位关联 + insertUserPost(user); + // 新增用户与角色管理 + insertUserRole(user); + return rows; + } + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean registerUser(SysUser user) + { + return userMapper.insertUser(user) > 0; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int updateUser(SysUser user) + { + Long userId = user.getUserId(); + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 新增用户与角色管理 + insertUserRole(user); + // 删除用户与岗位关联 + userPostMapper.deleteUserPostByUserId(userId); + // 新增用户与岗位管理 + insertUserPost(user); + return userMapper.updateUser(user); + } + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + @Override + @Transactional + public void insertUserAuth(Long userId, Long[] roleIds) + { + userRoleMapper.deleteUserRoleByUserId(userId); + insertUserRole(userId, roleIds); + } + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserStatus(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfile(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + @Override + public boolean updateUserAvatar(String userName, String avatar) + { + return userMapper.updateUserAvatar(userName, avatar) > 0; + } + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int resetPwd(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(String userName, String password) + { + return userMapper.resetUserPwd(userName, password); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserRole(SysUser user) + { + this.insertUserRole(user.getUserId(), user.getRoleIds()); + } + + /** + * 新增用户岗位信息 + * + * @param user 用户对象 + */ + public void insertUserPost(SysUser user) + { + Long[] posts = user.getPostIds(); + if (StringUtils.isNotEmpty(posts)) + { + // 新增用户与岗位管理 + List list = new ArrayList(posts.length); + for (Long postId : posts) + { + SysUserPost up = new SysUserPost(); + up.setUserId(user.getUserId()); + up.setPostId(postId); + list.add(up); + } + userPostMapper.batchUserPost(list); + } + } + + /** + * 新增用户角色信息 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserRole(Long userId, Long[] roleIds) + { + if (StringUtils.isNotEmpty(roleIds)) + { + // 新增用户与角色管理 + List list = new ArrayList(roleIds.length); + for (Long roleId : roleIds) + { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + userRoleMapper.batchUserRole(list); + } + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserById(Long userId) + { + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 删除用户与岗位表 + userPostMapper.deleteUserPostByUserId(userId); + return userMapper.deleteUserById(userId); + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserByIds(Long[] userIds) + { + for (Long userId : userIds) + { + checkUserAllowed(new SysUser(userId)); + checkUserDataScope(userId); + } + // 删除用户与角色关联 + userRoleMapper.deleteUserRole(userIds); + // 删除用户与岗位关联 + userPostMapper.deleteUserPost(userIds); + return userMapper.deleteUserByIds(userIds); + } + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + @Override + public String importUser(List userList, Boolean isUpdateSupport, String operName) + { + if (StringUtils.isNull(userList) || userList.size() == 0) + { + throw new ServiceException("导入用户数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + String password = configService.selectConfigByKey("sys.user.initPassword"); + for (SysUser user : userList) + { + try + { + // 验证是否存在这个用户 + SysUser u = userMapper.selectUserByUserName(user.getUserName()); + if (StringUtils.isNull(u)) + { + BeanValidators.validateWithException(validator, user); + user.setPassword(SecurityUtils.encryptPassword(password)); + user.setCreateBy(operName); + userMapper.insertUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 导入成功"); + } + else if (isUpdateSupport) + { + BeanValidators.validateWithException(validator, user); + checkUserAllowed(u); + checkUserDataScope(u.getUserId()); + user.setUserId(u.getUserId()); + user.setUpdateBy(operName); + userMapper.updateUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 更新成功"); + } + else + { + failureNum++; + failureMsg.append("
" + failureNum + "、账号 " + user.getUserName() + " 已存在"); + } + } + catch (Exception e) + { + failureNum++; + String msg = "
" + failureNum + "、账号 " + user.getUserName() + " 导入失败:"; + failureMsg.append(msg + e.getMessage()); + log.error(msg, e); + } + } + if (failureNum > 0) + { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } + else + { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } +} diff --git a/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..ca39f47 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark + from sys_config + + + + + + + and config_id = #{configId} + + + and config_key = #{configKey} + + + + + + + + + + + + + + insert into sys_config ( + config_name, + config_key, + config_value, + config_type, + create_by, + remark, + create_time + )values( + #{configName}, + #{configKey}, + #{configValue}, + #{configType}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_config + + config_name = #{configName}, + config_key = #{configKey}, + config_value = #{configValue}, + config_type = #{configType}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where config_id = #{configId} + + + + delete from sys_config where config_id = #{configId} + + + + delete from sys_config where config_id in + + #{configId} + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml new file mode 100644 index 0000000..cf439f6 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time + from sys_dept d + + + + + + + + + + + + + + + + + + + + insert into sys_dept( + dept_id, + parent_id, + dept_name, + ancestors, + order_num, + leader, + phone, + email, + status, + create_by, + create_time + )values( + #{deptId}, + #{parentId}, + #{deptName}, + #{ancestors}, + #{orderNum}, + #{leader}, + #{phone}, + #{email}, + #{status}, + #{createBy}, + sysdate() + ) + + + + update sys_dept + + parent_id = #{parentId}, + dept_name = #{deptName}, + ancestors = #{ancestors}, + order_num = #{orderNum}, + leader = #{leader}, + phone = #{phone}, + email = #{email}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where dept_id = #{deptId} + + + + update sys_dept set ancestors = + + when #{item.deptId} then #{item.ancestors} + + where dept_id in + + #{item.deptId} + + + + + update sys_dept set status = '0' where dept_id in + + #{deptId} + + + + + update sys_dept set del_flag = '2' where dept_id = #{deptId} + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml new file mode 100644 index 0000000..6f24cb1 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + select dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark + from sys_dict_data + + + + + + + + + + + + + + + + delete from sys_dict_data where dict_code = #{dictCode} + + + + delete from sys_dict_data where dict_code in + + #{dictCode} + + + + + update sys_dict_data + + dict_sort = #{dictSort}, + dict_label = #{dictLabel}, + dict_value = #{dictValue}, + dict_type = #{dictType}, + css_class = #{cssClass}, + list_class = #{listClass}, + is_default = #{isDefault}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_code = #{dictCode} + + + + update sys_dict_data set dict_type = #{newDictType} where dict_type = #{oldDictType} + + + + insert into sys_dict_data( + dict_sort, + dict_label, + dict_value, + dict_type, + css_class, + list_class, + is_default, + status, + remark, + create_by, + create_time + )values( + #{dictSort}, + #{dictLabel}, + #{dictValue}, + #{dictType}, + #{cssClass}, + #{listClass}, + #{isDefault}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..55b4075 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + select dict_id, dict_name, dict_type, status, create_by, create_time, remark + from sys_dict_type + + + + + + + + + + + + + + delete from sys_dict_type where dict_id = #{dictId} + + + + delete from sys_dict_type where dict_id in + + #{dictId} + + + + + update sys_dict_type + + dict_name = #{dictName}, + dict_type = #{dictType}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_id = #{dictId} + + + + insert into sys_dict_type( + dict_name, + dict_type, + status, + remark, + create_by, + create_time + )values( + #{dictName}, + #{dictType}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml new file mode 100644 index 0000000..822d665 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time) + values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate()) + + + + + + delete from sys_logininfor where info_id in + + #{infoId} + + + + + truncate table sys_logininfor + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..6762007 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select menu_id, menu_name, parent_id, order_num, path, component, `query`, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time + from sys_menu + + + + + + + + + + + + + + + + + + + + + + + + + + update sys_menu + + menu_name = #{menuName}, + parent_id = #{parentId}, + order_num = #{orderNum}, + path = #{path}, + component = #{component}, + `query` = #{query}, + is_frame = #{isFrame}, + is_cache = #{isCache}, + menu_type = #{menuType}, + visible = #{visible}, + status = #{status}, + perms = #{perms}, + icon = #{icon}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where menu_id = #{menuId} + + + + insert into sys_menu( + menu_id, + parent_id, + menu_name, + order_num, + path, + component, + `query`, + is_frame, + is_cache, + menu_type, + visible, + status, + perms, + icon, + remark, + create_by, + create_time + )values( + #{menuId}, + #{parentId}, + #{menuName}, + #{orderNum}, + #{path}, + #{component}, + #{query}, + #{isFrame}, + #{isCache}, + #{menuType}, + #{visible}, + #{status}, + #{perms}, + #{icon}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_menu where menu_id = #{menuId} + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml new file mode 100644 index 0000000..65d3079 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark + from sys_notice + + + + + + + + insert into sys_notice ( + notice_title, + notice_type, + notice_content, + status, + remark, + create_by, + create_time + )values( + #{noticeTitle}, + #{noticeType}, + #{noticeContent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_notice + + notice_title = #{noticeTitle}, + notice_type = #{noticeType}, + notice_content = #{noticeContent}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id in + + #{noticeId} + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 0000000..201db07 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time, cost_time + from sys_oper_log + + + + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, cost_time, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate()) + + + + + + delete from sys_oper_log where oper_id in + + #{operId} + + + + + + + truncate table sys_oper_log + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml new file mode 100644 index 0000000..227c459 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark + from sys_post + + + + + + + + + + + + + + + + + + update sys_post + + post_code = #{postCode}, + post_name = #{postName}, + post_sort = #{postSort}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where post_id = #{postId} + + + + insert into sys_post( + post_id, + post_code, + post_name, + post_sort, + status, + remark, + create_by, + create_time + )values( + #{postId}, + #{postCode}, + #{postName}, + #{postSort}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete from sys_post where post_id = #{postId} + + + + delete from sys_post where post_id in + + #{postId} + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..7c4139b --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_role_dept where role_id=#{roleId} + + + + + + delete from sys_role_dept where role_id in + + #{roleId} + + + + + insert into sys_role_dept(role_id, dept_id) values + + (#{item.roleId},#{item.deptId}) + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..52306c2 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly, + r.status, r.del_flag, r.create_time, r.remark + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + + + + + + + + insert into sys_role( + role_id, + role_name, + role_key, + role_sort, + data_scope, + menu_check_strictly, + dept_check_strictly, + status, + remark, + create_by, + create_time + )values( + #{roleId}, + #{roleName}, + #{roleKey}, + #{roleSort}, + #{dataScope}, + #{menuCheckStrictly}, + #{deptCheckStrictly}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_role + + role_name = #{roleName}, + role_key = #{roleKey}, + role_sort = #{roleSort}, + data_scope = #{dataScope}, + menu_check_strictly = #{menuCheckStrictly}, + dept_check_strictly = #{deptCheckStrictly}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id in + + #{roleId} + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..cb60a85 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + delete from sys_role_menu where role_id=#{roleId} + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + insert into sys_role_menu(role_id, menu_id) values + + (#{item.roleId},#{item.menuId}) + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..eca3694 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, + d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status, + r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + + + + + + + + + + + + + + + + + + + + insert into sys_user( + user_id, + dept_id, + user_name, + nick_name, + email, + avatar, + phonenumber, + sex, + password, + status, + create_by, + remark, + create_time + )values( + #{userId}, + #{deptId}, + #{userName}, + #{nickName}, + #{email}, + #{avatar}, + #{phonenumber}, + #{sex}, + #{password}, + #{status}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_user + + dept_id = #{deptId}, + user_name = #{userName}, + nick_name = #{nickName}, + email = #{email}, + phonenumber = #{phonenumber}, + sex = #{sex}, + avatar = #{avatar}, + password = #{password}, + status = #{status}, + login_ip = #{loginIp}, + login_date = #{loginDate}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where user_id = #{userId} + + + + update sys_user set status = #{status} where user_id = #{userId} + + + + update sys_user set avatar = #{avatar} where user_name = #{userName} + + + + update sys_user set password = #{password} where user_name = #{userName} + + + + update sys_user set del_flag = '2' where user_id = #{userId} + + + + update sys_user set del_flag = '2' where user_id in + + #{userId} + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml new file mode 100644 index 0000000..2b90bc4 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_user_post where user_id=#{userId} + + + + + + delete from sys_user_post where user_id in + + #{userId} + + + + + insert into sys_user_post(user_id, post_id) values + + (#{item.userId},#{item.postId}) + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..dd72689 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + delete from sys_user_role where user_id=#{userId} + + + + + + delete from sys_user_role where user_id in + + #{userId} + + + + + insert into sys_user_role(user_id, role_id) values + + (#{item.userId},#{item.roleId}) + + + + + delete from sys_user_role where user_id=#{userId} and role_id=#{roleId} + + + + delete from sys_user_role where role_id=#{roleId} and user_id in + + #{userId} + + + \ No newline at end of file diff --git a/ruoyi-ui/.editorconfig b/ruoyi-ui/.editorconfig new file mode 100644 index 0000000..7034f9b --- /dev/null +++ b/ruoyi-ui/.editorconfig @@ -0,0 +1,22 @@ +# 告诉EditorConfig插件,这是根文件,不用继续往上查找 +root = true + +# 匹配全部文件 +[*] +# 设置字符集 +charset = utf-8 +# 缩进风格,可选space、tab +indent_style = space +# 缩进的空格数 +indent_size = 2 +# 结尾换行符,可选lf、cr、crlf +end_of_line = lf +# 在文件结尾插入新行 +insert_final_newline = true +# 删除一行中的前后空格 +trim_trailing_whitespace = true + +# 匹配md结尾的文件 +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/ruoyi-ui/.env.development b/ruoyi-ui/.env.development new file mode 100644 index 0000000..565ca22 --- /dev/null +++ b/ruoyi-ui/.env.development @@ -0,0 +1,16 @@ +# 页面标题 +VUE_APP_TITLE = 后台管理系统 + +# 开发环境配置 +ENV = 'development' + +# skins管理系统/开发环境 +VUE_APP_BASE_API = 'http://localhost:8081' +# VUE_APP_BASE_API = 'http://101.37.69.189:8080' +# VUE_APP_BASE_API = 'http://159.138.29.147:8080' +# VUE_APP_BASE_API = 'http://124.71.163.124:8080' +# VUE_APP_BASE_API = 'http://api.antcsgo.com/prod-api' +# VUE_APP_BASE_API = 'http://188.166.250.170:8080' + +# 路由懒加载 +VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/ruoyi-ui/.env.production b/ruoyi-ui/.env.production new file mode 100644 index 0000000..71251b8 --- /dev/null +++ b/ruoyi-ui/.env.production @@ -0,0 +1,9 @@ +# 页面标题 +VUE_APP_TITLE = WildCS2 + +# 生产环境配置 +ENV = 'production' + +# skins管理系统/生产环境 +# VUE_APP_BASE_API = 'http://8.160.181.6:8081' +VUE_APP_BASE_API = 'https://hou.wildcs2.com' diff --git a/ruoyi-ui/.env.staging b/ruoyi-ui/.env.staging new file mode 100644 index 0000000..59213bf --- /dev/null +++ b/ruoyi-ui/.env.staging @@ -0,0 +1,10 @@ +# 页面标题 +VUE_APP_TITLE = 后台管理系统 + +NODE_ENV = production + +# 测试环境配置 +ENV = 'staging' + +# skins管理系统/测试环境 +VUE_APP_BASE_API = '/stage-api' diff --git a/ruoyi-ui/.eslintignore b/ruoyi-ui/.eslintignore new file mode 100644 index 0000000..89be6f6 --- /dev/null +++ b/ruoyi-ui/.eslintignore @@ -0,0 +1,10 @@ +# 忽略build目录下类型为js的文件的语法检查 +build/*.js +# 忽略src/assets目录下文件的语法检查 +src/assets +# 忽略public目录下文件的语法检查 +public +# 忽略当前目录下为js的文件的语法检查 +*.js +# 忽略当前目录下为vue的文件的语法检查 +*.vue \ No newline at end of file diff --git a/ruoyi-ui/.eslintrc.js b/ruoyi-ui/.eslintrc.js new file mode 100644 index 0000000..82bbdee --- /dev/null +++ b/ruoyi-ui/.eslintrc.js @@ -0,0 +1,199 @@ +// ESlint 检查配置 +module.exports = { + root: true, + parserOptions: { + parser: 'babel-eslint', + sourceType: 'module' + }, + env: { + browser: true, + node: true, + es6: true, + }, + extends: ['plugin:vue/recommended', 'eslint:recommended'], + + // add your custom rules here + //it is base on https://github.com/vuejs/eslint-config-vue + rules: { + "vue/max-attributes-per-line": [2, { + "singleline": 10, + "multiline": { + "max": 1, + "allowFirstLine": false + } + }], + "vue/singleline-html-element-content-newline": "off", + "vue/multiline-html-element-content-newline":"off", + "vue/name-property-casing": ["error", "PascalCase"], + "vue/no-v-html": "off", + 'accessor-pairs': 2, + 'arrow-spacing': [2, { + 'before': true, + 'after': true + }], + 'block-spacing': [2, 'always'], + 'brace-style': [2, '1tbs', { + 'allowSingleLine': true + }], + 'camelcase': [0, { + 'properties': 'always' + }], + 'comma-dangle': [2, 'never'], + 'comma-spacing': [2, { + 'before': false, + 'after': true + }], + 'comma-style': [2, 'last'], + 'constructor-super': 2, + 'curly': [2, 'multi-line'], + 'dot-location': [2, 'property'], + 'eol-last': 2, + 'eqeqeq': ["error", "always", {"null": "ignore"}], + 'generator-star-spacing': [2, { + 'before': true, + 'after': true + }], + 'handle-callback-err': [2, '^(err|error)$'], + 'indent': [2, 2, { + 'SwitchCase': 1 + }], + 'jsx-quotes': [2, 'prefer-single'], + 'key-spacing': [2, { + 'beforeColon': false, + 'afterColon': true + }], + 'keyword-spacing': [2, { + 'before': true, + 'after': true + }], + 'new-cap': [2, { + 'newIsCap': true, + 'capIsNew': false + }], + 'new-parens': 2, + 'no-array-constructor': 2, + 'no-caller': 2, + 'no-console': 'off', + 'no-class-assign': 2, + 'no-cond-assign': 2, + 'no-const-assign': 2, + 'no-control-regex': 0, + 'no-delete-var': 2, + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty-character-class': 2, + 'no-empty-pattern': 2, + 'no-eval': 2, + 'no-ex-assign': 2, + 'no-extend-native': 2, + 'no-extra-bind': 2, + 'no-extra-boolean-cast': 2, + 'no-extra-parens': [2, 'functions'], + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-func-assign': 2, + 'no-implied-eval': 2, + 'no-inner-declarations': [2, 'functions'], + 'no-invalid-regexp': 2, + 'no-irregular-whitespace': 2, + 'no-iterator': 2, + 'no-label-var': 2, + 'no-labels': [2, { + 'allowLoop': false, + 'allowSwitch': false + }], + 'no-lone-blocks': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 2, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, { + 'max': 1 + }], + 'no-native-reassign': 2, + 'no-negated-in-lhs': 2, + 'no-new-object': 2, + 'no-new-require': 2, + 'no-new-symbol': 2, + 'no-new-wrappers': 2, + 'no-obj-calls': 2, + 'no-octal': 2, + 'no-octal-escape': 2, + 'no-path-concat': 2, + 'no-proto': 2, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-return-assign': [2, 'except-parens'], + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-shadow-restricted-names': 2, + 'no-spaced-func': 2, + 'no-sparse-arrays': 2, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 2, + 'no-undef': 2, + 'no-undef-init': 2, + 'no-unexpected-multiline': 2, + 'no-unmodified-loop-condition': 2, + 'no-unneeded-ternary': [2, { + 'defaultAssignment': false + }], + 'no-unreachable': 2, + 'no-unsafe-finally': 2, + 'no-unused-vars': [2, { + 'vars': 'all', + 'args': 'none' + }], + 'no-useless-call': 2, + 'no-useless-computed-key': 2, + 'no-useless-constructor': 2, + 'no-useless-escape': 0, + 'no-whitespace-before-property': 2, + 'no-with': 2, + 'one-var': [2, { + 'initialized': 'never' + }], + 'operator-linebreak': [2, 'after', { + 'overrides': { + '?': 'before', + ':': 'before' + } + }], + 'padded-blocks': [2, 'never'], + 'quotes': [2, 'single', { + 'avoidEscape': true, + 'allowTemplateLiterals': true + }], + 'semi': [2, 'never'], + 'semi-spacing': [2, { + 'before': false, + 'after': true + }], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'never'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, { + 'words': true, + 'nonwords': false + }], + 'spaced-comment': [2, 'always', { + 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] + }], + 'template-curly-spacing': [2, 'never'], + 'use-isnan': 2, + 'valid-typeof': 2, + 'wrap-iife': [2, 'any'], + 'yield-star-spacing': [2, 'both'], + 'yoda': [2, 'never'], + 'prefer-const': 2, + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + 'object-curly-spacing': [2, 'always', { + objectsInObjects: false + }], + 'array-bracket-spacing': [2, 'never'] + } +} diff --git a/ruoyi-ui/.gitignore b/ruoyi-ui/.gitignore new file mode 100644 index 0000000..78a752d --- /dev/null +++ b/ruoyi-ui/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + +tests/**/coverage/ +tests/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock diff --git a/ruoyi-ui/README.md b/ruoyi-ui/README.md new file mode 100644 index 0000000..58c11e2 --- /dev/null +++ b/ruoyi-ui/README.md @@ -0,0 +1,3 @@ +node v22.22.0 + +npm 10.9.4 \ No newline at end of file diff --git a/ruoyi-ui/babel.config.js b/ruoyi-ui/babel.config.js new file mode 100644 index 0000000..c8267b2 --- /dev/null +++ b/ruoyi-ui/babel.config.js @@ -0,0 +1,13 @@ +module.exports = { + presets: [ + // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app + '@vue/cli-plugin-babel/preset' + ], + 'env': { + 'development': { + // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). + // This plugin can significantly increase the speed of hot updates, when you have a large number of pages. + 'plugins': ['dynamic-import-node'] + } + } +} \ No newline at end of file diff --git a/ruoyi-ui/bin/build.bat b/ruoyi-ui/bin/build.bat new file mode 100644 index 0000000..dda590d --- /dev/null +++ b/ruoyi-ui/bin/build.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [Ϣ] Weḅdistļ +echo. + +%~d0 +cd %~dp0 + +cd .. +npm run build:prod + +pause \ No newline at end of file diff --git a/ruoyi-ui/bin/package.bat b/ruoyi-ui/bin/package.bat new file mode 100644 index 0000000..0e5bc0f --- /dev/null +++ b/ruoyi-ui/bin/package.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [Ϣ] װWeḅnode_modulesļ +echo. + +%~d0 +cd %~dp0 + +cd .. +npm install --registry=https://registry.npmmirror.com + +pause \ No newline at end of file diff --git a/ruoyi-ui/bin/run-web.bat b/ruoyi-ui/bin/run-web.bat new file mode 100644 index 0000000..d30deae --- /dev/null +++ b/ruoyi-ui/bin/run-web.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [Ϣ] ʹ Vue CLI Web ̡ +echo. + +%~d0 +cd %~dp0 + +cd .. +npm run dev + +pause \ No newline at end of file diff --git a/ruoyi-ui/build/index.js b/ruoyi-ui/build/index.js new file mode 100644 index 0000000..0c57de2 --- /dev/null +++ b/ruoyi-ui/build/index.js @@ -0,0 +1,35 @@ +const { run } = require('runjs') +const chalk = require('chalk') +const config = require('../vue.config.js') +const rawArgv = process.argv.slice(2) +const args = rawArgv.join(' ') + +if (process.env.npm_config_preview || rawArgv.includes('--preview')) { + const report = rawArgv.includes('--report') + + run(`vue-cli-service build ${args}`) + + const port = 9526 + const publicPath = config.publicPath + + var connect = require('connect') + var serveStatic = require('serve-static') + const app = connect() + + app.use( + publicPath, + serveStatic('./dist', { + index: ['index.html', '/'] + }) + ) + + app.listen(port, function () { + console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`)) + if (report) { + console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`)) + } + + }) +} else { + run(`vue-cli-service build ${args}`) +} diff --git a/ruoyi-ui/main.py b/ruoyi-ui/main.py new file mode 100644 index 0000000..bcee4e5 --- /dev/null +++ b/ruoyi-ui/main.py @@ -0,0 +1,51 @@ +tables = [ + "game_sugar_record", + "game_sugar_user", + "game_wheel_round_bet", + "game_wheel_user", + "tt_announcement_read", + "tt_attendance_record", + "tt_bonus_receive_record", + "tt_box_records", + "tt_commission_record", + "tt_delivery_record", + "tt_exponent_user", + "tt_exponent_user_box", + "tt_fight", + "tt_fight_user", + "tt_notice", + "tt_order", + "tt_promotion_record", + "tt_recharge_record", + "tt_red_packet", + "tt_red_packet_record", + "tt_roll", + "tt_roll_user", + "tt_task_center_user", + "tt_time_roll_user", + "tt_upgrade_record", + "tt_user_blend_ercash", + "tt_welfare_record", + "tt_recharge_record", + "game_sugar_win", + "game_wheel_round", + "tt_recharge_card", +] + + +def clear_data(): + for table in tables: + print( + f"""CREATE TABLE {table}_new LIKE {table}; + RENAME TABLE {table} TO {table}_old, {table}_new TO {table};""" + ) + + +def clear(): + for table in tables: + print(f"DROP TABLE IF EXISTS {table}_old;") + print(f"DROP TABLE IF EXISTS {table}_new;") + + +# clear() +clear_data() diff --git a/ruoyi-ui/package.json b/ruoyi-ui/package.json new file mode 100644 index 0000000..3bb188d --- /dev/null +++ b/ruoyi-ui/package.json @@ -0,0 +1,91 @@ +{ + "name": "ruoyi", + "version": "3.8.6", + "description": "管理系统", + "author": "linh", + "license": "MIT", + "scripts": { + "dev": "set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve", + "build:prod": "set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build", + "build:stage": "set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build --mode staging", + "build:test": "set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build --mode test", + "preview": "set NODE_OPTIONS=--openssl-legacy-provider && node build/index.js --preview", + "lint": "eslint --ext .js,.vue src" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "src/**/*.{js,vue}": [ + "eslint --fix", + "git add" + ] + }, + "keywords": [ + "vue", + "admin", + "dashboard", + "element-ui", + "boilerplate", + "admin-template", + "management-system" + ], + "repository": { + "type": "git", + "url": "https://gitee.com/y_project/RuoYi-Vue.git" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "axios": "0.24.0", + "clipboard": "2.0.8", + "core-js": "3.25.3", + "echarts": "5.4.0", + "element-ui": "2.15.13", + "file-saver": "2.0.5", + "fuse.js": "6.4.3", + "highlight.js": "9.18.5", + "js-beautify": "1.13.0", + "js-cookie": "3.0.1", + "jsencrypt": "3.0.0-rc.1", + "nprogress": "0.2.0", + "quill": "1.3.7", + "screenfull": "5.0.2", + "sortablejs": "1.10.2", + "vue": "2.6.12", + "vue-count-to": "1.0.13", + "vue-cropper": "0.5.5", + "vue-meta": "2.4.0", + "vue-router": "3.4.9", + "vuedraggable": "2.24.3", + "vuex": "3.6.0" + }, + "devDependencies": { + "@vue/cli-plugin-babel": "4.4.6", + "@vue/cli-plugin-eslint": "4.4.6", + "@vue/cli-service": "4.4.6", + "babel-eslint": "10.1.0", + "babel-plugin-dynamic-import-node": "2.3.3", + "chalk": "4.1.0", + "compression-webpack-plugin": "5.0.2", + "connect": "3.6.6", + "eslint": "7.15.0", + "eslint-plugin-vue": "7.2.0", + "lint-staged": "10.5.3", + "runjs": "4.4.2", + "sass": "1.32.13", + "sass-loader": "10.1.1", + "script-ext-html-webpack-plugin": "2.1.5", + "svg-sprite-loader": "5.1.1", + "vue-template-compiler": "2.6.12" + }, + "engines": { + "node": ">=8.9", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions" + ] +} diff --git a/ruoyi-ui/public/favicon.ico b/ruoyi-ui/public/favicon.ico new file mode 100644 index 0000000..e263760 Binary files /dev/null and b/ruoyi-ui/public/favicon.ico differ diff --git a/ruoyi-ui/public/html/ie.html b/ruoyi-ui/public/html/ie.html new file mode 100644 index 0000000..052ffcd --- /dev/null +++ b/ruoyi-ui/public/html/ie.html @@ -0,0 +1,46 @@ + + + + + + 请升级您的浏览器 + + + + + + +

请升级您的浏览器,以便我们更好的为您提供服务!

+

您正在使用 Internet Explorer 的早期版本(IE11以下版本或使用该内核的浏览器)。这意味着在升级浏览器前,您将无法访问此网站。

+
+

请注意:微软公司对Windows XP 及 Internet Explorer 早期版本的支持已经结束

+

自 2016 年 1 月 12 日起,Microsoft 不再为 IE 11 以下版本提供相应支持和更新。没有关键的浏览器安全更新,您的电脑可能易受有害病毒、间谍软件和其他恶意软件的攻击,它们可以窃取或损害您的业务数据和信息。请参阅 微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明

+
+

您可以选择更先进的浏览器

+

推荐使用以下浏览器的最新版本。如果您的电脑已有以下浏览器的最新版本则直接使用该浏览器访问即可。

+ +
+ + \ No newline at end of file diff --git a/ruoyi-ui/public/index.html b/ruoyi-ui/public/index.html new file mode 100644 index 0000000..925455c --- /dev/null +++ b/ruoyi-ui/public/index.html @@ -0,0 +1,208 @@ + + + + + + + + + <%= webpackConfig.name %> + + + + +
+
+
+
+
+
正在加载系统资源,请耐心等待
+
+
+ + diff --git a/ruoyi-ui/public/robots.txt b/ruoyi-ui/public/robots.txt new file mode 100644 index 0000000..77470cb --- /dev/null +++ b/ruoyi-ui/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/ruoyi-ui/recover.py b/ruoyi-ui/recover.py new file mode 100644 index 0000000..055e55a --- /dev/null +++ b/ruoyi-ui/recover.py @@ -0,0 +1,19 @@ +tables = [ + "tt_box", + "tt_box_open_chance", + "tt_box_ornaments", + "tt_box_records", + "tt_ornament", + "tt_upgrade_fail_ornaments", + "tt_upgrade_ornaments", + "tt_upgrade_record", + "tt_roll", + "tt_roll_jackpot", + "tt_roll_jackpot_ornaments", + "tt_roll_user_prize" +] + +for table in tables: + print(f"""RENAME TABLE {table} TO {table}_new, {table}_old TO {table};""") + + diff --git a/ruoyi-ui/restapi/admin_index.http b/ruoyi-ui/restapi/admin_index.http new file mode 100644 index 0000000..e69de29 diff --git a/ruoyi-ui/restapi/admin_request.http b/ruoyi-ui/restapi/admin_request.http new file mode 100644 index 0000000..ec5acf7 --- /dev/null +++ b/ruoyi-ui/restapi/admin_request.http @@ -0,0 +1,89 @@ +POST http://localhost:8081/login HTTP/1.1 +Accept: */* +Accept-Encoding: gzip, deflate +Accept-Language: zh-CN,zh;q=0.9 +Cache-Control: no-cache +Connection: keep-alive +Content-Type: application/json; charset=UTF-8 +Host: 154.12.94.98:8081 +Origin: http://154.12.94.98:10002 +Pragma: no-cache +Referer: http://154.12.94.98:10002/ +User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1 + +{"username":"admin","password":"123456"} + + +### 生成账号 +GET http://localhost:8081/admin/user/generateAccountByPhoneNum?phoneNum=13214201158&password=123456789 HTTP/1.1 +Accept: */* +Accept-Language: zh-CN,zh;q=0.9 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjliMTY3MDdmLTAzZmYtNGQ4Zi05MjkzLTlkZWM0ZjgwNTVmYSJ9.CXeQ-IJKY3GK7aL2gQBOSr2NfUuLSUPsqV0mW7F-eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImJjYzhiYjc3LWU4OGQtNDM0Ny1iODAyLTNjMWUwNmQ0NDBmZSJ9.5TDTp1_hED9A6Gg177FZ-CY655LeyTzPjAjOYo5cqg79odNmH5GAs0JNkaF8iT7o8BnDgS4vpt8Ikchp8YfZGA +Cache-Control: no-cache +Connection: keep-alive +Origin: http://154.12.94.98:10002 +Pragma: no-cache' +Referer: http://154.12.94.98:10002/ +User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1 + + +### + +GET http://localhost:8081/admin/sugar/user/list?pageNum=1&pageSize=10&userId=2 HTTP/1.1 +Accept: */* +Accept-Language: zh-CN,zh;q=0.9 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjliMTY3MDdmLTAzZmYtNGQ4Zi05MjkzLTlkZWM0ZjgwNTVmYSJ9.CXeQ-IJKY3GK7aL2gQBOSr2NfUuLSUPsqV0mW7F-042BGqOPWBCwKFGukNCQIjqYB6aDcYtIhO0e8pzUZJp9bA +Cache-Control: no-cache +Connection: keep-alive +Origin: http://154.12.94.98:10002 +Pragma: no-cache' +Referer: http://154.12.94.98:10002/ +User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1 + +### + +GET http://localhost:8081/admin/sugar/gameconfig HTTP/1.1 +Accept: */* +Accept-Language: zh-CN,zh;q=0.9 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjliMTY3MDdmLTAzZmYtNGQ4Zi05MjkzLTlkZWM0ZjgwNTVmYSJ9.CXeQ-IJKY3GK7aL2gQBOSr2NfUuLSUPsqV0mW7F-042BGqOPWBCwKFGukNCQIjqYB6aDcYtIhO0e8pzUZJp9bA +Cache-Control: no-cache +Connection: keep-alive +Origin: http://154.12.94.98:10002 +Pragma: no-cache' +Referer: http://154.12.94.98:10002/ +User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1 + +### +POST http://localhost:8081/admin/sugar/gameconfig HTTP/1.1 +Content-Type: application/json; charset=UTF-8 +Accept: */* +Accept-Language: zh-CN,zh;q=0.9 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjliMTY3MDdmLTAzZmYtNGQ4Zi05MjkzLTlkZWM0ZjgwNTVmYSJ9.CXeQ-IJKY3GK7aL2gQBOSr2NfUuLSUPsqV0mW7F-042BGqOPWBCwKFGukNCQIjqYB6aDcYtIhO0e8pzUZJp9bA +Cache-Control: no-cache +Connection: keep-alive +Origin: http://154.12.94.98:10002 +Pragma: no-cache' +Referer: http://154.12.94.98:10002/ +User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1 + +{ +"defaultAwardPool": 10000.0, +"jpPercent": [ + 5, + 100 +], +"rtp": 0.80, +"minProfit": 0.3 +} + +### +GET http://localhost:8081/admin/sugar/dailywin/list?endDate=2026-03-30T00:00:00 HTTP/1.1 +Accept: */* +Accept-Language: zh-CN,zh;q=0.9 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjliMTY3MDdmLTAzZmYtNGQ4Zi05MjkzLTlkZWM0ZjgwNTVmYSJ9.CXeQ-IJKY3GK7aL2gQBOSr2NfUuLSUPsqV0mW7F-eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImJjYzhiYjc3LWU4OGQtNDM0Ny1iODAyLTNjMWUwNmQ0NDBmZSJ9.5TDTp1_hED9A6Gg177FZ-CY655LeyTzPjAjOYo5cqg79odNmH5GAs0JNkaF8iT7o8BnDgS4vpt8Ikchp8YfZGA +Cache-Control: no-cache +Connection: keep-alive +Origin: http://154.12.94.98:10002 +Pragma: no-cache' +Referer: http://154.12.94.98:10002/ +User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1 diff --git a/ruoyi-ui/restapi/admin_sugar_api.http b/ruoyi-ui/restapi/admin_sugar_api.http new file mode 100644 index 0000000..8420762 --- /dev/null +++ b/ruoyi-ui/restapi/admin_sugar_api.http @@ -0,0 +1,36 @@ +POST http://localhost:8081/login HTTP/1.1 +Accept: */* +Accept-Encoding: gzip, deflate +Accept-Language: zh-CN,zh;q=0.9 +Cache-Control: no-cache +Connection: keep-alive +Content-Type: application/json; charset=UTF-8 +Host: 154.12.94.98:8081 +Origin: http://154.12.94.98:10002 +Pragma: no-cache +Referer: http://154.12.94.98:10002/ +User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1 + +{"username":"admin","password":"123456"} + + +### 获取临时结果列表 +GET http://localhost:8081/admin/sugar/mockresult/list?uid=11 HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjliMTY3MDdmLTAzZmYtNGQ4Zi05MjkzLTlkZWM0ZjgwNTVmYSJ9.CXeQ-IJKY3GK7aL2gQBOSr2NfUuLSUPsqV0mW7F-eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImJjYzhiYjc3LWU4OGQtNDM0Ny1iODAyLTNjMWUwNmQ0NDBmZSJ9.5TDTp1_hED9A6Gg177FZ-CY655LeyTzPjAjOYo5cqg79odNmH5GAs0JNkaF8iT7o8BnDgS4vpt8Ikchp8YfZGA + +### 生成临时结果列表 +POST http://localhost:8081/admin/sugar/mockresult/list HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjliMTY3MDdmLTAzZmYtNGQ4Zi05MjkzLTlkZWM0ZjgwNTVmYSJ9.CXeQ-IJKY3GK7aL2gQBOSr2NfUuLSUPsqV0mW7F-eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImJjYzhiYjc3LWU4OGQtNDM0Ny1iODAyLTNjMWUwNmQ0NDBmZSJ9.5TDTp1_hED9A6Gg177FZ-CY655LeyTzPjAjOYo5cqg79odNmH5GAs0JNkaF8iT7o8BnDgS4vpt8Ikchp8YfZGA +Content-Type: application/json; charset=UTF-8 + +{ + "uid": 11, + "normalScores": [100,90,80], + "freeScores": [100,90], + "superScores": [100,90,800] +} + +### 获取玩家记录 + +GET http://localhost:8081/admin/sugar/user/record/list?uid=2&startTime=2026-04-02%2009:40:00 HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjliMTY3MDdmLTAzZmYtNGQ4Zi05MjkzLTlkZWM0ZjgwNTVmYSJ9.CXeQ-IJKY3GK7aL2gQBOSr2NfUuLSUPsqV0mW7F-eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImJjYzhiYjc3LWU4OGQtNDM0Ny1iODAyLTNjMWUwNmQ0NDBmZSJ9.5TDTp1_hED9A6Gg177FZ-CY655LeyTzPjAjOYo5cqg79odNmH5GAs0JNkaF8iT7o8BnDgS4vpt8Ikchp8YfZGA diff --git a/ruoyi-ui/restapi/admin_wheel_api.http b/ruoyi-ui/restapi/admin_wheel_api.http new file mode 100644 index 0000000..ed3b85c --- /dev/null +++ b/ruoyi-ui/restapi/admin_wheel_api.http @@ -0,0 +1,26 @@ +POST http://localhost:8081/login HTTP/1.1 +Accept: */* +Accept-Encoding: gzip, deflate +Accept-Language: zh-CN,zh;q=0.9 +Cache-Control: no-cache +Connection: keep-alive +Content-Type: application/json; charset=UTF-8 +Host: 154.12.94.98:8081 +Origin: http://154.12.94.98:10002 +Pragma: no-cache +Referer: http://154.12.94.98:10002/ +User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1 + +{"username":"admin","password":"123456"} + + +### +GET http://localhost:8081/admin/wheel/gameconfig HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjliMTY3MDdmLTAzZmYtNGQ4Zi05MjkzLTlkZWM0ZjgwNTVmYSJ9.CXeQ-IJKY3GK7aL2gQBOSr2NfUuLSUPsqV0mW7F-eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImJjYzhiYjc3LWU4OGQtNDM0Ny1iODAyLTNjMWUwNmQ0NDBmZSJ9.5TDTp1_hED9A6Gg177FZ-CY655LeyTzPjAjOYo5cqg79odNmH5GAs0JNkaF8iT7o8BnDgS4vpt8Ikchp8YfZGA + +### +POST http://localhost:8081/admin/wheel/gameconfig HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjliMTY3MDdmLTAzZmYtNGQ4Zi05MjkzLTlkZWM0ZjgwNTVmYSJ9.CXeQ-IJKY3GK7aL2gQBOSr2NfUuLSUPsqV0mW7F-eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImJjYzhiYjc3LWU4OGQtNDM0Ny1iODAyLTNjMWUwNmQ0NDBmZSJ9.5TDTp1_hED9A6Gg177FZ-CY655LeyTzPjAjOYo5cqg79odNmH5GAs0JNkaF8iT7o8BnDgS4vpt8Ikchp8YfZGA +Content-Type: application/json; charset=UTF-8 + +{} \ No newline at end of file diff --git a/ruoyi-ui/restapi/cs340item.http b/ruoyi-ui/restapi/cs340item.http new file mode 100644 index 0000000..ac87c7e --- /dev/null +++ b/ruoyi-ui/restapi/cs340item.http @@ -0,0 +1,10 @@ +### 获取账户余额 +GET http://95.40.65.4:8081/admin/cs340item/checkMerchantBalance HTTP/1.1 + +### 查询库存 +POST http://95.40.65.4:8081/admin/cs340item/queryOnSaleInfo HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + +{ + "marketHashName": "10 Year Birthday Sticker Capsule" +} \ No newline at end of file diff --git a/ruoyi-ui/restapi/fight.http b/ruoyi-ui/restapi/fight.http new file mode 100644 index 0000000..64f0cdf --- /dev/null +++ b/ruoyi-ui/restapi/fight.http @@ -0,0 +1,39 @@ +POST http://95.40.65.4:8081/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"username":"17688554522","password":"123456"} + +### 获取个人战绩列表。除了page,size外,其他参数都是用来查询想要的战绩数据的,参数不传或者传null都表示不过滤这个条件 + +POST http://95.40.65.4:8081/api/fight/fightOnMyOwn HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImM5ZmQ5MTE5LTk5YzgtNDlhMy04YjU4LWYzYzVlOWI3MTUyNSJ9.VQHwPNaDliBaDvlVeC3SxB_AxJ7Io4kWssqXzh3saW10kteT9sEaXNtVn4CN7xIA-N49geqTzPO4zCEJNRg9oQ +Content-Type: application/json; charset=UTF-8 + +{ + "fightId": 1, + "model": 0, + "statusList": [2], + "page": 1, + "size": 10 +} + + +### 获取全服战绩列表。除了page,size外,其他参数都是用来查询想要的战绩数据的,参数不传或者传null都表示不过滤这个条件 + +POST http://95.40.65.4:8081/api/fight/fightOnAll HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImM5ZmQ5MTE5LTk5YzgtNDlhMy04YjU4LWYzYzVlOWI3MTUyNSJ9.VQHwPNaDliBaDvlVeC3SxB_AxJ7Io4kWssqXzh3saW10kteT9sEaXNtVn4CN7xIA-N49geqTzPO4zCEJNRg9oQ +Content-Type: application/json; charset=UTF-8 + +{ + + "model": 0, + "statusList": [2], + "page": 1, + "size": 10 +} + +### 十佳排行榜 + +GET http://95.40.65.4:8081/api/fight/tenTopFight?maxType=1 HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjMxZTBmNTc5LWViNDQtNDU1Yy04Y2ZiLTM1ZWM3MGU0NTU0OSJ9.CcD5eXGaMB2DjtJkdZS9UuNQEAvz2WB1jf0VpSjZ7-PTNUZLvznuAJXqVZnNTqlGg3aDN6QP_Pl3WZAMD2MVBA diff --git a/ruoyi-ui/restapi/roll.http b/ruoyi-ui/restapi/roll.http new file mode 100644 index 0000000..60617d6 --- /dev/null +++ b/ruoyi-ui/restapi/roll.http @@ -0,0 +1,68 @@ +POST https://wildcs2.com/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"username":"19156010758","password":"123456"} + +### 获取房间列表 + +GET http://localhost:8081/api/roll/getRollList?rollType=0&rollStatus=0&pageNum=1&pageSize=10&rollName=&userId=11 HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImM5ZmQ5MTE5LTk5YzgtNDlhMy04YjU4LWYzYzVlOWI3MTUyNSJ9.VQHwPNaDliBaDvlVeC3SxB_AxJ7Io4kWssqXzh3saW10kteT9sEaXNtVn4CN7xIA-N49geqTzPO4zCEJNRg9oQ + + + +### 获取房间详情 + +GET http://localhost:8081/api/roll/getRollDetails/1 HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImM5ZmQ5MTE5LTk5YzgtNDlhMy04YjU4LWYzYzVlOWI3MTUyNSJ9.VQHwPNaDliBaDvlVeC3SxB_AxJ7Io4kWssqXzh3saW10kteT9sEaXNtVn4CN7xIA-N49geqTzPO4zCEJNRg9oQ + + +### 获取房间玩家列表 + +POST http://localhost:8081/api/roll/getRollPlayers HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImM5ZmQ5MTE5LTk5YzgtNDlhMy04YjU4LWYzYzVlOWI3MTUyNSJ9.VQHwPNaDliBaDvlVeC3SxB_AxJ7Io4kWssqXzh3saW10kteT9sEaXNtVn4CN7xIA-N49geqTzPO4zCEJNRg9oQ +Content-Type: application/json; charset=UTF-8 + +{ + "rollId": 1, + "page": 1, + "size": 10 +} + +### 加入房间 +POST http://localhost:8081/api/roll/joinRoll HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImM5ZmQ5MTE5LTk5YzgtNDlhMy04YjU4LWYzYzVlOWI3MTUyNSJ9.VQHwPNaDliBaDvlVeC3SxB_AxJ7Io4kWssqXzh3saW10kteT9sEaXNtVn4CN7xIA-N49geqTzPO4zCEJNRg9oQ +Content-Type: application/json; charset=UTF-8 + +{ + "rollId": 1, + "rollPassword": "123456" +} + +### 获取ROLL房奖池详情 暂时用不到,房间详情接口有 +POST http://localhost:8081/api/roll/getRollPrizePool HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImM5ZmQ5MTE5LTk5YzgtNDlhMy04YjU4LWYzYzVlOWI3MTUyNSJ9.VQHwPNaDliBaDvlVeC3SxB_AxJ7Io4kWssqXzh3saW10kteT9sEaXNtVn4CN7xIA-N49geqTzPO4zCEJNRg9oQ +Content-Type: application/json; charset=UTF-8 + +{ + "rollId": 1, + "page": 1, + "size": 10 +} + + +#### 获取ROLL房开奖数据 +POST http://localhost:8081/api/roll/getRollOpenPrize HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImM5ZmQ5MTE5LTk5YzgtNDlhMy04YjU4LWYzYzVlOWI3MTUyNSJ9.VQHwPNaDliBaDvlVeC3SxB_AxJ7Io4kWssqXzh3saW10kteT9sEaXNtVn4CN7xIA-N49geqTzPO4zCEJNRg9oQ +Content-Type: application/json; charset=UTF-8 + +{ + "rollId": 1, + "page": 1, + "size": 10 +} + + + + + diff --git a/ruoyi-ui/restapi/sugar_api.http b/ruoyi-ui/restapi/sugar_api.http new file mode 100644 index 0000000..87c7ae2 --- /dev/null +++ b/ruoyi-ui/restapi/sugar_api.http @@ -0,0 +1,34 @@ +POST http://localhost:8081/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"username":"17774038557","password":"123456"} + +### + +GET http://localhost:8081/api/sugar/dospin?bet=10 HTTP/1.1 +Accept: */* +Accept-Language: zh-CN,zh;q=0.9 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImI0Mjk5ZmRjLTY4YmMtNDVhZi04ZDY1LWU3YjM1NDNhYTM5NCJ9.zsx5cfrqC_BICELsK0Kn0TsQQDYPENLQEDHPIAw76u8OpfsxUU9_T-vMWQI7UEU5NvFD7WoDuFu8AH0aFZRTVQ + +### + +GET http://localhost:8081/api/sugar/buy_free_spins?kind=standard&bet=100 HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImI0Mjk5ZmRjLTY4YmMtNDVhZi04ZDY1LWU3YjM1NDNhYTM5NCJ9.zsx5cfrqC_BICELsK0Kn0TsQQDYPENLQEDHPIAw76u8OpfsxUU9_T-vMWQI7UEU5NvFD7WoDuFu8AH0aFZRTVQ + + +### + +GET http://localhost:8081/api/sugar/has_free_spin HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImI0Mjk5ZmRjLTY4YmMtNDVhZi04ZDY1LWU3YjM1NDNhYTM5NCJ9.zsx5cfrqC_BICELsK0Kn0TsQQDYPENLQEDHPIAw76u8OpfsxUU9_T-vMWQI7UEU5NvFD7WoDuFu8AH0aFZRTVQ + +### 个人记录 +GET http://localhost:8081/api/sugar/records?pageNum=1&pageSize=20&kind= +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImI0Mjk5ZmRjLTY4YmMtNDVhZi04ZDY1LWU3YjM1NDNhYTM5NCJ9.zsx5cfrqC_BICELsK0Kn0TsQQDYPENLQEDHPIAw76u8OpfsxUU9_T-vMWQI7UEU5NvFD7WoDuFu8AH0aFZRTVQ + +### 全局记录 +GET http://localhost:8081/api/sugar/rank_records?pageNum=1&pageSize=20&kind=normal&rankType=win +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImI0Mjk5ZmRjLTY4YmMtNDVhZi04ZDY1LWU3YjM1NDNhYTM5NCJ9.zsx5cfrqC_BICELsK0Kn0TsQQDYPENLQEDHPIAw76u8OpfsxUU9_T-vMWQI7UEU5NvFD7WoDuFu8AH0aFZRTVQ + + + diff --git a/ruoyi-ui/restapi/user.http b/ruoyi-ui/restapi/user.http new file mode 100644 index 0000000..847b502 --- /dev/null +++ b/ruoyi-ui/restapi/user.http @@ -0,0 +1,32 @@ +POST http://localhost:8081/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"username":"17688554522","password":"123456"} + + +### 更新提货链接 + +POST http://localhost:8081/api/user/updateDeliveryAddress HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImMxOTQ2YzIyLTUzZGMtNDdkMS1hMGUzLTg5ZWY2NjgxYzY2YyJ9.06j6rdNdZ23Ws52Kf_7Uo5FWRKNxGCrJf-xPDi5PCi7N6YTAiD1O3hgBtQ1pTWYE_f5dupbjzs4s0-FN0RWPaw +Content-Type: application/json; charset=UTF-8 + +{ + "transactionLink": "123" +} + +### 认证系统 + +POST http://localhost:8081/api/user/realNameAuthentication HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImQ5YTk0MzU0LWEyYzEtNDI5ZC04MTg1LTNiMDRmMzllMzAxMSJ9.uaFFxKiXNfM-bNeWAdSUNv85yfiNK8vAi8xeraTDULjxjUXCKimr7X_AprrKiFCaIt1FMsGbN_0AaOY9KBjajQ +Content-Type: application/json; charset=UTF-8 + +{ + "realName": "123", + "idNum": "411425199502074510" +} + +### 获取个人账户信息 +GET http://localhost:8081/api/getAccountAmount HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjZmMGU5ZTdjLTc4OTktNDAxNS1hYTJkLTU3N2ViOTY3MDQxOSJ9.a8w6jH-psbmJsxWr9S_lLX05C8Yz3K8D8Ajtkm7tItt8uWErE4kJMFSxs9QZpE4UgeXkwRo8lXlc8dSdxYuXPw +Content-Type: application/json; charset=UTF-8 \ No newline at end of file diff --git a/ruoyi-ui/restapi/wheel_api.http b/ruoyi-ui/restapi/wheel_api.http new file mode 100644 index 0000000..ddc63c3 --- /dev/null +++ b/ruoyi-ui/restapi/wheel_api.http @@ -0,0 +1,26 @@ +POST http://localhost:8081/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"username":"19156010758","password":"123456"} + +### + +GET http://localhost:8081/api/wheel/game_config HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjRjNDY5MGJhLTdkYTgtNGYyYi1iZGQwLWYyMDE3MTcwMTM5ZSJ9.7CvtJhZDbdgfMx5emTEWg-lwBIRKkPDOQITEtyQz5vqzG5p8X6lwJCdALJz69bFIog-DpvlnsFL9Yn7xo28DWA + +### 获取最后一轮转盘信息 +GET http://localhost:8081/api/wheel/last_round_info HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjRjNDY5MGJhLTdkYTgtNGYyYi1iZGQwLWYyMDE3MTcwMTM5ZSJ9.7CvtJhZDbdgfMx5emTEWg-lwBIRKkPDOQITEtyQz5vqzG5p8X6lwJCdALJz69bFIog-DpvlnsFL9Yn7xo28DWA + +### 下注 +GET http://localhost:8081/api/wheel/dobet?bet=5&symbol=A HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjRjNDY5MGJhLTdkYTgtNGYyYi1iZGQwLWYyMDE3MTcwMTM5ZSJ9.7CvtJhZDbdgfMx5emTEWg-lwBIRKkPDOQITEtyQz5vqzG5p8X6lwJCdALJz69bFIog-DpvlnsFL9Yn7xo28DWA + +### 个人游戏历史记录 +GET http://localhost:8081/api/wheel/history_bet_info?pageNum=1&pageSize=10 HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjRjNDY5MGJhLTdkYTgtNGYyYi1iZGQwLWYyMDE3MTcwMTM5ZSJ9.7CvtJhZDbdgfMx5emTEWg-lwBIRKkPDOQITEtyQz5vqzG5p8X6lwJCdALJz69bFIog-DpvlnsFL9Yn7xo28DWA + +### 游戏历史记录 +GET http://localhost:8081/api/wheel/round_history_bet_info?pageNum=10&pageSize=10 HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjRjNDY5MGJhLTdkYTgtNGYyYi1iZGQwLWYyMDE3MTcwMTM5ZSJ9.7CvtJhZDbdgfMx5emTEWg-lwBIRKkPDOQITEtyQz5vqzG5p8X6lwJCdALJz69bFIog-DpvlnsFL9Yn7xo28DWA \ No newline at end of file diff --git a/ruoyi-ui/restapi/个人记录.http b/ruoyi-ui/restapi/个人记录.http new file mode 100644 index 0000000..06b3d2b --- /dev/null +++ b/ruoyi-ui/restapi/个人记录.http @@ -0,0 +1,36 @@ +POST http://localhost:8081/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"username":"19156010758","password":"123456"} + +### 个人记录 充值、消费、红包记录 + +POST http://localhost:8081/api/userAmountRecords/personalRecord +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImQ2NTBkOWVlLTljNmYtNDIxNy04NTQzLTcwMWQ1M2NjZjc5ZiJ9.5PN9bmzzAliJrbeSbPXQsZ4jr1HpX-crusfUswU0YqIv4kwCxgoG-m4S5lZbL6nOTIYBPfB0V-1aP3Zpcs_X_A +Content-Type: application/json; charset=UTF-8 + +{ + "page": 1, + "pageSize": 2, + "type": 3, + "source": 2 +} + +### 个人记录 提货记录 + +POST http://localhost:8081/api/userAmountRecords/deliveryRecords +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImQ2NTBkOWVlLTljNmYtNDIxNy04NTQzLTcwMWQ1M2NjZjc5ZiJ9.5PN9bmzzAliJrbeSbPXQsZ4jr1HpX-crusfUswU0YqIv4kwCxgoG-m4S5lZbL6nOTIYBPfB0V-1aP3Zpcs_X_A +Content-Type: application/json; charset=UTF-8 + +{ + "page": 1, + "size": 2, + "statusList": [1] +} + +### 个人记录 转增记录 + +GET http://localhost:8081/api/userPackSack/transfer/records?pageNum=1&pageSize=2&kind=src +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImZhZTJkZTY3LTQ0OTMtNGUwZS04NzQ1LTBlNTdjNmFjZGY5ZiJ9.l9DAJTA7UkOhRXKbIMIDQBDefeiMWvoYPRXtJCSaamnuczaBULKF-mtDuYd2FXW3OEE4YHmnpmj0ZPZ10Oq2Zw + diff --git a/ruoyi-ui/restapi/商城.http b/ruoyi-ui/restapi/商城.http new file mode 100644 index 0000000..66f8815 --- /dev/null +++ b/ruoyi-ui/restapi/商城.http @@ -0,0 +1,12 @@ +POST http://localhost:8081/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"username":"19156010758","password":"123456"} + +### 商城商品列表 + +GET http://localhost:8081/api/shopping/list?pageNum=2&pageSize=2 HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImFmMDM4M2RhLTdiZGYtNDBlYi1iNDhkLTFiMjVlMmIxOGYwZCJ9.Y76_V_Zr7B26yyr6DHiH64Mt-JkgmZFFoaXIo0-cWwdUsArSatLK_P4sGG4gJWS9N1nbaw4IfmGn4Hd-4aKbqQ +Content-Type: application/json; charset=UTF-8 + diff --git a/ruoyi-ui/restapi/定时任务.http b/ruoyi-ui/restapi/定时任务.http new file mode 100644 index 0000000..4fe7148 --- /dev/null +++ b/ruoyi-ui/restapi/定时任务.http @@ -0,0 +1 @@ +GET http://localhost:8081/api/ttTask/test?kind=zhubo HTTP/1.1 diff --git a/ruoyi-ui/restapi/排行榜.http b/ruoyi-ui/restapi/排行榜.http new file mode 100644 index 0000000..ac7200a --- /dev/null +++ b/ruoyi-ui/restapi/排行榜.http @@ -0,0 +1,14 @@ +POST http://localhost:8081/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + +{"username":"19156010758","password":"123456"} + +### 获取综合流水排行榜 +POST http://localhost:8081/api/userAmountRecords/blendErcashRank HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"page":1,"size":10,"type":1} + +### 获取综合流水排行榜奖励 +GET http://localhost:8081/api/userAmountRecords/rechargeRankingRewardsIntroduction HTTP/1.1 diff --git a/ruoyi-ui/restapi/提货.http b/ruoyi-ui/restapi/提货.http new file mode 100644 index 0000000..dd24934 --- /dev/null +++ b/ruoyi-ui/restapi/提货.http @@ -0,0 +1,45 @@ +POST http://localhost:8081/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"username":"19156010758","password":"123456"} + + +### 背包 +POST http://localhost:8081/api/userPackSack/getPackSack? HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImZhZTJkZTY3LTQ0OTMtNGUwZS04NzQ1LTBlNTdjNmFjZGY5ZiJ9.l9DAJTA7UkOhRXKbIMIDQBDefeiMWvoYPRXtJCSaamnuczaBULKF-mtDuYd2FXW3OEE4YHmnpmj0ZPZ10Oq2Zw +Content-Type: application/json; charset=UTF-8 + +{ + "page":1, + "size":10 +} + +### 转增 + +POST http://localhost:8081/api/userPackSack/transfer HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImZhZTJkZTY3LTQ0OTMtNGUwZS04NzQ1LTBlNTdjNmFjZGY5ZiJ9.l9DAJTA7UkOhRXKbIMIDQBDefeiMWvoYPRXtJCSaamnuczaBULKF-mtDuYd2FXW3OEE4YHmnpmj0ZPZ10Oq2Zw +Content-Type: application/json; charset=UTF-8 + +{"targetUid":1,"packSackIds":[2160]} + +### 提货 +POST http://localhost:8081/api/userPackSack/delivery HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImZhZTJkZTY3LTQ0OTMtNGUwZS04NzQ1LTBlNTdjNmFjZGY5ZiJ9.l9DAJTA7UkOhRXKbIMIDQBDefeiMWvoYPRXtJCSaamnuczaBULKF-mtDuYd2FXW3OEE4YHmnpmj0ZPZ10Oq2Zw +Content-Type: application/json; charset=UTF-8 + +{"packSackIds":[2159], "isAll": false} + + +### 购买 + +POST http://localhost:8081/api/shopping/exchange?ornamentsId=1878515908 HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImZhZTJkZTY3LTQ0OTMtNGUwZS04NzQ1LTBlNTdjNmFjZGY5ZiJ9.l9DAJTA7UkOhRXKbIMIDQBDefeiMWvoYPRXtJCSaamnuczaBULKF-mtDuYd2FXW3OEE4YHmnpmj0ZPZ10Oq2Zw +Content-Type: application/json; charset=UTF-8 + +### 查询库存 +POST http://localhost:8081/admin/cs340item/queryOnSaleInfo HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImZhZTJkZTY3LTQ0OTMtNGUwZS04NzQ1LTBlNTdjNmFjZGY5ZiJ9.l9DAJTA7UkOhRXKbIMIDQBDefeiMWvoYPRXtJCSaamnuczaBULKF-mtDuYd2FXW3OEE4YHmnpmj0ZPZ10Oq2Zw +Content-Type: application/json; charset=UTF-8 + +{"marketHashName":"P90 | Desert Warfare (Well-Worn)"} \ No newline at end of file diff --git a/ruoyi-ui/restapi/支付.http b/ruoyi-ui/restapi/支付.http new file mode 100644 index 0000000..8b417c7 --- /dev/null +++ b/ruoyi-ui/restapi/支付.http @@ -0,0 +1,41 @@ +POST http://154.12.94.98:8081/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"username":"17688554522","password":"123456"} + +### 卡密支付 + +POST http://localhost:8081/api/recharge/cardPay?password=ABB3CBC8E36A49AB8DFC26D64D1E2F92 HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImFjNGJlZWI3LWFkZDMtNDY3Yy05YTRhLTRkZmFiMWU0N2RiMyJ9.Wjt2kcwyFkzTF-qOzh5Yi_GEhIpX6F1CnilhcZj0WUhv7Q-RAJLgq2px16Bo8dg8HJiQrj0c7l2x0P5kY5w-mw + + +### 在线支付 + +POST http://154.12.94.98:8081/api/hipay/pay HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjI3MmU4M2JhLTgxMzYtNDc3Yi05MDIyLTYyMDc0MTQxNDI1MiJ9.GfB7Bgt5zmK4wvJPN7Qv8Csuk5_RhI4qewS-YpCAHD0vyvXI50VtixsHW3IIC3e0ekn44nLw4_THt28opVyGBw +Content-Type: application/json; charset=UTF-8 + +{ + "goodsId": 6, + "goodsNum": 1 +} + +### 回调 +POST http://localhost:8081/api/hipay/notify HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjI3MmU4M2JhLTgxMzYtNDc3Yi05MDIyLTYyMDc0MTQxNDI1MiJ9.GfB7Bgt5zmK4wvJPN7Qv8Csuk5_RhI4qewS-YpCAHD0vyvXI50VtixsHW3IIC3e0ekn44nLw4_THt28opVyGBw +Content-Type: application/json; charset=UTF-8 + +{"code":"1","data":"{\"amount\":100.00,\"createtime\":1774019940000,\"id\":\"89878e6d7ada40e18bb76da3276fc9e5\",\"note\":\"充值100.00\",\"notifyurl\":\"http://154.12.94.98:8081/api/hipay/notify\",\"orderid\":\"2035013237443006464\",\"ordertype\":1,\"recvcharge\":12.000000,\"recvid\":\"e42835c685f0712b19f27d693ee11f42\",\"recvnickname\":\"星耀科技\",\"resign\":\"edced5cb250a97f5775cdd4f72814129\",\"returnurl\":\"\",\"sign\":\"c0ca2c25fbcb2a8cdfaf72fda7cb4b98\",\"state\":5,\"transtime\":1774020221000}","msg":"success"} + +### 制作红包 +POST http://localhost:8081/admin/redPacket HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjI3MmU4M2JhLTgxMzYtNDc3Yi05MDIyLTYyMDc0MTQxNDI1MiJ9.GfB7Bgt5zmK4wvJPN7Qv8Csuk5_RhI4qewS-YpCAHD0vyvXI50VtixsHW3IIC3e0ekn44nLw4_THt28opVyGBw +Content-Type: application/json; charset=UTF-8 + +{"num":2,"userId":1,"createNum":1,"title":"红包","openingTime":"2026-03-24 20:54:05","amount":"[1,1]"} + +### 红包兑换 + +GET http://154.12.94.98:8081/api/bonus/receiveRedPacket/219D1DDB124440EB875FDCCF03310D2F HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjEzNTFhNGUzLTk5ZDItNGQzMy1iOGFkLTA3ODhjNDhmNDcwNiJ9.THdSGhpP8F1VOBldWdI3kqhro1wSUvRKYEHUw5bcK4wJJFynUkJSjoT9Acnzb4EuJ-Su2pcvp9Jc4BoUX-6deQ diff --git a/ruoyi-ui/restapi/消息.http b/ruoyi-ui/restapi/消息.http new file mode 100644 index 0000000..4a67ee4 --- /dev/null +++ b/ruoyi-ui/restapi/消息.http @@ -0,0 +1,9 @@ +POST http://localhost:8081/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"username":"19156010758","password":"123456"} + +### 获取消息列表 +GET http://localhost:8081/api/message/getMessageList HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6Ijg2YzcxOGQ4LTc0MzItNDAyNS04MmVhLWQxYjgyNTg4OTk2MyJ9.6nybsO4P4cUxQP_r_CUpYJKJfFtvx9qTpRF4RkjpZ_JgCKvajoYP4NibJgU9mfOHl32cuYV0yOemkFSEs_Ilgg \ No newline at end of file diff --git a/ruoyi-ui/restapi/福利.http b/ruoyi-ui/restapi/福利.http new file mode 100644 index 0000000..b6b04b6 --- /dev/null +++ b/ruoyi-ui/restapi/福利.http @@ -0,0 +1,10 @@ +POST http://localhost:8081/api/login HTTP/1.1 +Content-Type: application/json; charset=UTF-8 + + +{"username":"17774038557","password":"123456"} + +### 查询返利福利 +GET http://localhost:8081/api/bonus/vipreward/list HTTP/1.1 +Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImUyYTE1ODU5LTQ0MzktNGM2Zi1hNjBjLTRhOGNmMmE1NTdiNyJ9.l32RqHK4IV8DTH6tR7oq-rQyJ8YvOTqlRWCzgdUR2Iea-_-Svu6R-eCz4UOz4-QNGs9yoBbldrAfvaaMfqYK7w + diff --git a/ruoyi-ui/src/App.vue b/ruoyi-ui/src/App.vue new file mode 100644 index 0000000..f392ff0 --- /dev/null +++ b/ruoyi-ui/src/App.vue @@ -0,0 +1,32 @@ + + + + diff --git a/ruoyi-ui/src/api/game/box/mock/api.js b/ruoyi-ui/src/api/game/box/mock/api.js new file mode 100644 index 0000000..167cd84 --- /dev/null +++ b/ruoyi-ui/src/api/game/box/mock/api.js @@ -0,0 +1,19 @@ +import request from '@/utils/request' + +// 查询:GET /admin/sugar/mockresult/list +export function list(query) { + return request({ + url: '/admin/box/mockresult/list', + method: 'get', + params: query + }) +} + +// 更新:POST /admin/sugar/mockresult/list +export function update(data) { + return request({ + url: '/admin/box/mockresult/list', + method: 'post', + data: data + }) +} diff --git a/ruoyi-ui/src/api/game/sugar/config/api.js b/ruoyi-ui/src/api/game/sugar/config/api.js new file mode 100644 index 0000000..50b9cf1 --- /dev/null +++ b/ruoyi-ui/src/api/game/sugar/config/api.js @@ -0,0 +1,17 @@ +import request from '@/utils/request' + + +export function getConfig() { + return request({ + url: '/admin/sugar/gameconfig', + method: 'get' + }) +} + +export function updateConfig(data) { + return request({ + url: '/admin/sugar/gameconfig', + method: 'post', + data: data + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/game/sugar/dailywin/api.js b/ruoyi-ui/src/api/game/sugar/dailywin/api.js new file mode 100644 index 0000000..96c1d32 --- /dev/null +++ b/ruoyi-ui/src/api/game/sugar/dailywin/api.js @@ -0,0 +1,17 @@ +import request from '@/utils/request' + + +export function list(query) { + return request({ + url: '/admin/sugar/dailywin/list', + method: 'get', + params: query + }) +} + +export function resetRewardPool() { + return request({ + url: '/api/sugar/rewardpool/reset', + method: 'post' + }) +} diff --git a/ruoyi-ui/src/api/game/sugar/mock/api.js b/ruoyi-ui/src/api/game/sugar/mock/api.js new file mode 100644 index 0000000..cce9960 --- /dev/null +++ b/ruoyi-ui/src/api/game/sugar/mock/api.js @@ -0,0 +1,18 @@ +import request from '@/utils/request' + + +export function list(query) { + return request({ + url: '/admin/sugar/mockresult/list', + method: 'get', + params: query + }) +} + +export function update(data) { + return request({ + url: '/admin/sugar/mockresult/list', + method: 'POST', + data: data + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/game/sugar/record/api.js b/ruoyi-ui/src/api/game/sugar/record/api.js new file mode 100644 index 0000000..c6ace5d --- /dev/null +++ b/ruoyi-ui/src/api/game/sugar/record/api.js @@ -0,0 +1,10 @@ +import request from '@/utils/request' + + +export function listRecord(query) { + return request({ + url: '/admin/sugar/user/record/list', + method: 'get', + params: query + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/game/sugar/user/api.js b/ruoyi-ui/src/api/game/sugar/user/api.js new file mode 100644 index 0000000..1c388d1 --- /dev/null +++ b/ruoyi-ui/src/api/game/sugar/user/api.js @@ -0,0 +1,10 @@ +import request from '@/utils/request' + +// 查询注册用户信息列表 +export function listUser(query) { + return request({ + url: '/admin/sugar/user/list', + method: 'get', + params: query + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/game/wheel/config/api.js b/ruoyi-ui/src/api/game/wheel/config/api.js new file mode 100644 index 0000000..f62630f --- /dev/null +++ b/ruoyi-ui/src/api/game/wheel/config/api.js @@ -0,0 +1,17 @@ +import request from '@/utils/request' + + +export function getConfig() { + return request({ + url: '/admin/wheel/gameconfig', + method: 'get' + }) +} + +export function updateConfig(data) { + return request({ + url: '/admin/wheel/gameconfig', + method: 'post', + data: data + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/index.js b/ruoyi-ui/src/api/index.js new file mode 100644 index 0000000..f939a02 --- /dev/null +++ b/ruoyi-ui/src/api/index.js @@ -0,0 +1,28 @@ +import request from '@/utils/request' + +export function getBalance() { + return request({ + url: '/admin/cs340item/checkMerchantBalance', + method: 'get', + }) +} +export function developmentInfo() { + return request({ + url: '/admin/zbt/developmentInfo', + method: 'get', + }) +} + +export function getV5itemBalance() { + return request({ + url: '/admin/v5item/checkMerchantBalance', + method: 'get', + }) +} + +export function getHomeStats() { + return request({ + url: '/admin/rechargeRecord/homeStats', + method: 'get', + }) +} diff --git a/ruoyi-ui/src/api/login.js b/ruoyi-ui/src/api/login.js new file mode 100644 index 0000000..9b71d78 --- /dev/null +++ b/ruoyi-ui/src/api/login.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 登录方法 +export function login(username, password, code, uuid) { + const data = { + username, + password, + code, + uuid + } + return request({ + url: '/login', + headers: { + isToken: false, + repeatSubmit: false + }, + method: 'post', + data: data + }) +} + +// 注册方法 +export function register(data) { + return request({ + url: '/register', + headers: { + isToken: false + }, + method: 'post', + data: data + }) +} + +// 获取用户详细信息 +export function getInfo() { + return request({ + url: '/getInfo', + method: 'get' + }) +} + +// 退出方法 +export function logout() { + return request({ + url: '/logout', + method: 'post' + }) +} + +// 获取验证码 +export function getCodeImg() { + return request({ + url: '/captchaImage', + headers: { + isToken: false + }, + method: 'get', + timeout: 20000 + }) +} diff --git a/ruoyi-ui/src/api/menu.js b/ruoyi-ui/src/api/menu.js new file mode 100644 index 0000000..faef101 --- /dev/null +++ b/ruoyi-ui/src/api/menu.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 获取路由 +export const getRouters = () => { + return request({ + url: '/getRouters', + method: 'get' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/monitor/cache.js b/ruoyi-ui/src/api/monitor/cache.js new file mode 100644 index 0000000..72c5f6a --- /dev/null +++ b/ruoyi-ui/src/api/monitor/cache.js @@ -0,0 +1,57 @@ +import request from '@/utils/request' + +// 查询缓存详细 +export function getCache() { + return request({ + url: '/monitor/cache', + method: 'get' + }) +} + +// 查询缓存名称列表 +export function listCacheName() { + return request({ + url: '/monitor/cache/getNames', + method: 'get' + }) +} + +// 查询缓存键名列表 +export function listCacheKey(cacheName) { + return request({ + url: '/monitor/cache/getKeys/' + cacheName, + method: 'get' + }) +} + +// 查询缓存内容 +export function getCacheValue(cacheName, cacheKey) { + return request({ + url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey, + method: 'get' + }) +} + +// 清理指定名称缓存 +export function clearCacheName(cacheName) { + return request({ + url: '/monitor/cache/clearCacheName/' + cacheName, + method: 'delete' + }) +} + +// 清理指定键名缓存 +export function clearCacheKey(cacheKey) { + return request({ + url: '/monitor/cache/clearCacheKey/' + cacheKey, + method: 'delete' + }) +} + +// 清理全部缓存 +export function clearCacheAll() { + return request({ + url: '/monitor/cache/clearCacheAll', + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/monitor/job.js b/ruoyi-ui/src/api/monitor/job.js new file mode 100644 index 0000000..3815569 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/job.js @@ -0,0 +1,71 @@ +import request from '@/utils/request' + +// 查询定时任务调度列表 +export function listJob(query) { + return request({ + url: '/monitor/job/list', + method: 'get', + params: query + }) +} + +// 查询定时任务调度详细 +export function getJob(jobId) { + return request({ + url: '/monitor/job/' + jobId, + method: 'get' + }) +} + +// 新增定时任务调度 +export function addJob(data) { + return request({ + url: '/monitor/job', + method: 'post', + data: data + }) +} + +// 修改定时任务调度 +export function updateJob(data) { + return request({ + url: '/monitor/job', + method: 'put', + data: data + }) +} + +// 删除定时任务调度 +export function delJob(jobId) { + return request({ + url: '/monitor/job/' + jobId, + method: 'delete' + }) +} + +// 任务状态修改 +export function changeJobStatus(jobId, status) { + const data = { + jobId, + status + } + return request({ + url: '/monitor/job/changeStatus', + method: 'put', + data: data + }) +} + + +// 定时任务立即执行一次 +export function runJob(jobId, jobGroup) { + const data = { + jobId, + jobGroup + } + return request({ + url: '/monitor/job/run', + method: 'put', + data: data + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/monitor/jobLog.js b/ruoyi-ui/src/api/monitor/jobLog.js new file mode 100644 index 0000000..6e0be61 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/jobLog.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +// 查询调度日志列表 +export function listJobLog(query) { + return request({ + url: '/monitor/jobLog/list', + method: 'get', + params: query + }) +} + +// 删除调度日志 +export function delJobLog(jobLogId) { + return request({ + url: '/monitor/jobLog/' + jobLogId, + method: 'delete' + }) +} + +// 清空调度日志 +export function cleanJobLog() { + return request({ + url: '/monitor/jobLog/clean', + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/monitor/logininfor.js b/ruoyi-ui/src/api/monitor/logininfor.js new file mode 100644 index 0000000..4d112b7 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/logininfor.js @@ -0,0 +1,34 @@ +import request from '@/utils/request' + +// 查询登录日志列表 +export function list(query) { + return request({ + url: '/monitor/logininfor/list', + method: 'get', + params: query + }) +} + +// 删除登录日志 +export function delLogininfor(infoId) { + return request({ + url: '/monitor/logininfor/' + infoId, + method: 'delete' + }) +} + +// 解锁用户登录状态 +export function unlockLogininfor(userName) { + return request({ + url: '/monitor/logininfor/unlock/' + userName, + method: 'get' + }) +} + +// 清空登录日志 +export function cleanLogininfor() { + return request({ + url: '/monitor/logininfor/clean', + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/monitor/online.js b/ruoyi-ui/src/api/monitor/online.js new file mode 100644 index 0000000..bd22137 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/online.js @@ -0,0 +1,18 @@ +import request from '@/utils/request' + +// 查询在线用户列表 +export function list(query) { + return request({ + url: '/monitor/online/list', + method: 'get', + params: query + }) +} + +// 强退用户 +export function forceLogout(tokenId) { + return request({ + url: '/monitor/online/' + tokenId, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/monitor/operlog.js b/ruoyi-ui/src/api/monitor/operlog.js new file mode 100644 index 0000000..a04bca8 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/operlog.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +// 查询操作日志列表 +export function list(query) { + return request({ + url: '/monitor/operlog/list', + method: 'get', + params: query + }) +} + +// 删除操作日志 +export function delOperlog(operId) { + return request({ + url: '/monitor/operlog/' + operId, + method: 'delete' + }) +} + +// 清空操作日志 +export function cleanOperlog() { + return request({ + url: '/monitor/operlog/clean', + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/monitor/server.js b/ruoyi-ui/src/api/monitor/server.js new file mode 100644 index 0000000..e1f9ca2 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/server.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 获取服务信息 +export function getServer() { + return request({ + url: '/monitor/server', + method: 'get' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/skins/ttBonus/api.js b/ruoyi-ui/src/api/skins/ttBonus/api.js new file mode 100644 index 0000000..5deeea9 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttBonus/api.js @@ -0,0 +1,39 @@ +import request from '@/utils/request' +//福利-查询福利列表 +export function getBonus(query) { + return request({ + url: '/admin/bonus/list', + method: 'get', + params: query + }) +} +//福利-获取福利详细信息 +export function getBonusid(id) { + return request({ + url: `/admin/bonus/${id}`, + method: 'get', + }) +} +//福利-获取福利详细信息 +export function delBonus(ids) { + return request({ + url: `/admin/bonus/${ids}`, + method: 'delete', + }) +} +//福利-新增福利 +export function addBonus(data) { + return request({ + url: '/admin/bonus', + method: 'post', + data: data + }) +} +//福利-修改福利 +export function updateBonus (data) { + return request({ + url: '/admin/bonus', + method: 'put', + data: data + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/skins/ttBox/api.js b/ruoyi-ui/src/api/skins/ttBox/api.js new file mode 100644 index 0000000..79ce615 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttBox/api.js @@ -0,0 +1,168 @@ +import request from '@/utils/request' + +// 查询宝箱数据列表 +export function listBox(query) { + return request({ + url: '/admin/box/list', + method: 'get', + params: query + }) +} + +// 查询宝箱数据详细 +export function getBox(boxId) { + return request({ + url: '/admin/box/' + boxId, + method: 'get' + }) +} + +// 新增宝箱数据 +export function addBox(data) { + return request({ + url: '/admin/box', + method: 'post', + data: data + }) +} + +// 修改宝箱数据 +export function updateBox(data) { + return request({ + url: '/admin/box', + method: 'put', + data: data + }) +} + +// 删除宝箱数据 +export function delBox(boxId) { + return request({ + url: '/admin/box/' + boxId, + method: 'delete' + }) +} + +//宝箱类型 +export function boxTypelist(data) { + return request({ + url: '/admin/boxType/list', + method: 'get', + params:data + }) +} +//新增宝箱 + +export function boxType(data) { + return request({ + url: '/admin/boxType', + method: 'post', + data:data + }) +} +// +export function boxTypeChange(data) { + return request({ + url: '/admin/boxType', + method: 'put', + data:data + }) +} +// +export function boxTypeId(id) { + return request({ + url: `/admin/boxType/${id}`, + method: 'get', + }) +} +// +export function boxChange(data) { + return request({ + url: `/admin/box`, + method: 'put', + data:data + }) +} +//宝箱类型-删除宝箱类型数据 +export function delBoxType(ids) { + return request({ + url: `/admin/boxType/${ids}`, + method: 'delete', + }) +} +//宝箱-查看奖项 +export function getBoxOrnaments(boxId,page,size) { + return request({ + url: `/admin/boxOrnaments/list/${boxId}?pageNum=${page}&pageSize=${size}`, + method: 'get', + }) +} +//宝箱-修改奖项 +export function saveBoxOrnaments(data) { + return request({ + url: `/admin/boxOrnaments`, + method: 'put', + data:data + }) +} +//宝箱-新增奖项 +export function addBoxOrnaments(data) { + return request({ + url: `/admin/boxOrnaments`, + method: 'post', + data:data + }) +} +//宝箱-获取奖项详细信息 +export function getidBoxOrnaments(id) { + return request({ + url: `/admin/boxOrnaments/${id}`, + method: 'get', + }) +} +//宝箱-获取奖项详细信息 +export function delBoxOrnaments(boxId,ids) { + return request({ + url: `/admin/boxOrnaments/${boxId}/${ids}`, + method: 'delete', + }) +} +//宝箱记录-获取宝箱记录数据列表 +export function getBoxRecords(data) { + return request({ + url: `/admin/boxRecords/list`, + method: 'get', + params:data + }) +} +//获取宝箱利润率 +export function getProfitMargin(boxId) { + return request({ + url: `/admin/boxOrnaments/getProfitMargin/${boxId}`, + method: 'get', + }) +} + +//宝箱饰品-批量填货 + export function batchAdd(data) { + return request({ + url: `/admin/boxOrnaments/batchAdd`, + method: 'post', + data: data + }) +} +//宝箱-重置宝箱 + export function resetBox(boxId) { + return request({ + url: `/admin/box/resetBox/${boxId}`, + method: 'get', + }) +} +// +//宝箱-统计宝箱数据 + export function countBox(boxId,data) { + return request({ + url: `/admin/box/statisticsBoxData/${boxId}?date=${data}`, + method: 'get', + }) +} diff --git a/ruoyi-ui/src/api/skins/ttFight/api.js b/ruoyi-ui/src/api/skins/ttFight/api.js new file mode 100644 index 0000000..f13c1c4 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttFight/api.js @@ -0,0 +1,24 @@ +import request from '@/utils/request' + +//获取对战列表 +export function getFightlist(data){ + return request({ + url: '/admin/fight/list', + method: 'get', + params: data + }) +} +//获取对战宝箱列表 +export function getFightBoxList(fightId){ + return request({ + url: `/admin/fight/getFightBoxList/${fightId}`, + method: 'get', + }) +} +//查询对战结果 +export function getFightResult (fightId){ + return request({ + url: `/admin/fightResult/getFightResult/${fightId}`, + method: 'get', + }) +} diff --git a/ruoyi-ui/src/api/skins/ttFight/fightRankingReward.js b/ruoyi-ui/src/api/skins/ttFight/fightRankingReward.js new file mode 100644 index 0000000..176a7d5 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttFight/fightRankingReward.js @@ -0,0 +1,58 @@ +import request from '@/utils/request' + +// 查询对战奖励金额列表 +export function listReward(query) { + return request({ + url: '/admin/reward/list', + method: 'get', + params: query + }) +} + +// 查询对战奖励金额详细 +export function getReward(id) { + return request({ + url: '/admin/reward/' + id, + method: 'get' + }) +} + +// 新增对战奖励金额 +export function addReward(data) { + return request({ + url: '/admin/reward', + method: 'post', + data: data + }) +} + +export function generateRankingReward() { + return request({ + url: '/admin/reward/generateRankingReward/', + method: 'post' + }) +} + +// 修改对战奖励金额 +export function updateReward(data) { + return request({ + url: '/admin/reward', + method: 'put', + data: data + }) +} + +// 删除对战奖励金额 +export function delReward(id) { + return request({ + url: '/admin/reward/' + id, + method: 'delete' + }) +} + +export function truncateRankingReward() { + return request({ + url: '/admin/reward/truncateRankingReward', + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/skins/ttFight/robotFightGroup.js b/ruoyi-ui/src/api/skins/ttFight/robotFightGroup.js new file mode 100644 index 0000000..49a7bf7 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttFight/robotFightGroup.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询机器人对战分组列表 +export function listGroup(query) { + return request({ + url: '/admin/group/list', + method: 'get', + params: query + }) +} + +// 查询机器人对战分组详细 +export function getGroup(groupId) { + return request({ + url: '/admin/group/' + groupId, + method: 'get' + }) +} + +// 新增机器人对战分组 +export function addGroup(data) { + return request({ + url: '/admin/group', + method: 'post', + data: data + }) +} + +// 修改机器人对战分组 +export function updateGroup(data) { + return request({ + url: '/admin/group', + method: 'put', + data: data + }) +} + +// 删除机器人对战分组 +export function delGroup(groupId) { + return request({ + url: '/admin/group/' + groupId, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/skins/ttFight/robotFightGroupBox.js b/ruoyi-ui/src/api/skins/ttFight/robotFightGroupBox.js new file mode 100644 index 0000000..fae361c --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttFight/robotFightGroupBox.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询机器人对战分组宝箱列表 +export function listGroupBox(groupId, query) { + return request({ + url: '/admin/group/box/list/' + groupId, + method: 'get', + params: query + }) +} + +// 查询机器人对战分组宝箱详细 +export function getGroupBox(groupId, boxId) { + return request({ + url: '/admin/group/box/' + groupId + '/' + boxId, + method: 'get' + }) +} + +// 新增机器人对战分组宝箱 +export function addGroupBox(data) { + return request({ + url: '/admin/group/box', + method: 'post', + data: data + }) +} + +// 修改机器人对战分组宝箱 +export function updateGroupBox(data) { + return request({ + url: '/admin/group/box', + method: 'put', + data: data + }) +} + +// 删除机器人对战分组宝箱 +export function delGroupBox(groupId, boxId) { + return request({ + url: '/admin/group/box/' + groupId + "/" + boxId, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/skins/ttFirstRecharge/api.js b/ruoyi-ui/src/api/skins/ttFirstRecharge/api.js new file mode 100644 index 0000000..91e4c3f --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttFirstRecharge/api.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询首充赠送列表 +export function listRecharge(query) { + return request({ + url: '/admin/recharge/list', + method: 'get', + params: query + }) +} + +// 查询首充赠送详细 +export function getRecharge(id) { + return request({ + url: '/admin/recharge/' + id, + method: 'get' + }) +} + +// 新增首充赠送 +export function addRecharge(data) { + return request({ + url: '/admin/recharge', + method: 'post', + data: data + }) +} + +// 修改首充赠送 +export function updateRecharge(data) { + return request({ + url: '/admin/recharge', + method: 'put', + data: data + }) +} + +// 删除首充赠送 +export function delRecharge(id) { + return request({ + url: '/admin/recharge/' + id, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/skins/ttGenerateCard/api.js b/ruoyi-ui/src/api/skins/ttGenerateCard/api.js new file mode 100644 index 0000000..6f49f68 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttGenerateCard/api.js @@ -0,0 +1,23 @@ +import request from '@/utils/request' + + +export function getRechargeCard(query) { + return request({ + url: '/admin/rechargeCard/list', + method: 'get', + params: query + }) +} +export function addRechargeCard(rechargeListId,num) { + return request({ + url: `/admin/rechargeCard/generateCard/${rechargeListId}/${num}`, + method:'post' + }) +} + +export function downloadRechargeCard() { + return request({ + url: `/admin/rechargeCard/export`, + method:'post' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/skins/ttOrnaments/api.js b/ruoyi-ui/src/api/skins/ttOrnaments/api.js new file mode 100644 index 0000000..1a818ab --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttOrnaments/api.js @@ -0,0 +1,96 @@ +import request from '@/utils/request' + +// 查询饰品数据列表 +export function listOrnaments(query) { + return request({ + url: '/admin/ornaments/list', + method: 'get', + params: query + }) +} + +// 查询饰品数据详细 +export function getOrnaments(id) { + return request({ + url: '/admin/ornaments/' + id, + method: 'get' + }) +} + +// 新增饰品数据 +export function addOrnaments(data) { + return request({ + url: '/admin/ornaments', + method: 'post', + data: data + }) +} +// 发放饰品 +export function grantOrnaments(data,userId,ornamentsId,num) { + return request({ + url: `/admin/ornaments/grantOrnaments/${userId}/${ornamentsId}/${num}`, + method: 'post', + params:data + }) +} + +// 修改饰品数据 +export function updateOrnaments(data) { + return request({ + url: '/admin/ornaments', + method: 'put', + data: data + }) +} + +// 删除饰品数据 +export function delOrnaments(id) { + return request({ + url: '/admin/ornaments/' + id, + method: 'delete' + }) +} + +// 网站道具-查询网站道具列表 +export function getWebsiteProperty(params) { + return request({ + url: '/admin/websiteProperty/list', + method: 'get', + params: params + }) +} + +// 网站道具-查询网站道具详细信息 +export function getWebsitePropertyid(id) { + return request({ + url: `/admin/websiteProperty/${id}`, + method: 'get', + }) +} + +// 网站道具-删除网站道具 +export function delWebsiteProperty(ids) { + return request({ + url: `/admin/websiteProperty/${ids}`, + method: 'delete' + }) +} + +// 网站道具-修改网站道具 +export function changeWebsiteProperty(data) { + return request({ + url: '/admin/websiteProperty', + method: 'put', + data: data + }) +} + +// 网站道具-新增网站道具 +export function addWebsiteProperty(data) { + return request({ + url: '/admin/websiteProperty', + method: 'post', + data: data + }) +} + diff --git a/ruoyi-ui/src/api/skins/ttOrnaments/boxOpenChance.js b/ruoyi-ui/src/api/skins/ttOrnaments/boxOpenChance.js new file mode 100644 index 0000000..e201e08 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttOrnaments/boxOpenChance.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询开箱机会列表 +export function listChance(query) { + return request({ + url: '/admin/chance/list', + method: 'get', + params: query + }) +} + +// 查询开箱机会详细 +export function getChance(ornamentId) { + return request({ + url: '/admin/chance/' + ornamentId, + method: 'get' + }) +} + +// 新增开箱机会 +export function addChance(data) { + return request({ + url: '/admin/chance', + method: 'post', + data: data + }) +} + +// 修改开箱机会 +export function updateChance(data) { + return request({ + url: '/admin/chance', + method: 'put', + data: data + }) +} + +// 删除开箱机会 +export function delChance(ornamentId) { + return request({ + url: '/admin/chance/' + ornamentId, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/skins/ttOrnamentsLevel/api.js b/ruoyi-ui/src/api/skins/ttOrnamentsLevel/api.js new file mode 100644 index 0000000..048089e --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttOrnamentsLevel/api.js @@ -0,0 +1,50 @@ +import request from '@/utils/request' + +// 查询饰品级别数据列表 +export function listLevel(query) { + return request({ + url: '/admin/ornamentsLevel/list', + method: 'get', + params: query + }) +} + +// 查询饰品级别数据详细 +export function getLevel(id) { + return request({ + url: '/admin/ornamentsLevel/' + id, + method: 'get' + }) +} + +// 新增饰品级别数据 +export function addLevel(num) { + return request({ + url: `/admin/ornamentsLevel/generateOrnamentsLevel/${num}`, + method: 'post', + }) +} + +// 修改饰品级别数据 +export function updateLevel(data) { + return request({ + url: '/admin/ornamentsLevel', + method: 'put', + data: data + }) +} + +// 删除饰品级别数据 +// export function delLevel(id) { +// return request({ +// url: '/admin/ornamentsLevel/' + id, +// method: 'delete' +// }) +// } +//重置列表 +export function resetLevel() { + return request({ + url: '/admin/ornamentsLevel', + method: 'post' + }) +} diff --git a/ruoyi-ui/src/api/skins/ttRecharge/api.js b/ruoyi-ui/src/api/skins/ttRecharge/api.js new file mode 100644 index 0000000..3ec840b --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttRecharge/api.js @@ -0,0 +1,48 @@ +import request from '@/utils/request' + +//充值记录 +export function rechargeRecord(data) { + return request({ + url: '/admin/rechargeRecord/list', + method: 'get', + params:data + }) +} +//充值 +export function getRechargeList(data) { + return request({ + url: '/admin/rechargeList/list', + method: 'get', + params:data + }) +} +//修改 +export function changeRechargeList(data) { + return request({ + url: '/admin/rechargeList', + method: 'put', + data:data + }) +} +//新增 +export function addRechargeList(data) { + return request({ + url: '/admin/rechargeList', + method: 'post', + data:data + }) +} +//id查询 +export function getRechargeListid(id) { + return request({ + url: `/admin/rechargeList/${id}`, + method: 'get', + }) +} +//删除 +export function delRechargeList(id) { + return request({ + url: `/admin/rechargeList/${id}`, + method: 'delete', + }) +} diff --git a/ruoyi-ui/src/api/skins/ttRedPack/api.js b/ruoyi-ui/src/api/skins/ttRedPack/api.js new file mode 100644 index 0000000..34e3eb3 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttRedPack/api.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询【请填写功能名称】列表 +export function listTtRedPack(query) { + return request({ + url: '/admin/redPack/list', + method: 'get', + params: query + }) +} + +// 查询【请填写功能名称】详细 +export function getTtRedPack(id) { + return request({ + url: '/admin/redPack/' + id, + method: 'get' + }) +} + +// 新增【请填写功能名称】 +export function addTtRedPack(data) { + return request({ + url: '/admin/redPack', + method: 'post', + data: data + }) +} + +// 修改【请填写功能名称】 +export function updateTtRedPack(data) { + return request({ + url: '/admin/redPack', + method: 'put', + data: data + }) +} + +// 删除【请填写功能名称】 +export function delTtRedPack(id) { + return request({ + url: '/admin/redPack/' + id, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/skins/ttReplacementRecord/api.js b/ruoyi-ui/src/api/skins/ttReplacementRecord/api.js new file mode 100644 index 0000000..071c69a --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttReplacementRecord/api.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询汰换记录列表 +export function listReplacementRecord(query) { + return request({ + url: '/admin/replacementRecord/list', + method: 'get', + params: query + }) +} + +// 查询汰换记录详细 +export function getReplacementRecord(id) { + return request({ + url: '/admin/replacementRecord/' + id, + method: 'get' + }) +} + +// 新增汰换记录 +export function addReplacementRecord(data) { + return request({ + url: '/admin/replacementRecord', + method: 'post', + data: data + }) +} + +// 修改汰换记录 +export function updateReplacementRecord(data) { + return request({ + url: '/admin/replacementRecord', + method: 'put', + data: data + }) +} + +// 删除汰换记录 +export function delReplacementRecord(id) { + return request({ + url: '/admin/replacementRecord/' + id, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/skins/ttRoll/api.js b/ruoyi-ui/src/api/skins/ttRoll/api.js new file mode 100644 index 0000000..368b0e1 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttRoll/api.js @@ -0,0 +1,215 @@ +import request from "@/utils/request"; + +export function list(query) { + return request({ + url: "/admin/roll/list", + method: "get", + params: query, + }); +} +export function rollJackpotList(query) { + return request({ + url: "/admin/rollJackpot/list", + method: "get", + params: query + }); +} +//Roll房-查看奖项列表 +export function getRollPrizeList(rollId) { + return request({ + url: `/admin/roll/getRollPrizeList/${rollId}`, + method: "get", + }); +} +// 查看roll奖池的饰品 +export function rollJackpotOrnamentsList(query) { + return request({ + url: `/admin/rollJackpotOrnaments/list`, + method: "get", + params: query, + }); +} +//指定获奖者 +export function namedWinner(query) { + return request({ + url: "/admin/roll/namedWinner", + method: "post", + data: query, + }); +} +//取消指定roll房奖品 +export function cancelNamedWinner(data) { + return request({ + url: "/admin/roll/cancelNamedWinner", + method: "delete", + data: data, + }); +} +//获取指定将品用户 /admin/roll/getRollUsers/{rollId} +export function getzhidinguser(rollId) { + return request({ + url: `/admin/roll/getRollUsers/${rollId}`, + method: "get", + }); +} + + +export function roll(query) { + return request({ + url: "/admin/roll", + method: "post", + data: query, + }); +} +export function rollJackpotCreat(query) { + return request({ + url: "/admin/rollJackpot", + method: "post", + data: query, + }); +} +export function rollJackpotAdd(data) { + return request({ + url: "/admin/rollJackpotOrnaments", + method: "post", + data: data, + }); +} +export function rollJackpotChange(query) { + return request({ + url: "/admin/rollJackpot", + method: "put", + data: query, + }); +} +export function rollChange(query) { + return request({ + url: "/admin/roll", + method: "put", + data: query, + }); +} +export function delRoll(rollId) { + return request({ + url: `/admin/roll/remove/${rollId}`, + method: "delete", + }); +} +export function rollJackpotOrnamentDel(jackpotId, rollJackpotOrnamentsIds) { + return request({ + url: `/admin/rollJackpotOrnaments/${jackpotId}/${rollJackpotOrnamentsIds}`, + method: "delete", + }); +} +export function delRollJackpot(jackpotId) { + return request({ + url: `/admin/rollJackpot/remove/${jackpotId}`, + method: "delete", + }); +} +// 根据id差奖池 +export function rollJackpotdetial(jackpotId) { + return request({ + url: `/admin/rollJackpot/${jackpotId}`, + method: "get", + }); +} +//修改奖池饰品 +export function rollJackpotOrnamentsChange(query) { + return request({ + url: `/admin/rollJackpotOrnaments`, + method: "put", + data: query, + }); +} +//Roll房奖池饰品-批量填货 +export function batchAdd(rollId, OrnamentsIds) { + return request({ + url: `/admin/rollJackpotOrnaments/batchAdd/${rollId}/${OrnamentsIds}`, + method: "post", + }); +} + +//时间Roll房-新增时间Roll房 +export function addtimeRoll(data) { + return request({ + url: `/admin/timeRoll`, + method: "post", + data: data, + }); +} + +//时间Roll房-修改时间Roll房数据 +export function changetimeRoll(data) { + return request({ + url: `/admin/timeRoll`, + method: "put", + data: data, + }); +} + +//时间Roll房-状态修改 +export function changeStatus() { + return request({ + url: `/admin/timeRoll/changeStatus`, + method: "put", + }); +} + +//时间Roll房-查看奖项列表 + +export function getTimeRollPrizeList() { + return request({ + url: `/admin/timeRoll/getTimeRollPrizeList/${id}`, + method: "get", + }); +} + +//时间Roll房-查询时间Roll房列表 +export function getTimeRoll() { + return request({ + url: `/admin/timeRoll/list`, + method: "get", + }); +} + +//时间Roll房-指定获奖者 +export function timeNamedWinner(query) { + return request({ + url: `/admin/timeRoll/namedWinner`, + method: "post", + data: query, + }); +} + +//时间Roll房-删除时间Roll房 +export function delTimeroll(id) { + return request({ + url: `/admin/timeRoll/remove/${id}`, + method: "delete", + }); +} + +//时间Roll房-获取时间Roll房详细信息 + +export function getTimeRollDetial(timeRollId) { + return request({ + url: `/admin/timeRoll/${timeRollId}`, + method: "get", + }); +} + +export function getRollUser(rollId) { + return request({ + url: `/admin/roll/getRollUser/${rollId}`, + method: "get" + }); +} + +export function addRobot(data) { + return request({ + url: `/admin/roll/addRobot`, + method: "post", + data: data + }); +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/skins/ttSetting/announcement.js b/ruoyi-ui/src/api/skins/ttSetting/announcement.js new file mode 100644 index 0000000..3694873 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttSetting/announcement.js @@ -0,0 +1,35 @@ +import request from '@/utils/request' + +// 查询公告列表 +export function listAnnouncement(query) { + return request({ + url: '/admin/announcement/list', + method: 'get', + params: query + }) +} + +// 查询公告详细 +export function getAnnouncement(announcementId) { + return request({ + url: '/admin/announcement/' + announcementId, + method: 'get' + }) +} + +// 新增公告 +export function addAnnouncement(data) { + return request({ + url: '/admin/announcement', + method: 'post', + data: data + }) +} + +// 删除公告 +export function delAnnouncement(announcementId) { + return request({ + url: '/admin/announcement/' + announcementId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/skins/ttSetting/api.js b/ruoyi-ui/src/api/skins/ttSetting/api.js new file mode 100644 index 0000000..71e39fe --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttSetting/api.js @@ -0,0 +1,263 @@ +import request from '@/utils/request' + +//banner +//banner列表 +export function getBanner(query) { + return request({ + url: '/admin/banner/list', + method: 'get', + params: query + }) +} +export function getBannerid(id) { + return request({ + url: `/admin/banner/${id}`, + method: 'get', + }) +} +//添加banner +export function addBanner(query) { + return request({ + url: '/admin/banner', + method: 'post', + data: query + }) +} +//删除 +export function delBanner(ids) { + return request({ + url: `/admin/banner/${ids}`, + method: 'delete', + }) +} +//修改banner +export function changeBanner(query) { + return request({ + url: '/admin/banner', + method: 'put', + data: query + }) +} + + +//avatar +//avatar列表 + +export function getuserAvatar(query) { + return request({ + url: '/admin/userAvatar/list', + method: 'get', + params: query + }) +} +//新增 +export function adduserAvatar(query) { + return request({ + url: '/admin/userAvatar', + method: 'post', + data: query + }) +} +//修改 +export function changheuserAvatar(query) { + return request({ + url: '/admin/userAvatar', + method: 'put', + data: query + }) +} +//删除 +export function deluserAvatar(ids) { + return request({ + url: `/admin/userAvatar/${ids}`, + method: 'delete', + }) +} +//id +export function getuserAvatarid(id) { + return request({ + url: `/admin/userAvatar/${id}`, + method: 'get', + }) +} + + +//综合设置 +export function getParameterSetting() { + return request({ + url: `/admin/websiteSetup/getParameterSetting`, + method: 'post', + }) +} + +// +export function updateParameterSetting(data) { + return request({ + url: `/admin/websiteSetup/updateParameterSetting`, + method: 'post', + data:data + }) +} +//vip等级-生成vip等级 +export function addVipLevel(num) { + return request({ + url: `/admin/vipLevel/generateVipLevel/${num}`, + method: 'post', + }) +} +//vip等级-获取vip等级详细信息 +export function getVipLevelid(id) { + return request({ + url: `/admin/vipLevel/${id}`, + method: 'get', + }) +} +//vip等级-重置vip等级 +export function resetVipLevel(id) { + return request({ + url: `/admin/vipLevel`, + method: 'post', + }) +} + +//vip等级-获取vip等级列表 +export function getVipLevel() { + return request({ + url: `/admin/vipLevel/list`, + method: 'get', + }) +} +//vip等级-修改vip等级 +export function changeVipLevel(data) { + return request({ + url: `/admin/vipLevel`, + method: 'put', + data:data + }) +} + +//推广等级-生成推广等级 +export function generatePromotionLevel(num) { + return request({ + url: `/admin/promotionLevel/generatePromotionLevel/${num}`, + method: 'post', + }) +} +//推广等级-获取推广等级详细信息 +export function getPromotionLevelid(id) { + return request({ + url: `/admin/promotionLevel/${id}`, + method: 'get', + }) +} +//推广等级-重置推广等级 +export function resetPromotionLevel() { + return request({ + url: `/admin/promotionLevel`, + method: 'post', + }) +} + +//推广等级-获取推广等级列表 +export function getPromotionLevel() { + return request({ + url: `/admin/promotionLevel/list`, + method: 'get', + }) +} + +//推广等级-修改推广等级 +export function changePromotionLevel(data) { + return request({ + url: `/admin/promotionLevel`, + method: 'put', + data:data + }) +} + + +//文章管理-查询文章列表 +export function getContent(query) { + return request({ + url: `admin/content/list`, + method: 'get', + params: query + }) +} +//文章管理-查询文章详细信息显示 +export function getContentid(id) { + return request({ + url: `/admin/content/${id}`, + method: 'get', + }) +} +//删除 +export function delContent(ids) { + return request({ + url: `/admin/content/${ids}`, + method: 'delete', + }) +} +//文章管理-新增文章列表 +export function addContent(data) { + return request({ + url: `/admin/content`, + method: 'post', + data:data + }) +} +//文章管理-修改文章 +export function changeContent(data) { + return request({ + url: `/admin/content`, + method: 'put', + data:data + }) +} + + +//文章类别管理-文章类别列表 +export function getContentType(data) { + return request({ + url: `admin/contentType/list`, + method: 'get', + params:data + }) +} +//文章类别管理-获取文章类别详细信息 +export function getContentTypeid(id) { + return request({ + url: `/admin/contentType/${id}`, + method: 'get', + }) +} +//文章类别管理-删除文章类别 +export function delContentType(ids) { + return request({ + url: `/admin/contentType/${ids}`, + method: 'delete', + }) +} +//文章类别管理-新增文章类别 +export function addContentType(data) { + return request({ + url: `/admin/contentType`, + method: 'post', + data:data + }) +} +//文章类别管理-修改文章类别 +export function changeContentType(data) { + return request({ + url: `/admin/contentType`, + method: 'put', + data:data + }) +} +// +export function getOperationalStatistics() { + return request({ + url: `/admin/websiteSetup/getOperationalStatistics`, + method: 'get', + }) +} diff --git a/ruoyi-ui/src/api/skins/ttShopping/api.js b/ruoyi-ui/src/api/skins/ttShopping/api.js new file mode 100644 index 0000000..b6a3cca --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttShopping/api.js @@ -0,0 +1,16 @@ +import request from '@/utils/request' + +export function shoppingList(query) { + return request({ + url: '/admin/shopping/list', + method: 'get', + params: query + }) +} +export function batchPutAwayOrSoldOut(query, status) { + return request({ + url: `/admin/shopping/batchPutAwayOrSoldOut/${status}`, + method: 'post', + data: query + }) +} diff --git a/ruoyi-ui/src/api/skins/ttStatistics/api.js b/ruoyi-ui/src/api/skins/ttStatistics/api.js new file mode 100644 index 0000000..e29acb3 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttStatistics/api.js @@ -0,0 +1,168 @@ +import request from '@/utils/request' + +// 查询大盘统计数据 +export function getStatistics(query) { + return request({ + url: '/admin/statistics/info', + method: 'get', + params: query + }) +} + +// 查询宝箱数据详细 +export function getBox(boxId) { + return request({ + url: '/admin/box/' + boxId, + method: 'get' + }) +} + +// 新增宝箱数据 +export function addBox(data) { + return request({ + url: '/admin/box', + method: 'post', + data: data + }) +} + +// 修改宝箱数据 +export function updateBox(data) { + return request({ + url: '/admin/box', + method: 'put', + data: data + }) +} + +// 删除宝箱数据 +export function delBox(boxId) { + return request({ + url: '/admin/box/' + boxId, + method: 'delete' + }) +} + +//宝箱类型 +export function boxTypelist(data) { + return request({ + url: '/admin/boxType/list', + method: 'get', + params:data + }) +} +//新增宝箱 + +export function boxType(data) { + return request({ + url: '/admin/boxType', + method: 'post', + data:data + }) +} +// +export function boxTypeChange(data) { + return request({ + url: '/admin/boxType', + method: 'put', + data:data + }) +} +// +export function boxTypeId(id) { + return request({ + url: `/admin/boxType/${id}`, + method: 'get', + }) +} +// +export function boxChange(data) { + return request({ + url: `/admin/box`, + method: 'put', + data:data + }) +} +//宝箱类型-删除宝箱类型数据 +export function delBoxType(ids) { + return request({ + url: `/admin/boxType/${ids}`, + method: 'delete', + }) +} +//宝箱-查看奖项 +export function getBoxOrnaments(boxId,page,size) { + return request({ + url: `/admin/boxOrnaments/list/${boxId}?pageNum=${page}&pageSize=${size}`, + method: 'get', + }) +} +//宝箱-修改奖项 +export function saveBoxOrnaments(data) { + return request({ + url: `/admin/boxOrnaments`, + method: 'put', + data:data + }) +} +//宝箱-新增奖项 +export function addBoxOrnaments(data) { + return request({ + url: `/admin/boxOrnaments`, + method: 'post', + data:data + }) +} +//宝箱-获取奖项详细信息 +export function getidBoxOrnaments(id) { + return request({ + url: `/admin/boxOrnaments/${id}`, + method: 'get', + }) +} +//宝箱-获取奖项详细信息 +export function delBoxOrnaments(boxId,ids) { + return request({ + url: `/admin/boxOrnaments/${boxId}/${ids}`, + method: 'delete', + }) +} +//宝箱记录-获取宝箱记录数据列表 +export function getBoxRecords(data) { + return request({ + url: `/admin/boxRecords/list`, + method: 'get', + params:data + }) +} +//获取宝箱利润率 +export function getProfitMargin(boxId) { + return request({ + url: `/admin/boxOrnaments/getProfitMargin/${boxId}`, + method: 'get', + }) +} + +//宝箱饰品-批量填货 + export function batchAdd(data) { + return request({ + url: `/admin/boxOrnaments/batchAdd`, + method: 'post', + data: data + }) +} +//宝箱-重置宝箱 + export function resetBox(boxId) { + return request({ + url: `/admin/box/resetBox/${boxId}`, + method: 'get', + }) +} +// +//宝箱-统计宝箱数据 + export function countBox(boxId,data) { + return request({ + url: `/admin/box/statisticsBoxData/${boxId}?date=${data}`, + method: 'get', + }) +} diff --git a/ruoyi-ui/src/api/skins/ttTaskCenter/api.js b/ruoyi-ui/src/api/skins/ttTaskCenter/api.js new file mode 100644 index 0000000..dfcf086 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttTaskCenter/api.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询任务中心列表 +export function listTaskCenter(query) { + return request({ + url: '/admin/taskCenter/list', + method: 'get', + params: query + }) +} + +// 查询任务中心详细 +export function getTaskCenter(id) { + return request({ + url: '/admin/taskCenter/' + id, + method: 'get' + }) +} + +// 新增任务中心 +export function addTaskCenter(data) { + return request({ + url: '/admin/taskCenter', + method: 'post', + data: data + }) +} + +// 修改任务中心 +export function updateTaskCenter(data) { + return request({ + url: '/admin/taskCenter', + method: 'put', + data: data + }) +} + +// 删除任务中心 +export function delTaskCenter(id) { + return request({ + url: '/admin/taskCenter/' + id, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/skins/ttUpgrade/api.js b/ruoyi-ui/src/api/skins/ttUpgrade/api.js new file mode 100644 index 0000000..28cb4dd --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttUpgrade/api.js @@ -0,0 +1,93 @@ +import request from '@/utils/request' + +// 查询幸运升级数据列表 +export function getupgradeOrnaments(query) { + return request({ + url: '/admin/upgradeOrnaments/list', + method: 'get', + params: query + }) +} + +// 查询幸运升级数据详细 +export function getUpgradeOrnamentsid(id) { + return request({ + url: '/admin/upgradeOrnaments/'+ id, + method: 'get' + }) +} + +// 新增幸运升级数据 +export function addUpgradeOrnaments(data) { + return request({ + url: `/admin/upgradeOrnaments/batchAdd/${data}`, + method: 'post', + // data: data + }) +} + +// 修改幸运升级数据 +export function updateUpgradeOrnaments(data) { + return request({ + url: '/admin/upgradeOrnaments', + method: 'put', + data: data + }) +} + +// 删除幸运升级数据 +export function delUpgradeOrnaments(ids) { + return request({ + url: `/admin/upgradeOrnaments/${ids}`, + method: 'delete' + }) +} +//失败奖励-删除奖励 +export function delFail(ids) { + return request({ + url: `/admin/upgradeFailOrnaments/${ids}`, + method: 'delete' + }) +} +//失败奖励-获取奖励列表 +export function getFail(data) { + return request({ + url: `/admin/upgradeFailOrnaments/list`, + method: 'post', + data: data + }) +} +//失败 添加 +export function addFail(data) { + return request({ + url: `/admin/upgradeFailOrnaments/batchAdd`, + method: 'post', + data: data + }) +} +//失败 更新 +export function upFail(data) { + return request({ + url: `/admin/upgradeFailOrnaments`, + method: 'put', + data:data + }) +} + +export function getupgradeRecord(data) { + return request({ + url: '/admin/upgradeRecord/list', + method: 'post', + data: data + }) +} + +//统计-获取升级饰品利润统计 +export function getUserProfitStatistics(id) { + return request({ + url:`/admin/upgradeOrnaments/getUpgradeProfitStatistics/${id}`, + method: 'get', + }) +} + + diff --git a/ruoyi-ui/src/api/skins/ttUserBlendErcash/api.js b/ruoyi-ui/src/api/skins/ttUserBlendErcash/api.js new file mode 100644 index 0000000..0177b2d --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttUserBlendErcash/api.js @@ -0,0 +1,10 @@ +import request from '@/utils/request' + +// 查询收支明细 +export function getUserBlendErcash(data) { + return request({ + url: '/admin/blendErcash/list', + method: 'post', + data: data + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/skins/ttWelfare/api.js b/ruoyi-ui/src/api/skins/ttWelfare/api.js new file mode 100644 index 0000000..ac73301 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttWelfare/api.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询福利列表列表 +export function listWelfare(query) { + return request({ + url: '/admin/welfare/list', + method: 'get', + params: query + }) +} + +// 查询福利列表详细 +export function getWelfare(welfareId) { + return request({ + url: '/admin/welfare/' + welfareId, + method: 'get' + }) +} + +// 新增福利列表 +export function addWelfare(data) { + return request({ + url: '/admin/welfare', + method: 'post', + data: data + }) +} + +// 修改福利列表 +export function updateWelfare(data) { + return request({ + url: '/admin/welfare', + method: 'put', + data: data + }) +} + +// 删除福利列表 +export function delWelfare(welfareId) { + return request({ + url: '/admin/welfare/' + welfareId, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/skins/ttWelfare/monthlyRecharges.js b/ruoyi-ui/src/api/skins/ttWelfare/monthlyRecharges.js new file mode 100644 index 0000000..ca04b23 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttWelfare/monthlyRecharges.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询每月充值福利列表 +export function listRecharges(query) { + return request({ + url: '/admin/recharges/list', + method: 'get', + params: query + }) +} + +// 查询每月充值福利详细 +export function getRecharges(id) { + return request({ + url: '/admin/recharges/' + id, + method: 'get' + }) +} + +// 新增每月充值福利 +export function addRecharges(data) { + return request({ + url: '/admin/recharges', + method: 'post', + data: data + }) +} + +// 修改每月充值福利 +export function updateRecharges(data) { + return request({ + url: '/admin/recharges', + method: 'put', + data: data + }) +} + +// 删除每月充值福利 +export function delRecharges(id) { + return request({ + url: '/admin/recharges/' + id, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/skins/ttWelfare/rechargeRankingReward.js b/ruoyi-ui/src/api/skins/ttWelfare/rechargeRankingReward.js new file mode 100644 index 0000000..bf1ec51 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttWelfare/rechargeRankingReward.js @@ -0,0 +1,58 @@ +import request from '@/utils/request' + +// 查询充值奖励金额列表 +export function listReward(query) { + return request({ + url: '/admin/rechargeRankingReward/list', + method: 'get', + params: query + }) +} + +// 查询充值奖励金额详细 +export function getReward(id) { + return request({ + url: '/admin/rechargeRankingReward/' + id, + method: 'get' + }) +} + +// 新增充值奖励金额 +export function addReward(data) { + return request({ + url: '/admin/rechargeRankingReward', + method: 'post', + data: data + }) +} + +export function generateRankingReward() { + return request({ + url: '/admin/rechargeRankingReward/generateRankingReward', + method: 'post' + }) +} + +// 修改充值奖励金额 +export function updateReward(data) { + return request({ + url: '/admin/rechargeRankingReward', + method: 'put', + data: data + }) +} + +// 删除充值奖励金额 +export function delReward(id) { + return request({ + url: '/admin/rechargeRankingReward/' + id, + method: 'delete' + }) +} + +export function truncateRankingReward() { + return request({ + url: '/admin/rechargeRankingReward/truncateRankingReward', + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/skins/ttdelivery/api.js b/ruoyi-ui/src/api/skins/ttdelivery/api.js new file mode 100644 index 0000000..1c714c0 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttdelivery/api.js @@ -0,0 +1,67 @@ +import request from '@/utils/request' + +//申请记录 +export function deliverGoodslist(query) { + return request({ + url: '/admin/deliverGoods/getDeliveryApplyList', + method: 'get', + params: query + }) +} +//获取饰品所有在售列表 +export function getAvailableMarketList(data) { + return request({ + url: '/admin/deliverGoods/getAvailableMarketList', + method: 'post', + data: data + }) +} +//同步状态 +export function synchronousStatus(query) { + return request({ + url: `/admin/deliverGoods/synchronousStatus`, + method: 'get', + params:query + }) +} +// 一键同步状态 +export function syncAllStatus() { + return request({ + url: '/admin/deliverGoods/syncAllStatus', + method: 'get', + timeout: 30000 + }) +} +//提货记录 +export function getDeliveryRecordList(query) { + return request({ + url: '/admin/deliverGoods/getDeliveryRecordList', + method: 'get', + params: query + }) +} +//发货 +export function tradeBuy(data) { + return request({ + url: '/admin/deliverGoods/tradeBuy', + method: 'post', + data: data + }) +} +//退回 +export function deliveryFail(query) { + return request({ + url: '/admin/deliverGoods/deliveryFail', + method: 'post', + params: query + }) +} + +// 查询cs2pifa在售信息 +export function queryOnSaleInfo(data) { + return request({ + url: '/admin/cs340item/queryOnSaleInfo', + method: 'post', + data: data + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/skins/ttredPacket/api.js b/ruoyi-ui/src/api/skins/ttredPacket/api.js new file mode 100644 index 0000000..13f965a --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttredPacket/api.js @@ -0,0 +1,48 @@ + +import request from '@/utils/request' +export function getRedPacket(query) { + return request({ + url: '/admin/redPacket/list', + method: 'get', + params: query + }) +} +export function addRedPacket(query) { + return request({ + url: '/admin/redPacket', + method: 'post', + data: query + }) +} +//删除 +export function delRedPacket(ids) { + return request({ + url: `/admin/redPacket/${ids}`, + method: 'delete', + }) +} +//根据id查询 +export function getRedPacketid(ids) { + return request({ + url: `/admin/redPacket/${ids}`, + method: 'get', + }) +} +//修改 +export function changeRedPacket(query) { + return request({ + url: `/admin/redPacket`, + method: 'put', + data: query + + }) +} +//开启记录 +export function getRedPacketRecordList(query) { + return request({ + url: `/admin/redPacketRecord/getRedPacketRecordList`, + method: 'get', + params: query + + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/skins/ttuser/api.js b/ruoyi-ui/src/api/skins/ttuser/api.js new file mode 100644 index 0000000..738cb15 --- /dev/null +++ b/ruoyi-ui/src/api/skins/ttuser/api.js @@ -0,0 +1,126 @@ +import request from '@/utils/request' + +// 查询注册用户信息列表 +export function listUser(query) { + return request({ + url: '/admin/user/list', + method: 'get', + params: query + }) +} + +// 查询注册用户信息详细 +export function getUser(userId) { + return request({ + url: '/admin/user/' + userId, + method: 'get' + }) +} + +// 修改注册用户信息 +export function updateUser(data) { + return request({ + url: '/admin/user', + method: 'put', + data: data + }) +} + +// 删除注册用户信息 +export function delUser(userId) { + return request({ + url: '/admin/user/' + userId, + method: 'delete' + }) +} + +export function listUser_Online(query) { + return request({ + url: '/admin/user/online/list', + method: 'get', + params: query + }) +} + +export function generateAccount(query) { + return request({ + url:`/admin/user/generateAccount/${query}`, + method:'post' + }) +} + +export function generateRobot(query) { + return request({ + url:`/admin/user/generateRobot/${query}`, + method:'post' + }) +} + +//用户账户-余额变动记录 +export function userAmountRecords(data) { + return request({ + url:`/admin/userAmountRecords/list`, + method: 'post', + data: data + }) +} +//用户账户-积分变动记录 +export function userCreditsRecords(data) { + return request({ + url:`/admin/userCreditsRecords/list`, + method: 'get', + params:data + }) +} +//获取用户背包 +export function getPackSack(data) { + return request({ + url:`/admin/user/getPackSack`, + method: 'get', + params:data + }) +} + +// 推广记录-获取推广记录 +export function getPromotionRecord(data) { + return request({ + url:`/admin/promotionRecord/getPromotionRecord`, + method: 'get', + params:data + }) +} +export function statisticsPromotionData(userId) { + return request({ + url:`/admin/promotionRecord/statisticsPromotionData/${userId}`, + method: 'get', + }) +} + +//用户背包-删除用户背包饰品 +export function removeUserPackSackData(ids) { + return request({ + url:`/admin/user/removeUserPackSackData/${ids}`, + method: 'post', + }) +} +//统计-获取用户利润统计 +export function getUserProfitStatistics(userId) { + return request({ + url:`/admin/user/getUserProfitStatistics/${userId}`, + method: 'get', + }) +} +// 重置用户余额和积分 +export function resetUserBalance(userId) { + return request({ + url: `/admin/user/resetBalance/${userId}`, + method: 'post' + }) +} +// 真删除用户(物理删除) +export function forceDeleteUser(userId) { + return request({ + url: `/admin/user/forceDelete/${userId}`, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/system/config.js b/ruoyi-ui/src/api/system/config.js new file mode 100644 index 0000000..a404d82 --- /dev/null +++ b/ruoyi-ui/src/api/system/config.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询参数列表 +export function listConfig(query) { + return request({ + url: '/system/config/list', + method: 'get', + params: query + }) +} + +// 查询参数详细 +export function getConfig(configId) { + return request({ + url: '/system/config/' + configId, + method: 'get' + }) +} + +// 根据参数键名查询参数值 +export function getConfigKey(configKey) { + return request({ + url: '/system/config/configKey/' + configKey, + method: 'get' + }) +} + +// 新增参数配置 +export function addConfig(data) { + return request({ + url: '/system/config', + method: 'post', + data: data + }) +} + +// 修改参数配置 +export function updateConfig(data) { + return request({ + url: '/system/config', + method: 'put', + data: data + }) +} + +// 删除参数配置 +export function delConfig(configId) { + return request({ + url: '/system/config/' + configId, + method: 'delete' + }) +} + +// 刷新参数缓存 +export function refreshCache() { + return request({ + url: '/system/config/refreshCache', + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/system/dept.js b/ruoyi-ui/src/api/system/dept.js new file mode 100644 index 0000000..fc943cd --- /dev/null +++ b/ruoyi-ui/src/api/system/dept.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 查询部门列表 +export function listDept(query) { + return request({ + url: '/system/dept/list', + method: 'get', + params: query + }) +} + +// 查询部门列表(排除节点) +export function listDeptExcludeChild(deptId) { + return request({ + url: '/system/dept/list/exclude/' + deptId, + method: 'get' + }) +} + +// 查询部门详细 +export function getDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'get' + }) +} + +// 新增部门 +export function addDept(data) { + return request({ + url: '/system/dept', + method: 'post', + data: data + }) +} + +// 修改部门 +export function updateDept(data) { + return request({ + url: '/system/dept', + method: 'put', + data: data + }) +} + +// 删除部门 +export function delDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/dict/data.js b/ruoyi-ui/src/api/system/dict/data.js new file mode 100644 index 0000000..6c9eb79 --- /dev/null +++ b/ruoyi-ui/src/api/system/dict/data.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 查询字典数据列表 +export function listData(query) { + return request({ + url: '/system/dict/data/list', + method: 'get', + params: query + }) +} + +// 查询字典数据详细 +export function getData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'get' + }) +} + +// 根据字典类型查询字典数据信息 +export function getDicts(dictType) { + return request({ + url: '/system/dict/data/type/' + dictType, + method: 'get' + }) +} + +// 新增字典数据 +export function addData(data) { + return request({ + url: '/system/dict/data', + method: 'post', + data: data + }) +} + +// 修改字典数据 +export function updateData(data) { + return request({ + url: '/system/dict/data', + method: 'put', + data: data + }) +} + +// 删除字典数据 +export function delData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/system/dict/type.js b/ruoyi-ui/src/api/system/dict/type.js new file mode 100644 index 0000000..a7a6e01 --- /dev/null +++ b/ruoyi-ui/src/api/system/dict/type.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询字典类型列表 +export function listType(query) { + return request({ + url: '/system/dict/type/list', + method: 'get', + params: query + }) +} + +// 查询字典类型详细 +export function getType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'get' + }) +} + +// 新增字典类型 +export function addType(data) { + return request({ + url: '/system/dict/type', + method: 'post', + data: data + }) +} + +// 修改字典类型 +export function updateType(data) { + return request({ + url: '/system/dict/type', + method: 'put', + data: data + }) +} + +// 删除字典类型 +export function delType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'delete' + }) +} + +// 刷新字典缓存 +export function refreshCache() { + return request({ + url: '/system/dict/type/refreshCache', + method: 'delete' + }) +} + +// 获取字典选择框列表 +export function optionselect() { + return request({ + url: '/system/dict/type/optionselect', + method: 'get' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/menu.js b/ruoyi-ui/src/api/system/menu.js new file mode 100644 index 0000000..f6415c6 --- /dev/null +++ b/ruoyi-ui/src/api/system/menu.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询菜单列表 +export function listMenu(query) { + return request({ + url: '/system/menu/list', + method: 'get', + params: query + }) +} + +// 查询菜单详细 +export function getMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'get' + }) +} + +// 查询菜单下拉树结构 +export function treeselect() { + return request({ + url: '/system/menu/treeselect', + method: 'get' + }) +} + +// 根据角色ID查询菜单下拉树结构 +export function roleMenuTreeselect(roleId) { + return request({ + url: '/system/menu/roleMenuTreeselect/' + roleId, + method: 'get' + }) +} + +// 新增菜单 +export function addMenu(data) { + return request({ + url: '/system/menu', + method: 'post', + data: data + }) +} + +// 修改菜单 +export function updateMenu(data) { + return request({ + url: '/system/menu', + method: 'put', + data: data + }) +} + +// 删除菜单 +export function delMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/notice.js b/ruoyi-ui/src/api/system/notice.js new file mode 100644 index 0000000..c274ea5 --- /dev/null +++ b/ruoyi-ui/src/api/system/notice.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询公告列表 +export function listNotice(query) { + return request({ + url: '/system/notice/list', + method: 'get', + params: query + }) +} + +// 查询公告详细 +export function getNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'get' + }) +} + +// 新增公告 +export function addNotice(data) { + return request({ + url: '/system/notice', + method: 'post', + data: data + }) +} + +// 修改公告 +export function updateNotice(data) { + return request({ + url: '/system/notice', + method: 'put', + data: data + }) +} + +// 删除公告 +export function delNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/post.js b/ruoyi-ui/src/api/system/post.js new file mode 100644 index 0000000..1a8e9ca --- /dev/null +++ b/ruoyi-ui/src/api/system/post.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询岗位列表 +export function listPost(query) { + return request({ + url: '/system/post/list', + method: 'get', + params: query + }) +} + +// 查询岗位详细 +export function getPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'get' + }) +} + +// 新增岗位 +export function addPost(data) { + return request({ + url: '/system/post', + method: 'post', + data: data + }) +} + +// 修改岗位 +export function updatePost(data) { + return request({ + url: '/system/post', + method: 'put', + data: data + }) +} + +// 删除岗位 +export function delPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/system/role.js b/ruoyi-ui/src/api/system/role.js new file mode 100644 index 0000000..f13e6f4 --- /dev/null +++ b/ruoyi-ui/src/api/system/role.js @@ -0,0 +1,119 @@ +import request from '@/utils/request' + +// 查询角色列表 +export function listRole(query) { + return request({ + url: '/system/role/list', + method: 'get', + params: query + }) +} + +// 查询角色详细 +export function getRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'get' + }) +} + +// 新增角色 +export function addRole(data) { + return request({ + url: '/system/role', + method: 'post', + data: data + }) +} + +// 修改角色 +export function updateRole(data) { + return request({ + url: '/system/role', + method: 'put', + data: data + }) +} + +// 角色数据权限 +export function dataScope(data) { + return request({ + url: '/system/role/dataScope', + method: 'put', + data: data + }) +} + +// 角色状态修改 +export function changeRoleStatus(roleId, status) { + const data = { + roleId, + status + } + return request({ + url: '/system/role/changeStatus', + method: 'put', + data: data + }) +} + +// 删除角色 +export function delRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'delete' + }) +} + +// 查询角色已授权用户列表 +export function allocatedUserList(query) { + return request({ + url: '/system/role/authUser/allocatedList', + method: 'get', + params: query + }) +} + +// 查询角色未授权用户列表 +export function unallocatedUserList(query) { + return request({ + url: '/system/role/authUser/unallocatedList', + method: 'get', + params: query + }) +} + +// 取消用户授权角色 +export function authUserCancel(data) { + return request({ + url: '/system/role/authUser/cancel', + method: 'put', + data: data + }) +} + +// 批量取消用户授权角色 +export function authUserCancelAll(data) { + return request({ + url: '/system/role/authUser/cancelAll', + method: 'put', + params: data + }) +} + +// 授权用户选择 +export function authUserSelectAll(data) { + return request({ + url: '/system/role/authUser/selectAll', + method: 'put', + params: data + }) +} + +// 根据角色ID查询部门树结构 +export function deptTreeSelect(roleId) { + return request({ + url: '/system/role/deptTree/' + roleId, + method: 'get' + }) +} diff --git a/ruoyi-ui/src/api/system/user.js b/ruoyi-ui/src/api/system/user.js new file mode 100644 index 0000000..f2f76ef --- /dev/null +++ b/ruoyi-ui/src/api/system/user.js @@ -0,0 +1,135 @@ +import request from '@/utils/request' +import { parseStrEmpty } from "@/utils/ruoyi"; + +// 查询用户列表 +export function listUser(query) { + return request({ + url: '/system/user/list', + method: 'get', + params: query + }) +} + +// 查询用户详细 +export function getUser(userId) { + return request({ + url: '/system/user/' + parseStrEmpty(userId), + method: 'get' + }) +} + +// 新增用户 +export function addUser(data) { + return request({ + url: '/system/user', + method: 'post', + data: data + }) +} + +// 修改用户 +export function updateUser(data) { + return request({ + url: '/system/user', + method: 'put', + data: data + }) +} + +// 删除用户 +export function delUser(userId) { + return request({ + url: '/system/user/' + userId, + method: 'delete' + }) +} + +// 用户密码重置 +export function resetUserPwd(userId, password) { + const data = { + userId, + password + } + return request({ + url: '/system/user/resetPwd', + method: 'put', + data: data + }) +} + +// 用户状态修改 +export function changeUserStatus(userId, status) { + const data = { + userId, + status + } + return request({ + url: '/system/user/changeStatus', + method: 'put', + data: data + }) +} + +// 查询用户个人信息 +export function getUserProfile() { + return request({ + url: '/system/user/profile', + method: 'get' + }) +} + +// 修改用户个人信息 +export function updateUserProfile(data) { + return request({ + url: '/system/user/profile', + method: 'put', + data: data + }) +} + +// 用户密码重置 +export function updateUserPwd(oldPassword, newPassword) { + const data = { + oldPassword, + newPassword + } + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + params: data + }) +} + +// 用户头像上传 +export function uploadAvatar(data) { + return request({ + url: '/system/user/profile/avatar', + method: 'post', + data: data + }) +} + +// 查询授权角色 +export function getAuthRole(userId) { + return request({ + url: '/system/user/authRole/' + userId, + method: 'get' + }) +} + +// 保存授权角色 +export function updateAuthRole(data) { + return request({ + url: '/system/user/authRole', + method: 'put', + params: data + }) +} + +// 查询部门下拉树结构 +export function deptTreeSelect() { + return request({ + url: '/system/user/deptTree', + method: 'get' + }) +} diff --git a/ruoyi-ui/src/api/tool/gen.js b/ruoyi-ui/src/api/tool/gen.js new file mode 100644 index 0000000..4506927 --- /dev/null +++ b/ruoyi-ui/src/api/tool/gen.js @@ -0,0 +1,76 @@ +import request from '@/utils/request' + +// 查询生成表数据 +export function listTable(query) { + return request({ + url: '/tool/gen/list', + method: 'get', + params: query + }) +} +// 查询db数据库列表 +export function listDbTable(query) { + return request({ + url: '/tool/gen/db/list', + method: 'get', + params: query + }) +} + +// 查询表详细信息 +export function getGenTable(tableId) { + return request({ + url: '/tool/gen/' + tableId, + method: 'get' + }) +} + +// 修改代码生成信息 +export function updateGenTable(data) { + return request({ + url: '/tool/gen', + method: 'put', + data: data + }) +} + +// 导入表 +export function importTable(data) { + return request({ + url: '/tool/gen/importTable', + method: 'post', + params: data + }) +} + +// 预览生成代码 +export function previewTable(tableId) { + return request({ + url: '/tool/gen/preview/' + tableId, + method: 'get' + }) +} + +// 删除表数据 +export function delTable(tableId) { + return request({ + url: '/tool/gen/' + tableId, + method: 'delete' + }) +} + +// 生成代码(自定义路径) +export function genCode(tableName) { + return request({ + url: '/tool/gen/genCode/' + tableName, + method: 'get' + }) +} + +// 同步数据库 +export function synchDb(tableName) { + return request({ + url: '/tool/gen/synchDb/' + tableName, + method: 'get' + }) +} diff --git a/ruoyi-ui/src/assets/401_images/401.gif b/ruoyi-ui/src/assets/401_images/401.gif new file mode 100644 index 0000000..cd6e0d9 Binary files /dev/null and b/ruoyi-ui/src/assets/401_images/401.gif differ diff --git a/ruoyi-ui/src/assets/404_images/404.png b/ruoyi-ui/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/ruoyi-ui/src/assets/404_images/404.png differ diff --git a/ruoyi-ui/src/assets/404_images/404_cloud.png b/ruoyi-ui/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/ruoyi-ui/src/assets/404_images/404_cloud.png differ diff --git a/ruoyi-ui/src/assets/icons/index.js b/ruoyi-ui/src/assets/icons/index.js new file mode 100644 index 0000000..2c6b309 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/ruoyi-ui/src/assets/icons/svg/404.svg b/ruoyi-ui/src/assets/icons/svg/404.svg new file mode 100644 index 0000000..6df5019 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/bug.svg b/ruoyi-ui/src/assets/icons/svg/bug.svg new file mode 100644 index 0000000..05a150d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/build.svg b/ruoyi-ui/src/assets/icons/svg/build.svg new file mode 100644 index 0000000..97c4688 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/build.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/button.svg b/ruoyi-ui/src/assets/icons/svg/button.svg new file mode 100644 index 0000000..904fddc --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/cascader.svg b/ruoyi-ui/src/assets/icons/svg/cascader.svg new file mode 100644 index 0000000..e256024 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/cascader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/chart.svg b/ruoyi-ui/src/assets/icons/svg/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/checkbox.svg b/ruoyi-ui/src/assets/icons/svg/checkbox.svg new file mode 100644 index 0000000..013fd3a --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/checkbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/clipboard.svg b/ruoyi-ui/src/assets/icons/svg/clipboard.svg new file mode 100644 index 0000000..90923ff --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/code.svg b/ruoyi-ui/src/assets/icons/svg/code.svg new file mode 100644 index 0000000..5f9c5ab --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/color.svg b/ruoyi-ui/src/assets/icons/svg/color.svg new file mode 100644 index 0000000..44a81aa --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/component.svg b/ruoyi-ui/src/assets/icons/svg/component.svg new file mode 100644 index 0000000..29c3458 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/component.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/dashboard.svg b/ruoyi-ui/src/assets/icons/svg/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/date-range.svg b/ruoyi-ui/src/assets/icons/svg/date-range.svg new file mode 100644 index 0000000..fda571e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/date-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/date.svg b/ruoyi-ui/src/assets/icons/svg/date.svg new file mode 100644 index 0000000..52dc73e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/date.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/dict.svg b/ruoyi-ui/src/assets/icons/svg/dict.svg new file mode 100644 index 0000000..4849377 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/dict.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/documentation.svg b/ruoyi-ui/src/assets/icons/svg/documentation.svg new file mode 100644 index 0000000..7043122 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/documentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/download.svg b/ruoyi-ui/src/assets/icons/svg/download.svg new file mode 100644 index 0000000..c896951 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/drag.svg b/ruoyi-ui/src/assets/icons/svg/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/druid.svg b/ruoyi-ui/src/assets/icons/svg/druid.svg new file mode 100644 index 0000000..a2b4b4e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/druid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/edit.svg b/ruoyi-ui/src/assets/icons/svg/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/education.svg b/ruoyi-ui/src/assets/icons/svg/education.svg new file mode 100644 index 0000000..7bfb01d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/education.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/email.svg b/ruoyi-ui/src/assets/icons/svg/email.svg new file mode 100644 index 0000000..74d25e2 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/example.svg b/ruoyi-ui/src/assets/icons/svg/example.svg new file mode 100644 index 0000000..46f42b5 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/excel.svg b/ruoyi-ui/src/assets/icons/svg/excel.svg new file mode 100644 index 0000000..74d97b8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/excel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/exit-fullscreen.svg b/ruoyi-ui/src/assets/icons/svg/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/eye-open.svg b/ruoyi-ui/src/assets/icons/svg/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/eye.svg b/ruoyi-ui/src/assets/icons/svg/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/form.svg b/ruoyi-ui/src/assets/icons/svg/form.svg new file mode 100644 index 0000000..dcbaa18 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/fullscreen.svg b/ruoyi-ui/src/assets/icons/svg/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/github.svg b/ruoyi-ui/src/assets/icons/svg/github.svg new file mode 100644 index 0000000..db0a0d4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/guide.svg b/ruoyi-ui/src/assets/icons/svg/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/icon.svg b/ruoyi-ui/src/assets/icons/svg/icon.svg new file mode 100644 index 0000000..82be8ee --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/input.svg b/ruoyi-ui/src/assets/icons/svg/input.svg new file mode 100644 index 0000000..ab91381 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/input.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/international.svg b/ruoyi-ui/src/assets/icons/svg/international.svg new file mode 100644 index 0000000..e9b56ee --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/job.svg b/ruoyi-ui/src/assets/icons/svg/job.svg new file mode 100644 index 0000000..2a93a25 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/job.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/language.svg b/ruoyi-ui/src/assets/icons/svg/language.svg new file mode 100644 index 0000000..0082b57 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/link.svg b/ruoyi-ui/src/assets/icons/svg/link.svg new file mode 100644 index 0000000..48197ba --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/list.svg b/ruoyi-ui/src/assets/icons/svg/list.svg new file mode 100644 index 0000000..20259ed --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/lock.svg b/ruoyi-ui/src/assets/icons/svg/lock.svg new file mode 100644 index 0000000..74fee54 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/log.svg b/ruoyi-ui/src/assets/icons/svg/log.svg new file mode 100644 index 0000000..d879d33 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/log.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/logininfor.svg b/ruoyi-ui/src/assets/icons/svg/logininfor.svg new file mode 100644 index 0000000..267f844 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/logininfor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/message.svg b/ruoyi-ui/src/assets/icons/svg/message.svg new file mode 100644 index 0000000..14ca817 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/money.svg b/ruoyi-ui/src/assets/icons/svg/money.svg new file mode 100644 index 0000000..c1580de --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/monitor.svg b/ruoyi-ui/src/assets/icons/svg/monitor.svg new file mode 100644 index 0000000..bc308cb --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/monitor.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/nested.svg b/ruoyi-ui/src/assets/icons/svg/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/number.svg b/ruoyi-ui/src/assets/icons/svg/number.svg new file mode 100644 index 0000000..ad5ce9a --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/number.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/online.svg b/ruoyi-ui/src/assets/icons/svg/online.svg new file mode 100644 index 0000000..330a202 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/online.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/password.svg b/ruoyi-ui/src/assets/icons/svg/password.svg new file mode 100644 index 0000000..6c64def --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/pdf.svg b/ruoyi-ui/src/assets/icons/svg/pdf.svg new file mode 100644 index 0000000..957aa0c --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/people.svg b/ruoyi-ui/src/assets/icons/svg/people.svg new file mode 100644 index 0000000..2bd54ae --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/peoples.svg b/ruoyi-ui/src/assets/icons/svg/peoples.svg new file mode 100644 index 0000000..aab852e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/phone.svg b/ruoyi-ui/src/assets/icons/svg/phone.svg new file mode 100644 index 0000000..ab8e8c4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/post.svg b/ruoyi-ui/src/assets/icons/svg/post.svg new file mode 100644 index 0000000..2922c61 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/post.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/qq.svg b/ruoyi-ui/src/assets/icons/svg/qq.svg new file mode 100644 index 0000000..ee13d4e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/qq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/question.svg b/ruoyi-ui/src/assets/icons/svg/question.svg new file mode 100644 index 0000000..cf75bd4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/radio.svg b/ruoyi-ui/src/assets/icons/svg/radio.svg new file mode 100644 index 0000000..0cde345 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/radio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/rate.svg b/ruoyi-ui/src/assets/icons/svg/rate.svg new file mode 100644 index 0000000..aa3b14d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/rate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/redis-list.svg b/ruoyi-ui/src/assets/icons/svg/redis-list.svg new file mode 100644 index 0000000..98a15b2 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/redis-list.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/redis.svg b/ruoyi-ui/src/assets/icons/svg/redis.svg new file mode 100644 index 0000000..2f1d62d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/redis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/row.svg b/ruoyi-ui/src/assets/icons/svg/row.svg new file mode 100644 index 0000000..0780992 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/row.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/search.svg b/ruoyi-ui/src/assets/icons/svg/search.svg new file mode 100644 index 0000000..84233dd --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/select.svg b/ruoyi-ui/src/assets/icons/svg/select.svg new file mode 100644 index 0000000..d628382 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/server.svg b/ruoyi-ui/src/assets/icons/svg/server.svg new file mode 100644 index 0000000..eb287e3 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/shopping.svg b/ruoyi-ui/src/assets/icons/svg/shopping.svg new file mode 100644 index 0000000..87513e7 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/size.svg b/ruoyi-ui/src/assets/icons/svg/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/skill.svg b/ruoyi-ui/src/assets/icons/svg/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/skill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/slider.svg b/ruoyi-ui/src/assets/icons/svg/slider.svg new file mode 100644 index 0000000..fbe4f39 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/slider.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/star.svg b/ruoyi-ui/src/assets/icons/svg/star.svg new file mode 100644 index 0000000..6cf86e6 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/swagger.svg b/ruoyi-ui/src/assets/icons/svg/swagger.svg new file mode 100644 index 0000000..05d4e7b --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/swagger.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/switch.svg b/ruoyi-ui/src/assets/icons/svg/switch.svg new file mode 100644 index 0000000..0ba61e3 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/switch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/system.svg b/ruoyi-ui/src/assets/icons/svg/system.svg new file mode 100644 index 0000000..5992593 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/system.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/tab.svg b/ruoyi-ui/src/assets/icons/svg/tab.svg new file mode 100644 index 0000000..b4b48e4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/table.svg b/ruoyi-ui/src/assets/icons/svg/table.svg new file mode 100644 index 0000000..0e3dc9d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/textarea.svg b/ruoyi-ui/src/assets/icons/svg/textarea.svg new file mode 100644 index 0000000..2709f29 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/textarea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/theme.svg b/ruoyi-ui/src/assets/icons/svg/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/time-range.svg b/ruoyi-ui/src/assets/icons/svg/time-range.svg new file mode 100644 index 0000000..13c1202 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/time-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/time.svg b/ruoyi-ui/src/assets/icons/svg/time.svg new file mode 100644 index 0000000..b376e32 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/tool.svg b/ruoyi-ui/src/assets/icons/svg/tool.svg new file mode 100644 index 0000000..48e0e35 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/tool.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/tree-table.svg b/ruoyi-ui/src/assets/icons/svg/tree-table.svg new file mode 100644 index 0000000..8aafdb8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/tree-table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/tree.svg b/ruoyi-ui/src/assets/icons/svg/tree.svg new file mode 100644 index 0000000..dd4b7dd --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/upload.svg b/ruoyi-ui/src/assets/icons/svg/upload.svg new file mode 100644 index 0000000..bae49c0 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/upload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/user.svg b/ruoyi-ui/src/assets/icons/svg/user.svg new file mode 100644 index 0000000..0ba0716 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/validCode.svg b/ruoyi-ui/src/assets/icons/svg/validCode.svg new file mode 100644 index 0000000..cfb1021 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/validCode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/wechat.svg b/ruoyi-ui/src/assets/icons/svg/wechat.svg new file mode 100644 index 0000000..c586e55 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/wechat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/zip.svg b/ruoyi-ui/src/assets/icons/svg/zip.svg new file mode 100644 index 0000000..f806fc4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/zip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svgo.yml b/ruoyi-ui/src/assets/icons/svgo.yml new file mode 100644 index 0000000..d11906a --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/ruoyi-ui/src/assets/images/dark.svg b/ruoyi-ui/src/assets/images/dark.svg new file mode 100644 index 0000000..f646bd7 --- /dev/null +++ b/ruoyi-ui/src/assets/images/dark.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/images/light.svg b/ruoyi-ui/src/assets/images/light.svg new file mode 100644 index 0000000..ab7cc08 --- /dev/null +++ b/ruoyi-ui/src/assets/images/light.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/images/login-background.jpg b/ruoyi-ui/src/assets/images/login-background.jpg new file mode 100644 index 0000000..d153531 Binary files /dev/null and b/ruoyi-ui/src/assets/images/login-background.jpg differ diff --git a/ruoyi-ui/src/assets/images/pay.png b/ruoyi-ui/src/assets/images/pay.png new file mode 100644 index 0000000..bb8b967 Binary files /dev/null and b/ruoyi-ui/src/assets/images/pay.png differ diff --git a/ruoyi-ui/src/assets/images/profile.jpg b/ruoyi-ui/src/assets/images/profile.jpg new file mode 100644 index 0000000..b3a940b Binary files /dev/null and b/ruoyi-ui/src/assets/images/profile.jpg differ diff --git a/ruoyi-ui/src/assets/logo/logo.png b/ruoyi-ui/src/assets/logo/logo.png new file mode 100644 index 0000000..3a40ab1 Binary files /dev/null and b/ruoyi-ui/src/assets/logo/logo.png differ diff --git a/ruoyi-ui/src/assets/styles/btn.scss b/ruoyi-ui/src/assets/styles/btn.scss new file mode 100644 index 0000000..e6ba1a8 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/btn.scss @@ -0,0 +1,99 @@ +@import './variables.scss'; + +@mixin colorBtn($color) { + background: $color; + + &:hover { + color: $color; + + &:before, + &:after { + background: $color; + } + } +} + +.blue-btn { + @include colorBtn($blue) +} + +.light-blue-btn { + @include colorBtn($light-blue) +} + +.red-btn { + @include colorBtn($red) +} + +.pink-btn { + @include colorBtn($pink) +} + +.green-btn { + @include colorBtn($green) +} + +.tiffany-btn { + @include colorBtn($tiffany) +} + +.yellow-btn { + @include colorBtn($yellow) +} + +.pan-btn { + font-size: 14px; + color: #fff; + padding: 14px 36px; + border-radius: 8px; + border: none; + outline: none; + transition: 600ms ease all; + position: relative; + display: inline-block; + + &:hover { + background: #fff; + + &:before, + &:after { + width: 100%; + transition: 600ms ease all; + } + } + + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 2px; + width: 0; + transition: 400ms ease all; + } + + &::after { + right: inherit; + top: inherit; + left: 0; + bottom: 0; + } +} + +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; +} diff --git a/ruoyi-ui/src/assets/styles/element-ui.scss b/ruoyi-ui/src/assets/styles/element-ui.scss new file mode 100644 index 0000000..363092a --- /dev/null +++ b/ruoyi-ui/src/assets/styles/element-ui.scss @@ -0,0 +1,92 @@ +// cover some element-ui styles + +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +.cell { + .el-tag { + margin-right: 0px; + } +} + +.small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } +} + +.fixed-width { + .el-button--mini { + padding: 7px 10px; + width: 60px; + } +} + +.status-col { + .cell { + padding: 0 10px; + text-align: center; + + .el-tag { + margin-right: 0px; + } + } +} + +// to fixed https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +// refine element ui upload +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} + +// dropdown +.el-dropdown-menu { + a { + display: block + } +} + +// fix date-picker ui bug in filter-item +.el-range-editor.el-input__inner { + display: inline-flex !important; +} + +// to fix el-date-picker css style +.el-range-separator { + box-sizing: content-box; +} + +.el-menu--collapse + > div + > .el-submenu + > .el-submenu__title + .el-submenu__icon-arrow { + display: none; +} \ No newline at end of file diff --git a/ruoyi-ui/src/assets/styles/element-variables.scss b/ruoyi-ui/src/assets/styles/element-variables.scss new file mode 100644 index 0000000..1615ff2 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/element-variables.scss @@ -0,0 +1,31 @@ +/** +* I think element-ui's default theme color is too light for long-term use. +* So I modified the default color and you can modify it to your liking. +**/ + +/* theme color */ +$--color-primary: #1890ff; +$--color-success: #13ce66; +$--color-warning: #ffba00; +$--color-danger: #ff4949; +// $--color-info: #1E1E1E; + +$--button-font-weight: 400; + +// $--color-text-regular: #1f2d3d; + +$--border-color-light: #dfe4ed; +$--border-color-lighter: #e6ebf5; + +$--table-border: 1px solid #dfe6ec; + +/* icon font path, required */ +$--font-path: '~element-ui/lib/theme-chalk/fonts'; + +@import "~element-ui/packages/theme-chalk/src/index"; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + theme: $--color-primary; +} diff --git a/ruoyi-ui/src/assets/styles/index.scss b/ruoyi-ui/src/assets/styles/index.scss new file mode 100644 index 0000000..2f3b9ef --- /dev/null +++ b/ruoyi-ui/src/assets/styles/index.scss @@ -0,0 +1,182 @@ +@import './variables.scss'; +@import './mixin.scss'; +@import './transition.scss'; +@import './element-ui.scss'; +@import './sidebar.scss'; +@import './btn.scss'; + +body { + height: 100%; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +label { + font-weight: 700; +} + +html { + height: 100%; + box-sizing: border-box; +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +.no-padding { + padding: 0px !important; +} + +.padding-content { + padding: 4px 0; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.fr { + float: right; +} + +.fl { + float: left; +} + +.pr-5 { + padding-right: 5px; +} + +.pl-5 { + padding-left: 5px; +} + +.block { + display: block; +} + +.pointer { + cursor: pointer; +} + +.inlineBlock { + display: block; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +aside { + background: #eef1f6; + padding: 8px 24px; + margin-bottom: 20px; + border-radius: 2px; + display: block; + line-height: 32px; + font-size: 16px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + color: #2c3e50; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + a { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } + } +} + +//main-container全局样式 +.app-container { + padding: 20px; +} + +.components-container { + margin: 30px 50px; + position: relative; +} + +.pagination-container { + margin-top: 30px; +} + +.text-center { + text-align: center +} + +.sub-navbar { + height: 50px; + line-height: 50px; + position: relative; + width: 100%; + text-align: right; + padding-right: 20px; + transition: 600ms ease position; + background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); + + .subtitle { + font-size: 20px; + color: #fff; + } + + &.draft { + background: #d0d0d0; + } + + &.deleted { + background: #d0d0d0; + } +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } +} + +.filter-container { + padding-bottom: 10px; + + .filter-item { + display: inline-block; + vertical-align: middle; + margin-bottom: 10px; + } +} diff --git a/ruoyi-ui/src/assets/styles/mixin.scss b/ruoyi-ui/src/assets/styles/mixin.scss new file mode 100644 index 0000000..06fa061 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/mixin.scss @@ -0,0 +1,66 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } + + @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/ruoyi-ui/src/assets/styles/ruoyi.scss b/ruoyi-ui/src/assets/styles/ruoyi.scss new file mode 100644 index 0000000..db8c29b --- /dev/null +++ b/ruoyi-ui/src/assets/styles/ruoyi.scss @@ -0,0 +1,277 @@ + /** + * 通用css样式布局处理 + * Copyright (c) 2019 ruoyi + */ + + /** 基础通用 **/ +.pt5 { + padding-top: 5px; +} +.pr5 { + padding-right: 5px; +} +.pb5 { + padding-bottom: 5px; +} +.mt5 { + margin-top: 5px; +} +.mr5 { + margin-right: 5px; +} +.mb5 { + margin-bottom: 5px; +} +.mb8 { + margin-bottom: 8px; +} +.ml5 { + margin-left: 5px; +} +.mt10 { + margin-top: 10px; +} +.mr10 { + margin-right: 10px; +} +.mb10 { + margin-bottom: 10px; +} +.ml10 { + margin-left: 10px; +} +.mt20 { + margin-top: 20px; +} +.mr20 { + margin-right: 20px; +} +.mb20 { + margin-bottom: 20px; +} +.ml20 { + margin-left: 20px; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.el-message-box__status + .el-message-box__message{ + word-break: break-word; +} + +.el-dialog:not(.is-fullscreen) { + margin-top: 6vh !important; +} + +.el-dialog__wrapper.scrollbar .el-dialog .el-dialog__body { + overflow: auto; + overflow-x: hidden; + max-height: 70vh; + padding: 10px 20px 0; +} + +.el-table { + .el-table__header-wrapper, .el-table__fixed-header-wrapper { + th { + word-break: break-word; + background-color: #f8f8f9; + color: #515a6e; + height: 40px; + font-size: 13px; + } + } + .el-table__body-wrapper { + .el-button [class*="el-icon-"] + span { + margin-left: 1px; + } + } +} + +/** 表单布局 **/ +.form-header { + font-size:15px; + color:#6379bb; + border-bottom:1px solid #ddd; + margin:8px 10px 25px 10px; + padding-bottom:5px +} + +/** 表格布局 **/ +.pagination-container { + position: relative; + height: 25px; + margin-bottom: 10px; + margin-top: 15px; + padding: 10px 20px !important; +} + +/* tree border */ +.tree-border { + margin-top: 5px; + border: 1px solid #e5e6e7; + background: #FFFFFF none; + border-radius:4px; +} + +.pagination-container .el-pagination { + right: 0; + position: absolute; +} + +@media ( max-width : 768px) { + .pagination-container .el-pagination > .el-pagination__jump { + display: none !important; + } + .pagination-container .el-pagination > .el-pagination__sizes { + display: none !important; + } +} + +.el-table .fixed-width .el-button--mini { + padding-left: 0; + padding-right: 0; + width: inherit; +} + +/** 表格更多操作下拉样式 */ +.el-table .el-dropdown-link,.el-table .el-dropdown-selfdefine { + cursor: pointer; + margin-left: 5px; +} + +.el-table .el-dropdown, .el-icon-arrow-down { + font-size: 12px; +} + +.el-tree-node__content > .el-checkbox { + margin-right: 8px; +} + +.list-group-striped > .list-group-item { + border-left: 0; + border-right: 0; + border-radius: 0; + padding-left: 0; + padding-right: 0; +} + +.list-group { + padding-left: 0px; + list-style: none; +} + +.list-group-item { + border-bottom: 1px solid #e7eaec; + border-top: 1px solid #e7eaec; + margin-bottom: -1px; + padding: 11px 0px; + font-size: 13px; +} + +.pull-right { + float: right !important; +} + +.el-card__header { + padding: 14px 15px 7px; + min-height: 40px; +} + +.el-card__body { + padding: 15px 20px 20px 20px; +} + +.card-box { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 10px; +} + +/* button color */ +.el-button--cyan.is-active, +.el-button--cyan:active { + background: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +.el-button--cyan:focus, +.el-button--cyan:hover { + background: #48D1CC; + border-color: #48D1CC; + color: #FFFFFF; +} + +.el-button--cyan { + background-color: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +/* text color */ +.text-navy { + color: #1ab394; +} + +.text-primary { + color: inherit; +} + +.text-success { + color: #1c84c6; +} + +.text-info { + color: #23c6c8; +} + +.text-warning { + color: #f8ac59; +} + +.text-danger { + color: #ed5565; +} + +.text-muted { + color: #888888; +} + +/* image */ +.img-circle { + border-radius: 50%; +} + +.img-lg { + width: 120px; + height: 120px; +} + +.avatar-upload-preview { + position: relative; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 200px; + height: 200px; + border-radius: 50%; + box-shadow: 0 0 4px #ccc; + overflow: hidden; +} + +/* 拖拽列样式 */ +.sortable-ghost{ + opacity: .8; + color: #fff!important; + background: #42b983!important; +} + +.top-right-btn { + position: relative; + float: right; +} diff --git a/ruoyi-ui/src/assets/styles/sidebar.scss b/ruoyi-ui/src/assets/styles/sidebar.scss new file mode 100644 index 0000000..abe5b63 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/sidebar.scss @@ -0,0 +1,227 @@ +#app { + + .main-container { + height: 100%; + transition: margin-left .28s; + margin-left: $base-sidebar-width; + position: relative; + } + + .sidebarHide { + margin-left: 0!important; + } + + .sidebar-container { + -webkit-transition: width .28s; + transition: width 0.28s; + width: $base-sidebar-width !important; + background-color: $base-menu-background; + height: 100%; + position: fixed; + font-size: 0px; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + -webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35); + box-shadow: 2px 0 6px rgba(0,21,41,.35); + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0px; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 16px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + .el-menu-item, .el-submenu__title { + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + } + + // menu hover + .submenu-title-noDropdown, + .el-submenu__title { + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .is-active > .el-submenu__title { + color: $base-menu-color-active !important; + } + + & .nest-menu .el-submenu>.el-submenu__title, + & .el-submenu .el-menu-item { + min-width: $base-sidebar-width !important; + + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .nest-menu .el-submenu>.el-submenu__title, + & .theme-dark .el-submenu .el-menu-item { + background-color: $base-sub-menu-background !important; + + &:hover { + background-color: $base-sub-menu-hover !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .submenu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-submenu { + overflow: hidden; + + &>.el-submenu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + } + } + + .el-menu--collapse { + .el-submenu { + &>.el-submenu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-submenu { + min-width: $base-sidebar-width !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + transition: transform .28s; + width: $base-sidebar-width !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$base-sidebar-width, 0, 0); + } + } + } + + .withoutAnimation { + + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + } + + .nest-menu .el-submenu>.el-submenu__title, + .el-menu-item { + &:hover { + // you can use $subMenuHover + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + // the scroll bar appears when the subMenu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} diff --git a/ruoyi-ui/src/assets/styles/transition.scss b/ruoyi-ui/src/assets/styles/transition.scss new file mode 100644 index 0000000..073f8c6 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/transition.scss @@ -0,0 +1,49 @@ +// global transition css + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform--move, +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/ruoyi-ui/src/assets/styles/variables.scss b/ruoyi-ui/src/assets/styles/variables.scss new file mode 100644 index 0000000..34484d4 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/variables.scss @@ -0,0 +1,54 @@ +// base color +$blue:#324157; +$light-blue:#3A71A8; +$red:#C03639; +$pink: #E65D6E; +$green: #30B08F; +$tiffany: #4AB7BD; +$yellow:#FEC171; +$panGreen: #30B08F; + +// 默认菜单主题风格 +$base-menu-color:#bfcbd9; +$base-menu-color-active:#f4f4f5; +$base-menu-background:#304156; +$base-logo-title-color: #ffffff; + +$base-menu-light-color:rgba(0,0,0,.70); +$base-menu-light-background:#ffffff; +$base-logo-light-title-color: #001529; + +$base-sub-menu-background:#1f2d3d; +$base-sub-menu-hover:#001528; + +// 自定义暗色菜单风格 +/** +$base-menu-color:hsla(0,0%,100%,.65); +$base-menu-color-active:#fff; +$base-menu-background:#001529; +$base-logo-title-color: #ffffff; + +$base-menu-light-color:rgba(0,0,0,.70); +$base-menu-light-background:#ffffff; +$base-logo-light-title-color: #001529; + +$base-sub-menu-background:#000c17; +$base-sub-menu-hover:#001528; +*/ + +$base-sidebar-width: 200px; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + menuColor: $base-menu-color; + menuLightColor: $base-menu-light-color; + menuColorActive: $base-menu-color-active; + menuBackground: $base-menu-background; + menuLightBackground: $base-menu-light-background; + subMenuBackground: $base-sub-menu-background; + subMenuHover: $base-sub-menu-hover; + sideBarWidth: $base-sidebar-width; + logoTitleColor: $base-logo-title-color; + logoLightTitleColor: $base-logo-light-title-color +} diff --git a/ruoyi-ui/src/components/Breadcrumb/index.vue b/ruoyi-ui/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..1696f54 --- /dev/null +++ b/ruoyi-ui/src/components/Breadcrumb/index.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/ruoyi-ui/src/components/Crontab/day.vue b/ruoyi-ui/src/components/Crontab/day.vue new file mode 100644 index 0000000..fe3eaf0 --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/day.vue @@ -0,0 +1,161 @@ + + + diff --git a/ruoyi-ui/src/components/Crontab/hour.vue b/ruoyi-ui/src/components/Crontab/hour.vue new file mode 100644 index 0000000..4b1f1fc --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/hour.vue @@ -0,0 +1,114 @@ + + + diff --git a/ruoyi-ui/src/components/Crontab/index.vue b/ruoyi-ui/src/components/Crontab/index.vue new file mode 100644 index 0000000..3963df2 --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/index.vue @@ -0,0 +1,430 @@ + + + + diff --git a/ruoyi-ui/src/components/Crontab/min.vue b/ruoyi-ui/src/components/Crontab/min.vue new file mode 100644 index 0000000..43cab90 --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/min.vue @@ -0,0 +1,116 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/components/Crontab/month.vue b/ruoyi-ui/src/components/Crontab/month.vue new file mode 100644 index 0000000..fd0ac38 --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/month.vue @@ -0,0 +1,114 @@ + + + diff --git a/ruoyi-ui/src/components/Crontab/result.vue b/ruoyi-ui/src/components/Crontab/result.vue new file mode 100644 index 0000000..aea6e0e --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/result.vue @@ -0,0 +1,559 @@ + + + diff --git a/ruoyi-ui/src/components/Crontab/second.vue b/ruoyi-ui/src/components/Crontab/second.vue new file mode 100644 index 0000000..e7b7761 --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/second.vue @@ -0,0 +1,117 @@ + + + diff --git a/ruoyi-ui/src/components/Crontab/week.vue b/ruoyi-ui/src/components/Crontab/week.vue new file mode 100644 index 0000000..1cec700 --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/week.vue @@ -0,0 +1,202 @@ + + + diff --git a/ruoyi-ui/src/components/Crontab/year.vue b/ruoyi-ui/src/components/Crontab/year.vue new file mode 100644 index 0000000..5487a6c --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/year.vue @@ -0,0 +1,131 @@ + + + diff --git a/ruoyi-ui/src/components/DictData/index.js b/ruoyi-ui/src/components/DictData/index.js new file mode 100644 index 0000000..7b85d4a --- /dev/null +++ b/ruoyi-ui/src/components/DictData/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import store from '@/store' +import DataDict from '@/utils/dict' +import { getDicts as getDicts } from '@/api/system/dict/data' + +function searchDictByKey(dict, key) { + if (key == null && key == "") { + return null + } + try { + for (let i = 0; i < dict.length; i++) { + if (dict[i].key == key) { + return dict[i].value + } + } + } catch (e) { + return null + } +} + +function install() { + Vue.use(DataDict, { + metas: { + '*': { + labelField: 'dictLabel', + valueField: 'dictValue', + request(dictMeta) { + const storeDict = searchDictByKey(store.getters.dict, dictMeta.type) + if (storeDict) { + return new Promise(resolve => { resolve(storeDict) }) + } else { + return new Promise((resolve, reject) => { + getDicts(dictMeta.type).then(res => { + store.dispatch('dict/setDict', { key: dictMeta.type, value: res.data }) + resolve(res.data) + }).catch(error => { + reject(error) + }) + }) + } + }, + }, + }, + }) +} + +export default { + install, +} \ No newline at end of file diff --git a/ruoyi-ui/src/components/DictTag/index.vue b/ruoyi-ui/src/components/DictTag/index.vue new file mode 100644 index 0000000..1ef13b9 --- /dev/null +++ b/ruoyi-ui/src/components/DictTag/index.vue @@ -0,0 +1,92 @@ + + + + diff --git a/ruoyi-ui/src/components/Editor/index.vue b/ruoyi-ui/src/components/Editor/index.vue new file mode 100644 index 0000000..8981d76 --- /dev/null +++ b/ruoyi-ui/src/components/Editor/index.vue @@ -0,0 +1,274 @@ + + + + + diff --git a/ruoyi-ui/src/components/FileUpload/index.vue b/ruoyi-ui/src/components/FileUpload/index.vue new file mode 100644 index 0000000..6c583cf --- /dev/null +++ b/ruoyi-ui/src/components/FileUpload/index.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/ruoyi-ui/src/components/Hamburger/index.vue b/ruoyi-ui/src/components/Hamburger/index.vue new file mode 100644 index 0000000..368b002 --- /dev/null +++ b/ruoyi-ui/src/components/Hamburger/index.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/ruoyi-ui/src/components/HeaderSearch/index.vue b/ruoyi-ui/src/components/HeaderSearch/index.vue new file mode 100644 index 0000000..7d6780b --- /dev/null +++ b/ruoyi-ui/src/components/HeaderSearch/index.vue @@ -0,0 +1,198 @@ + + + + + diff --git a/ruoyi-ui/src/components/IconSelect/index.vue b/ruoyi-ui/src/components/IconSelect/index.vue new file mode 100644 index 0000000..8dadc02 --- /dev/null +++ b/ruoyi-ui/src/components/IconSelect/index.vue @@ -0,0 +1,104 @@ + + + + + + diff --git a/ruoyi-ui/src/components/IconSelect/requireIcons.js b/ruoyi-ui/src/components/IconSelect/requireIcons.js new file mode 100644 index 0000000..99e5c54 --- /dev/null +++ b/ruoyi-ui/src/components/IconSelect/requireIcons.js @@ -0,0 +1,11 @@ + +const req = require.context('../../assets/icons/svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys() + +const re = /\.\/(.*)\.svg/ + +const icons = requireAll(req).map(i => { + return i.match(re)[1] +}) + +export default icons diff --git a/ruoyi-ui/src/components/ImagePreview/index.vue b/ruoyi-ui/src/components/ImagePreview/index.vue new file mode 100644 index 0000000..3c770c7 --- /dev/null +++ b/ruoyi-ui/src/components/ImagePreview/index.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/ruoyi-ui/src/components/ImageUpload/index.vue b/ruoyi-ui/src/components/ImageUpload/index.vue new file mode 100644 index 0000000..b57a15e --- /dev/null +++ b/ruoyi-ui/src/components/ImageUpload/index.vue @@ -0,0 +1,226 @@ + + + + + diff --git a/ruoyi-ui/src/components/Pagination/index.vue b/ruoyi-ui/src/components/Pagination/index.vue new file mode 100644 index 0000000..56f5a6b --- /dev/null +++ b/ruoyi-ui/src/components/Pagination/index.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/ruoyi-ui/src/components/PanThumb/index.vue b/ruoyi-ui/src/components/PanThumb/index.vue new file mode 100644 index 0000000..1bcf417 --- /dev/null +++ b/ruoyi-ui/src/components/PanThumb/index.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/ruoyi-ui/src/components/ParentView/index.vue b/ruoyi-ui/src/components/ParentView/index.vue new file mode 100644 index 0000000..7bf6148 --- /dev/null +++ b/ruoyi-ui/src/components/ParentView/index.vue @@ -0,0 +1,3 @@ + diff --git a/ruoyi-ui/src/components/RightPanel/index.vue b/ruoyi-ui/src/components/RightPanel/index.vue new file mode 100644 index 0000000..5abeecb --- /dev/null +++ b/ruoyi-ui/src/components/RightPanel/index.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/ruoyi-ui/src/components/RightToolbar/index.vue b/ruoyi-ui/src/components/RightToolbar/index.vue new file mode 100644 index 0000000..527e07c --- /dev/null +++ b/ruoyi-ui/src/components/RightToolbar/index.vue @@ -0,0 +1,104 @@ + + + diff --git a/ruoyi-ui/src/components/RuoYi/Doc/index.vue b/ruoyi-ui/src/components/RuoYi/Doc/index.vue new file mode 100644 index 0000000..75fa864 --- /dev/null +++ b/ruoyi-ui/src/components/RuoYi/Doc/index.vue @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/components/RuoYi/Git/index.vue b/ruoyi-ui/src/components/RuoYi/Git/index.vue new file mode 100644 index 0000000..bdafbae --- /dev/null +++ b/ruoyi-ui/src/components/RuoYi/Git/index.vue @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/components/Screenfull/index.vue b/ruoyi-ui/src/components/Screenfull/index.vue new file mode 100644 index 0000000..d4e539c --- /dev/null +++ b/ruoyi-ui/src/components/Screenfull/index.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/ruoyi-ui/src/components/SizeSelect/index.vue b/ruoyi-ui/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..069b5de --- /dev/null +++ b/ruoyi-ui/src/components/SizeSelect/index.vue @@ -0,0 +1,56 @@ + + + diff --git a/ruoyi-ui/src/components/SvgIcon/index.vue b/ruoyi-ui/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..e4bf5ad --- /dev/null +++ b/ruoyi-ui/src/components/SvgIcon/index.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/ruoyi-ui/src/components/ThemePicker/index.vue b/ruoyi-ui/src/components/ThemePicker/index.vue new file mode 100644 index 0000000..1714e1f --- /dev/null +++ b/ruoyi-ui/src/components/ThemePicker/index.vue @@ -0,0 +1,173 @@ + + + + + diff --git a/ruoyi-ui/src/components/TopNav/index.vue b/ruoyi-ui/src/components/TopNav/index.vue new file mode 100644 index 0000000..daee1b8 --- /dev/null +++ b/ruoyi-ui/src/components/TopNav/index.vue @@ -0,0 +1,194 @@ + + + + + diff --git a/ruoyi-ui/src/components/iFrame/index.vue b/ruoyi-ui/src/components/iFrame/index.vue new file mode 100644 index 0000000..426857f --- /dev/null +++ b/ruoyi-ui/src/components/iFrame/index.vue @@ -0,0 +1,36 @@ + + + diff --git a/ruoyi-ui/src/layout/components/Navbar.vue b/ruoyi-ui/src/layout/components/Navbar.vue new file mode 100644 index 0000000..0ca33e9 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Navbar.vue @@ -0,0 +1,200 @@ + + + + + diff --git a/ruoyi-ui/src/layout/components/Settings/index.vue b/ruoyi-ui/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..8b49842 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Settings/index.vue @@ -0,0 +1,260 @@ + + + + + diff --git a/ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js b/ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js new file mode 100644 index 0000000..6823726 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js @@ -0,0 +1,25 @@ +export default { + computed: { + device() { + return this.$store.state.app.device + } + }, + mounted() { + // In order to fix the click on menu on the ios device will trigger the mouseleave bug + this.fixBugIniOS() + }, + methods: { + fixBugIniOS() { + const $subMenu = this.$refs.subMenu + if ($subMenu) { + const handleMouseleave = $subMenu.handleMouseleave + $subMenu.handleMouseleave = (e) => { + if (this.device === 'mobile') { + return + } + handleMouseleave(e) + } + } + } + } +} diff --git a/ruoyi-ui/src/layout/components/Sidebar/Item.vue b/ruoyi-ui/src/layout/components/Sidebar/Item.vue new file mode 100644 index 0000000..be3285d --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/Item.vue @@ -0,0 +1,33 @@ + diff --git a/ruoyi-ui/src/layout/components/Sidebar/Link.vue b/ruoyi-ui/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..8b0bc93 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,43 @@ + + + diff --git a/ruoyi-ui/src/layout/components/Sidebar/Logo.vue b/ruoyi-ui/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..2774cc8 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue b/ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..4853fbb --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,100 @@ + + + diff --git a/ruoyi-ui/src/layout/components/Sidebar/index.vue b/ruoyi-ui/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..51d0839 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/index.vue @@ -0,0 +1,57 @@ + + + diff --git a/ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue b/ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..bb753a1 --- /dev/null +++ b/ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/ruoyi-ui/src/layout/components/TagsView/index.vue b/ruoyi-ui/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..96585a5 --- /dev/null +++ b/ruoyi-ui/src/layout/components/TagsView/index.vue @@ -0,0 +1,332 @@ + + + + + + + diff --git a/ruoyi-ui/src/layout/components/index.js b/ruoyi-ui/src/layout/components/index.js new file mode 100644 index 0000000..104bd3a --- /dev/null +++ b/ruoyi-ui/src/layout/components/index.js @@ -0,0 +1,5 @@ +export { default as AppMain } from './AppMain' +export { default as Navbar } from './Navbar' +export { default as Settings } from './Settings' +export { default as Sidebar } from './Sidebar/index.vue' +export { default as TagsView } from './TagsView/index.vue' diff --git a/ruoyi-ui/src/layout/index.vue b/ruoyi-ui/src/layout/index.vue new file mode 100644 index 0000000..dba4393 --- /dev/null +++ b/ruoyi-ui/src/layout/index.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/ruoyi-ui/src/layout/mixin/ResizeHandler.js b/ruoyi-ui/src/layout/mixin/ResizeHandler.js new file mode 100644 index 0000000..e8d0df8 --- /dev/null +++ b/ruoyi-ui/src/layout/mixin/ResizeHandler.js @@ -0,0 +1,45 @@ +import store from '@/store' + +const { body } = document +const WIDTH = 992 // refer to Bootstrap's responsive design + +export default { + watch: { + $route(route) { + if (this.device === 'mobile' && this.sidebar.opened) { + store.dispatch('app/closeSideBar', { withoutAnimation: false }) + } + } + }, + beforeMount() { + window.addEventListener('resize', this.$_resizeHandler) + }, + beforeDestroy() { + window.removeEventListener('resize', this.$_resizeHandler) + }, + mounted() { + const isMobile = this.$_isMobile() + if (isMobile) { + store.dispatch('app/toggleDevice', 'mobile') + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_isMobile() { + const rect = body.getBoundingClientRect() + return rect.width - 1 < WIDTH + }, + $_resizeHandler() { + if (!document.hidden) { + const isMobile = this.$_isMobile() + store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') + + if (isMobile) { + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + } + } + } +} diff --git a/ruoyi-ui/src/main.js b/ruoyi-ui/src/main.js new file mode 100644 index 0000000..0a02f44 --- /dev/null +++ b/ruoyi-ui/src/main.js @@ -0,0 +1,87 @@ +import Vue from 'vue' + +import Cookies from 'js-cookie' + +import Element from 'element-ui' +import './assets/styles/element-variables.scss' + +import '@/assets/styles/index.scss' // global css +import '@/assets/styles/ruoyi.scss' // ruoyi css +import App from './App' +import store from './store' +import router from './router' +import directive from './directive' // directive +import plugins from './plugins' // plugins +import { download } from '@/utils/request' + +import './assets/icons' // icon +import './permission' // permission control +import { getDicts } from "@/api/system/dict/data"; +import { getConfigKey } from "@/api/system/config"; +import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"; +// 分页组件 +import Pagination from "@/components/Pagination"; +// 自定义表格工具组件 +import RightToolbar from "@/components/RightToolbar" +// 富文本组件 +import Editor from "@/components/Editor" +// 文件上传组件 +import FileUpload from "@/components/FileUpload" +// 图片上传组件 +import ImageUpload from "@/components/ImageUpload" +// 图片预览组件 +import ImagePreview from "@/components/ImagePreview" +// 字典标签组件 +import DictTag from '@/components/DictTag' +// 头部标签组件 +import VueMeta from 'vue-meta' +// 字典数据组件 +import DictData from '@/components/DictData' + + +// 全局方法挂载 +Vue.prototype.getDicts = getDicts +Vue.prototype.getConfigKey = getConfigKey +Vue.prototype.parseTime = parseTime +Vue.prototype.resetForm = resetForm +Vue.prototype.addDateRange = addDateRange +Vue.prototype.selectDictLabel = selectDictLabel +Vue.prototype.selectDictLabels = selectDictLabels +Vue.prototype.download = download +Vue.prototype.handleTree = handleTree + +// 全局组件挂载 +Vue.component('DictTag', DictTag) +Vue.component('Pagination', Pagination) +Vue.component('RightToolbar', RightToolbar) +Vue.component('Editor', Editor) +Vue.component('FileUpload', FileUpload) +Vue.component('ImageUpload', ImageUpload) +Vue.component('ImagePreview', ImagePreview) + +Vue.use(directive) +Vue.use(plugins) +Vue.use(VueMeta) +DictData.install() + +/** + * If you don't want to use mock-server + * you want to use MockJs for mock api + * you can execute: mockXHR() + * + * Currently MockJs will be used in the production environment, + * please remove it before going online! ! ! + */ + +Vue.use(Element, { + size: Cookies.get('size') || 'medium' // set element-ui default size +}) + +Vue.config.productionTip = false + +new Vue({ + el: '#app', + router, + store, + render: h => h(App) +}) diff --git a/ruoyi-ui/src/permission.js b/ruoyi-ui/src/permission.js new file mode 100644 index 0000000..e1a14da --- /dev/null +++ b/ruoyi-ui/src/permission.js @@ -0,0 +1,56 @@ +import router from './router' +import store from './store' +import { Message } from 'element-ui' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import { getToken } from '@/utils/auth' +import { isRelogin } from '@/utils/request' + +NProgress.configure({ showSpinner: false }) + +const whiteList = ['/login', '/register'] + +router.beforeEach((to, from, next) => { + NProgress.start() + if (getToken()) { + to.meta.title && store.dispatch('settings/setTitle', to.meta.title) + /* has token*/ + if (to.path === '/login') { + next({ path: '/' }) + NProgress.done() + } else { + if (store.getters.roles.length === 0) { + isRelogin.show = true + // 判断当前用户是否已拉取完user_info信息 + store.dispatch('GetInfo').then(() => { + isRelogin.show = false + store.dispatch('GenerateRoutes').then(accessRoutes => { + // 根据roles权限生成可访问的路由表 + router.addRoutes(accessRoutes) // 动态添加可访问路由表 + next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 + }) + }).catch(err => { + store.dispatch('LogOut').then(() => { + Message.error(err) + next({ path: '/' }) + }) + }) + } else { + next() + } + } + } else { + // 没有token + if (whiteList.indexOf(to.path) !== -1) { + // 在免登录白名单,直接进入 + next() + } else { + next(`/login?redirect=${encodeURIComponent(to.fullPath)}`) // 否则全部重定向到登录页 + NProgress.done() + } + } +}) + +router.afterEach(() => { + NProgress.done() +}) diff --git a/ruoyi-ui/src/plugins/auth.js b/ruoyi-ui/src/plugins/auth.js new file mode 100644 index 0000000..6c6bc24 --- /dev/null +++ b/ruoyi-ui/src/plugins/auth.js @@ -0,0 +1,60 @@ +import store from '@/store' + +function authPermission(permission) { + const all_permission = "*:*:*"; + const permissions = store.getters && store.getters.permissions + if (permission && permission.length > 0) { + return permissions.some(v => { + return all_permission === v || v === permission + }) + } else { + return false + } +} + +function authRole(role) { + const super_admin = "admin"; + const roles = store.getters && store.getters.roles + if (role && role.length > 0) { + return roles.some(v => { + return super_admin === v || v === role + }) + } else { + return false + } +} + +export default { + // 验证用户是否具备某权限 + hasPermi(permission) { + return authPermission(permission); + }, + // 验证用户是否含有指定权限,只需包含其中一个 + hasPermiOr(permissions) { + return permissions.some(item => { + return authPermission(item) + }) + }, + // 验证用户是否含有指定权限,必须全部拥有 + hasPermiAnd(permissions) { + return permissions.every(item => { + return authPermission(item) + }) + }, + // 验证用户是否具备某角色 + hasRole(role) { + return authRole(role); + }, + // 验证用户是否含有指定角色,只需包含其中一个 + hasRoleOr(roles) { + return roles.some(item => { + return authRole(item) + }) + }, + // 验证用户是否含有指定角色,必须全部拥有 + hasRoleAnd(roles) { + return roles.every(item => { + return authRole(item) + }) + } +} diff --git a/ruoyi-ui/src/plugins/cache.js b/ruoyi-ui/src/plugins/cache.js new file mode 100644 index 0000000..6b5c00b --- /dev/null +++ b/ruoyi-ui/src/plugins/cache.js @@ -0,0 +1,77 @@ +const sessionCache = { + set (key, value) { + if (!sessionStorage) { + return + } + if (key != null && value != null) { + sessionStorage.setItem(key, value) + } + }, + get (key) { + if (!sessionStorage) { + return null + } + if (key == null) { + return null + } + return sessionStorage.getItem(key) + }, + setJSON (key, jsonValue) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)) + } + }, + getJSON (key) { + const value = this.get(key) + if (value != null) { + return JSON.parse(value) + } + }, + remove (key) { + sessionStorage.removeItem(key); + } +} +const localCache = { + set (key, value) { + if (!localStorage) { + return + } + if (key != null && value != null) { + localStorage.setItem(key, value) + } + }, + get (key) { + if (!localStorage) { + return null + } + if (key == null) { + return null + } + return localStorage.getItem(key) + }, + setJSON (key, jsonValue) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)) + } + }, + getJSON (key) { + const value = this.get(key) + if (value != null) { + return JSON.parse(value) + } + }, + remove (key) { + localStorage.removeItem(key); + } +} + +export default { + /** + * 会话级缓存 + */ + session: sessionCache, + /** + * 本地缓存 + */ + local: localCache +} diff --git a/ruoyi-ui/src/plugins/download.js b/ruoyi-ui/src/plugins/download.js new file mode 100644 index 0000000..ffb8c14 --- /dev/null +++ b/ruoyi-ui/src/plugins/download.js @@ -0,0 +1,72 @@ +import axios from 'axios' +import { Message } from 'element-ui' +import { saveAs } from 'file-saver' +import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' +import { blobValidate } from "@/utils/ruoyi"; + +const baseURL = process.env.VUE_APP_BASE_API + +export default { + name(name, isDelete = true) { + var url = baseURL + "/common/download?fileName=" + encodeURIComponent(name) + "&delete=" + isDelete + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then((res) => { + const isBlob = blobValidate(res.data); + if (isBlob) { + const blob = new Blob([res.data]) + this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) + } else { + this.printErrMsg(res.data); + } + }) + }, + resource(resource) { + var url = baseURL + "/common/download/resource?resource=" + encodeURIComponent(resource); + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then((res) => { + const isBlob = blobValidate(res.data); + if (isBlob) { + const blob = new Blob([res.data]) + this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) + } else { + this.printErrMsg(res.data); + } + }) + }, + zip(url, name) { + var url = baseURL + url + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then((res) => { + const isBlob = blobValidate(res.data); + if (isBlob) { + const blob = new Blob([res.data], { type: 'application/zip' }) + this.saveAs(blob, name) + } else { + this.printErrMsg(res.data); + } + }) + }, + saveAs(text, name, opts) { + saveAs(text, name, opts); + }, + async printErrMsg(data) { + const resText = await data.text(); + const rspObj = JSON.parse(resText); + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + Message.error(errMsg); + } +} + diff --git a/ruoyi-ui/src/plugins/index.js b/ruoyi-ui/src/plugins/index.js new file mode 100644 index 0000000..d000f2d --- /dev/null +++ b/ruoyi-ui/src/plugins/index.js @@ -0,0 +1,20 @@ +import tab from './tab' +import auth from './auth' +import cache from './cache' +import modal from './modal' +import download from './download' + +export default { + install(Vue) { + // 页签操作 + Vue.prototype.$tab = tab + // 认证对象 + Vue.prototype.$auth = auth + // 缓存对象 + Vue.prototype.$cache = cache + // 模态框对象 + Vue.prototype.$modal = modal + // 下载文件 + Vue.prototype.$download = download + } +} diff --git a/ruoyi-ui/src/plugins/modal.js b/ruoyi-ui/src/plugins/modal.js new file mode 100644 index 0000000..b37ca14 --- /dev/null +++ b/ruoyi-ui/src/plugins/modal.js @@ -0,0 +1,83 @@ +import { Message, MessageBox, Notification, Loading } from 'element-ui' + +let loadingInstance; + +export default { + // 消息提示 + msg(content) { + Message.info(content) + }, + // 错误消息 + msgError(content) { + Message.error(content) + }, + // 成功消息 + msgSuccess(content) { + Message.success(content) + }, + // 警告消息 + msgWarning(content) { + Message.warning(content) + }, + // 弹出提示 + alert(content) { + MessageBox.alert(content, "系统提示") + }, + // 错误提示 + alertError(content) { + MessageBox.alert(content, "系统提示", { type: 'error' }) + }, + // 成功提示 + alertSuccess(content) { + MessageBox.alert(content, "系统提示", { type: 'success' }) + }, + // 警告提示 + alertWarning(content) { + MessageBox.alert(content, "系统提示", { type: 'warning' }) + }, + // 通知提示 + notify(content) { + Notification.info(content) + }, + // 错误通知 + notifyError(content) { + Notification.error(content); + }, + // 成功通知 + notifySuccess(content) { + Notification.success(content) + }, + // 警告通知 + notifyWarning(content) { + Notification.warning(content) + }, + // 确认窗体 + confirm(content) { + return MessageBox.confirm(content, "系统提示", { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: "warning", + }) + }, + // 提交内容 + prompt(content) { + return MessageBox.prompt(content, "系统提示", { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: "warning", + }) + }, + // 打开遮罩层 + loading(content) { + loadingInstance = Loading.service({ + lock: true, + text: content, + spinner: "el-icon-loading", + background: "rgba(0, 0, 0, 0.7)", + }) + }, + // 关闭遮罩层 + closeLoading() { + loadingInstance.close(); + } +} diff --git a/ruoyi-ui/src/plugins/tab.js b/ruoyi-ui/src/plugins/tab.js new file mode 100644 index 0000000..b029c0e --- /dev/null +++ b/ruoyi-ui/src/plugins/tab.js @@ -0,0 +1,71 @@ +import store from '@/store' +import router from '@/router'; + +export default { + // 刷新当前tab页签 + refreshPage(obj) { + const { path, query, matched } = router.currentRoute; + if (obj === undefined) { + matched.forEach((m) => { + if (m.components && m.components.default && m.components.default.name) { + if (!['Layout', 'ParentView'].includes(m.components.default.name)) { + obj = { name: m.components.default.name, path: path, query: query }; + } + } + }); + } + return store.dispatch('tagsView/delCachedView', obj).then(() => { + const { path, query } = obj + router.replace({ + path: '/redirect' + path, + query: query + }) + }) + }, + // 关闭当前tab页签,打开新页签 + closeOpenPage(obj) { + store.dispatch("tagsView/delView", router.currentRoute); + if (obj !== undefined) { + return router.push(obj); + } + }, + // 关闭指定tab页签 + closePage(obj) { + if (obj === undefined) { + return store.dispatch('tagsView/delView', router.currentRoute).then(({ visitedViews }) => { + const latestView = visitedViews.slice(-1)[0] + if (latestView) { + return router.push(latestView.fullPath) + } + return router.push('/'); + }); + } + return store.dispatch('tagsView/delView', obj); + }, + // 关闭所有tab页签 + closeAllPage() { + return store.dispatch('tagsView/delAllViews'); + }, + // 关闭左侧tab页签 + closeLeftPage(obj) { + return store.dispatch('tagsView/delLeftTags', obj || router.currentRoute); + }, + // 关闭右侧tab页签 + closeRightPage(obj) { + return store.dispatch('tagsView/delRightTags', obj || router.currentRoute); + }, + // 关闭其他tab页签 + closeOtherPage(obj) { + return store.dispatch('tagsView/delOthersViews', obj || router.currentRoute); + }, + // 添加tab页签 + openPage(title, url, params) { + var obj = { path: url, meta: { title: title } } + store.dispatch('tagsView/addView', obj); + return router.push({ path: url, query: params }); + }, + // 修改tab页签 + updatePage(obj) { + return store.dispatch('tagsView/updateVisitedView', obj); + } +} diff --git a/ruoyi-ui/src/router/index.js b/ruoyi-ui/src/router/index.js new file mode 100644 index 0000000..eb549e1 --- /dev/null +++ b/ruoyi-ui/src/router/index.js @@ -0,0 +1,184 @@ +import Vue from 'vue' +import Router from 'vue-router' + +Vue.use(Router) + +/* Layout */ +import Layout from '@/layout' + +/** + * Note: 路由配置项 + * + * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 + * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 + * // 若你想不管路由下面的 children 声明的个数都显示你的根路由 + * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 + * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + * name:'router-name' // 设定路由的名字,一定要填写不然使用时会出现各种问题 + * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数 + * roles: ['admin', 'common'] // 访问路由的角色权限 + * permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限 + * meta : { + noCache: true // 如果设置为true,则不会被 缓存(默认 false) + title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 + icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg + breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示 + activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。 + } + */ + +// 公共路由 +export const constantRoutes = [ + { + path: '/redirect', + component: Layout, + hidden: true, + children: [ + { + path: '/redirect/:path(.*)', + component: () => import('@/views/redirect') + } + ] + }, + { + path: '/login', + component: () => import('@/views/login'), + hidden: true + }, + { + path: '/register', + component: () => import('@/views/register'), + hidden: true + }, + { + path: '/404', + component: () => import('@/views/error/404'), + hidden: true + }, + { + path: '/401', + component: () => import('@/views/error/401'), + hidden: true + }, + { + path: '', + component: Layout, + redirect: 'index', + children: [ + { + path: 'index', + component: () => import('@/views/index'), + name: 'Index', + meta: { title: '首页', icon: 'dashboard', affix: true } + } + ] + }, + { + path: '/user', + component: Layout, + hidden: true, + redirect: 'noredirect', + children: [ + { + path: 'profile', + component: () => import('@/views/system/user/profile/index'), + name: 'Profile', + meta: { title: '个人中心', icon: 'user' } + } + ] + }, + +] + +// 动态路由,基于用户权限动态去加载 +export const dynamicRoutes = [ + { + path: '/system/user-auth', + component: Layout, + hidden: true, + permissions: ['system:user:edit'], + children: [ + { + path: 'role/:userId(\\d+)', + component: () => import('@/views/system/user/authRole'), + name: 'AuthRole', + meta: { title: '分配角色', activeMenu: '/system/user' } + } + ] + }, + { + path: '/system/role-auth', + component: Layout, + hidden: true, + permissions: ['system:role:edit'], + children: [ + { + path: 'user/:roleId(\\d+)', + component: () => import('@/views/system/role/authUser'), + name: 'AuthUser', + meta: { title: '分配用户', activeMenu: '/system/role' } + } + ] + }, + { + path: '/system/dict-data', + component: Layout, + hidden: true, + permissions: ['system:dict:list'], + children: [ + { + path: 'index/:dictId(\\d+)', + component: () => import('@/views/system/dict/data'), + name: 'Data', + meta: { title: '字典数据', activeMenu: '/system/dict' } + } + ] + }, + { + path: '/monitor/job-log', + component: Layout, + hidden: true, + permissions: ['monitor:job:list'], + children: [ + { + path: 'index/:jobId(\\d+)', + component: () => import('@/views/monitor/job/log'), + name: 'JobLog', + meta: { title: '调度日志', activeMenu: '/monitor/job' } + } + ] + }, + { + path: '/tool/gen-edit', + component: Layout, + hidden: true, + permissions: ['tool:gen:edit'], + children: [ + { + path: 'index/:tableId(\\d+)', + component: () => import('@/views/tool/gen/editTable'), + name: 'GenEdit', + meta: { title: '修改生成配置', activeMenu: '/tool/gen' } + } + ] + } +] + +// 防止连续点击多次路由报错 +let routerPush = Router.prototype.push; +let routerReplace = Router.prototype.replace; +// push +Router.prototype.push = function push(location) { + return routerPush.call(this, location).catch(err => err) +} +// replace +Router.prototype.replace = function push(location) { + return routerReplace.call(this, location).catch(err => err) +} + +export default new Router({ + mode: 'history', // 去掉url中的# + scrollBehavior: () => ({ y: 0 }), + routes: constantRoutes +}) diff --git a/ruoyi-ui/src/settings.js b/ruoyi-ui/src/settings.js new file mode 100644 index 0000000..6a0b09f --- /dev/null +++ b/ruoyi-ui/src/settings.js @@ -0,0 +1,44 @@ +module.exports = { + /** + * 侧边栏主题 深色主题theme-dark,浅色主题theme-light + */ + sideTheme: 'theme-dark', + + /** + * 是否系统布局配置 + */ + showSettings: false, + + /** + * 是否显示顶部导航 + */ + topNav: false, + + /** + * 是否显示 tagsView + */ + tagsView: true, + + /** + * 是否固定头部 + */ + fixedHeader: false, + + /** + * 是否显示logo + */ + sidebarLogo: true, + + /** + * 是否显示动态标题 + */ + dynamicTitle: false, + + /** + * @type {string | array} 'production' | ['production', 'development'] + * @description Need show err logs component. + * The default is only used in the production env + * If you want to also use it in dev, you can pass ['production', 'development'] + */ + errorLog: 'production' +} diff --git a/ruoyi-ui/src/store/getters.js b/ruoyi-ui/src/store/getters.js new file mode 100644 index 0000000..8adb1b6 --- /dev/null +++ b/ruoyi-ui/src/store/getters.js @@ -0,0 +1,19 @@ +const getters = { + sidebar: state => state.app.sidebar, + size: state => state.app.size, + device: state => state.app.device, + dict: state => state.dict.dict, + visitedViews: state => state.tagsView.visitedViews, + cachedViews: state => state.tagsView.cachedViews, + token: state => state.user.token, + avatar: state => state.user.avatar, + name: state => state.user.name, + introduction: state => state.user.introduction, + roles: state => state.user.roles, + permissions: state => state.user.permissions, + permission_routes: state => state.permission.routes, + topbarRouters:state => state.permission.topbarRouters, + defaultRoutes:state => state.permission.defaultRoutes, + sidebarRouters:state => state.permission.sidebarRouters, +} +export default getters diff --git a/ruoyi-ui/src/store/index.js b/ruoyi-ui/src/store/index.js new file mode 100644 index 0000000..97aaef8 --- /dev/null +++ b/ruoyi-ui/src/store/index.js @@ -0,0 +1,25 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import app from './modules/app' +import dict from './modules/dict' +import user from './modules/user' +import tagsView from './modules/tagsView' +import permission from './modules/permission' +import settings from './modules/settings' +import getters from './getters' + +Vue.use(Vuex) + +const store = new Vuex.Store({ + modules: { + app, + dict, + user, + tagsView, + permission, + settings + }, + getters +}) + +export default store diff --git a/ruoyi-ui/src/store/modules/app.js b/ruoyi-ui/src/store/modules/app.js new file mode 100644 index 0000000..3e22d1c --- /dev/null +++ b/ruoyi-ui/src/store/modules/app.js @@ -0,0 +1,66 @@ +import Cookies from 'js-cookie' + +const state = { + sidebar: { + opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, + withoutAnimation: false, + hide: false + }, + device: 'desktop', + size: Cookies.get('size') || 'medium' +} + +const mutations = { + TOGGLE_SIDEBAR: state => { + if (state.sidebar.hide) { + return false; + } + state.sidebar.opened = !state.sidebar.opened + state.sidebar.withoutAnimation = false + if (state.sidebar.opened) { + Cookies.set('sidebarStatus', 1) + } else { + Cookies.set('sidebarStatus', 0) + } + }, + CLOSE_SIDEBAR: (state, withoutAnimation) => { + Cookies.set('sidebarStatus', 0) + state.sidebar.opened = false + state.sidebar.withoutAnimation = withoutAnimation + }, + TOGGLE_DEVICE: (state, device) => { + state.device = device + }, + SET_SIZE: (state, size) => { + state.size = size + Cookies.set('size', size) + }, + SET_SIDEBAR_HIDE: (state, status) => { + state.sidebar.hide = status + } +} + +const actions = { + toggleSideBar({ commit }) { + commit('TOGGLE_SIDEBAR') + }, + closeSideBar({ commit }, { withoutAnimation }) { + commit('CLOSE_SIDEBAR', withoutAnimation) + }, + toggleDevice({ commit }, device) { + commit('TOGGLE_DEVICE', device) + }, + setSize({ commit }, size) { + commit('SET_SIZE', size) + }, + toggleSideBarHide({ commit }, status) { + commit('SET_SIDEBAR_HIDE', status) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/ruoyi-ui/src/store/modules/dict.js b/ruoyi-ui/src/store/modules/dict.js new file mode 100644 index 0000000..7a1b2f0 --- /dev/null +++ b/ruoyi-ui/src/store/modules/dict.js @@ -0,0 +1,50 @@ +const state = { + dict: new Array() +} +const mutations = { + SET_DICT: (state, { key, value }) => { + if (key !== null && key !== "") { + state.dict.push({ + key: key, + value: value + }) + } + }, + REMOVE_DICT: (state, key) => { + try { + for (let i = 0; i < state.dict.length; i++) { + if (state.dict[i].key == key) { + state.dict.splice(i, 1) + return true + } + } + } catch (e) { + } + }, + CLEAN_DICT: (state) => { + state.dict = new Array() + } +} + +const actions = { + // 设置字典 + setDict({ commit }, data) { + commit('SET_DICT', data) + }, + // 删除字典 + removeDict({ commit }, key) { + commit('REMOVE_DICT', key) + }, + // 清空字典 + cleanDict({ commit }) { + commit('CLEAN_DICT') + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} + diff --git a/ruoyi-ui/src/store/modules/permission.js b/ruoyi-ui/src/store/modules/permission.js new file mode 100644 index 0000000..2287665 --- /dev/null +++ b/ruoyi-ui/src/store/modules/permission.js @@ -0,0 +1,133 @@ +import auth from '@/plugins/auth' +import router, { constantRoutes, dynamicRoutes } from '@/router' +import { getRouters } from '@/api/menu' +import Layout from '@/layout/index' +import ParentView from '@/components/ParentView' +import InnerLink from '@/layout/components/InnerLink' + +const permission = { + state: { + routes: [], + addRoutes: [], + defaultRoutes: [], + topbarRouters: [], + sidebarRouters: [] + }, + mutations: { + SET_ROUTES: (state, routes) => { + state.addRoutes = routes + state.routes = constantRoutes.concat(routes) + }, + SET_DEFAULT_ROUTES: (state, routes) => { + state.defaultRoutes = constantRoutes.concat(routes) + }, + SET_TOPBAR_ROUTES: (state, routes) => { + state.topbarRouters = routes + }, + SET_SIDEBAR_ROUTERS: (state, routes) => { + state.sidebarRouters = routes + }, + }, + actions: { + // 生成路由 + GenerateRoutes({ commit }) { + return new Promise(resolve => { + // 向后端请求路由数据 + getRouters().then(res => { + const sdata = JSON.parse(JSON.stringify(res.data)) + const rdata = JSON.parse(JSON.stringify(res.data)) + const sidebarRoutes = filterAsyncRouter(sdata) + const rewriteRoutes = filterAsyncRouter(rdata, false, true) + const asyncRoutes = filterDynamicRoutes(dynamicRoutes); + rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true }) + router.addRoutes(asyncRoutes); + commit('SET_ROUTES', rewriteRoutes) + commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes)) + commit('SET_DEFAULT_ROUTES', sidebarRoutes) + commit('SET_TOPBAR_ROUTES', sidebarRoutes) + resolve(rewriteRoutes) + }) + }) + } + } +} + +// 遍历后台传来的路由字符串,转换为组件对象 +function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) { + return asyncRouterMap.filter(route => { + if (type && route.children) { + route.children = filterChildren(route.children) + } + if (route.component) { + // Layout ParentView 组件特殊处理 + if (route.component === 'Layout') { + route.component = Layout + } else if (route.component === 'ParentView') { + route.component = ParentView + } else if (route.component === 'InnerLink') { + route.component = InnerLink + } else { + route.component = loadView(route.component) + } + } + if (route.children != null && route.children && route.children.length) { + route.children = filterAsyncRouter(route.children, route, type) + } else { + delete route['children'] + delete route['redirect'] + } + return true + }) +} + +function filterChildren(childrenMap, lastRouter = false) { + var children = [] + childrenMap.forEach((el, index) => { + if (el.children && el.children.length) { + if (el.component === 'ParentView' && !lastRouter) { + el.children.forEach(c => { + c.path = el.path + '/' + c.path + if (c.children && c.children.length) { + children = children.concat(filterChildren(c.children, c)) + return + } + children.push(c) + }) + return + } + } + if (lastRouter) { + el.path = lastRouter.path + '/' + el.path + } + children = children.concat(el) + }) + return children +} + +// 动态路由遍历,验证是否具备权限 +export function filterDynamicRoutes(routes) { + const res = [] + routes.forEach(route => { + if (route.permissions) { + if (auth.hasPermiOr(route.permissions)) { + res.push(route) + } + } else if (route.roles) { + if (auth.hasRoleOr(route.roles)) { + res.push(route) + } + } + }) + return res +} + +export const loadView = (view) => { + if (process.env.NODE_ENV === 'development') { + return (resolve) => require([`@/views/${view}`], resolve) + } else { + // 使用 import 实现生产环境的路由懒加载 + return () => import(`@/views/${view}`) + } +} + +export default permission diff --git a/ruoyi-ui/src/store/modules/settings.js b/ruoyi-ui/src/store/modules/settings.js new file mode 100644 index 0000000..2455a1e --- /dev/null +++ b/ruoyi-ui/src/store/modules/settings.js @@ -0,0 +1,42 @@ +import defaultSettings from '@/settings' + +const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings + +const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || '' +const state = { + title: '', + theme: storageSetting.theme || '#409EFF', + sideTheme: storageSetting.sideTheme || sideTheme, + showSettings: showSettings, + topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav, + tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView, + fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader, + sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo, + dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle +} +const mutations = { + CHANGE_SETTING: (state, { key, value }) => { + if (state.hasOwnProperty(key)) { + state[key] = value + } + } +} + +const actions = { + // 修改布局设置 + changeSetting({ commit }, data) { + commit('CHANGE_SETTING', data) + }, + // 设置网页标题 + setTitle({ commit }, title) { + state.title = title + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} + diff --git a/ruoyi-ui/src/store/modules/tagsView.js b/ruoyi-ui/src/store/modules/tagsView.js new file mode 100644 index 0000000..5fc011c --- /dev/null +++ b/ruoyi-ui/src/store/modules/tagsView.js @@ -0,0 +1,228 @@ +const state = { + visitedViews: [], + cachedViews: [], + iframeViews: [] +} + +const mutations = { + ADD_IFRAME_VIEW: (state, view) => { + if (state.iframeViews.some(v => v.path === view.path)) return + state.iframeViews.push( + Object.assign({}, view, { + title: view.meta.title || 'no-name' + }) + ) + }, + ADD_VISITED_VIEW: (state, view) => { + if (state.visitedViews.some(v => v.path === view.path)) return + state.visitedViews.push( + Object.assign({}, view, { + title: view.meta.title || 'no-name' + }) + ) + }, + ADD_CACHED_VIEW: (state, view) => { + if (state.cachedViews.includes(view.name)) return + if (view.meta && !view.meta.noCache) { + state.cachedViews.push(view.name) + } + }, + DEL_VISITED_VIEW: (state, view) => { + for (const [i, v] of state.visitedViews.entries()) { + if (v.path === view.path) { + state.visitedViews.splice(i, 1) + break + } + } + state.iframeViews = state.iframeViews.filter(item => item.path !== view.path) + }, + DEL_IFRAME_VIEW: (state, view) => { + state.iframeViews = state.iframeViews.filter(item => item.path !== view.path) + }, + DEL_CACHED_VIEW: (state, view) => { + const index = state.cachedViews.indexOf(view.name) + index > -1 && state.cachedViews.splice(index, 1) + }, + + DEL_OTHERS_VISITED_VIEWS: (state, view) => { + state.visitedViews = state.visitedViews.filter(v => { + return v.meta.affix || v.path === view.path + }) + state.iframeViews = state.iframeViews.filter(item => item.path === view.path) + }, + DEL_OTHERS_CACHED_VIEWS: (state, view) => { + const index = state.cachedViews.indexOf(view.name) + if (index > -1) { + state.cachedViews = state.cachedViews.slice(index, index + 1) + } else { + state.cachedViews = [] + } + }, + DEL_ALL_VISITED_VIEWS: state => { + // keep affix tags + const affixTags = state.visitedViews.filter(tag => tag.meta.affix) + state.visitedViews = affixTags + state.iframeViews = [] + }, + DEL_ALL_CACHED_VIEWS: state => { + state.cachedViews = [] + }, + UPDATE_VISITED_VIEW: (state, view) => { + for (let v of state.visitedViews) { + if (v.path === view.path) { + v = Object.assign(v, view) + break + } + } + }, + DEL_RIGHT_VIEWS: (state, view) => { + const index = state.visitedViews.findIndex(v => v.path === view.path) + if (index === -1) { + return + } + state.visitedViews = state.visitedViews.filter((item, idx) => { + if (idx <= index || (item.meta && item.meta.affix)) { + return true + } + const i = state.cachedViews.indexOf(item.name) + if (i > -1) { + state.cachedViews.splice(i, 1) + } + if(item.meta.link) { + const fi = state.iframeViews.findIndex(v => v.path === item.path) + state.iframeViews.splice(fi, 1) + } + return false + }) + }, + DEL_LEFT_VIEWS: (state, view) => { + const index = state.visitedViews.findIndex(v => v.path === view.path) + if (index === -1) { + return + } + state.visitedViews = state.visitedViews.filter((item, idx) => { + if (idx >= index || (item.meta && item.meta.affix)) { + return true + } + const i = state.cachedViews.indexOf(item.name) + if (i > -1) { + state.cachedViews.splice(i, 1) + } + if(item.meta.link) { + const fi = state.iframeViews.findIndex(v => v.path === item.path) + state.iframeViews.splice(fi, 1) + } + return false + }) + } +} + +const actions = { + addView({ dispatch }, view) { + dispatch('addVisitedView', view) + dispatch('addCachedView', view) + }, + addIframeView({ commit }, view) { + commit('ADD_IFRAME_VIEW', view) + }, + addVisitedView({ commit }, view) { + commit('ADD_VISITED_VIEW', view) + }, + addCachedView({ commit }, view) { + commit('ADD_CACHED_VIEW', view) + }, + delView({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delVisitedView', view) + dispatch('delCachedView', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delVisitedView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_VISITED_VIEW', view) + resolve([...state.visitedViews]) + }) + }, + delIframeView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_IFRAME_VIEW', view) + resolve([...state.iframeViews]) + }) + }, + delCachedView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_CACHED_VIEW', view) + resolve([...state.cachedViews]) + }) + }, + delOthersViews({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delOthersVisitedViews', view) + dispatch('delOthersCachedViews', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delOthersVisitedViews({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_OTHERS_VISITED_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, + delOthersCachedViews({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_OTHERS_CACHED_VIEWS', view) + resolve([...state.cachedViews]) + }) + }, + delAllViews({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delAllVisitedViews', view) + dispatch('delAllCachedViews', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delAllVisitedViews({ commit, state }) { + return new Promise(resolve => { + commit('DEL_ALL_VISITED_VIEWS') + resolve([...state.visitedViews]) + }) + }, + delAllCachedViews({ commit, state }) { + return new Promise(resolve => { + commit('DEL_ALL_CACHED_VIEWS') + resolve([...state.cachedViews]) + }) + }, + updateVisitedView({ commit }, view) { + commit('UPDATE_VISITED_VIEW', view) + }, + delRightTags({ commit }, view) { + return new Promise(resolve => { + commit('DEL_RIGHT_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, + delLeftTags({ commit }, view) { + return new Promise(resolve => { + commit('DEL_LEFT_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/ruoyi-ui/src/store/modules/user.js b/ruoyi-ui/src/store/modules/user.js new file mode 100644 index 0000000..cdbab1e --- /dev/null +++ b/ruoyi-ui/src/store/modules/user.js @@ -0,0 +1,101 @@ +import { login, logout, getInfo } from '@/api/login' +import { getToken, setToken, removeToken } from '@/utils/auth' + +const user = { + state: { + token: getToken(), + id: '', + name: '', + avatar: '', + roles: [], + permissions: [] + }, + + mutations: { + SET_TOKEN: (state, token) => { + state.token = token + }, + SET_ID: (state, id) => { + state.id = id + }, + SET_NAME: (state, name) => { + state.name = name + }, + SET_AVATAR: (state, avatar) => { + state.avatar = avatar + }, + SET_ROLES: (state, roles) => { + state.roles = roles + }, + SET_PERMISSIONS: (state, permissions) => { + state.permissions = permissions + } + }, + + actions: { + // 登录 + Login({ commit }, userInfo) { + const username = userInfo.username.trim() + const password = userInfo.password + const code = userInfo.code + const uuid = userInfo.uuid + return new Promise((resolve, reject) => { + login(username, password, code, uuid).then(res => { + setToken(res.token) + commit('SET_TOKEN', res.token) + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 获取用户信息 + GetInfo({ commit, state }) { + return new Promise((resolve, reject) => { + getInfo().then(res => { + const user = res.user + const avatar = (user.avatar == "" || user.avatar == null) ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar; + if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组 + commit('SET_ROLES', res.roles) + commit('SET_PERMISSIONS', res.permissions) + } else { + commit('SET_ROLES', ['ROLE_DEFAULT']) + } + commit('SET_ID', user.userId) + commit('SET_NAME', user.userName) + commit('SET_AVATAR', avatar) + resolve(res) + }).catch(error => { + reject(error) + }) + }) + }, + + // 退出系统 + LogOut({ commit, state }) { + return new Promise((resolve, reject) => { + logout(state.token).then(() => { + commit('SET_TOKEN', '') + commit('SET_ROLES', []) + commit('SET_PERMISSIONS', []) + removeToken() + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 前端 登出 + FedLogOut({ commit }) { + return new Promise(resolve => { + commit('SET_TOKEN', '') + removeToken() + resolve() + }) + } + } +} + +export default user diff --git a/ruoyi-ui/src/utils/auth.js b/ruoyi-ui/src/utils/auth.js new file mode 100644 index 0000000..08a43d6 --- /dev/null +++ b/ruoyi-ui/src/utils/auth.js @@ -0,0 +1,15 @@ +import Cookies from 'js-cookie' + +const TokenKey = 'Admin-Token' + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token) { + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} diff --git a/ruoyi-ui/src/utils/dict/Dict.js b/ruoyi-ui/src/utils/dict/Dict.js new file mode 100644 index 0000000..104bd6e --- /dev/null +++ b/ruoyi-ui/src/utils/dict/Dict.js @@ -0,0 +1,82 @@ +import Vue from 'vue' +import { mergeRecursive } from "@/utils/ruoyi"; +import DictMeta from './DictMeta' +import DictData from './DictData' + +const DEFAULT_DICT_OPTIONS = { + types: [], +} + +/** + * @classdesc 字典 + * @property {Object} label 标签对象,内部属性名为字典类型名称 + * @property {Object} dict 字段数组,内部属性名为字典类型名称 + * @property {Array.} _dictMetas 字典元数据数组 + */ +export default class Dict { + constructor() { + this.owner = null + this.label = {} + this.type = {} + } + + init(options) { + if (options instanceof Array) { + options = { types: options } + } + const opts = mergeRecursive(DEFAULT_DICT_OPTIONS, options) + if (opts.types === undefined) { + throw new Error('need dict types') + } + const ps = [] + this._dictMetas = opts.types.map(t => DictMeta.parse(t)) + this._dictMetas.forEach(dictMeta => { + const type = dictMeta.type + Vue.set(this.label, type, {}) + Vue.set(this.type, type, []) + if (dictMeta.lazy) { + return + } + ps.push(loadDict(this, dictMeta)) + }) + return Promise.all(ps) + } + + /** + * 重新加载字典 + * @param {String} type 字典类型 + */ + reloadDict(type) { + const dictMeta = this._dictMetas.find(e => e.type === type) + if (dictMeta === undefined) { + return Promise.reject(`the dict meta of ${type} was not found`) + } + return loadDict(this, dictMeta) + } +} + +/** + * 加载字典 + * @param {Dict} dict 字典 + * @param {DictMeta} dictMeta 字典元数据 + * @returns {Promise} + */ +function loadDict(dict, dictMeta) { + return dictMeta.request(dictMeta) + .then(response => { + const type = dictMeta.type + let dicts = dictMeta.responseConverter(response, dictMeta) + if (!(dicts instanceof Array)) { + console.error('the return of responseConverter must be Array.') + dicts = [] + } else if (dicts.filter(d => d instanceof DictData).length !== dicts.length) { + console.error('the type of elements in dicts must be DictData') + dicts = [] + } + dict.type[type].splice(0, Number.MAX_SAFE_INTEGER, ...dicts) + dicts.forEach(d => { + Vue.set(dict.label[type], d.value, d.label) + }) + return dicts + }) +} diff --git a/ruoyi-ui/src/utils/dict/DictConverter.js b/ruoyi-ui/src/utils/dict/DictConverter.js new file mode 100644 index 0000000..0cf5df8 --- /dev/null +++ b/ruoyi-ui/src/utils/dict/DictConverter.js @@ -0,0 +1,17 @@ +import DictOptions from './DictOptions' +import DictData from './DictData' + +export default function(dict, dictMeta) { + const label = determineDictField(dict, dictMeta.labelField, ...DictOptions.DEFAULT_LABEL_FIELDS) + const value = determineDictField(dict, dictMeta.valueField, ...DictOptions.DEFAULT_VALUE_FIELDS) + return new DictData(dict[label], dict[value], dict) +} + +/** + * 确定字典字段 + * @param {DictData} dict + * @param {...String} fields + */ +function determineDictField(dict, ...fields) { + return fields.find(f => Object.prototype.hasOwnProperty.call(dict, f)) +} diff --git a/ruoyi-ui/src/utils/dict/DictData.js b/ruoyi-ui/src/utils/dict/DictData.js new file mode 100644 index 0000000..afc763e --- /dev/null +++ b/ruoyi-ui/src/utils/dict/DictData.js @@ -0,0 +1,13 @@ +/** + * @classdesc 字典数据 + * @property {String} label 标签 + * @property {*} value 标签 + * @property {Object} raw 原始数据 + */ +export default class DictData { + constructor(label, value, raw) { + this.label = label + this.value = value + this.raw = raw + } +} diff --git a/ruoyi-ui/src/utils/dict/DictMeta.js b/ruoyi-ui/src/utils/dict/DictMeta.js new file mode 100644 index 0000000..9779daa --- /dev/null +++ b/ruoyi-ui/src/utils/dict/DictMeta.js @@ -0,0 +1,38 @@ +import { mergeRecursive } from "@/utils/ruoyi"; +import DictOptions from './DictOptions' + +/** + * @classdesc 字典元数据 + * @property {String} type 类型 + * @property {Function} request 请求 + * @property {String} label 标签字段 + * @property {String} value 值字段 + */ +export default class DictMeta { + constructor(options) { + this.type = options.type + this.request = options.request + this.responseConverter = options.responseConverter + this.labelField = options.labelField + this.valueField = options.valueField + this.lazy = options.lazy === true + } +} + + +/** + * 解析字典元数据 + * @param {Object} options + * @returns {DictMeta} + */ +DictMeta.parse= function(options) { + let opts = null + if (typeof options === 'string') { + opts = DictOptions.metas[options] || {} + opts.type = options + } else if (typeof options === 'object') { + opts = options + } + opts = mergeRecursive(DictOptions.metas['*'], opts) + return new DictMeta(opts) +} diff --git a/ruoyi-ui/src/utils/dict/DictOptions.js b/ruoyi-ui/src/utils/dict/DictOptions.js new file mode 100644 index 0000000..338a94e --- /dev/null +++ b/ruoyi-ui/src/utils/dict/DictOptions.js @@ -0,0 +1,51 @@ +import { mergeRecursive } from "@/utils/ruoyi"; +import dictConverter from './DictConverter' + +export const options = { + metas: { + '*': { + /** + * 字典请求,方法签名为function(dictMeta: DictMeta): Promise + */ + request: (dictMeta) => { + console.log(`load dict ${dictMeta.type}`) + return Promise.resolve([]) + }, + /** + * 字典响应数据转换器,方法签名为function(response: Object, dictMeta: DictMeta): DictData + */ + responseConverter, + labelField: 'label', + valueField: 'value', + }, + }, + /** + * 默认标签字段 + */ + DEFAULT_LABEL_FIELDS: ['label', 'name', 'title'], + /** + * 默认值字段 + */ + DEFAULT_VALUE_FIELDS: ['value', 'id', 'uid', 'key'], +} + +/** + * 映射字典 + * @param {Object} response 字典数据 + * @param {DictMeta} dictMeta 字典元数据 + * @returns {DictData} + */ +function responseConverter(response, dictMeta) { + const dicts = response.content instanceof Array ? response.content : response + if (dicts === undefined) { + console.warn(`no dict data of "${dictMeta.type}" found in the response`) + return [] + } + return dicts.map(d => dictConverter(d, dictMeta)) +} + +export function mergeOptions(src) { + mergeRecursive(options, src) +} + +export default options diff --git a/ruoyi-ui/src/utils/dict/index.js b/ruoyi-ui/src/utils/dict/index.js new file mode 100644 index 0000000..215eb9e --- /dev/null +++ b/ruoyi-ui/src/utils/dict/index.js @@ -0,0 +1,33 @@ +import Dict from './Dict' +import { mergeOptions } from './DictOptions' + +export default function(Vue, options) { + mergeOptions(options) + Vue.mixin({ + data() { + if (this.$options === undefined || this.$options.dicts === undefined || this.$options.dicts === null) { + return {} + } + const dict = new Dict() + dict.owner = this + return { + dict + } + }, + created() { + if (!(this.dict instanceof Dict)) { + return + } + options.onCreated && options.onCreated(this.dict) + this.dict.init(this.$options.dicts).then(() => { + options.onReady && options.onReady(this.dict) + this.$nextTick(() => { + this.$emit('dictReady', this.dict) + if (this.$options.methods && this.$options.methods.onDictReady instanceof Function) { + this.$options.methods.onDictReady.call(this, this.dict) + } + }) + }) + }, + }) +} diff --git a/ruoyi-ui/src/utils/errorCode.js b/ruoyi-ui/src/utils/errorCode.js new file mode 100644 index 0000000..d2111ee --- /dev/null +++ b/ruoyi-ui/src/utils/errorCode.js @@ -0,0 +1,6 @@ +export default { + '401': '认证失败,无法访问系统资源', + '403': '当前操作没有权限', + '404': '访问资源不存在', + 'default': '系统未知错误,请反馈给管理员' +} diff --git a/ruoyi-ui/src/utils/generator/config.js b/ruoyi-ui/src/utils/generator/config.js new file mode 100644 index 0000000..7abf227 --- /dev/null +++ b/ruoyi-ui/src/utils/generator/config.js @@ -0,0 +1,438 @@ +export const formConf = { + formRef: 'elForm', + formModel: 'formData', + size: 'medium', + labelPosition: 'right', + labelWidth: 100, + formRules: 'rules', + gutter: 15, + disabled: false, + span: 24, + formBtns: true +} + +export const inputComponents = [ + { + label: '单行文本', + tag: 'el-input', + tagIcon: 'input', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': '', + 'suffix-icon': '', + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input' + }, + { + label: '多行文本', + tag: 'el-input', + tagIcon: 'textarea', + type: 'textarea', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + labelWidth: null, + autosize: { + minRows: 4, + maxRows: 4 + }, + style: { width: '100%' }, + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input' + }, + { + label: '密码', + tag: 'el-input', + tagIcon: 'password', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + 'show-password': true, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': '', + 'suffix-icon': '', + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input' + }, + { + label: '计数器', + tag: 'el-input-number', + tagIcon: 'number', + placeholder: '', + defaultValue: undefined, + span: 24, + labelWidth: null, + min: undefined, + max: undefined, + step: undefined, + 'step-strictly': false, + precision: undefined, + 'controls-position': '', + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input-number' + } +] + +export const selectComponents = [ + { + label: '下拉选择', + tag: 'el-select', + tagIcon: 'select', + placeholder: '请选择', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + disabled: false, + required: true, + filterable: false, + multiple: false, + options: [{ + label: '选项一', + value: 1 + }, { + label: '选项二', + value: 2 + }], + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/select' + }, + { + label: '级联选择', + tag: 'el-cascader', + tagIcon: 'cascader', + placeholder: '请选择', + defaultValue: [], + span: 24, + labelWidth: null, + style: { width: '100%' }, + props: { + props: { + multiple: false + } + }, + 'show-all-levels': true, + disabled: false, + clearable: true, + filterable: false, + required: true, + options: [{ + id: 1, + value: 1, + label: '选项1', + children: [{ + id: 2, + value: 2, + label: '选项1-1' + }] + }], + dataType: 'dynamic', + labelKey: 'label', + valueKey: 'value', + childrenKey: 'children', + separator: '/', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/cascader' + }, + { + label: '单选框组', + tag: 'el-radio-group', + tagIcon: 'radio', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: {}, + optionType: 'default', + border: false, + size: 'medium', + disabled: false, + required: true, + options: [{ + label: '选项一', + value: 1 + }, { + label: '选项二', + value: 2 + }], + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/radio' + }, + { + label: '多选框组', + tag: 'el-checkbox-group', + tagIcon: 'checkbox', + defaultValue: [], + span: 24, + labelWidth: null, + style: {}, + optionType: 'default', + border: false, + size: 'medium', + disabled: false, + required: true, + options: [{ + label: '选项一', + value: 1 + }, { + label: '选项二', + value: 2 + }], + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/checkbox' + }, + { + label: '开关', + tag: 'el-switch', + tagIcon: 'switch', + defaultValue: false, + span: 24, + labelWidth: null, + style: {}, + disabled: false, + required: true, + 'active-text': '', + 'inactive-text': '', + 'active-color': null, + 'inactive-color': null, + 'active-value': true, + 'inactive-value': false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/switch' + }, + { + label: '滑块', + tag: 'el-slider', + tagIcon: 'slider', + defaultValue: null, + span: 24, + labelWidth: null, + disabled: false, + required: true, + min: 0, + max: 100, + step: 1, + 'show-stops': false, + range: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/slider' + }, + { + label: '时间选择', + tag: 'el-time-picker', + tagIcon: 'time', + placeholder: '请选择', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + 'picker-options': { + selectableRange: '00:00:00-23:59:59' + }, + format: 'HH:mm:ss', + 'value-format': 'HH:mm:ss', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/time-picker' + }, + { + label: '时间范围', + tag: 'el-time-picker', + tagIcon: 'time-range', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + 'is-range': true, + 'range-separator': '至', + 'start-placeholder': '开始时间', + 'end-placeholder': '结束时间', + format: 'HH:mm:ss', + 'value-format': 'HH:mm:ss', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/time-picker' + }, + { + label: '日期选择', + tag: 'el-date-picker', + tagIcon: 'date', + placeholder: '请选择', + defaultValue: null, + type: 'date', + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + format: 'yyyy-MM-dd', + 'value-format': 'yyyy-MM-dd', + readonly: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/date-picker' + }, + { + label: '日期范围', + tag: 'el-date-picker', + tagIcon: 'date-range', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + type: 'daterange', + 'range-separator': '至', + 'start-placeholder': '开始日期', + 'end-placeholder': '结束日期', + disabled: false, + clearable: true, + required: true, + format: 'yyyy-MM-dd', + 'value-format': 'yyyy-MM-dd', + readonly: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/date-picker' + }, + { + label: '评分', + tag: 'el-rate', + tagIcon: 'rate', + defaultValue: 0, + span: 24, + labelWidth: null, + style: {}, + max: 5, + 'allow-half': false, + 'show-text': false, + 'show-score': false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/rate' + }, + { + label: '颜色选择', + tag: 'el-color-picker', + tagIcon: 'color', + defaultValue: null, + labelWidth: null, + 'show-alpha': false, + 'color-format': '', + disabled: false, + required: true, + size: 'medium', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/color-picker' + }, + { + label: '上传', + tag: 'el-upload', + tagIcon: 'upload', + action: 'https://jsonplaceholder.typicode.com/posts/', + defaultValue: null, + labelWidth: null, + disabled: false, + required: true, + accept: '', + name: 'file', + 'auto-upload': true, + showTip: false, + buttonText: '点击上传', + fileSize: 2, + sizeUnit: 'MB', + 'list-type': 'text', + multiple: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/upload' + } +] + +export const layoutComponents = [ + { + layout: 'rowFormItem', + tagIcon: 'row', + type: 'default', + justify: 'start', + align: 'top', + label: '行容器', + layoutTree: true, + children: [], + document: 'https://element.eleme.cn/#/zh-CN/component/layout' + }, + { + layout: 'colFormItem', + label: '按钮', + changeTag: true, + labelWidth: null, + tag: 'el-button', + tagIcon: 'button', + span: 24, + default: '主要按钮', + type: 'primary', + icon: 'el-icon-search', + size: 'medium', + disabled: false, + document: 'https://element.eleme.cn/#/zh-CN/component/button' + } +] + +// 组件rule的触发方式,无触发方式的组件不生成rule +export const trigger = { + 'el-input': 'blur', + 'el-input-number': 'blur', + 'el-select': 'change', + 'el-radio-group': 'change', + 'el-checkbox-group': 'change', + 'el-cascader': 'change', + 'el-time-picker': 'change', + 'el-date-picker': 'change', + 'el-rate': 'change' +} diff --git a/ruoyi-ui/src/utils/generator/css.js b/ruoyi-ui/src/utils/generator/css.js new file mode 100644 index 0000000..c1c62e6 --- /dev/null +++ b/ruoyi-ui/src/utils/generator/css.js @@ -0,0 +1,18 @@ +const styles = { + 'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}', + 'el-upload': '.el-upload__tip{line-height: 1.2;}' +} + +function addCss(cssList, el) { + const css = styles[el.tag] + css && cssList.indexOf(css) === -1 && cssList.push(css) + if (el.children) { + el.children.forEach(el2 => addCss(cssList, el2)) + } +} + +export function makeUpCss(conf) { + const cssList = [] + conf.fields.forEach(el => addCss(cssList, el)) + return cssList.join('\n') +} diff --git a/ruoyi-ui/src/utils/generator/drawingDefault.js b/ruoyi-ui/src/utils/generator/drawingDefault.js new file mode 100644 index 0000000..09f133c --- /dev/null +++ b/ruoyi-ui/src/utils/generator/drawingDefault.js @@ -0,0 +1,29 @@ +export default [ + { + layout: 'colFormItem', + tagIcon: 'input', + label: '手机号', + vModel: 'mobile', + formId: 6, + tag: 'el-input', + placeholder: '请输入手机号', + defaultValue: '', + span: 24, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': 'el-icon-mobile', + 'suffix-icon': '', + maxlength: 11, + 'show-word-limit': true, + readonly: false, + disabled: false, + required: true, + changeTag: true, + regList: [{ + pattern: '/^1(3|4|5|7|8|9)\\d{9}$/', + message: '手机号格式错误' + }] + } +] diff --git a/ruoyi-ui/src/utils/generator/html.js b/ruoyi-ui/src/utils/generator/html.js new file mode 100644 index 0000000..9bcc536 --- /dev/null +++ b/ruoyi-ui/src/utils/generator/html.js @@ -0,0 +1,359 @@ +/* eslint-disable max-len */ +import { trigger } from './config' + +let confGlobal +let someSpanIsNot24 + +export function dialogWrapper(str) { + return ` + ${str} +
+ 取消 + 确定 +
+
` +} + +export function vueTemplate(str) { + return `` +} + +export function vueScript(str) { + return `` +} + +export function cssStyle(cssStr) { + return `` +} + +function buildFormTemplate(conf, child, type) { + let labelPosition = '' + if (conf.labelPosition !== 'right') { + labelPosition = `label-position="${conf.labelPosition}"` + } + const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : '' + let str = ` + ${child} + ${buildFromBtns(conf, type)} + ` + if (someSpanIsNot24) { + str = ` + ${str} + ` + } + return str +} + +function buildFromBtns(conf, type) { + let str = '' + if (conf.formBtns && type === 'file') { + str = ` + 提交 + 重置 + ` + if (someSpanIsNot24) { + str = ` + ${str} + ` + } + } + return str +} + +// span不为24的用el-col包裹 +function colWrapper(element, str) { + if (someSpanIsNot24 || element.span !== 24) { + return ` + ${str} + ` + } + return str +} + +const layouts = { + colFormItem(element) { + let labelWidth = '' + if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) { + labelWidth = `label-width="${element.labelWidth}px"` + } + const required = !trigger[element.tag] && element.required ? 'required' : '' + const tagDom = tags[element.tag] ? tags[element.tag](element) : null + let str = ` + ${tagDom} + ` + str = colWrapper(element, str) + return str + }, + rowFormItem(element) { + const type = element.type === 'default' ? '' : `type="${element.type}"` + const justify = element.type === 'default' ? '' : `justify="${element.justify}"` + const align = element.type === 'default' ? '' : `align="${element.align}"` + const gutter = element.gutter ? `gutter="${element.gutter}"` : '' + const children = element.children.map(el => layouts[el.layout](el)) + let str = ` + ${children.join('\n')} + ` + str = colWrapper(element, str) + return str + } +} + +const tags = { + 'el-button': el => { + const { + tag, disabled + } = attrBuilder(el) + const type = el.type ? `type="${el.type}"` : '' + const icon = el.icon ? `icon="${el.icon}"` : '' + const size = el.size ? `size="${el.size}"` : '' + let child = buildElButtonChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}` + }, + 'el-input': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : '' + const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : '' + const readonly = el.readonly ? 'readonly' : '' + const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : '' + const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : '' + const showPassword = el['show-password'] ? 'show-password' : '' + const type = el.type ? `type="${el.type}"` : '' + const autosize = el.autosize && el.autosize.minRows + ? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"` + : '' + let child = buildElInputChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}` + }, + 'el-input-number': el => { + const { disabled, vModel, placeholder } = attrBuilder(el) + const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : '' + const min = el.min ? `:min='${el.min}'` : '' + const max = el.max ? `:max='${el.max}'` : '' + const step = el.step ? `:step='${el.step}'` : '' + const stepStrictly = el['step-strictly'] ? 'step-strictly' : '' + const precision = el.precision ? `:precision='${el.precision}'` : '' + + return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}>` + }, + 'el-select': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const filterable = el.filterable ? 'filterable' : '' + const multiple = el.multiple ? 'multiple' : '' + let child = buildElSelectChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}` + }, + 'el-radio-group': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + let child = buildElRadioGroupChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${size} ${disabled}>${child}` + }, + 'el-checkbox-group': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + const min = el.min ? `:min="${el.min}"` : '' + const max = el.max ? `:max="${el.max}"` : '' + let child = buildElCheckboxGroupChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}` + }, + 'el-switch': el => { + const { disabled, vModel } = attrBuilder(el) + const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : '' + const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : '' + const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : '' + const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : '' + const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : '' + const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : '' + + return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}>` + }, + 'el-cascader': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const options = el.options ? `:options="${el.vModel}Options"` : '' + const props = el.props ? `:props="${el.vModel}Props"` : '' + const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"' + const filterable = el.filterable ? 'filterable' : '' + const separator = el.separator === '/' ? '' : `separator="${el.separator}"` + + return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}>` + }, + 'el-slider': el => { + const { disabled, vModel } = attrBuilder(el) + const min = el.min ? `:min='${el.min}'` : '' + const max = el.max ? `:max='${el.max}'` : '' + const step = el.step ? `:step='${el.step}'` : '' + const range = el.range ? 'range' : '' + const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : '' + + return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}>` + }, + 'el-time-picker': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' + const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' + const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' + const isRange = el['is-range'] ? 'is-range' : '' + const format = el.format ? `format="${el.format}"` : '' + const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' + const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : '' + + return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}>` + }, + 'el-date-picker': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' + const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' + const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' + const format = el.format ? `format="${el.format}"` : '' + const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' + const type = el.type === 'date' ? '' : `type="${el.type}"` + const readonly = el.readonly ? 'readonly' : '' + + return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}>` + }, + 'el-rate': el => { + const { disabled, vModel } = attrBuilder(el) + const max = el.max ? `:max='${el.max}'` : '' + const allowHalf = el['allow-half'] ? 'allow-half' : '' + const showText = el['show-text'] ? 'show-text' : '' + const showScore = el['show-score'] ? 'show-score' : '' + + return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}>` + }, + 'el-color-picker': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + const showAlpha = el['show-alpha'] ? 'show-alpha' : '' + const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : '' + + return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}>` + }, + 'el-upload': el => { + const disabled = el.disabled ? ':disabled=\'true\'' : '' + const action = el.action ? `:action="${el.vModel}Action"` : '' + const multiple = el.multiple ? 'multiple' : '' + const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : '' + const accept = el.accept ? `accept="${el.accept}"` : '' + const name = el.name !== 'file' ? `name="${el.name}"` : '' + const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : '' + const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"` + const fileList = `:file-list="${el.vModel}fileList"` + const ref = `ref="${el.vModel}"` + let child = buildElUploadChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}` + } +} + +function attrBuilder(el) { + return { + vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`, + clearable: el.clearable ? 'clearable' : '', + placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '', + width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '', + disabled: el.disabled ? ':disabled=\'true\'' : '' + } +} + +// el-buttin 子级 +function buildElButtonChild(conf) { + const children = [] + if (conf.default) { + children.push(conf.default) + } + return children.join('\n') +} + +// el-input innerHTML +function buildElInputChild(conf) { + const children = [] + if (conf.prepend) { + children.push(``) + } + if (conf.append) { + children.push(``) + } + return children.join('\n') +} + +function buildElSelectChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + children.push(``) + } + return children.join('\n') +} + +function buildElRadioGroupChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio' + const border = conf.border ? 'border' : '' + children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) + } + return children.join('\n') +} + +function buildElCheckboxGroupChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox' + const border = conf.border ? 'border' : '' + children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) + } + return children.join('\n') +} + +function buildElUploadChild(conf) { + const list = [] + if (conf['list-type'] === 'picture-card') list.push('') + else list.push(`${conf.buttonText}`) + if (conf.showTip) list.push(`
只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件
`) + return list.join('\n') +} + +export function makeUpHtml(conf, type) { + const htmlList = [] + confGlobal = conf + someSpanIsNot24 = conf.fields.some(item => item.span !== 24) + conf.fields.forEach(el => { + htmlList.push(layouts[el.layout](el)) + }) + const htmlStr = htmlList.join('\n') + + let temp = buildFormTemplate(conf, htmlStr, type) + if (type === 'dialog') { + temp = dialogWrapper(temp) + } + confGlobal = null + return temp +} diff --git a/ruoyi-ui/src/utils/generator/icon.json b/ruoyi-ui/src/utils/generator/icon.json new file mode 100644 index 0000000..2d9999a --- /dev/null +++ b/ruoyi-ui/src/utils/generator/icon.json @@ -0,0 +1 @@ +["platform-eleme","eleme","delete-solid","delete","s-tools","setting","user-solid","user","phone","phone-outline","more","more-outline","star-on","star-off","s-goods","goods","warning","warning-outline","question","info","remove","circle-plus","success","error","zoom-in","zoom-out","remove-outline","circle-plus-outline","circle-check","circle-close","s-help","help","minus","plus","check","close","picture","picture-outline","picture-outline-round","upload","upload2","download","camera-solid","camera","video-camera-solid","video-camera","message-solid","bell","s-cooperation","s-order","s-platform","s-fold","s-unfold","s-operation","s-promotion","s-home","s-release","s-ticket","s-management","s-open","s-shop","s-marketing","s-flag","s-comment","s-finance","s-claim","s-custom","s-opportunity","s-data","s-check","s-grid","menu","share","d-caret","caret-left","caret-right","caret-bottom","caret-top","bottom-left","bottom-right","back","right","bottom","top","top-left","top-right","arrow-left","arrow-right","arrow-down","arrow-up","d-arrow-left","d-arrow-right","video-pause","video-play","refresh","refresh-right","refresh-left","finished","sort","sort-up","sort-down","rank","loading","view","c-scale-to-original","date","edit","edit-outline","folder","folder-opened","folder-add","folder-remove","folder-delete","folder-checked","tickets","document-remove","document-delete","document-copy","document-checked","document","document-add","printer","paperclip","takeaway-box","search","monitor","attract","mobile","scissors","umbrella","headset","brush","mouse","coordinate","magic-stick","reading","data-line","data-board","pie-chart","data-analysis","collection-tag","film","suitcase","suitcase-1","receiving","collection","files","notebook-1","notebook-2","toilet-paper","office-building","school","table-lamp","house","no-smoking","smoking","shopping-cart-full","shopping-cart-1","shopping-cart-2","shopping-bag-1","shopping-bag-2","sold-out","sell","present","box","bank-card","money","coin","wallet","discount","price-tag","news","guide","male","female","thumb","cpu","link","connection","open","turn-off","set-up","chat-round","chat-line-round","chat-square","chat-dot-round","chat-dot-square","chat-line-square","message","postcard","position","turn-off-microphone","microphone","close-notification","bangzhu","time","odometer","crop","aim","switch-button","full-screen","copy-document","mic","stopwatch","medal-1","medal","trophy","trophy-1","first-aid-kit","discover","place","location","location-outline","location-information","add-location","delete-location","map-location","alarm-clock","timer","watch-1","watch","lock","unlock","key","service","mobile-phone","bicycle","truck","ship","basketball","football","soccer","baseball","wind-power","light-rain","lightning","heavy-rain","sunrise","sunrise-1","sunset","sunny","cloudy","partly-cloudy","cloudy-and-sunny","moon","moon-night","dish","dish-1","food","chicken","fork-spoon","knife-fork","burger","tableware","sugar","dessert","ice-cream","hot-water","water-cup","coffee-cup","cold-drink","goblet","goblet-full","goblet-square","goblet-square-full","refrigerator","grape","watermelon","cherry","apple","pear","orange","coffee","ice-tea","ice-drink","milk-tea","potato-strips","lollipop","ice-cream-square","ice-cream-round"] \ No newline at end of file diff --git a/ruoyi-ui/src/utils/generator/js.js b/ruoyi-ui/src/utils/generator/js.js new file mode 100644 index 0000000..ee8668d --- /dev/null +++ b/ruoyi-ui/src/utils/generator/js.js @@ -0,0 +1,235 @@ +import { exportDefault, titleCase } from '@/utils/index' +import { trigger } from './config' + +const units = { + KB: '1024', + MB: '1024 / 1024', + GB: '1024 / 1024 / 1024' +} +let confGlobal +const inheritAttrs = { + file: '', + dialog: 'inheritAttrs: false,' +} + + +export function makeUpJs(conf, type) { + confGlobal = conf = JSON.parse(JSON.stringify(conf)) + const dataList = [] + const ruleList = [] + const optionsList = [] + const propsList = [] + const methodList = mixinMethod(type) + const uploadVarList = [] + + conf.fields.forEach(el => { + buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) + }) + + const script = buildexport( + conf, + type, + dataList.join('\n'), + ruleList.join('\n'), + optionsList.join('\n'), + uploadVarList.join('\n'), + propsList.join('\n'), + methodList.join('\n') + ) + confGlobal = null + return script +} + +function buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) { + buildData(el, dataList) + buildRules(el, ruleList) + + if (el.options && el.options.length) { + buildOptions(el, optionsList) + if (el.dataType === 'dynamic') { + const model = `${el.vModel}Options` + const options = titleCase(model) + buildOptionMethod(`get${options}`, model, methodList) + } + } + + if (el.props && el.props.props) { + buildProps(el, propsList) + } + + if (el.action && el.tag === 'el-upload') { + uploadVarList.push( + `${el.vModel}Action: '${el.action}', + ${el.vModel}fileList: [],` + ) + methodList.push(buildBeforeUpload(el)) + if (!el['auto-upload']) { + methodList.push(buildSubmitUpload(el)) + } + } + + if (el.children) { + el.children.forEach(el2 => { + buildAttributes(el2, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) + }) + } +} + +function mixinMethod(type) { + const list = []; const + minxins = { + file: confGlobal.formBtns ? { + submitForm: `submitForm() { + this.$refs['${confGlobal.formRef}'].validate(valid => { + if(!valid) return + // TODO 提交表单 + }) + },`, + resetForm: `resetForm() { + this.$refs['${confGlobal.formRef}'].resetFields() + },` + } : null, + dialog: { + onOpen: 'onOpen() {},', + onClose: `onClose() { + this.$refs['${confGlobal.formRef}'].resetFields() + },`, + close: `close() { + this.$emit('update:visible', false) + },`, + handleConfirm: `handleConfirm() { + this.$refs['${confGlobal.formRef}'].validate(valid => { + if(!valid) return + this.close() + }) + },` + } + } + + const methods = minxins[type] + if (methods) { + Object.keys(methods).forEach(key => { + list.push(methods[key]) + }) + } + + return list +} + +function buildData(conf, dataList) { + if (conf.vModel === undefined) return + let defaultValue + if (typeof (conf.defaultValue) === 'string' && !conf.multiple) { + defaultValue = `'${conf.defaultValue}'` + } else { + defaultValue = `${JSON.stringify(conf.defaultValue)}` + } + dataList.push(`${conf.vModel}: ${defaultValue},`) +} + +function buildRules(conf, ruleList) { + if (conf.vModel === undefined) return + const rules = [] + if (trigger[conf.tag]) { + if (conf.required) { + const type = Array.isArray(conf.defaultValue) ? 'type: \'array\',' : '' + let message = Array.isArray(conf.defaultValue) ? `请至少选择一个${conf.vModel}` : conf.placeholder + if (message === undefined) message = `${conf.label}不能为空` + rules.push(`{ required: true, ${type} message: '${message}', trigger: '${trigger[conf.tag]}' }`) + } + if (conf.regList && Array.isArray(conf.regList)) { + conf.regList.forEach(item => { + if (item.pattern) { + rules.push(`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${trigger[conf.tag]}' }`) + } + }) + } + ruleList.push(`${conf.vModel}: [${rules.join(',')}],`) + } +} + +function buildOptions(conf, optionsList) { + if (conf.vModel === undefined) return + if (conf.dataType === 'dynamic') { conf.options = [] } + const str = `${conf.vModel}Options: ${JSON.stringify(conf.options)},` + optionsList.push(str) +} + +function buildProps(conf, propsList) { + if (conf.dataType === 'dynamic') { + conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey) + conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey) + conf.childrenKey !== 'children' && (conf.props.props.children = conf.childrenKey) + } + const str = `${conf.vModel}Props: ${JSON.stringify(conf.props.props)},` + propsList.push(str) +} + +function buildBeforeUpload(conf) { + const unitNum = units[conf.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const + returnList = [] + if (conf.fileSize) { + rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize} + if(!isRightSize){ + this.$message.error('文件大小超过 ${conf.fileSize}${conf.sizeUnit}') + }` + returnList.push('isRightSize') + } + if (conf.accept) { + acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type) + if(!isAccept){ + this.$message.error('应该选择${conf.accept}类型的文件') + }` + returnList.push('isAccept') + } + const str = `${conf.vModel}BeforeUpload(file) { + ${rightSizeCode} + ${acceptCode} + return ${returnList.join('&&')} + },` + return returnList.length ? str : '' +} + +function buildSubmitUpload(conf) { + const str = `submitUpload() { + this.$refs['${conf.vModel}'].submit() + },` + return str +} + +function buildOptionMethod(methodName, model, methodList) { + const str = `${methodName}() { + // TODO 发起请求获取数据 + this.${model} + },` + methodList.push(str) +} + +function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) { + const str = `${exportDefault}{ + ${inheritAttrs[type]} + components: {}, + props: [], + data () { + return { + ${conf.formModel}: { + ${data} + }, + ${conf.formRules}: { + ${rules} + }, + ${uploadVar} + ${selectOptions} + ${props} + } + }, + computed: {}, + watch: {}, + created () {}, + mounted () {}, + methods: { + ${methods} + } +}` + return str +} diff --git a/ruoyi-ui/src/utils/generator/render.js b/ruoyi-ui/src/utils/generator/render.js new file mode 100644 index 0000000..e8640f0 --- /dev/null +++ b/ruoyi-ui/src/utils/generator/render.js @@ -0,0 +1,126 @@ +import { makeMap } from '@/utils/index' + +// 参考https://github.com/vuejs/vue/blob/v2.6.10/src/platforms/web/server/util.js +const isAttr = makeMap( + 'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,' + + 'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,' + + 'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,' + + 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' + + 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,' + + 'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,' + + 'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,' + + 'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,' + + 'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,' + + 'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,' + + 'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,' + + 'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,' + + 'target,title,type,usemap,value,width,wrap' +) + +function vModel(self, dataObject, defaultValue) { + dataObject.props.value = defaultValue + + dataObject.on.input = val => { + self.$emit('input', val) + } +} + +const componentChild = { + 'el-button': { + default(h, conf, key) { + return conf[key] + }, + }, + 'el-input': { + prepend(h, conf, key) { + return + }, + append(h, conf, key) { + return + } + }, + 'el-select': { + options(h, conf, key) { + const list = [] + conf.options.forEach(item => { + list.push() + }) + return list + } + }, + 'el-radio-group': { + options(h, conf, key) { + const list = [] + conf.options.forEach(item => { + if (conf.optionType === 'button') list.push({item.label}) + else list.push({item.label}) + }) + return list + } + }, + 'el-checkbox-group': { + options(h, conf, key) { + const list = [] + conf.options.forEach(item => { + if (conf.optionType === 'button') { + list.push({item.label}) + } else { + list.push({item.label}) + } + }) + return list + } + }, + 'el-upload': { + 'list-type': (h, conf, key) => { + const list = [] + if (conf['list-type'] === 'picture-card') { + list.push() + } else { + list.push({conf.buttonText}) + } + if (conf.showTip) { + list.push(
只能上传不超过 {conf.fileSize}{conf.sizeUnit} 的{conf.accept}文件
) + } + return list + } + } +} + +export default { + render(h) { + const dataObject = { + attrs: {}, + props: {}, + on: {}, + style: {} + } + const confClone = JSON.parse(JSON.stringify(this.conf)) + const children = [] + + const childObjs = componentChild[confClone.tag] + if (childObjs) { + Object.keys(childObjs).forEach(key => { + const childFunc = childObjs[key] + if (confClone[key]) { + children.push(childFunc(h, confClone, key)) + } + }) + } + + Object.keys(confClone).forEach(key => { + const val = confClone[key] + if (key === 'vModel') { + vModel(this, dataObject, confClone.defaultValue) + } else if (dataObject[key]) { + dataObject[key] = val + } else if (!isAttr(key)) { + dataObject.props[key] = val + } else { + dataObject.attrs[key] = val + } + }) + return h(this.conf.tag, dataObject, children) + }, + props: ['conf'] +} diff --git a/ruoyi-ui/src/utils/index.js b/ruoyi-ui/src/utils/index.js new file mode 100644 index 0000000..4e65504 --- /dev/null +++ b/ruoyi-ui/src/utils/index.js @@ -0,0 +1,390 @@ +import { parseTime } from './ruoyi' + +/** + * 表格时间格式化 + */ +export function formatDate(cellValue) { + if (cellValue == null || cellValue == "") return ""; + var date = new Date(cellValue) + var year = date.getFullYear() + var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 + var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() + var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() + var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() + var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() + return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds +} + +/** + * @param {number} time + * @param {string} option + * @returns {string} + */ +export function formatTime(time, option) { + if (('' + time).length === 10) { + time = parseInt(time) * 1000 + } else { + time = +time + } + const d = new Date(time) + const now = Date.now() + + const diff = (now - d) / 1000 + + if (diff < 30) { + return '刚刚' + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前' + } else if (diff < 3600 * 24 * 2) { + return '1天前' + } + if (option) { + return parseTime(time, option) + } else { + return ( + d.getMonth() + + 1 + + '月' + + d.getDate() + + '日' + + d.getHours() + + '时' + + d.getMinutes() + + '分' + ) + } +} + +/** + * @param {string} url + * @returns {Object} + */ +export function getQueryObject(url) { + url = url == null ? window.location.href : url + const search = url.substring(url.lastIndexOf('?') + 1) + const obj = {} + const reg = /([^?&=]+)=([^?&=]*)/g + search.replace(reg, (rs, $1, $2) => { + const name = decodeURIComponent($1) + let val = decodeURIComponent($2) + val = String(val) + obj[name] = val + return rs + }) + return obj +} + +/** + * @param {string} input value + * @returns {number} output value + */ +export function byteLength(str) { + // returns the byte length of an utf8 string + let s = str.length + for (var i = str.length - 1; i >= 0; i--) { + const code = str.charCodeAt(i) + if (code > 0x7f && code <= 0x7ff) s++ + else if (code > 0x7ff && code <= 0xffff) s += 2 + if (code >= 0xDC00 && code <= 0xDFFF) i-- + } + return s +} + +/** + * @param {Array} actual + * @returns {Array} + */ +export function cleanArray(actual) { + const newArray = [] + for (let i = 0; i < actual.length; i++) { + if (actual[i]) { + newArray.push(actual[i]) + } + } + return newArray +} + +/** + * @param {Object} json + * @returns {Array} + */ +export function param(json) { + if (!json) return '' + return cleanArray( + Object.keys(json).map(key => { + if (json[key] === undefined) return '' + return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) + }) + ).join('&') +} + +/** + * @param {string} url + * @returns {Object} + */ +export function param2Obj(url) { + const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') + if (!search) { + return {} + } + const obj = {} + const searchArr = search.split('&') + searchArr.forEach(v => { + const index = v.indexOf('=') + if (index !== -1) { + const name = v.substring(0, index) + const val = v.substring(index + 1, v.length) + obj[name] = val + } + }) + return obj +} + +/** + * @param {string} val + * @returns {string} + */ +export function html2Text(val) { + const div = document.createElement('div') + div.innerHTML = val + return div.textContent || div.innerText +} + +/** + * Merges two objects, giving the last one precedence + * @param {Object} target + * @param {(Object|Array)} source + * @returns {Object} + */ +export function objectMerge(target, source) { + if (typeof target !== 'object') { + target = {} + } + if (Array.isArray(source)) { + return source.slice() + } + Object.keys(source).forEach(property => { + const sourceProperty = source[property] + if (typeof sourceProperty === 'object') { + target[property] = objectMerge(target[property], sourceProperty) + } else { + target[property] = sourceProperty + } + }) + return target +} + +/** + * @param {HTMLElement} element + * @param {string} className + */ +export function toggleClass(element, className) { + if (!element || !className) { + return + } + let classString = element.className + const nameIndex = classString.indexOf(className) + if (nameIndex === -1) { + classString += '' + className + } else { + classString = + classString.substr(0, nameIndex) + + classString.substr(nameIndex + className.length) + } + element.className = classString +} + +/** + * @param {string} type + * @returns {Date} + */ +export function getTime(type) { + if (type === 'start') { + return new Date().getTime() - 3600 * 1000 * 24 * 90 + } else { + return new Date(new Date().toDateString()) + } +} + +/** + * @param {Function} func + * @param {number} wait + * @param {boolean} immediate + * @return {*} + */ +export function debounce(func, wait, immediate) { + let timeout, args, context, timestamp, result + + const later = function() { + // 据上一次触发时间间隔 + const last = +new Date() - timestamp + + // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last) + } else { + timeout = null + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 + if (!immediate) { + result = func.apply(context, args) + if (!timeout) context = args = null + } + } + } + + return function(...args) { + context = this + timestamp = +new Date() + const callNow = immediate && !timeout + // 如果延时不存在,重新设定延时 + if (!timeout) timeout = setTimeout(later, wait) + if (callNow) { + result = func.apply(context, args) + context = args = null + } + + return result + } +} + +/** + * This is just a simple version of deep copy + * Has a lot of edge cases bug + * If you want to use a perfect deep copy, use lodash's _.cloneDeep + * @param {Object} source + * @returns {Object} + */ +export function deepClone(source) { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'deepClone') + } + const targetObj = source.constructor === Array ? [] : {} + Object.keys(source).forEach(keys => { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = deepClone(source[keys]) + } else { + targetObj[keys] = source[keys] + } + }) + return targetObj +} + +/** + * @param {Array} arr + * @returns {Array} + */ +export function uniqueArr(arr) { + return Array.from(new Set(arr)) +} + +/** + * @returns {string} + */ +export function createUniqueString() { + const timestamp = +new Date() + '' + const randomNum = parseInt((1 + Math.random()) * 65536) + '' + return (+(randomNum + timestamp)).toString(32) +} + +/** + * Check if an element has a class + * @param {HTMLElement} elm + * @param {string} cls + * @returns {boolean} + */ +export function hasClass(ele, cls) { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) +} + +/** + * Add class to element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function addClass(ele, cls) { + if (!hasClass(ele, cls)) ele.className += ' ' + cls +} + +/** + * Remove class from element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function removeClass(ele, cls) { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') + ele.className = ele.className.replace(reg, ' ') + } +} + +export function makeMap(str, expectsLowerCase) { + const map = Object.create(null) + const list = str.split(',') + for (let i = 0; i < list.length; i++) { + map[list[i]] = true + } + return expectsLowerCase + ? val => map[val.toLowerCase()] + : val => map[val] +} + +export const exportDefault = 'export default ' + +export const beautifierConf = { + html: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'separate', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: false, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + }, + js: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'normal', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: true, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + } +} + +// 首字母大小 +export function titleCase(str) { + return str.replace(/( |^)[a-z]/g, L => L.toUpperCase()) +} + +// 下划转驼峰 +export function camelCase(str) { + return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase()) +} + +export function isNumberStr(str) { + return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) +} + diff --git a/ruoyi-ui/src/utils/jsencrypt.js b/ruoyi-ui/src/utils/jsencrypt.js new file mode 100644 index 0000000..78d9523 --- /dev/null +++ b/ruoyi-ui/src/utils/jsencrypt.js @@ -0,0 +1,30 @@ +import JSEncrypt from 'jsencrypt/bin/jsencrypt.min' + +// 密钥对生成 http://web.chacuo.net/netrsakeypair + +const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' + +const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' + + '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' + + 'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' + + 'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' + + 'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' + + 'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' + + 'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' + + 'UP8iWi1Qw0Y=' + +// 加密 +export function encrypt(txt) { + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) // 设置公钥 + return encryptor.encrypt(txt) // 对数据进行加密 +} + +// 解密 +export function decrypt(txt) { + const encryptor = new JSEncrypt() + encryptor.setPrivateKey(privateKey) // 设置私钥 + return encryptor.decrypt(txt) // 对数据进行解密 +} + diff --git a/ruoyi-ui/src/utils/permission.js b/ruoyi-ui/src/utils/permission.js new file mode 100644 index 0000000..1730e33 --- /dev/null +++ b/ruoyi-ui/src/utils/permission.js @@ -0,0 +1,51 @@ +import store from '@/store' + +/** + * 字符权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export function checkPermi(value) { + if (value && value instanceof Array && value.length > 0) { + const permissions = store.getters && store.getters.permissions + const permissionDatas = value + const all_permission = "*:*:*"; + + const hasPermission = permissions.some(permission => { + return all_permission === permission || permissionDatas.includes(permission) + }) + + if (!hasPermission) { + return false + } + return true + } else { + console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`) + return false + } +} + +/** + * 角色权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export function checkRole(value) { + if (value && value instanceof Array && value.length > 0) { + const roles = store.getters && store.getters.roles + const permissionRoles = value + const super_admin = "admin"; + + const hasRole = roles.some(role => { + return super_admin === role || permissionRoles.includes(role) + }) + + if (!hasRole) { + return false + } + return true + } else { + console.error(`need roles! Like checkRole="['admin','editor']"`) + return false + } +} \ No newline at end of file diff --git a/ruoyi-ui/src/utils/request.js b/ruoyi-ui/src/utils/request.js new file mode 100644 index 0000000..5ff3d4e --- /dev/null +++ b/ruoyi-ui/src/utils/request.js @@ -0,0 +1,152 @@ +import axios from 'axios' +import { Notification, MessageBox, Message, Loading } from 'element-ui' +import store from '@/store' +import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' +import { tansParams, blobValidate } from "@/utils/ruoyi"; +import cache from '@/plugins/cache' +import { saveAs } from 'file-saver' + +let downloadLoadingInstance; +// 是否显示重新登录 +export let isRelogin = { show: false }; + +axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' +// 创建axios实例 +const service = axios.create({ + // axios中请求配置有baseURL选项,表示请求URL公共部分 + baseURL: process.env.VUE_APP_BASE_API, + // 超时 + timeout: 10000 +}) + +// request拦截器 +service.interceptors.request.use(config => { + // 是否需要设置 token + const isToken = (config.headers || {}).isToken === false + // 是否需要防止数据重复提交 + const isRepeatSubmit = (config.headers || {}).repeatSubmit === false + if (getToken() && !isToken) { + config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 + } + // get请求映射params参数 + if (config.method === 'get' && config.params) { + let url = config.url + '?' + tansParams(config.params); + url = url.slice(0, -1); + config.params = {}; + config.url = url; + } + if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { + const requestObj = { + url: config.url, + data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, + time: new Date().getTime() + } + const requestSize = Object.keys(JSON.stringify(requestObj)).length; // 请求数据大小 + const limitSize = 5 * 1024 * 1024; // 限制存放数据5M + if (requestSize >= limitSize) { + console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。') + return config; + } + const sessionObj = cache.session.getJSON('sessionObj') + if (sessionObj === undefined || sessionObj === null || sessionObj === '') { + cache.session.setJSON('sessionObj', requestObj) + } else { + const s_url = sessionObj.url; // 请求地址 + const s_data = sessionObj.data; // 请求数据 + const s_time = sessionObj.time; // 请求时间 + const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交 + if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { + const message = '数据正在处理,请勿重复提交'; + console.warn(`[${s_url}]: ` + message) + return Promise.reject(new Error(message)) + } else { + cache.session.setJSON('sessionObj', requestObj) + } + } + } + return config +}, error => { + console.log(error) + Promise.reject(error) +}) + +// 响应拦截器 +service.interceptors.response.use(res => { + // 未设置状态码则默认成功状态 + const code = res.data.code || 200; + // 获取错误信息 + const msg = errorCode[code] || res.data.msg || errorCode['default'] + // 二进制数据则直接返回 + if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { + return res.data + } + if (code === 401) { + if (!isRelogin.show) { + isRelogin.show = true; + MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { + isRelogin.show = false; + store.dispatch('LogOut').then(() => { + location.href = '/index'; + }) + }).catch(() => { + isRelogin.show = false; + }); + } + return Promise.reject('无效的会话,或者会话已过期,请重新登录。') + } else if (code === 500) { + Message({ message: msg, type: 'error' }) + return Promise.reject(new Error(msg)) + } else if (code === 601) { + Message({ message: msg, type: 'warning' }) + return Promise.reject('error') + } else if (code !== 200) { + Notification.error({ title: msg }) + return Promise.reject('error') + } else { + return res.data + } + }, + error => { + console.log('err' + error) + let { message } = error; + if (message == "Network Error") { + message = "后端接口连接异常"; + } else if (message.includes("timeout")) { + message = "系统接口请求超时"; + } else if (message.includes("Request failed with status code")) { + message = "系统接口" + message.substr(message.length - 3) + "异常"; + } + Message({ message: message, type: 'error', duration: 5 * 1000 }) + return Promise.reject(error) + } +) + +// 通用下载方法 +export function download(url, params, filename, config) { + downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) + return service.post(url, params, { + transformRequest: [(params) => { return tansParams(params) }], + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + responseType: 'blob', + ...config + }).then(async (data) => { + const isBlob = blobValidate(data); + if (isBlob) { + const blob = new Blob([data]) + saveAs(blob, filename) + } else { + const resText = await data.text(); + const rspObj = JSON.parse(resText); + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + Message.error(errMsg); + } + downloadLoadingInstance.close(); + }).catch((r) => { + console.error(r) + Message.error('下载文件出现错误,请联系管理员!') + downloadLoadingInstance.close(); + }) +} + +export default service diff --git a/ruoyi-ui/src/utils/ruoyi.js b/ruoyi-ui/src/utils/ruoyi.js new file mode 100644 index 0000000..44bf9c4 --- /dev/null +++ b/ruoyi-ui/src/utils/ruoyi.js @@ -0,0 +1,233 @@ + + +/** + * 通用js方法封装处理 + * Copyright (c) 2019 ruoyi + */ + +// 日期格式化 +export function parseTime(time, pattern) { + if (arguments.length === 0 || !time) { + return null + } + const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'object') { + date = time + } else { + if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { + time = parseInt(time) + } else if (typeof time === 'string') { + time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), ''); + } + if ((typeof time === 'number') && (time.toString().length === 10)) { + time = time * 1000 + } + date = new Date(time) + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + } + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { + let value = formatObj[key] + // Note: getDay() returns 0 on Sunday + if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] } + if (result.length > 0 && value < 10) { + value = '0' + value + } + return value || 0 + }) + return time_str +} + +// 表单重置 +export function resetForm(refName) { + if (this.$refs[refName]) { + this.$refs[refName].resetFields(); + } +} + +// 添加日期范围 +export function addDateRange(params, dateRange, propName) { + let search = params; + search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {}; + dateRange = Array.isArray(dateRange) ? dateRange : []; + if (typeof (propName) === 'undefined') { + search.params['beginTime'] = dateRange[0]; + search.params['endTime'] = dateRange[1]; + } else { + search.params['begin' + propName] = dateRange[0]; + search.params['end' + propName] = dateRange[1]; + } + return search; +} + +// 回显数据字典 +export function selectDictLabel(datas, value) { + if (value === undefined) { + return ""; + } + var actions = []; + Object.keys(datas).some((key) => { + if (datas[key].value == ('' + value)) { + actions.push(datas[key].label); + return true; + } + }) + if (actions.length === 0) { + actions.push(value); + } + return actions.join(''); +} + +// 回显数据字典(字符串、数组) +export function selectDictLabels(datas, value, separator) { + if (value === undefined || value.length ===0) { + return ""; + } + if (Array.isArray(value)) { + value = value.join(","); + } + var actions = []; + var currentSeparator = undefined === separator ? "," : separator; + var temp = value.split(currentSeparator); + Object.keys(value.split(currentSeparator)).some((val) => { + var match = false; + Object.keys(datas).some((key) => { + if (datas[key].value == ('' + temp[val])) { + actions.push(datas[key].label + currentSeparator); + match = true; + } + }) + if (!match) { + actions.push(temp[val] + currentSeparator); + } + }) + return actions.join('').substring(0, actions.join('').length - 1); +} + +// 字符串格式化(%s ) +export function sprintf(str) { + var args = arguments, flag = true, i = 1; + str = str.replace(/%s/g, function () { + var arg = args[i++]; + if (typeof arg === 'undefined') { + flag = false; + return ''; + } + return arg; + }); + return flag ? str : ''; +} + +// 转换字符串,undefined,null等转化为"" +export function parseStrEmpty(str) { + if (!str || str == "undefined" || str == "null") { + return ""; + } + return str; +} + +// 数据合并 +export function mergeRecursive(source, target) { + for (var p in target) { + try { + if (target[p].constructor == Object) { + source[p] = mergeRecursive(source[p], target[p]); + } else { + source[p] = target[p]; + } + } catch (e) { + source[p] = target[p]; + } + } + return source; +}; + +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + */ +export function handleTree(data, id, parentId, children) { + let config = { + id: id || 'id', + parentId: parentId || 'parentId', + childrenList: children || 'children' + }; + + var childrenListMap = {}; + var nodeIds = {}; + var tree = []; + + for (let d of data) { + let parentId = d[config.parentId]; + if (childrenListMap[parentId] == null) { + childrenListMap[parentId] = []; + } + nodeIds[d[config.id]] = d; + childrenListMap[parentId].push(d); + } + + for (let d of data) { + let parentId = d[config.parentId]; + if (nodeIds[parentId] == null) { + tree.push(d); + } + } + + for (let t of tree) { + adaptToChildrenList(t); + } + + function adaptToChildrenList(o) { + if (childrenListMap[o[config.id]] !== null) { + o[config.childrenList] = childrenListMap[o[config.id]]; + } + if (o[config.childrenList]) { + for (let c of o[config.childrenList]) { + adaptToChildrenList(c); + } + } + } + return tree; +} + +/** +* 参数处理 +* @param {*} params 参数 +*/ +export function tansParams(params) { + let result = '' + for (const propName of Object.keys(params)) { + const value = params[propName]; + var part = encodeURIComponent(propName) + "="; + if (value !== null && value !== "" && typeof (value) !== "undefined") { + if (typeof value === 'object') { + for (const key of Object.keys(value)) { + if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') { + let params = propName + '[' + key + ']'; + var subPart = encodeURIComponent(params) + "="; + result += subPart + encodeURIComponent(value[key]) + "&"; + } + } + } else { + result += part + encodeURIComponent(value) + "&"; + } + } + } + return result +} + +// 验证是否为blob格式 +export function blobValidate(data) { + return data.type !== 'application/json' +} diff --git a/ruoyi-ui/src/utils/scroll-to.js b/ruoyi-ui/src/utils/scroll-to.js new file mode 100644 index 0000000..c5d8e04 --- /dev/null +++ b/ruoyi-ui/src/utils/scroll-to.js @@ -0,0 +1,58 @@ +Math.easeInOutQuad = function(t, b, c, d) { + t /= d / 2 + if (t < 1) { + return c / 2 * t * t + b + } + t-- + return -c / 2 * (t * (t - 2) - 1) + b +} + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +var requestAnimFrame = (function() { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) } +})() + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param {number} amount + */ +function move(amount) { + document.documentElement.scrollTop = amount + document.body.parentNode.scrollTop = amount + document.body.scrollTop = amount +} + +function position() { + return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop +} + +/** + * @param {number} to + * @param {number} duration + * @param {Function} callback + */ +export function scrollTo(to, duration, callback) { + const start = position() + const change = to - start + const increment = 20 + let currentTime = 0 + duration = (typeof (duration) === 'undefined') ? 500 : duration + var animateScroll = function() { + // increment the time + currentTime += increment + // find the value with the quadratic in-out easing function + var val = Math.easeInOutQuad(currentTime, start, change, duration) + // move the document.body + move(val) + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll) + } else { + if (callback && typeof (callback) === 'function') { + // the animation is done so lets callback + callback() + } + } + } + animateScroll() +} diff --git a/ruoyi-ui/src/utils/validate.js b/ruoyi-ui/src/utils/validate.js new file mode 100644 index 0000000..adfa254 --- /dev/null +++ b/ruoyi-ui/src/utils/validate.js @@ -0,0 +1,83 @@ +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUsername(str) { + const valid_map = ['admin', 'editor'] + return valid_map.indexOf(str.trim()) >= 0 +} + +/** + * @param {string} url + * @returns {Boolean} + */ +export function validURL(url) { + const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ + return reg.test(url) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validLowerCase(str) { + const reg = /^[a-z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUpperCase(str) { + const reg = /^[A-Z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validAlphabets(str) { + const reg = /^[A-Za-z]+$/ + return reg.test(str) +} + +/** + * @param {string} email + * @returns {Boolean} + */ +export function validEmail(email) { + const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return reg.test(email) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function isString(str) { + if (typeof str === 'string' || str instanceof String) { + return true + } + return false +} + +/** + * @param {Array} arg + * @returns {Boolean} + */ +export function isArray(arg) { + if (typeof Array.isArray === 'undefined') { + return Object.prototype.toString.call(arg) === '[object Array]' + } + return Array.isArray(arg) +} diff --git a/ruoyi-ui/src/views/components/icons/element-icons.js b/ruoyi-ui/src/views/components/icons/element-icons.js new file mode 100644 index 0000000..9ea4d63 --- /dev/null +++ b/ruoyi-ui/src/views/components/icons/element-icons.js @@ -0,0 +1,3 @@ +const elementIcons = ['platform-eleme', 'eleme', 'delete-solid', 'delete', 's-tools', 'setting', 'user-solid', 'user', 'phone', 'phone-outline', 'more', 'more-outline', 'star-on', 'star-off', 's-goods', 'goods', 'warning', 'warning-outline', 'question', 'info', 'remove', 'circle-plus', 'success', 'error', 'zoom-in', 'zoom-out', 'remove-outline', 'circle-plus-outline', 'circle-check', 'circle-close', 's-help', 'help', 'minus', 'plus', 'check', 'close', 'picture', 'picture-outline', 'picture-outline-round', 'upload', 'upload2', 'download', 'camera-solid', 'camera', 'video-camera-solid', 'video-camera', 'message-solid', 'bell', 's-cooperation', 's-order', 's-platform', 's-fold', 's-unfold', 's-operation', 's-promotion', 's-home', 's-release', 's-ticket', 's-management', 's-open', 's-shop', 's-marketing', 's-flag', 's-comment', 's-finance', 's-claim', 's-custom', 's-opportunity', 's-data', 's-check', 's-grid', 'menu', 'share', 'd-caret', 'caret-left', 'caret-right', 'caret-bottom', 'caret-top', 'bottom-left', 'bottom-right', 'back', 'right', 'bottom', 'top', 'top-left', 'top-right', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'd-arrow-left', 'd-arrow-right', 'video-pause', 'video-play', 'refresh', 'refresh-right', 'refresh-left', 'finished', 'sort', 'sort-up', 'sort-down', 'rank', 'loading', 'view', 'c-scale-to-original', 'date', 'edit', 'edit-outline', 'folder', 'folder-opened', 'folder-add', 'folder-remove', 'folder-delete', 'folder-checked', 'tickets', 'document-remove', 'document-delete', 'document-copy', 'document-checked', 'document', 'document-add', 'printer', 'paperclip', 'takeaway-box', 'search', 'monitor', 'attract', 'mobile', 'scissors', 'umbrella', 'headset', 'brush', 'mouse', 'coordinate', 'magic-stick', 'reading', 'data-line', 'data-board', 'pie-chart', 'data-analysis', 'collection-tag', 'film', 'suitcase', 'suitcase-1', 'receiving', 'collection', 'files', 'notebook-1', 'notebook-2', 'toilet-paper', 'office-building', 'school', 'table-lamp', 'house', 'no-smoking', 'smoking', 'shopping-cart-full', 'shopping-cart-1', 'shopping-cart-2', 'shopping-bag-1', 'shopping-bag-2', 'sold-out', 'sell', 'present', 'box', 'bank-card', 'money', 'coin', 'wallet', 'discount', 'price-tag', 'news', 'guide', 'male', 'female', 'thumb', 'cpu', 'link', 'connection', 'open', 'turn-off', 'set-up', 'chat-round', 'chat-line-round', 'chat-square', 'chat-dot-round', 'chat-dot-square', 'chat-line-square', 'message', 'postcard', 'position', 'turn-off-microphone', 'microphone', 'close-notification', 'bangzhu', 'time', 'odometer', 'crop', 'aim', 'switch-button', 'full-screen', 'copy-document', 'mic', 'stopwatch', 'medal-1', 'medal', 'trophy', 'trophy-1', 'first-aid-kit', 'discover', 'place', 'location', 'location-outline', 'location-information', 'add-location', 'delete-location', 'map-location', 'alarm-clock', 'timer', 'watch-1', 'watch', 'lock', 'unlock', 'key', 'service', 'mobile-phone', 'bicycle', 'truck', 'ship', 'basketball', 'football', 'soccer', 'baseball', 'wind-power', 'light-rain', 'lightning', 'heavy-rain', 'sunrise', 'sunrise-1', 'sunset', 'sunny', 'cloudy', 'partly-cloudy', 'cloudy-and-sunny', 'moon', 'moon-night', 'dish', 'dish-1', 'food', 'chicken', 'fork-spoon', 'knife-fork', 'burger', 'tableware', 'sugar', 'dessert', 'ice-cream', 'hot-water', 'water-cup', 'coffee-cup', 'cold-drink', 'goblet', 'goblet-full', 'goblet-square', 'goblet-square-full', 'refrigerator', 'grape', 'watermelon', 'cherry', 'apple', 'pear', 'orange', 'coffee', 'ice-tea', 'ice-drink', 'milk-tea', 'potato-strips', 'lollipop', 'ice-cream-square', 'ice-cream-round'] + +export default elementIcons diff --git a/ruoyi-ui/src/views/components/icons/index.vue b/ruoyi-ui/src/views/components/icons/index.vue new file mode 100644 index 0000000..d3c9a71 --- /dev/null +++ b/ruoyi-ui/src/views/components/icons/index.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/ruoyi-ui/src/views/components/icons/svg-icons.js b/ruoyi-ui/src/views/components/icons/svg-icons.js new file mode 100644 index 0000000..724cd8e --- /dev/null +++ b/ruoyi-ui/src/views/components/icons/svg-icons.js @@ -0,0 +1,10 @@ +const req = require.context('../../../assets/icons/svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys() + +const re = /\.\/(.*)\.svg/ + +const svgIcons = requireAll(req).map(i => { + return i.match(re)[1] +}) + +export default svgIcons diff --git a/ruoyi-ui/src/views/dashboard/BarChart.vue b/ruoyi-ui/src/views/dashboard/BarChart.vue new file mode 100644 index 0000000..cd33d2d --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/BarChart.vue @@ -0,0 +1,102 @@ + + + diff --git a/ruoyi-ui/src/views/dashboard/LineChart.vue b/ruoyi-ui/src/views/dashboard/LineChart.vue new file mode 100644 index 0000000..ddd1063 --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/LineChart.vue @@ -0,0 +1,135 @@ + + + diff --git a/ruoyi-ui/src/views/dashboard/PanelGroup.vue b/ruoyi-ui/src/views/dashboard/PanelGroup.vue new file mode 100644 index 0000000..1a1081f --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/PanelGroup.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/ruoyi-ui/src/views/dashboard/PieChart.vue b/ruoyi-ui/src/views/dashboard/PieChart.vue new file mode 100644 index 0000000..c360057 --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/PieChart.vue @@ -0,0 +1,79 @@ + + + diff --git a/ruoyi-ui/src/views/dashboard/RaddarChart.vue b/ruoyi-ui/src/views/dashboard/RaddarChart.vue new file mode 100644 index 0000000..b1790ca --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/RaddarChart.vue @@ -0,0 +1,116 @@ + + + diff --git a/ruoyi-ui/src/views/dashboard/mixins/resize.js b/ruoyi-ui/src/views/dashboard/mixins/resize.js new file mode 100644 index 0000000..b1e76e9 --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/mixins/resize.js @@ -0,0 +1,56 @@ +import { debounce } from '@/utils' + +export default { + data() { + return { + $_sidebarElm: null, + $_resizeHandler: null + } + }, + mounted() { + this.initListener() + }, + activated() { + if (!this.$_resizeHandler) { + // avoid duplication init + this.initListener() + } + + // when keep-alive chart activated, auto resize + this.resize() + }, + beforeDestroy() { + this.destroyListener() + }, + deactivated() { + this.destroyListener() + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_sidebarResizeHandler(e) { + if (e.propertyName === 'width') { + this.$_resizeHandler() + } + }, + initListener() { + this.$_resizeHandler = debounce(() => { + this.resize() + }, 100) + window.addEventListener('resize', this.$_resizeHandler) + + this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] + this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) + }, + destroyListener() { + window.removeEventListener('resize', this.$_resizeHandler) + this.$_resizeHandler = null + + this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) + }, + resize() { + const { chart } = this + chart && chart.resize() + } + } +} diff --git a/ruoyi-ui/src/views/error/401.vue b/ruoyi-ui/src/views/error/401.vue new file mode 100644 index 0000000..448b6ec --- /dev/null +++ b/ruoyi-ui/src/views/error/401.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/ruoyi-ui/src/views/error/404.vue b/ruoyi-ui/src/views/error/404.vue new file mode 100644 index 0000000..96f075c --- /dev/null +++ b/ruoyi-ui/src/views/error/404.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/ruoyi-ui/src/views/game/box/mock/index.vue b/ruoyi-ui/src/views/game/box/mock/index.vue new file mode 100644 index 0000000..7514f5e --- /dev/null +++ b/ruoyi-ui/src/views/game/box/mock/index.vue @@ -0,0 +1,152 @@ + + + diff --git a/ruoyi-ui/src/views/game/sugar/config/index.vue b/ruoyi-ui/src/views/game/sugar/config/index.vue new file mode 100644 index 0000000..257f7e5 --- /dev/null +++ b/ruoyi-ui/src/views/game/sugar/config/index.vue @@ -0,0 +1,95 @@ + + + diff --git a/ruoyi-ui/src/views/game/sugar/dailywin/index.vue b/ruoyi-ui/src/views/game/sugar/dailywin/index.vue new file mode 100644 index 0000000..a781900 --- /dev/null +++ b/ruoyi-ui/src/views/game/sugar/dailywin/index.vue @@ -0,0 +1,200 @@ + + + + diff --git a/ruoyi-ui/src/views/game/sugar/mock/index.vue b/ruoyi-ui/src/views/game/sugar/mock/index.vue new file mode 100644 index 0000000..a489208 --- /dev/null +++ b/ruoyi-ui/src/views/game/sugar/mock/index.vue @@ -0,0 +1,296 @@ + + + + diff --git a/ruoyi-ui/src/views/game/sugar/record/index.vue b/ruoyi-ui/src/views/game/sugar/record/index.vue new file mode 100644 index 0000000..9e6f105 --- /dev/null +++ b/ruoyi-ui/src/views/game/sugar/record/index.vue @@ -0,0 +1,343 @@ + + + + diff --git a/ruoyi-ui/src/views/game/sugar/user/index.vue b/ruoyi-ui/src/views/game/sugar/user/index.vue new file mode 100644 index 0000000..f226a66 --- /dev/null +++ b/ruoyi-ui/src/views/game/sugar/user/index.vue @@ -0,0 +1,440 @@ + + + + diff --git a/ruoyi-ui/src/views/game/wheel/config/index.vue b/ruoyi-ui/src/views/game/wheel/config/index.vue new file mode 100644 index 0000000..128a54e --- /dev/null +++ b/ruoyi-ui/src/views/game/wheel/config/index.vue @@ -0,0 +1,134 @@ + + + diff --git a/ruoyi-ui/src/views/index.vue b/ruoyi-ui/src/views/index.vue new file mode 100644 index 0000000..63f39d9 --- /dev/null +++ b/ruoyi-ui/src/views/index.vue @@ -0,0 +1,435 @@ + + + + + + diff --git a/ruoyi-ui/src/views/index_v1.vue b/ruoyi-ui/src/views/index_v1.vue new file mode 100644 index 0000000..81ff2d2 --- /dev/null +++ b/ruoyi-ui/src/views/index_v1.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/ruoyi-ui/src/views/login.vue b/ruoyi-ui/src/views/login.vue new file mode 100644 index 0000000..de09ae1 --- /dev/null +++ b/ruoyi-ui/src/views/login.vue @@ -0,0 +1,221 @@ + + + + + diff --git a/ruoyi-ui/src/views/monitor/cache/index.vue b/ruoyi-ui/src/views/monitor/cache/index.vue new file mode 100644 index 0000000..e81da2e --- /dev/null +++ b/ruoyi-ui/src/views/monitor/cache/index.vue @@ -0,0 +1,144 @@ + + + diff --git a/ruoyi-ui/src/views/monitor/cache/list.vue b/ruoyi-ui/src/views/monitor/cache/list.vue new file mode 100644 index 0000000..29a7c74 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/cache/list.vue @@ -0,0 +1,241 @@ + + + diff --git a/ruoyi-ui/src/views/monitor/druid/index.vue b/ruoyi-ui/src/views/monitor/druid/index.vue new file mode 100644 index 0000000..c6ad585 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/druid/index.vue @@ -0,0 +1,15 @@ + + diff --git a/ruoyi-ui/src/views/monitor/job/index.vue b/ruoyi-ui/src/views/monitor/job/index.vue new file mode 100644 index 0000000..892c727 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/job/index.vue @@ -0,0 +1,513 @@ + + + diff --git a/ruoyi-ui/src/views/monitor/job/log.vue b/ruoyi-ui/src/views/monitor/job/log.vue new file mode 100644 index 0000000..60bee1d --- /dev/null +++ b/ruoyi-ui/src/views/monitor/job/log.vue @@ -0,0 +1,295 @@ + + + diff --git a/ruoyi-ui/src/views/monitor/logininfor/index.vue b/ruoyi-ui/src/views/monitor/logininfor/index.vue new file mode 100644 index 0000000..d6af834 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/logininfor/index.vue @@ -0,0 +1,246 @@ + + + + diff --git a/ruoyi-ui/src/views/monitor/online/index.vue b/ruoyi-ui/src/views/monitor/online/index.vue new file mode 100644 index 0000000..ad613c9 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/online/index.vue @@ -0,0 +1,122 @@ + + + + diff --git a/ruoyi-ui/src/views/monitor/operlog/index.vue b/ruoyi-ui/src/views/monitor/operlog/index.vue new file mode 100644 index 0000000..988f983 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/operlog/index.vue @@ -0,0 +1,323 @@ + + + + diff --git a/ruoyi-ui/src/views/monitor/server/index.vue b/ruoyi-ui/src/views/monitor/server/index.vue new file mode 100644 index 0000000..15ffc9a --- /dev/null +++ b/ruoyi-ui/src/views/monitor/server/index.vue @@ -0,0 +1,207 @@ + + + diff --git a/ruoyi-ui/src/views/redirect.vue b/ruoyi-ui/src/views/redirect.vue new file mode 100644 index 0000000..db4c1d6 --- /dev/null +++ b/ruoyi-ui/src/views/redirect.vue @@ -0,0 +1,12 @@ + diff --git a/ruoyi-ui/src/views/register.vue b/ruoyi-ui/src/views/register.vue new file mode 100644 index 0000000..b50f238 --- /dev/null +++ b/ruoyi-ui/src/views/register.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/ruoyi-ui/src/views/skins/delivery/index.vue b/ruoyi-ui/src/views/skins/delivery/index.vue new file mode 100644 index 0000000..d24caa2 --- /dev/null +++ b/ruoyi-ui/src/views/skins/delivery/index.vue @@ -0,0 +1,214 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/delivery/sendGoods.vue b/ruoyi-ui/src/views/skins/delivery/sendGoods.vue new file mode 100644 index 0000000..fef1a93 --- /dev/null +++ b/ruoyi-ui/src/views/skins/delivery/sendGoods.vue @@ -0,0 +1,215 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/delivery/zbtrecord.vue b/ruoyi-ui/src/views/skins/delivery/zbtrecord.vue new file mode 100644 index 0000000..40fb173 --- /dev/null +++ b/ruoyi-ui/src/views/skins/delivery/zbtrecord.vue @@ -0,0 +1,275 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttBonus/firstRecharge.vue b/ruoyi-ui/src/views/skins/ttBonus/firstRecharge.vue new file mode 100644 index 0000000..5dbbe9a --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttBonus/firstRecharge.vue @@ -0,0 +1,279 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttBonus/index.vue b/ruoyi-ui/src/views/skins/ttBonus/index.vue new file mode 100644 index 0000000..27270df --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttBonus/index.vue @@ -0,0 +1,499 @@ + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttBox/boxRecords.vue b/ruoyi-ui/src/views/skins/ttBox/boxRecords.vue new file mode 100644 index 0000000..a23ef5b --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttBox/boxRecords.vue @@ -0,0 +1,180 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttBox/boxType.vue b/ruoyi-ui/src/views/skins/ttBox/boxType.vue new file mode 100644 index 0000000..5cfade2 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttBox/boxType.vue @@ -0,0 +1,248 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttBox/boxrPize.vue b/ruoyi-ui/src/views/skins/ttBox/boxrPize.vue new file mode 100644 index 0000000..774a98f --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttBox/boxrPize.vue @@ -0,0 +1,648 @@ + + + + + diff --git a/ruoyi-ui/src/views/skins/ttBox/index.vue b/ruoyi-ui/src/views/skins/ttBox/index.vue new file mode 100644 index 0000000..aab8599 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttBox/index.vue @@ -0,0 +1,1354 @@ + + + + diff --git a/ruoyi-ui/src/views/skins/ttFight/fightBox.vue b/ruoyi-ui/src/views/skins/ttFight/fightBox.vue new file mode 100644 index 0000000..3a48781 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttFight/fightBox.vue @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttFight/fightPrize.vue b/ruoyi-ui/src/views/skins/ttFight/fightPrize.vue new file mode 100644 index 0000000..3a48781 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttFight/fightPrize.vue @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttFight/fightRankingReward.vue b/ruoyi-ui/src/views/skins/ttFight/fightRankingReward.vue new file mode 100644 index 0000000..4760f8d --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttFight/fightRankingReward.vue @@ -0,0 +1,211 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttFight/fightResult.vue b/ruoyi-ui/src/views/skins/ttFight/fightResult.vue new file mode 100644 index 0000000..a46b102 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttFight/fightResult.vue @@ -0,0 +1,143 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttFight/index.vue b/ruoyi-ui/src/views/skins/ttFight/index.vue new file mode 100644 index 0000000..dc9490b --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttFight/index.vue @@ -0,0 +1,178 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttFight/robotFightGroup.vue b/ruoyi-ui/src/views/skins/ttFight/robotFightGroup.vue new file mode 100644 index 0000000..32cee9e --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttFight/robotFightGroup.vue @@ -0,0 +1,265 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttFight/robotFightGroupBox.vue b/ruoyi-ui/src/views/skins/ttFight/robotFightGroupBox.vue new file mode 100644 index 0000000..80358fd --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttFight/robotFightGroupBox.vue @@ -0,0 +1,326 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttGenerateCard/index.vue b/ruoyi-ui/src/views/skins/ttGenerateCard/index.vue new file mode 100644 index 0000000..2cbf3d4 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttGenerateCard/index.vue @@ -0,0 +1,420 @@ + + + + diff --git a/ruoyi-ui/src/views/skins/ttOrnaments/boxOpenChance.vue b/ruoyi-ui/src/views/skins/ttOrnaments/boxOpenChance.vue new file mode 100644 index 0000000..62528af --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttOrnaments/boxOpenChance.vue @@ -0,0 +1,253 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttOrnaments/index.vue b/ruoyi-ui/src/views/skins/ttOrnaments/index.vue new file mode 100644 index 0000000..67dce9e --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttOrnaments/index.vue @@ -0,0 +1,709 @@ + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttOrnaments/websiteProperty.vue b/ruoyi-ui/src/views/skins/ttOrnaments/websiteProperty.vue new file mode 100644 index 0000000..d207a0f --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttOrnaments/websiteProperty.vue @@ -0,0 +1,464 @@ + + + + diff --git a/ruoyi-ui/src/views/skins/ttOrnamentsLevel/index.vue b/ruoyi-ui/src/views/skins/ttOrnamentsLevel/index.vue new file mode 100644 index 0000000..0154c99 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttOrnamentsLevel/index.vue @@ -0,0 +1,250 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttOrnamentsType/index.vue b/ruoyi-ui/src/views/skins/ttOrnamentsType/index.vue new file mode 100644 index 0000000..b0d9e96 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttOrnamentsType/index.vue @@ -0,0 +1,291 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttRechargeList/index.vue b/ruoyi-ui/src/views/skins/ttRechargeList/index.vue new file mode 100644 index 0000000..9d1d496 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttRechargeList/index.vue @@ -0,0 +1,279 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttRechargeRecord/index.vue b/ruoyi-ui/src/views/skins/ttRechargeRecord/index.vue new file mode 100644 index 0000000..e76962d --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttRechargeRecord/index.vue @@ -0,0 +1,187 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttRedPack/index.vue b/ruoyi-ui/src/views/skins/ttRedPack/index.vue new file mode 100644 index 0000000..139aba1 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttRedPack/index.vue @@ -0,0 +1,350 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttReplacementRecord/index.vue b/ruoyi-ui/src/views/skins/ttReplacementRecord/index.vue new file mode 100644 index 0000000..100b14a --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttReplacementRecord/index.vue @@ -0,0 +1,340 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttRoll/awarddetali.vue b/ruoyi-ui/src/views/skins/ttRoll/awarddetali.vue new file mode 100644 index 0000000..6bdc28a --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttRoll/awarddetali.vue @@ -0,0 +1,178 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttRoll/index.vue b/ruoyi-ui/src/views/skins/ttRoll/index.vue new file mode 100644 index 0000000..588f8d1 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttRoll/index.vue @@ -0,0 +1,533 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttRoll/prize.vue b/ruoyi-ui/src/views/skins/ttRoll/prize.vue new file mode 100644 index 0000000..d93bf87 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttRoll/prize.vue @@ -0,0 +1,606 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttRoll/prizedetail.vue b/ruoyi-ui/src/views/skins/ttRoll/prizedetail.vue new file mode 100644 index 0000000..8e5d6a6 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttRoll/prizedetail.vue @@ -0,0 +1,446 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttRoll/rollUser.vue b/ruoyi-ui/src/views/skins/ttRoll/rollUser.vue new file mode 100644 index 0000000..b511531 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttRoll/rollUser.vue @@ -0,0 +1,113 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttRoll/timeRoll.vue b/ruoyi-ui/src/views/skins/ttRoll/timeRoll.vue new file mode 100644 index 0000000..914ca8a --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttRoll/timeRoll.vue @@ -0,0 +1,483 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttSetting/announcement.vue b/ruoyi-ui/src/views/skins/ttSetting/announcement.vue new file mode 100644 index 0000000..c94d58b --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttSetting/announcement.vue @@ -0,0 +1,196 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttSetting/avatar.vue b/ruoyi-ui/src/views/skins/ttSetting/avatar.vue new file mode 100644 index 0000000..ef04bc4 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttSetting/avatar.vue @@ -0,0 +1,244 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttSetting/banner.vue b/ruoyi-ui/src/views/skins/ttSetting/banner.vue new file mode 100644 index 0000000..b5601c3 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttSetting/banner.vue @@ -0,0 +1,304 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttSetting/contentList.vue b/ruoyi-ui/src/views/skins/ttSetting/contentList.vue new file mode 100644 index 0000000..411918c --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttSetting/contentList.vue @@ -0,0 +1,435 @@ + + + + diff --git a/ruoyi-ui/src/views/skins/ttSetting/contentType.vue b/ruoyi-ui/src/views/skins/ttSetting/contentType.vue new file mode 100644 index 0000000..36d2ec7 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttSetting/contentType.vue @@ -0,0 +1,409 @@ + + + + + diff --git a/ruoyi-ui/src/views/skins/ttSetting/index.vue b/ruoyi-ui/src/views/skins/ttSetting/index.vue new file mode 100644 index 0000000..fac470e --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttSetting/index.vue @@ -0,0 +1,234 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttSetting/maintenance.vue b/ruoyi-ui/src/views/skins/ttSetting/maintenance.vue new file mode 100644 index 0000000..684e7c6 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttSetting/maintenance.vue @@ -0,0 +1,114 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttSetting/promotionLevel.vue b/ruoyi-ui/src/views/skins/ttSetting/promotionLevel.vue new file mode 100644 index 0000000..d453de7 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttSetting/promotionLevel.vue @@ -0,0 +1,241 @@ + + + + diff --git a/ruoyi-ui/src/views/skins/ttSetting/setting.vue b/ruoyi-ui/src/views/skins/ttSetting/setting.vue new file mode 100644 index 0000000..cfa247f --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttSetting/setting.vue @@ -0,0 +1,135 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttSetting/vips.vue b/ruoyi-ui/src/views/skins/ttSetting/vips.vue new file mode 100644 index 0000000..74c1fa3 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttSetting/vips.vue @@ -0,0 +1,168 @@ + + + + diff --git a/ruoyi-ui/src/views/skins/ttShopping/index.vue b/ruoyi-ui/src/views/skins/ttShopping/index.vue new file mode 100644 index 0000000..d5f49ed --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttShopping/index.vue @@ -0,0 +1,297 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttTaskCenter/index.vue b/ruoyi-ui/src/views/skins/ttTaskCenter/index.vue new file mode 100644 index 0000000..39fcf11 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttTaskCenter/index.vue @@ -0,0 +1,281 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttUpgrade/index.vue b/ruoyi-ui/src/views/skins/ttUpgrade/index.vue new file mode 100644 index 0000000..789b18e --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttUpgrade/index.vue @@ -0,0 +1,817 @@ + + + + diff --git a/ruoyi-ui/src/views/skins/ttUpgrade/upgradeFall.vue b/ruoyi-ui/src/views/skins/ttUpgrade/upgradeFall.vue new file mode 100644 index 0000000..3abcbb0 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttUpgrade/upgradeFall.vue @@ -0,0 +1,506 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttUpgrade/upgradeRecord.vue b/ruoyi-ui/src/views/skins/ttUpgrade/upgradeRecord.vue new file mode 100644 index 0000000..9196d8b --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttUpgrade/upgradeRecord.vue @@ -0,0 +1,212 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttWelfare/index.vue b/ruoyi-ui/src/views/skins/ttWelfare/index.vue new file mode 100644 index 0000000..2cb178c --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttWelfare/index.vue @@ -0,0 +1,273 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttWelfare/monthlyRecharges.vue b/ruoyi-ui/src/views/skins/ttWelfare/monthlyRecharges.vue new file mode 100644 index 0000000..f163231 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttWelfare/monthlyRecharges.vue @@ -0,0 +1,344 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttWelfare/rechargeRankingReward.vue b/ruoyi-ui/src/views/skins/ttWelfare/rechargeRankingReward.vue new file mode 100644 index 0000000..b4bf7d2 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttWelfare/rechargeRankingReward.vue @@ -0,0 +1,211 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttredPacket/index.vue b/ruoyi-ui/src/views/skins/ttredPacket/index.vue new file mode 100644 index 0000000..ab10e1b --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttredPacket/index.vue @@ -0,0 +1,601 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttredPacket/packetRecord.vue b/ruoyi-ui/src/views/skins/ttredPacket/packetRecord.vue new file mode 100644 index 0000000..152f022 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttredPacket/packetRecord.vue @@ -0,0 +1,116 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttuser/amountRecords.vue b/ruoyi-ui/src/views/skins/ttuser/amountRecords.vue new file mode 100644 index 0000000..8bd63ea --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttuser/amountRecords.vue @@ -0,0 +1,146 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttuser/creditsRecords.vue b/ruoyi-ui/src/views/skins/ttuser/creditsRecords.vue new file mode 100644 index 0000000..57ea179 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttuser/creditsRecords.vue @@ -0,0 +1,150 @@ + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttuser/index.vue b/ruoyi-ui/src/views/skins/ttuser/index.vue new file mode 100644 index 0000000..1432cfa --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttuser/index.vue @@ -0,0 +1,828 @@ + + + + diff --git a/ruoyi-ui/src/views/skins/ttuser/ipAddr.vue b/ruoyi-ui/src/views/skins/ttuser/ipAddr.vue new file mode 100644 index 0000000..fcc7e28 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttuser/ipAddr.vue @@ -0,0 +1,106 @@ + + + diff --git a/ruoyi-ui/src/views/skins/ttuser/promotionRecord.vue b/ruoyi-ui/src/views/skins/ttuser/promotionRecord.vue new file mode 100644 index 0000000..14585f7 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttuser/promotionRecord.vue @@ -0,0 +1,474 @@ + + + + diff --git a/ruoyi-ui/src/views/skins/ttuser/spreadLevel.vue b/ruoyi-ui/src/views/skins/ttuser/spreadLevel.vue new file mode 100644 index 0000000..4e85076 --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttuser/spreadLevel.vue @@ -0,0 +1,69 @@ + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/skins/ttuser/userPack.vue b/ruoyi-ui/src/views/skins/ttuser/userPack.vue new file mode 100644 index 0000000..9c67b0d --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttuser/userPack.vue @@ -0,0 +1,345 @@ + + + + diff --git a/ruoyi-ui/src/views/skins/ttuser/vips.vue b/ruoyi-ui/src/views/skins/ttuser/vips.vue new file mode 100644 index 0000000..3295fab --- /dev/null +++ b/ruoyi-ui/src/views/skins/ttuser/vips.vue @@ -0,0 +1,21 @@ + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/config/index.vue b/ruoyi-ui/src/views/system/config/index.vue new file mode 100644 index 0000000..3ab81fc --- /dev/null +++ b/ruoyi-ui/src/views/system/config/index.vue @@ -0,0 +1,343 @@ + + + diff --git a/ruoyi-ui/src/views/system/dept/index.vue b/ruoyi-ui/src/views/system/dept/index.vue new file mode 100644 index 0000000..e502b4e --- /dev/null +++ b/ruoyi-ui/src/views/system/dept/index.vue @@ -0,0 +1,340 @@ + + + diff --git a/ruoyi-ui/src/views/system/dict/data.vue b/ruoyi-ui/src/views/system/dict/data.vue new file mode 100644 index 0000000..3befe4a --- /dev/null +++ b/ruoyi-ui/src/views/system/dict/data.vue @@ -0,0 +1,402 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/dict/index.vue b/ruoyi-ui/src/views/system/dict/index.vue new file mode 100644 index 0000000..6ca5457 --- /dev/null +++ b/ruoyi-ui/src/views/system/dict/index.vue @@ -0,0 +1,347 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/menu/index.vue b/ruoyi-ui/src/views/system/menu/index.vue new file mode 100644 index 0000000..c703fa0 --- /dev/null +++ b/ruoyi-ui/src/views/system/menu/index.vue @@ -0,0 +1,452 @@ + + + diff --git a/ruoyi-ui/src/views/system/notice/index.vue b/ruoyi-ui/src/views/system/notice/index.vue new file mode 100644 index 0000000..7982b54 --- /dev/null +++ b/ruoyi-ui/src/views/system/notice/index.vue @@ -0,0 +1,312 @@ + + + diff --git a/ruoyi-ui/src/views/system/post/index.vue b/ruoyi-ui/src/views/system/post/index.vue new file mode 100644 index 0000000..444bf63 --- /dev/null +++ b/ruoyi-ui/src/views/system/post/index.vue @@ -0,0 +1,309 @@ + + + diff --git a/ruoyi-ui/src/views/system/role/authUser.vue b/ruoyi-ui/src/views/system/role/authUser.vue new file mode 100644 index 0000000..147aa33 --- /dev/null +++ b/ruoyi-ui/src/views/system/role/authUser.vue @@ -0,0 +1,199 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/role/index.vue b/ruoyi-ui/src/views/system/role/index.vue new file mode 100644 index 0000000..fb3b5ef --- /dev/null +++ b/ruoyi-ui/src/views/system/role/index.vue @@ -0,0 +1,605 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/role/selectUser.vue b/ruoyi-ui/src/views/system/role/selectUser.vue new file mode 100644 index 0000000..b2b072f --- /dev/null +++ b/ruoyi-ui/src/views/system/role/selectUser.vue @@ -0,0 +1,138 @@ + + + diff --git a/ruoyi-ui/src/views/system/user/authRole.vue b/ruoyi-ui/src/views/system/user/authRole.vue new file mode 100644 index 0000000..ab5e72f --- /dev/null +++ b/ruoyi-ui/src/views/system/user/authRole.vue @@ -0,0 +1,117 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/user/index.vue b/ruoyi-ui/src/views/system/user/index.vue new file mode 100644 index 0000000..ae87fe4 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/index.vue @@ -0,0 +1,670 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/user/profile/index.vue b/ruoyi-ui/src/views/system/user/profile/index.vue new file mode 100644 index 0000000..529c564 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/profile/index.vue @@ -0,0 +1,91 @@ + + + diff --git a/ruoyi-ui/src/views/system/user/profile/resetPwd.vue b/ruoyi-ui/src/views/system/user/profile/resetPwd.vue new file mode 100644 index 0000000..64e8f8c --- /dev/null +++ b/ruoyi-ui/src/views/system/user/profile/resetPwd.vue @@ -0,0 +1,68 @@ + + + diff --git a/ruoyi-ui/src/views/system/user/profile/userAvatar.vue b/ruoyi-ui/src/views/system/user/profile/userAvatar.vue new file mode 100644 index 0000000..96aa01f --- /dev/null +++ b/ruoyi-ui/src/views/system/user/profile/userAvatar.vue @@ -0,0 +1,182 @@ + + + + diff --git a/ruoyi-ui/src/views/system/user/profile/userInfo.vue b/ruoyi-ui/src/views/system/user/profile/userInfo.vue new file mode 100644 index 0000000..c09a20b --- /dev/null +++ b/ruoyi-ui/src/views/system/user/profile/userInfo.vue @@ -0,0 +1,75 @@ + + + diff --git a/ruoyi-ui/src/views/tool/build/CodeTypeDialog.vue b/ruoyi-ui/src/views/tool/build/CodeTypeDialog.vue new file mode 100644 index 0000000..b5c2e2e --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/CodeTypeDialog.vue @@ -0,0 +1,106 @@ + + diff --git a/ruoyi-ui/src/views/tool/build/DraggableItem.vue b/ruoyi-ui/src/views/tool/build/DraggableItem.vue new file mode 100644 index 0000000..e881778 --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/DraggableItem.vue @@ -0,0 +1,100 @@ + diff --git a/ruoyi-ui/src/views/tool/build/IconsDialog.vue b/ruoyi-ui/src/views/tool/build/IconsDialog.vue new file mode 100644 index 0000000..958be50 --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/IconsDialog.vue @@ -0,0 +1,123 @@ + + + diff --git a/ruoyi-ui/src/views/tool/build/RightPanel.vue b/ruoyi-ui/src/views/tool/build/RightPanel.vue new file mode 100644 index 0000000..c2760eb --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/RightPanel.vue @@ -0,0 +1,946 @@ + + + + + diff --git a/ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue b/ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue new file mode 100644 index 0000000..fa7f0b2 --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue @@ -0,0 +1,149 @@ + + diff --git a/ruoyi-ui/src/views/tool/build/index.vue b/ruoyi-ui/src/views/tool/build/index.vue new file mode 100644 index 0000000..2bd298b --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/index.vue @@ -0,0 +1,768 @@ + + + + + diff --git a/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue b/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue new file mode 100644 index 0000000..7029529 --- /dev/null +++ b/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue @@ -0,0 +1,60 @@ + + + diff --git a/ruoyi-ui/src/views/tool/gen/editTable.vue b/ruoyi-ui/src/views/tool/gen/editTable.vue new file mode 100644 index 0000000..951497a --- /dev/null +++ b/ruoyi-ui/src/views/tool/gen/editTable.vue @@ -0,0 +1,234 @@ + + + diff --git a/ruoyi-ui/src/views/tool/gen/genInfoForm.vue b/ruoyi-ui/src/views/tool/gen/genInfoForm.vue new file mode 100644 index 0000000..3f8d67f --- /dev/null +++ b/ruoyi-ui/src/views/tool/gen/genInfoForm.vue @@ -0,0 +1,299 @@ + + + diff --git a/ruoyi-ui/src/views/tool/gen/importTable.vue b/ruoyi-ui/src/views/tool/gen/importTable.vue new file mode 100644 index 0000000..3ea9532 --- /dev/null +++ b/ruoyi-ui/src/views/tool/gen/importTable.vue @@ -0,0 +1,120 @@ + + + diff --git a/ruoyi-ui/src/views/tool/gen/index.vue b/ruoyi-ui/src/views/tool/gen/index.vue new file mode 100644 index 0000000..3f1f930 --- /dev/null +++ b/ruoyi-ui/src/views/tool/gen/index.vue @@ -0,0 +1,337 @@ + + + diff --git a/ruoyi-ui/src/views/tool/swagger/index.vue b/ruoyi-ui/src/views/tool/swagger/index.vue new file mode 100644 index 0000000..aa32064 --- /dev/null +++ b/ruoyi-ui/src/views/tool/swagger/index.vue @@ -0,0 +1,17 @@ + + diff --git a/ruoyi-ui/src/views/ttRollHome.vue b/ruoyi-ui/src/views/ttRollHome.vue new file mode 100644 index 0000000..9eabf34 --- /dev/null +++ b/ruoyi-ui/src/views/ttRollHome.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/ruoyi-ui/vue.config.js b/ruoyi-ui/vue.config.js new file mode 100644 index 0000000..3b07796 --- /dev/null +++ b/ruoyi-ui/vue.config.js @@ -0,0 +1,134 @@ +'use strict' +const path = require('path') + +function resolve(dir) { + return path.join(__dirname, dir) +} + +const CompressionPlugin = require('compression-webpack-plugin') + +const name = process.env.VUE_APP_TITLE || '后台管理系统' // 网页标题 + +const port = process.env.port || process.env.npm_config_port || 80 // 端口 + +// vue.config.js 配置说明 +//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions +// 这里只列一部分,具体配置参考文档 +module.exports = { + // 部署生产环境和开发环境下的URL。 + // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上 + // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。 + publicPath: process.env.NODE_ENV === "production" ? "/" : "/", + // 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist) + outputDir: 'dist', + // 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下) + assetsDir: 'static', + // 是否开启eslint保存检测,有效值:ture | false | 'error' + lintOnSave: process.env.NODE_ENV === 'development', + // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。 + productionSourceMap: false, + // webpack-dev-server 相关配置 + devServer: { + host: '0.0.0.0', + port: port, + open: true, + proxy: { + // detail: https://cli.vuejs.org/config/#devserver-proxy + [process.env.VUE_APP_BASE_API]: { + target: `http://localhost:8080`, + changeOrigin: true, + pathRewrite: { + ['^' + process.env.VUE_APP_BASE_API]: '' + } + } + }, + disableHostCheck: true + }, + css: { + loaderOptions: { + sass: { + sassOptions: { outputStyle: "expanded" } + } + } + }, + configureWebpack: { + name: name, + resolve: { + alias: { + '@': resolve('src') + } + }, + plugins: [ + // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件 + new CompressionPlugin({ + cache: false, // 不启用文件缓存 + test: /\.(js|css|html)?$/i, // 压缩文件格式 + filename: '[path].gz[query]', // 压缩后的文件名 + algorithm: 'gzip', // 使用gzip压缩 + minRatio: 0.8 // 压缩率小于1才会压缩 + }) + ], + }, + chainWebpack(config) { + config.plugins.delete('preload') // TODO: need test + config.plugins.delete('prefetch') // TODO: need test + + // set svg-sprite-loader + config.module + .rule('svg') + .exclude.add(resolve('src/assets/icons')) + .end() + config.module + .rule('icons') + .test(/\.svg$/) + .include.add(resolve('src/assets/icons')) + .end() + .use('svg-sprite-loader') + .loader('svg-sprite-loader') + .options({ + symbolId: 'icon-[name]' + }) + .end() + + config.when(process.env.NODE_ENV !== 'development', config => { + config + .plugin('ScriptExtHtmlWebpackPlugin') + .after('html') + .use('script-ext-html-webpack-plugin', [{ + // `runtime` must same as runtimeChunk name. default is `runtime` + inline: /runtime\..*\.js$/ + }]) + .end() + + config.optimization.splitChunks({ + chunks: 'all', + cacheGroups: { + libs: { + name: 'chunk-libs', + test: /[\\/]node_modules[\\/]/, + priority: 10, + chunks: 'initial' // only package third parties that are initially dependent + }, + elementUI: { + name: 'chunk-elementUI', // split elementUI into a single package + test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm + priority: 20 // the weight needs to be larger than libs and app or it will be packaged into libs or app + }, + commons: { + name: 'chunk-commons', + test: resolve('src/components'), // can customize your rules + minChunks: 3, // minimum common number + priority: 5, + reuseExistingChunk: true + } + } + }) + + config.optimization.runtimeChunk('single'), + { + from: path.resolve(__dirname, './public/robots.txt'), //防爬虫文件 + to: './' //到根目录下 + } + }) + } +} diff --git a/ry.bat b/ry.bat new file mode 100644 index 0000000..8294e13 --- /dev/null +++ b/ry.bat @@ -0,0 +1,67 @@ +@echo off + +rem jar平级目录 +set AppName=ruoyi-admin.jar + +rem JVM参数 +set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" + + +ECHO. + ECHO. [1] 启动%AppName% + ECHO. [2] 关闭%AppName% + ECHO. [3] 重启%AppName% + ECHO. [4] 启动状态 %AppName% + ECHO. [5] 退 出 +ECHO. + +ECHO.请输入选择项目的序号: +set /p ID= + IF "%id%"=="1" GOTO start + IF "%id%"=="2" GOTO stop + IF "%id%"=="3" GOTO restart + IF "%id%"=="4" GOTO status + IF "%id%"=="5" EXIT +PAUSE +:start + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if defined pid ( + echo %%is running + PAUSE + ) + +start javaw %JVM_OPTS% -jar %AppName% + +echo starting…… +echo Start %AppName% success... +goto:eof + +rem 函数stop通过jps命令查找pid并结束进程 +:stop + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% does not exists) else ( + echo prepare to kill %image_name% + echo start kill %pid% ... + rem 根据进程ID,kill进程 + taskkill /f /pid %pid% + ) +goto:eof +:restart + call :stop + call :start +goto:eof +:status + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% is dead ) else ( + echo %image_name% is running + ) +goto:eof diff --git a/ry.sh b/ry.sh new file mode 100644 index 0000000..e3d07fd --- /dev/null +++ b/ry.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# ./ry.sh start 启动 stop 停止 restart 重启 status 状态 +AppName=ruoyi-admin.jar + +# JVM参数 +JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:+UseAdaptiveSizePolicy -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:ParallelGCThreads=8 -XX:ConcGCThreads=2" +APP_HOME=`pwd` +LOG_PATH=$APP_HOME/logs/$AppName.log + +if [ "$1" = "" ]; +then + echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m" + exit 1 +fi + +if [ "$AppName" = "" ]; +then + echo -e "\033[0;31m 未输入应用名 \033[0m" + exit 1 +fi + +function start() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + + if [ x"$PID" != x"" ]; then + echo "$AppName is running..." + else + nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 & + echo "Start $AppName success..." + fi +} + +function stop() +{ + echo "Stop $AppName" + + PID="" + query(){ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + } + + query + if [ x"$PID" != x"" ]; then + kill -TERM $PID + echo "$AppName (pid:$PID) exiting..." + while [ x"$PID" != x"" ] + do + sleep 1 + query + done + echo "$AppName exited." + else + echo "$AppName already stopped." + fi +} + +function restart() +{ + stop + sleep 2 + start +} + +function status() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l` + if [ $PID != 0 ];then + echo "$AppName is running..." + else + echo "$AppName is not running..." + fi +} + +case $1 in + start) + start;; + stop) + stop;; + restart) + restart;; + status) + status;; + *) + +esac diff --git a/skins-model/.gitignore b/skins-model/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/skins-model/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/skins-model/pom.xml b/skins-model/pom.xml new file mode 100644 index 0000000..3e4a843 --- /dev/null +++ b/skins-model/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + com.ruoyi + ruoyi + 4.8.2 + + + skins-model + + + 8 + 8 + UTF-8 + + + + + + com.ruoyi + ruoyi-common + + + \ No newline at end of file diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/DeliveryOrderStatus.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/DeliveryOrderStatus.java new file mode 100644 index 0000000..b2e555c --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/DeliveryOrderStatus.java @@ -0,0 +1,45 @@ +package com.ruoyi.domain.common.constant; + +// 发货订单状态(0发起提货 1待发货 3待收货 10订单完成 11订单取消) +public enum DeliveryOrderStatus { + + // 0发起提货(多余) + //CREATE_ORDER(0,"0发起提货"), + + // 1待发货 + DELIVERY_BEFORE(1,"1待发货"), + + // 2已发货 + DELIVERY_AFTER(2,"2已发货"), + + // 3客户确认已收货 + OWN_BEFORE(3,"3客户确认已收货"), + + // // 4客户确认已收货 + // OWN_BEFORE(4,"3客户确认已收货"), + + // 10订单完成 + ORDER_COMPLETE(10,"10订单完成"), + + //11订单取消 + ORDER_CANCEL(11,"11订单取消"), + + // 12订单异常冻结 + ORDER_FREEZE(12,"12订单异常冻结"); + + private final Integer code; + private final String msg; + + DeliveryOrderStatus(Integer code,String msg){ + this.code = code; + this.msg = msg; + } + + public Integer getCode(){ + return this.code; + } + public String getMsg(){ + return this.msg; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/DeliveryPattern.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/DeliveryPattern.java new file mode 100644 index 0000000..17dfcdc --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/DeliveryPattern.java @@ -0,0 +1,23 @@ +package com.ruoyi.domain.common.constant; + +// 提货方式 +public enum DeliveryPattern { + + // 手动 + MANUAL(1), + // 自动 + AUTO(2), + // 主播提货 + ANCHOR(3); + + private Integer code; + + DeliveryPattern(Integer code){ + this.code = code; + } + + public Integer getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/LockKey/UpgradeLock.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/LockKey/UpgradeLock.java new file mode 100644 index 0000000..73d2446 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/LockKey/UpgradeLock.java @@ -0,0 +1,17 @@ +package com.ruoyi.domain.common.constant.LockKey; + +public enum UpgradeLock { + + UPGRADE_LOCK("upgrade_lock:"); + + private String lock; + + UpgradeLock(String lock){ + this.lock = lock; + } + + public String getLock(){ + return this.lock; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/PartyType.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/PartyType.java new file mode 100644 index 0000000..6228a0f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/PartyType.java @@ -0,0 +1,41 @@ +package com.ruoyi.domain.common.constant; + +// 第三方平台 +public enum PartyType { + + // 发货平台----------------- + // 扎比特 + ZBT(1), + + // yy有品 + YY_YOU_PING(2), + + // 支付平台------------------ + // 支付宝 + ZFB(3), + + // 微信 + VX(4), + + // 聚合支付 + JU_HE_ZHI_FU(5), + + // 九嘉支付宝 + JIU_JIA_ZFB(6), + + V5ITEM(7), + CS340(8), + INNER_PICTURE_PAY(9), + ; + + private final Integer code; + + PartyType(Integer code){ + this.code = code; + } + + public Integer getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/PayOrderStatus.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/PayOrderStatus.java new file mode 100644 index 0000000..7eab995 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/PayOrderStatus.java @@ -0,0 +1,35 @@ +package com.ruoyi.domain.common.constant; + +import lombok.Getter; + +// 支付订单状态 +@Getter +public enum PayOrderStatus { + + /** 订单状态:-1超时未付款 0未付款,1已付款,2已取消 */ + + // 0未付款 + NO_PAY("0"), + + // 用户支付成功 + PAY_YET("1"), + + // 用户支付失败 + PAY_FAIL("-1"), + + // 用户取消(超时未付款) + CANCEL("2"), + + // 用户已支付,但回调处理异常 + CALL_BACK_ERRO("3"), + + // 支付完成 + PAY_COMPLE("4"); + + private final String code; + + PayOrderStatus(String code){ + this.code = code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/PayType.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/PayType.java new file mode 100644 index 0000000..a24a335 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/PayType.java @@ -0,0 +1,46 @@ +package com.ruoyi.domain.common.constant; + +// 支付方式 +public enum PayType { + + // 中云 支付宝 + ZY_ZFB("1"), + + // 卡密 + MK_CARD("2"), + + // 支付宝 + ZFB("3"), + + // 微信 + VX("4"), + + // 聚合支付(蚂蚁支付) + JU_HE_ZHI_FU("5"), + + // 九嘉支付宝 + JIU_JIA_ZFB("6"), + + // 星火支付宝 + XIN_HUO_ZFB("7"), + + //QS聚合支付 + QS_PAY("8"), + + // 国富汇通 + GFHT_PAY("9"), + + // hipay + HI_PAY("10"); + + private final String code; + + PayType(String code){ + this.code = code; + } + + public String getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/TtAccountRecordSource.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/TtAccountRecordSource.java new file mode 100644 index 0000000..f0846e1 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/TtAccountRecordSource.java @@ -0,0 +1,197 @@ +package com.ruoyi.domain.common.constant; + +import java.util.Set; + +// 账户流水来源类型(这个类型金币和积分通用) +public enum TtAccountRecordSource { + + // 充值 + RECHARGE(1, TtAccountRecordType.INPUT, "充值收入"), + + // 玩转盲盒 + GAME_TYPE_01(2, TtAccountRecordType.OUTPUT, "经典盲盒消费"), + + // 玩转盲盒游戏奖励 + game_type_01_award(3, TtAccountRecordType.INPUT, "经典盲盒游戏奖励"), + + // 急速对战 + GAME_TYPE_02(4, TtAccountRecordType.OUTPUT, "百变竞技消费"), + + // 急速对战游戏奖励 + GAME_TYPE_02_AWARD(5, TtAccountRecordType.INPUT, "百变竞技游戏奖励"), + + // roll房 + GAME_TYPE_03(6, TtAccountRecordType.OUTPUT, "roll房消费"), + + // roll房游戏奖励 + GAME_TYPE_03_AWARD(7, TtAccountRecordType.INPUT, "roll房游戏奖励"), + + // 逐梦前行 + GAME_TYPE_04(8, TtAccountRecordType.OUTPUT, "逐梦前行消费"), + + // 逐梦前行游戏奖励 + GAME_TYPE_04_AWARD(9, TtAccountRecordType.INPUT, "逐梦前行游戏奖励"), + + // 分解饰品 + DECOMPOSE_ORNAMENT(10, TtAccountRecordType.INPUT, "分解饰品收入"), + + // 任务 + TASK(11, TtAccountRecordType.INPUT, "首次下载任务奖励"), + + // 商城兑换 + EXCHANGE(12, TtAccountRecordType.OUTPUT, "商城兑换消费"), + + // 推广福利 + P_WELFARE(13, TtAccountRecordType.INPUT, "推广福利收入"), + + // 后台操作 + ADMIN_UPDATA(14, TtAccountRecordType.INPUT, "后台操作"), + + // 奖励津贴(暂时没啥用) + BONUS(15, TtAccountRecordType.INPUT, "奖励津贴"), + + // 积分兑换金币 + INTEGRATING_CONVERSION(16, TtAccountRecordType.INPUT, "积分兑换金币"), + + // 口令红包 + RECEIVE_RED_PACKET(17, TtAccountRecordType.INPUT, "福利CDK收入"), + + // 注册奖励 + REGIST_AWARD(18, TtAccountRecordType.INPUT, "注册奖励"), + + RANK_BLEND_ERCASH(19, TtAccountRecordType.INPUT, "综合流水排行榜奖励"), + RANK_AMOUNT(20, TtAccountRecordType.INPUT, "金币消费排行榜奖励"), + RANK_CREDITS(21, TtAccountRecordType.INPUT, "积分消费排行榜奖励"), + RANK_PROD_ORN(22, TtAccountRecordType.INPUT, "出货排行榜奖励"), + + FIRST_CHARGE(23, TtAccountRecordType.INPUT, "首充赠送"), + + PROMOTION_LEVEL_CHARGE(24, TtAccountRecordType.INPUT, "推广等级充值赠送"), + + // 充值红包 + CHARGE_RED_PACKET(25, TtAccountRecordType.INPUT, "充值红包收入"), + + // 推广等级达标 + PROMOTION_LEVEL_REACH(26, TtAccountRecordType.INPUT, "推广等级达标赠送"), + + // 推广充值返佣 + PROMOTION_CHARGE_COMMISSION(27, TtAccountRecordType.INPUT, "推广充值返佣"), + + // 对战排行榜奖励 + FIGHT_RANKING_REWARD(28, TtAccountRecordType.INPUT, "百变竞技排行榜奖励"), + + // 消费排行榜奖励 + RECHARGE_RANKING_REWARD(29, TtAccountRecordType.INPUT, "消费排行榜奖励"), + + // VIP等级达标 + VIP_LEVEL_REACH(30, TtAccountRecordType.INPUT, "VIP等级达标赠送"), + + // 主播发放工资 + PROMOTION_RECHARGE(31, TtAccountRecordType.INPUT, "主播日常发放工资"), + + // 充值金额达到门槛奖励 + RECHARGE_RESEARCH_BONUS(32, TtAccountRecordType.INPUT, "充值金额达到门槛奖励"), + + // 玩转指数盲盒 + EXPONENT_GAME_TYPE_01(33, TtAccountRecordType.OUTPUT, "玩转指数盲盒消费"), + + SUGAR_SPIN_NORMAL(34, TtAccountRecordType.OUTPUT, "极速永恒普通旋转消费"), + SUGAR_BUY_FREE_SPIN(35, TtAccountRecordType.OUTPUT, "极速永恒购买免费旋转消费"), + SUGAR_BUY_SUPER_SPIN(36, TtAccountRecordType.OUTPUT, "极速永恒购买超级免费旋转消费"), + SUGAR_SPIN_REWARD(37, TtAccountRecordType.INPUT, "极速永恒奖励"), + + LUCKLY_WHEEL_SPIN(38, TtAccountRecordType.OUTPUT, "幸运转盘消费"), + LUCKLY_WHEEL_REWARD(39, TtAccountRecordType.INPUT, "幸运转盘奖励"), + + GOLD_DEVICE(40, TtAccountRecordType.OUTPUT, "黄金装备消费"), + GOLD_DEVICE_REWARD(41, TtAccountRecordType.INPUT, "黄金装备奖励"), + + ZHUBO_EXTRACT(42, TtAccountRecordType.OUTPUT, "主播后台提取消费"), + + RECHARGE_AWARD(43, TtAccountRecordType.INPUT, "充值福利奖励"), + VIP_CONSUME_AWARD(44, TtAccountRecordType.INPUT, "VIP流水奖励"), + ; + + private final Integer code; + private final TtAccountRecordType type; + private final String msg; + + + TtAccountRecordSource(Integer code, TtAccountRecordType t, String msg) { + this.code = code; + this.msg = msg; + this.type = t; + } + + public Integer getCode() { + return this.code; + } + + public String getMsg() { + return this.msg; + } + + // 计算推广福利的流水类型 + public static Set getWelfarePrizeCodes() { + return Set.of(RECHARGE.getCode(), + GAME_TYPE_01.getCode(), + GAME_TYPE_02.getCode(), + GOLD_DEVICE.getCode(), + LUCKLY_WHEEL_SPIN.getCode(), + SUGAR_SPIN_NORMAL.getCode(), + SUGAR_BUY_FREE_SPIN.getCode(), + SUGAR_BUY_SUPER_SPIN.getCode()); + } + + // 计算流水返利的游戏类型 + public static Set getGameConsumeCodes() { + return Set.of(GAME_TYPE_01.getCode(), + GAME_TYPE_02.getCode(), + GOLD_DEVICE.getCode(), + LUCKLY_WHEEL_SPIN.getCode(), + SUGAR_SPIN_NORMAL.getCode(), + SUGAR_BUY_FREE_SPIN.getCode(), + SUGAR_BUY_SUPER_SPIN.getCode()); + } + + // 今日总投注:所有游戏消费 source + public static Set getGameBetCodes() { + return Set.of( + GAME_TYPE_01.getCode(), // 经典盲盒消费 + GAME_TYPE_02.getCode(), // 百变竞技消费 + GAME_TYPE_03.getCode(), // roll房消费 + GAME_TYPE_04.getCode(), // 逐梦前行消费 + EXPONENT_GAME_TYPE_01.getCode(), // 指数盲盒消费 + SUGAR_SPIN_NORMAL.getCode(), // 极速永恒普通旋转 + SUGAR_BUY_FREE_SPIN.getCode(), // 极速永恒购买免费旋转 + SUGAR_BUY_SUPER_SPIN.getCode(), // 极速永恒购买超级免费旋转 + LUCKLY_WHEEL_SPIN.getCode(), // 幸运转盘消费 + GOLD_DEVICE.getCode() // 黄金装备消费 + ); + } + + // 今日总赢奖:所有游戏奖励 source + public static Set getGameRewardCodes() { + return Set.of( + game_type_01_award.getCode(), // 经典盲盒奖励 + GAME_TYPE_02_AWARD.getCode(), // 百变竞技奖励 + GAME_TYPE_03_AWARD.getCode(), // roll房奖励 + GAME_TYPE_04_AWARD.getCode(), // 逐梦前行奖励 + SUGAR_SPIN_REWARD.getCode(), // 极速永恒奖励 + LUCKLY_WHEEL_REWARD.getCode(), // 幸运转盘奖励 + GOLD_DEVICE_REWARD.getCode() // 黄金装备奖励 + ); + } + + public static boolean isNormalGameConsume(int code) { + return code == GAME_TYPE_01.getCode() || code == GAME_TYPE_02.getCode() || code == GOLD_DEVICE.getCode(); + } + + public static boolean isSpecialGameConsume(int code) { + return code == LUCKLY_WHEEL_SPIN.getCode() + || code == SUGAR_SPIN_NORMAL.getCode() + || code == SUGAR_BUY_FREE_SPIN.getCode() + || code == SUGAR_BUY_SUPER_SPIN.getCode(); + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/TtAccountRecordType.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/TtAccountRecordType.java new file mode 100644 index 0000000..8e6c4ca --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/TtAccountRecordType.java @@ -0,0 +1,21 @@ +package com.ruoyi.domain.common.constant; + +// 流水类型 +public enum TtAccountRecordType { + + // 收入 + INPUT(1), + // 支出 + OUTPUT(0); + + private Integer code; + + TtAccountRecordType(Integer code){ + this.code = code; + } + + public Integer getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/TtboxRecordSource.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/TtboxRecordSource.java new file mode 100644 index 0000000..dd2637f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/TtboxRecordSource.java @@ -0,0 +1,45 @@ +package com.ruoyi.domain.common.constant; + +// 账户流水来源类型 +// 注意和TtboxRecordSource区分 +public enum TtboxRecordSource { + + // 玩转盲盒 + BLIND_BOX(1,"玩转盲盒"), + + // 急速对战 + FIGHT(2,"急速对战"), + + // roll房 + ROLL(3,"roll房"), + + // 急速对战 + UPGRADE(4,"幸运升级"), + // roll房 + SYS_GRANT(5,"系统发放"), + + MALL_EXCHANGE(6,"商城兑换"), + + REPLACEMENT(7,"汰换"), + EXPONENT_BOX(8,"指数盲盒"), + TRANSFER(9,"转赠"), + ; + + private Integer code; + + private String msg; + + TtboxRecordSource(Integer code, String msg){ + this.code = code; + this.msg = msg; + } + + public Integer getCode(){ + return this.code; + } + + public String getMsg(){ + return this.msg; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/TtboxRecordStatus.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/TtboxRecordStatus.java new file mode 100644 index 0000000..9c8309c --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/TtboxRecordStatus.java @@ -0,0 +1,32 @@ +package com.ruoyi.domain.common.constant; + +// 开箱记录状态/背包饰品状态 +public enum TtboxRecordStatus { + + IN_PACKSACK_ON(0,"在背包显示"), + + // 用于对战模式,游戏完全结束才显示 + IN_PACKSACK_OFF(1,"在背包不显示"), + APPLY_DELIVERY(2,"申请提货"), + DELIVERY_YET(3,"已经提货"), + RESOLVE(5,"分解"), + ADMIN_DELETE(10,"管理员删除"); + + private final Integer code; + + private final String msg; + + TtboxRecordStatus(Integer code, String msg){ + this.code = code; + this.msg = msg; + } + + public Integer getCode(){ + return this.code; + } + + public String getMsg(){ + return this.msg; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/UserType.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/UserType.java new file mode 100644 index 0000000..f326921 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/UserType.java @@ -0,0 +1,35 @@ +package com.ruoyi.domain.common.constant; + +//用户类型 +public enum UserType { + + // 主播 + ANCHOR("01"), + // 普通用户 + COMMON_USER("02"), + // 机器人 + ROBOT_USER("03"), + // 招商号 + BUSINESS_USER("04"), + ; + + private String code; + + UserType(String code){ + this.code = code; + } + + public String getCode(){ + return this.code; + } + + public static UserType fromCode(String code){ + for (UserType userType : UserType.values()){ + if (userType.getCode().equals(code)){ + return userType; + } + } + return null; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/roll/RollGetPrizeWay.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/roll/RollGetPrizeWay.java new file mode 100644 index 0000000..1f8ef39 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/roll/RollGetPrizeWay.java @@ -0,0 +1,21 @@ +package com.ruoyi.domain.common.constant.roll; + +// roll房玩家获奖方式 +public enum RollGetPrizeWay { + + // 系统指定 + SYS(0), + // 随机 + COMMON(1); + + private final Integer code; + + RollGetPrizeWay(Integer code){ + this.code = code; + } + + public Integer getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/roll/RollStatus.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/roll/RollStatus.java new file mode 100644 index 0000000..612fc26 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/roll/RollStatus.java @@ -0,0 +1,21 @@ +package com.ruoyi.domain.common.constant.roll; + +// roll房状态 +public enum RollStatus { + + // 未开奖 + ING("0"), + // 已开奖 + END("1"); + + private final String code; + + RollStatus(String code){ + this.code = code; + } + + public String getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/roll/RollType.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/roll/RollType.java new file mode 100644 index 0000000..df98885 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/roll/RollType.java @@ -0,0 +1,21 @@ +package com.ruoyi.domain.common.constant.roll; + +// roll房状态 +public enum RollType { + + // 主播 + ANCHOR("1"), + // 官方 + OFFICIAL("0"); + + private final String code; + + RollType(String code){ + this.code = code; + } + + public String getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/sys/MoneyType.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/sys/MoneyType.java new file mode 100644 index 0000000..74275e7 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/sys/MoneyType.java @@ -0,0 +1,21 @@ +package com.ruoyi.domain.common.constant.sys; + +//用户类型 +public enum MoneyType { + + // 金币 + GOLD(1), + // 积分 + CREDITS(2); + + private final Integer code; + + MoneyType(Integer code){ + this.code = code; + } + + public Integer getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/common/constant/sys/UserStatus.java b/skins-model/src/main/java/com/ruoyi/domain/common/constant/sys/UserStatus.java new file mode 100644 index 0000000..4838991 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/common/constant/sys/UserStatus.java @@ -0,0 +1,18 @@ +package com.ruoyi.domain.common.constant.sys; + +public enum UserStatus { + + // 金币 + NORMAL(0); + + private final Integer code; + + UserStatus(Integer code){ + this.code = code; + } + + public Integer getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/ChildDetailByBoss.java b/skins-model/src/main/java/com/ruoyi/domain/dto/ChildDetailByBoss.java new file mode 100644 index 0000000..b68f6a1 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/ChildDetailByBoss.java @@ -0,0 +1,40 @@ +package com.ruoyi.domain.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Range; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class ChildDetailByBoss { + + private Integer bossId; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private String beginTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private String endTime; + + @Min(value = 1,message = "最小1") + @NotNull(message = "页码不能为空") + private Integer page; + + @Range(min = 1,max = 20) + @NotNull(message = "分页长度不能为空") + private Integer size; + + // 消费正序1 消费逆序2 + @NotNull(message = "排序方式不能为空") + private Integer orderType; + + private Integer lim; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/boxRecords/TenTopQuery.java b/skins-model/src/main/java/com/ruoyi/domain/dto/boxRecords/TenTopQuery.java new file mode 100644 index 0000000..7eb7382 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/boxRecords/TenTopQuery.java @@ -0,0 +1,30 @@ +package com.ruoyi.domain.dto.boxRecords; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Range; + +import jakarta.validation.constraints.Min; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class TenTopQuery { + + // 来源 + private List source; + + // 状态 + private List status; + + @Min(value = 1,message = "最小1") + private Integer page; + @Range(min = 1,max = 20) + private Integer size; + + private Integer limit; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/boxRecords/TtBoxRecordsNum.java b/skins-model/src/main/java/com/ruoyi/domain/dto/boxRecords/TtBoxRecordsNum.java new file mode 100644 index 0000000..873538c --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/boxRecords/TtBoxRecordsNum.java @@ -0,0 +1,14 @@ +package com.ruoyi.domain.dto.boxRecords; + +import lombok.Data; + +@Data +public class TtBoxRecordsNum { + + private Integer boxId; + private Long ornamentId; + /** + * 数量 + */ + private Integer num; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/boxRecords/TtBoxRecordsRemark.java b/skins-model/src/main/java/com/ruoyi/domain/dto/boxRecords/TtBoxRecordsRemark.java new file mode 100644 index 0000000..0e0ae3f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/boxRecords/TtBoxRecordsRemark.java @@ -0,0 +1,9 @@ +package com.ruoyi.domain.dto.boxRecords; + +import lombok.Data; + +@Data +public class TtBoxRecordsRemark { + + private String realOddsChange; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/boxRecords/queryCondition.java b/skins-model/src/main/java/com/ruoyi/domain/dto/boxRecords/queryCondition.java new file mode 100644 index 0000000..501dde6 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/boxRecords/queryCondition.java @@ -0,0 +1,56 @@ +package com.ruoyi.domain.dto.boxRecords; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Range; + +import jakarta.validation.constraints.Min; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class queryCondition { + + private Long boxRecordId; + + // 用户ID + private Integer userId; + + // 宝箱id + private Integer boxId; + + // 用户类型 + private String userType; + + // 来源 + private List source; + + // 物品价格区间 + @Min(value = 0,message = "最小0") + private BigDecimal ornamentPriceMin; + @Min(value = 0,message = "最小0") + private BigDecimal ornamentPriceMax; + + // 物品等级 + private List ornamentLevelIds; + + // 状态 + private List status; + + // 0时间升序 1时间降序 + private Integer orderByFie; + + @Min(value = 1,message = "最小1") + private Integer page; + @Range(min = 1,max = 20) + private Integer size; + + private Integer limit; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/deliver/TradeBuyParam.java b/skins-model/src/main/java/com/ruoyi/domain/dto/deliver/TradeBuyParam.java new file mode 100644 index 0000000..259431b --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/deliver/TradeBuyParam.java @@ -0,0 +1,25 @@ +package com.ruoyi.domain.dto.deliver; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class TradeBuyParam { + + // 提货记录id + private Integer deliveryRecordId; + + // 提货平台 + private Integer partyType; + + // 平台的物品在售记录id + private Long productId; + + /** 物品价格 */ + private BigDecimal price; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/deliver/TradeManualConfirmParam.java b/skins-model/src/main/java/com/ruoyi/domain/dto/deliver/TradeManualConfirmParam.java new file mode 100644 index 0000000..6f401e5 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/deliver/TradeManualConfirmParam.java @@ -0,0 +1,31 @@ +package com.ruoyi.domain.dto.deliver; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class TradeManualConfirmParam { + + // 提货记录id + private Integer deliveryRecordId; + + /** 物品价格 */ + private BigDecimal price; + /** + * 交易订单号 + */ + private String orderNo; + /** + * 发货状态 + */ + private int status; + /** + * 发货原因 + */ + private String message; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentBoxVo.java b/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentBoxVo.java new file mode 100644 index 0000000..c91bd46 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentBoxVo.java @@ -0,0 +1,18 @@ +package com.ruoyi.domain.dto.exponent; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ExponentBoxVo { + private Integer boxId; + private Integer boxTypeId; + private String boxName; + private BigDecimal price; + private String boxImg01; + private String boxImg02; + private String boxType; + private int buy_forward_up; + private int buy_forward_down; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentInfoVo.java b/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentInfoVo.java new file mode 100644 index 0000000..23e924c --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentInfoVo.java @@ -0,0 +1,79 @@ +package com.ruoyi.domain.dto.exponent; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +/** + * { + * "id": 361971, + * "exponent": 0, + * "period": "202412292560", + * "end_time": 1735481055, + * "points": "", + * "state": 0, + * "number": 2560, + * "forward": 0, + * "createtime": 1735481025, + * "updatetime": 1735481025, + * "timer": 17, + * "last": { + * "id": 361970, + * "exponent": 30003467, + * "period": "202412292559", + * "end_time": 1735481024, + * "points": "30000833,29994529,30007775,30006661,30002185,30005613,30003447,29991295,29990895,29995306,30003467", + * "state": 2, + * "number": 2559, + * "forward": 1, + * "createtime": 1735480994, + * "updatetime": 1735480994 + * }, + * "buy": 0, + * "buy_forward_up": 0, + * "buy_forward_down": 0, + * "jieguo": { + * "skinarr": [ + * { + * "id": 297, + * "image": "https:\/\/guangdun.oss-cn-beijing.aliyuncs.com\/uploads\/20241030\/9839b86148a39d2c69ea882dc40cccc3.webp", + * "count": 1 + * } + * ], + * "period": "202412292559", + * "exponent": 30003467, + * "forward": 1, + * "buy_forward_down": 0, + * "buy_forward_up": 10, + * "buy": 10, + * "buy_money": 10 + * } + * } + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class ExponentInfoVo { + private Integer id; + private Integer exponent; + private String period; + private Integer end_time; + private String points; + private Integer state; + private Integer number; + private Integer forward; + private Integer createtime; + private Integer updatetime; + private Integer timer; + private ExponentInfoVo last; + private Integer buy = 0; + private BigDecimal buy_forward_up = BigDecimal.ZERO; + private BigDecimal buy_forward_down = BigDecimal.ZERO; + private ExponentJieguoVo jieguo; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentJieguoVo.java b/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentJieguoVo.java new file mode 100644 index 0000000..460f65b --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentJieguoVo.java @@ -0,0 +1,72 @@ +package com.ruoyi.domain.dto.exponent; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.util.List; + +/** + * { + * "id": 361971, + * "exponent": 0, + * "period": "202412292560", + * "end_time": 1735481055, + * "points": "", + * "state": 0, + * "number": 2560, + * "forward": 0, + * "createtime": 1735481025, + * "updatetime": 1735481025, + * "timer": 17, + * "last": { + * "id": 361970, + * "exponent": 30003467, + * "period": "202412292559", + * "end_time": 1735481024, + * "points": "30000833,29994529,30007775,30006661,30002185,30005613,30003447,29991295,29990895,29995306,30003467", + * "state": 2, + * "number": 2559, + * "forward": 1, + * "createtime": 1735480994, + * "updatetime": 1735480994 + * }, + * "buy": 0, + * "buy_forward_up": 0, + * "buy_forward_down": 0, + * "jieguo": { + * "skinarr": [ + * { + * "id": 297, + * "image": "https:\/\/guangdun.oss-cn-beijing.aliyuncs.com\/uploads\/20241030\/9839b86148a39d2c69ea882dc40cccc3.webp", + * "count": 1 + * } + * ], + * "period": "202412292559", + * "exponent": 30003467, + * "forward": 1, + * "buy_forward_down": 0, + * "buy_forward_up": 10, + * "buy": 10, + * "buy_money": 10 + * } + * } + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class ExponentJieguoVo { + private List skinarr; + private Integer exponent; + private String period; + private Integer forward; + private Integer buy = 0; + private BigDecimal buy_money = BigDecimal.ZERO; + private BigDecimal buy_forward_up = BigDecimal.ZERO; + private BigDecimal buy_forward_down = BigDecimal.ZERO; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentJoinParam.java b/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentJoinParam.java new file mode 100644 index 0000000..8965bdf --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentJoinParam.java @@ -0,0 +1,25 @@ +package com.ruoyi.domain.dto.exponent; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class ExponentJoinParam { + /** + * 指数ID + */ + private Integer id; + /** + * 盒子ID + */ + private Integer boxId; + /** + * 1上升2下降 + */ + private Integer forward; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentOpenBoxVo.java b/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentOpenBoxVo.java new file mode 100644 index 0000000..5833b79 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentOpenBoxVo.java @@ -0,0 +1,21 @@ +package com.ruoyi.domain.dto.exponent; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +@Data +public class ExponentOpenBoxVo { + private Integer id; + private Integer forward; + private Integer boxId; + private Integer success; + private Integer failed; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + private String image; + private String image2; + // 1成功 2失败 + private int state; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentSkinarrVo.java b/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentSkinarrVo.java new file mode 100644 index 0000000..bd40118 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/exponent/ExponentSkinarrVo.java @@ -0,0 +1,65 @@ +package com.ruoyi.domain.dto.exponent; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * { + * "id": 361971, + * "exponent": 0, + * "period": "202412292560", + * "end_time": 1735481055, + * "points": "", + * "state": 0, + * "number": 2560, + * "forward": 0, + * "createtime": 1735481025, + * "updatetime": 1735481025, + * "timer": 17, + * "last": { + * "id": 361970, + * "exponent": 30003467, + * "period": "202412292559", + * "end_time": 1735481024, + * "points": "30000833,29994529,30007775,30006661,30002185,30005613,30003447,29991295,29990895,29995306,30003467", + * "state": 2, + * "number": 2559, + * "forward": 1, + * "createtime": 1735480994, + * "updatetime": 1735480994 + * }, + * "buy": 0, + * "buy_forward_up": 0, + * "buy_forward_down": 0, + * "jieguo": { + * "skinarr": [ + * { + * "id": 297, + * "image": "https:\/\/guangdun.oss-cn-beijing.aliyuncs.com\/uploads\/20241030\/9839b86148a39d2c69ea882dc40cccc3.webp", + * "count": 1 + * } + * ], + * "period": "202412292559", + * "exponent": 30003467, + * "forward": 1, + * "buy_forward_down": 0, + * "buy_forward_up": 10, + * "buy": 10, + * "buy_money": 10 + * } + * } + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class ExponentSkinarrVo { + private Integer id; + private String image; + private String image2; + private Integer count; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/fight/FightDetailParam.java b/skins-model/src/main/java/com/ruoyi/domain/dto/fight/FightDetailParam.java new file mode 100644 index 0000000..81976a2 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/fight/FightDetailParam.java @@ -0,0 +1,28 @@ +package com.ruoyi.domain.dto.fight; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class FightDetailParam { + + @NotNull(message = "fightId不能为空") + private Integer fightId; + + @Min(value = 1,message = "最小1") + private Integer page; + + @Min(value = 1,message = "最小1") + @Max(value = 20,message = "最大20") + private Integer size; + + private Integer limit; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/fight/FightOnMyOwnParam.java b/skins-model/src/main/java/com/ruoyi/domain/dto/fight/FightOnMyOwnParam.java new file mode 100644 index 0000000..3c62266 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/fight/FightOnMyOwnParam.java @@ -0,0 +1,27 @@ +package com.ruoyi.domain.dto.fight; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class FightOnMyOwnParam { + + private Integer fightId; + + private Integer playerId; + + // 模式 + private String model; + + // 状态列表 + private List statusList; + + private Integer page; + + private Integer size; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/mayi/ApiPayAddOrderParam.java b/skins-model/src/main/java/com/ruoyi/domain/dto/mayi/ApiPayAddOrderParam.java new file mode 100644 index 0000000..98b822e --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/mayi/ApiPayAddOrderParam.java @@ -0,0 +1,61 @@ +package com.ruoyi.domain.dto.mayi; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class ApiPayAddOrderParam { + + //平台分配商户号 + private String pay_memberid; + + //上送订单号唯一 + private String pay_orderid; + + //提交时间 2016-12-26 18:18:18 + private String pay_applydate; + + //银行编码 + private String pay_bankcode; + + //服务端通知 + private String pay_notifyurl; + + //页面跳转通知 + private String pay_callbackurl; + + //订单金额 + private String pay_amount; + + //MD5 签名 + private String pay_md5sign; + + //附加字段 + private String pay_attach; + + //商品名称 + private String pay_productname; + + //商户品数量 + private String pay_productnum; + + //商品描述 + private String pay_productdesc; + + //商户链接地址 + private String pay_producturl; + + //分账绑卡 id + private String pay_bindid; + + //客户端 ip + private String pay_clientip; + + //返回数据格式 + private String type; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/mayi/ApiPayAddOrderResponse.java b/skins-model/src/main/java/com/ruoyi/domain/dto/mayi/ApiPayAddOrderResponse.java new file mode 100644 index 0000000..c792c24 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/mayi/ApiPayAddOrderResponse.java @@ -0,0 +1,28 @@ +package com.ruoyi.domain.dto.mayi; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class ApiPayAddOrderResponse { + + //状态 + private String status; + + //消息 + private String msg; + + //金额 + private String pay_amount; + + //订单号 + private String pay_orderid; + + //支付地址 + private String payUrl; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/mayi/PayNotifyRequest.java b/skins-model/src/main/java/com/ruoyi/domain/dto/mayi/PayNotifyRequest.java new file mode 100644 index 0000000..8c6ee71 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/mayi/PayNotifyRequest.java @@ -0,0 +1,41 @@ +package com.ruoyi.domain.dto.mayi; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +// 回调参数 +@AllArgsConstructor +@NoArgsConstructor +@Data +public class PayNotifyRequest { + + //商户编号 + private String memberid; + + //订单号 + private String orderid; + + //订单金额 + private String amount; + + //实付金额 + private String true_amount; + + //交易流水号 + private String transaction_id; + + //交易时间 + private String datetime; + + //交易状态 + private String returncode; + + //扩展返回 + private String attach; + + //签名 + private String sign; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/packSack/DecomposeLogCondition.java b/skins-model/src/main/java/com/ruoyi/domain/dto/packSack/DecomposeLogCondition.java new file mode 100644 index 0000000..1732eec --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/packSack/DecomposeLogCondition.java @@ -0,0 +1,27 @@ +package com.ruoyi.domain.dto.packSack; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class DecomposeLogCondition { + + @Min(value = 1,message = "最小值1") + private Integer page; + + @Min(value = 1,message = "最小值1") + @Max(value = 20,message = "最大值20") + private Integer size; + + private Integer boxRecordStatus; + private Integer limit; + + private Integer userId; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/packSack/DecomposeParam.java b/skins-model/src/main/java/com/ruoyi/domain/dto/packSack/DecomposeParam.java new file mode 100644 index 0000000..99fb862 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/packSack/DecomposeParam.java @@ -0,0 +1,17 @@ +package com.ruoyi.domain.dto.packSack; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class DecomposeParam { + private List packSackIds; + @NotNull(message = "是否全选为必填项") + private Boolean isAll; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/packSack/DeliveryParam.java b/skins-model/src/main/java/com/ruoyi/domain/dto/packSack/DeliveryParam.java new file mode 100644 index 0000000..645a388 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/packSack/DeliveryParam.java @@ -0,0 +1,17 @@ +package com.ruoyi.domain.dto.packSack; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class DeliveryParam { + private List packSackIds; + @NotNull(message = "是否全选为必填项") + private Boolean isAll; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/packSack/PackSackCondition.java b/skins-model/src/main/java/com/ruoyi/domain/dto/packSack/PackSackCondition.java new file mode 100644 index 0000000..b8a4b68 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/packSack/PackSackCondition.java @@ -0,0 +1,44 @@ +package com.ruoyi.domain.dto.packSack; + +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class PackSackCondition { + + private List uidList; + + private List statusList; + + private String name; + + private String beginTime; + + private String endTime; + + @ApiModelProperty("排序字段(1获得时间 2价格)") + private Integer orderByFie; + + @ApiModelProperty("价格排序(1升序 2降序)") + private Integer orderByType; + + @NotNull(message = "页码不能为空") + @Min(value = 1, message = "最小1") + private Integer page; + + @NotNull(message = "分页长度不能为空") + @Min(value = 1, message = "最小1") + @Max(value = 20, message = "最大20") + private Integer size; + + private Integer limit; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/queryCondition/OrnamentCondition.java b/skins-model/src/main/java/com/ruoyi/domain/dto/queryCondition/OrnamentCondition.java new file mode 100644 index 0000000..0b9f277 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/queryCondition/OrnamentCondition.java @@ -0,0 +1,71 @@ +package com.ruoyi.domain.dto.queryCondition; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.Objects; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class OrnamentCondition implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableField(value = "id", fill = FieldFill.INSERT) + private Long id; + + @Excel(name = "长名称") + @TableField("name") + private String name; + + private BigDecimal minPrice; + + private BigDecimal maxPrice; + + @Excel(name = "类别") + private String type; + + @Excel(name = "品质") + private String quality; + + @Excel(name = "稀有度") + private String rarity; + + @Excel(name = "外观") + private String exterior; + + // 1上架 0下架 + private String isPutaway; + + // 0是本网站自定义道具 1否 + private Boolean isProprietaryProperty; + + @Min(value = 1,message = "最小1") + private Integer page; + + @Max(value = 20,message = "最大20") + private Integer size; + + @Min(value = 1,message = "最小1") + private Integer pageNum; + + @Max(value = 20,message = "最大20") + private Integer pageSize; + + private Integer limit; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/queryCondition/RollPrizesCondition.java b/skins-model/src/main/java/com/ruoyi/domain/dto/queryCondition/RollPrizesCondition.java new file mode 100644 index 0000000..163f922 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/queryCondition/RollPrizesCondition.java @@ -0,0 +1,29 @@ +package com.ruoyi.domain.dto.queryCondition; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class RollPrizesCondition { + + @NotNull(message = "rollId不能为空。") + private Integer rollId; + + @Min(value = 1,message = "最小1") + @NotNull(message = "page不能为空") + private Integer page; + + @Max(value = 20,message = "最大20") + @NotNull(message = "page不能为空") + private Integer size; + + private Integer limit; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/recharge/RechargeConfigOrderVo.java b/skins-model/src/main/java/com/ruoyi/domain/dto/recharge/RechargeConfigOrderVo.java new file mode 100644 index 0000000..1183461 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/recharge/RechargeConfigOrderVo.java @@ -0,0 +1,24 @@ +package com.ruoyi.domain.dto.recharge; + +import com.ruoyi.common.annotation.Excel; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class RechargeConfigOrderVo { + @Excel(name = "商品ID") + private Integer goodsId; + + @Excel(name = "商品价格") + private BigDecimal goodsPrice; + + @Excel(name = "商品数量") + private Integer goodsNum; + + @Excel(name = "商品总价") + private BigDecimal totalAmount; + + @Excel(name = "支付图片") + private String picture; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/roll/GetRollOpenPrizeParam.java b/skins-model/src/main/java/com/ruoyi/domain/dto/roll/GetRollOpenPrizeParam.java new file mode 100644 index 0000000..ec0612d --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/roll/GetRollOpenPrizeParam.java @@ -0,0 +1,27 @@ +package com.ruoyi.domain.dto.roll; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class GetRollOpenPrizeParam { + + @NotNull(message = "不能为空") + private Integer rollId; + + @Min(value = 1,message = "最小1") + private Integer page; + + @Min(value = 1,message = "最小1") + @Max(value = 20,message = "最大20") + private Integer size; + + private Integer limit; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/roll/GetRollPlayersParam.java b/skins-model/src/main/java/com/ruoyi/domain/dto/roll/GetRollPlayersParam.java new file mode 100644 index 0000000..5d2ba12 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/roll/GetRollPlayersParam.java @@ -0,0 +1,27 @@ +package com.ruoyi.domain.dto.roll; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class GetRollPlayersParam { + + @NotNull(message = "不能为空") + private Integer rollId; + + @Min(value = 1, message = "页码最小为1") + private Integer page; + + @Min(value = 1, message = "长度最小为1") + @Max(value = 20, message = "长度最大为20") + private Integer size; + + private Integer limit; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/roll/GetRollPrizePool.java b/skins-model/src/main/java/com/ruoyi/domain/dto/roll/GetRollPrizePool.java new file mode 100644 index 0000000..2d9f116 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/roll/GetRollPrizePool.java @@ -0,0 +1,29 @@ +package com.ruoyi.domain.dto.roll; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class GetRollPrizePool { + + @NotNull(message = "不能为空") + private Integer rollId; + + private Integer jackpotId; + + @Min(value = 1,message = "最小1") + private Integer page; + + @Min(value = 1,message = "最小1") + @Max(value = 20,message = "最大20") + private Integer size; + + private Integer limit; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/roll/InviteRollUser.java b/skins-model/src/main/java/com/ruoyi/domain/dto/roll/InviteRollUser.java new file mode 100644 index 0000000..6ee57f9 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/roll/InviteRollUser.java @@ -0,0 +1,24 @@ +package com.ruoyi.domain.dto.roll; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class InviteRollUser { + + @NotNull(message = "rollId不能为空") + private Integer rollId; + + @NotEmpty + private List userIds; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/rollJackpotOrnament/RollJOEdit.java b/skins-model/src/main/java/com/ruoyi/domain/dto/rollJackpotOrnament/RollJOEdit.java new file mode 100644 index 0000000..d8fbe3d --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/rollJackpotOrnament/RollJOEdit.java @@ -0,0 +1,60 @@ +package com.ruoyi.domain.dto.rollJackpotOrnament; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class RollJOEdit { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "Roll房奖池ID") + private Integer jackpotId; + + @Excel(name = "Roll房奖池饰品ID") + private Long ornamentsId; + + @Excel(name = "饰品价格") + private BigDecimal price; + + @Excel(name = "饰品图片id") + private String ornamentImgId; + + @Excel(name = "饰品图片") + private String imgUrl; + + @Excel(name = "饰品名称") + private String ornamentName; + + @Excel(name = "饰品级别ID") + private Integer ornamentsLevelId; + + @Excel(name = "饰品数量") + private Integer ornamentsNum; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/sys/OrderQueryCondition.java b/skins-model/src/main/java/com/ruoyi/domain/dto/sys/OrderQueryCondition.java new file mode 100644 index 0000000..359f210 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/sys/OrderQueryCondition.java @@ -0,0 +1,41 @@ +package com.ruoyi.domain.dto.sys; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class OrderQueryCondition { + + private List uidList; + + private List statusList; + + // 时间区间 + private String beginTime; + + private String endTime; + + // 排序字段: time、totalAmount + private String orderByFie; + + // 升1 降2 + private Integer orderByType; + + @Min(value = 1,message = "最小1") + private Integer page; + + @Min(value = 1,message = "最小1") + @Max(value = 20,message = "最大20") + private Integer size; + + private Integer limit; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/sys/TeamUsersParam.java b/skins-model/src/main/java/com/ruoyi/domain/dto/sys/TeamUsersParam.java new file mode 100644 index 0000000..9a23d44 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/sys/TeamUsersParam.java @@ -0,0 +1,34 @@ +package com.ruoyi.domain.dto.sys; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import java.util.List; +@AllArgsConstructor +@NoArgsConstructor +@Data +public class TeamUsersParam { + + private String userName; + private List bossIds; + private List employeeIds; + + // 排序字段 1、充值金额 2、综合消费 3、金币消费 4、积分消费 + private Integer orderByFie; + // 1升 2降 + private Integer orderType; + + // 时间区间 格式:yyyy-MM-dd HH:mm:ss + private String beginTime; + private String endTime; + + @Min(value = 1,message = "最小值1") + private Integer page; + @Min(value = 1,message = "最小值1") + @Max(value = 20,message = "最大值20") + private Integer size; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/upgrade/UpgradeCondition.java b/skins-model/src/main/java/com/ruoyi/domain/dto/upgrade/UpgradeCondition.java new file mode 100644 index 0000000..a619f64 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/upgrade/UpgradeCondition.java @@ -0,0 +1,29 @@ +package com.ruoyi.domain.dto.upgrade; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Range; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class UpgradeCondition { + + private Long upgradeRecordId; + + //@NotNull(message = "升级饰品id不能为空。") + private Long ornamentId; + private String userType; + private Integer userId; + + @Min(value = 1,message = "页码最小1") + private Integer page; + @Range(min = 1,max = 20) + private Integer size; + + private Integer limit; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/userRecord/AmountRecordsDetailCondition.java b/skins-model/src/main/java/com/ruoyi/domain/dto/userRecord/AmountRecordsDetailCondition.java new file mode 100644 index 0000000..8702fc1 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/userRecord/AmountRecordsDetailCondition.java @@ -0,0 +1,40 @@ +package com.ruoyi.domain.dto.userRecord; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import java.util.Date; + +@Data +public class AmountRecordsDetailCondition { + + private Integer userId; + + private String nickName; + + private String phoneNumber; + + // 收入1 支出0 + private Integer type; + + private String source; + + // 1金币 2积分 + @NotNull(message = "moneyType不能为空") + private Integer moneyType; + + @Min(value = 1,message = "最小值1") + private Integer page; + + @Min(value = 1,message = "最小值1") + private Integer size; + private Integer limit; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/userRecord/BlendErcashCondition.java b/skins-model/src/main/java/com/ruoyi/domain/dto/userRecord/BlendErcashCondition.java new file mode 100644 index 0000000..5ced473 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/userRecord/BlendErcashCondition.java @@ -0,0 +1,37 @@ +package com.ruoyi.domain.dto.userRecord; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class BlendErcashCondition { + + private Integer userId; + + private Integer source; + + private Integer type; + + private String userName; + + private Integer moneyType; + + @NotNull(message = "页码不能为空") + @Min(value = 1,message = "最小1") + private Integer page; + + @NotNull(message = "分页长度不能为空") + @Min(value = 1,message = "最小1") + @Max(value = 20,message = "最大20") + private Integer size; + + private Integer limit; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/userRecord/DeliveryRecordsConfition.java b/skins-model/src/main/java/com/ruoyi/domain/dto/userRecord/DeliveryRecordsConfition.java new file mode 100644 index 0000000..7d2aba6 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/userRecord/DeliveryRecordsConfition.java @@ -0,0 +1,32 @@ +package com.ruoyi.domain.dto.userRecord; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Data +public class DeliveryRecordsConfition { + + private List statusList; + + private List uIdList; + + @Min(value = 1,message = "最小1") + private Integer page; + + @Min(value = 1,message = "最小1") + @Max(value = 20,message = "最大20") + private Integer size; + + private Integer limit; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/userRecord/OrderCondition.java b/skins-model/src/main/java/com/ruoyi/domain/dto/userRecord/OrderCondition.java new file mode 100644 index 0000000..ff9aa8e --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/userRecord/OrderCondition.java @@ -0,0 +1,32 @@ +package com.ruoyi.domain.dto.userRecord; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class OrderCondition { + + private List userIdList; + + @Min(value = 1,message = "最小1") + private Integer page; + + @Min(value = 1,message = "最小1") + private Integer size; + + private Integer limit; + + // 1时间升序 2 时间降序 + private Integer orderBy; + + @NotNull(message = "订单状态不能为空") + private Integer status; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/yyyouping/OnSaleCommodityDTO.java b/skins-model/src/main/java/com/ruoyi/domain/dto/yyyouping/OnSaleCommodityDTO.java new file mode 100644 index 0000000..ba82a0a --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/yyyouping/OnSaleCommodityDTO.java @@ -0,0 +1,9 @@ +package com.ruoyi.domain.dto.yyyouping; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +public class OnSaleCommodityDTO { + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/yyyouping/OrnamentYY.java b/skins-model/src/main/java/com/ruoyi/domain/dto/yyyouping/OrnamentYY.java new file mode 100644 index 0000000..6d85379 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/yyyouping/OrnamentYY.java @@ -0,0 +1,67 @@ +package com.ruoyi.domain.dto.yyyouping; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.sql.Timestamp; + +// yy平台下载的饰品信息 +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class OrnamentYY implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(value = "id",type = IdType.AUTO) + private Long id; + + @Excel(name = "名称") + @TableField("name") + private String name; + + @Excel(name = "哈希名称") + @TableField("hash_name") + private String hashName; + + @Excel(name = "类型id") + @TableField("type_id") + private String typeId; + + @Excel(name = "type_name") + @TableField("type_name") + private String typeName; + + @Excel(name = "type_hash_name") + @TableField("type_hash_name") + private String typeHashName; + + @Excel(name = "weapon_id") + @TableField("weapon_id") + private Integer weaponId; + + @Excel(name = "weapon_name") + @TableField("weapon_name") + private String weaponName; + + @Excel(name = "weapon_hash_name") + @TableField("weapon_hash_name") + private String weaponHashName; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField("update_time") + private Timestamp updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/dto/zbt/OrnamentZBT.java b/skins-model/src/main/java/com/ruoyi/domain/dto/zbt/OrnamentZBT.java new file mode 100644 index 0000000..09a729f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/dto/zbt/OrnamentZBT.java @@ -0,0 +1,128 @@ +package com.ruoyi.domain.dto.zbt; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.Objects; + +// zbt平台下载的饰品信息 +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class OrnamentZBT implements Serializable { + + @AllArgsConstructor + @NoArgsConstructor + @Data + public static class PriceInfo{ + private Long userId; + private BigDecimal price; + private Integer quantity; + private BigDecimal autoDeliverPrice; + private Integer autoDeliverQuantity; + private BigDecimal manualDeliverPrice; + private Integer manualQuantity; + } + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + private String appId; + + @Excel(name = "ZBT官网饰品ID") + private Long itemId; + + @Excel(name = "价格和在售信息") + private PriceInfo priceInfo; + + @Excel(name = "长名称") + @TableField("name") + private String itemName; + + @Excel(name = "饰品唯一名称英文") + private String marketHashName; + + @Excel(name = "短名称") + private String shortName; + + @Excel(name = "图片") + private String imageUrl; + + @Excel(name = "类别") + private String type; + + @Excel(name = "类别中文名") + private String typeName; + + @Excel(name = "品质") + private String quality; + + @Excel(name = "品质名称") + private String qualityName; + + @Excel(name = "品质颜色") + private String qualityColor; + + @Excel(name = "稀有度") + private String rarity; + + @Excel(name = "稀有度名称") + private String rarityName; + + @Excel(name = "稀有度颜色") + private String rarityColor; + + @Excel(name = "外观") + private String exterior; + + @Excel(name = "外观名称") + private String exteriorName; + + // @Excel(name = "本网站使用价格") + // private BigDecimal usePrice; + + // @Excel(name = "在售最低价") + // private BigDecimal price; + + // @Excel(name = "在售数量") + // private Integer quantity; + + // @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + // private Date createTime; + // + // @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + // private Date updateTime; + // + // private String remark; + // + // private String isPutaway; + // + // private String isProprietaryProperty; + + // @Override + // public boolean equals(Object o) { + // if (this == o) return true; + // if (o == null || getClass() != o.getClass()) return false; + // OrnamentZBT that = (OrnamentZBT) o; + // return Objects.equals(marketHashName, that.marketHashName); + // } + // + // @Override + // public int hashCode() { + // return Objects.hash(marketHashName); + // } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/BoxTransferRecord.java b/skins-model/src/main/java/com/ruoyi/domain/entity/BoxTransferRecord.java new file mode 100644 index 0000000..b8a3031 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/BoxTransferRecord.java @@ -0,0 +1,71 @@ +package com.ruoyi.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@TableName("tt_box_transfer_record") +public class BoxTransferRecord implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 转增人uid + */ + private Long srcUid; + + /** + * 授予人uid + */ + private Long dstUid; + + /** + * 宝箱记录ID + */ + private Long boxRecordId; + + /** + * 宝箱中饰品ID + */ + private Long ornamentId; + + private String marketHashName; + + private String ornamentsZbtId; + + private String ornamentsYyId; + + private String ornamentName; + + private BigDecimal ornamentsPrice; + + private String imageUrl; + + private Integer ornamentsLevelId; + + private String ornamentLevelImg; + + /** + * 记录创建时间 + */ + private LocalDateTime createTime; + + /** + * 记录更新时间 + */ + private LocalDateTime updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/GameSugarSpin.java b/skins-model/src/main/java/com/ruoyi/domain/entity/GameSugarSpin.java new file mode 100644 index 0000000..8eccc29 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/GameSugarSpin.java @@ -0,0 +1,28 @@ +package com.ruoyi.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "game_sugar_spin") +public class GameSugarSpin implements Serializable { + private Long gid; + private BigDecimal score; + private Integer count; + private String feature; + private Integer extraFree; + private LocalDateTime createTime; + private LocalDateTime updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/GameSugarStepInfo.java b/skins-model/src/main/java/com/ruoyi/domain/entity/GameSugarStepInfo.java new file mode 100644 index 0000000..7b12426 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/GameSugarStepInfo.java @@ -0,0 +1,44 @@ +package com.ruoyi.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "game_sugar_step_info") +public class GameSugarStepInfo implements Serializable { + private Long id; + private Long gid; + private Integer freeSpinId; + private Integer aes; + private String multipler; + private List multipler2; + private String grid; + private String symbolLinks; + private List symbolLinks2; + private BigDecimal score; + private LocalDateTime createTime; + private LocalDateTime updateTime; + + @Data + public static class SymbolLink implements Serializable { + private String symbol; + private List loc; + private List multipler; + private Integer totalMulti; + private Double baseScore; + private Double score; + } +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/GameSugarUser.java b/skins-model/src/main/java/com/ruoyi/domain/entity/GameSugarUser.java new file mode 100644 index 0000000..eaf93c2 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/GameSugarUser.java @@ -0,0 +1,30 @@ +package com.ruoyi.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "game_sugar_user") +public class GameSugarUser implements Serializable { + private Long id; + private Integer userId; + private BigDecimal totalBet; + private BigDecimal totalWin; + private Integer count; + private Integer freeCount; + private Integer superFreeCount; + private LocalDateTime createTime; + private LocalDateTime updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/GameSugarWin.java b/skins-model/src/main/java/com/ruoyi/domain/entity/GameSugarWin.java new file mode 100644 index 0000000..828814e --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/GameSugarWin.java @@ -0,0 +1,31 @@ +package com.ruoyi.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "game_sugar_win") +public class GameSugarWin implements Serializable { + private Long id; + private LocalDateTime date; + private BigDecimal initBet; + private BigDecimal totalBet; + private BigDecimal totalWin; + private Integer count; + private Integer freeCount; + private Integer superFreeCount; + private LocalDateTime createTime; + private LocalDateTime updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/TtBoxRecords.java b/skins-model/src/main/java/com/ruoyi/domain/entity/TtBoxRecords.java new file mode 100644 index 0000000..073a432 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/TtBoxRecords.java @@ -0,0 +1,104 @@ +package com.ruoyi.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_box_records") +public class TtBoxRecords implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Long id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "宝箱ID") + private Integer boxId; + + @Excel(name = "宝箱名称") + private String boxName; + + @Excel(name = "宝箱价格") + private BigDecimal boxPrice; + + @Excel(name = "饰品ID") + @TableField("ornament_id") + private Long ornamentId; + + @Excel(name = "市场唯一hash名称") + @TableField("market_hash_name") + private String marketHashName; + + @Excel(name = "zbt ID") + @TableField("ornaments_zbt_id") + private String ornamentsZbtId; + + @Excel(name = "yy ID") + @TableField("ornaments_yy_id") + private String ornamentsYyId; + + @Excel(name = "饰品名称") + private String ornamentName; + + @Excel(name = "饰品价格") + private BigDecimal ornamentsPrice; + + @Excel(name = "饰品图片") + private String imageUrl; + + @Excel(name = "饰品级别ID") + private Integer ornamentsLevelId; + + @Excel(name = "饰品级别图片") + private String ornamentLevelImg; + + // 0、在背包显示 1、已分解 + @Excel(name = "状态") + private Integer status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + // 0、对战 + @Excel(name = "饰品来源") + private Integer source; + + @Excel(name = "对战ID") + private Integer fightId; + + @Excel(name = "对战模式:回合数") + private Integer fightRoundNumber; + + @Excel(name = "Roll房ID") + private Integer rollId; + + @Excel(name = "持有者_用户ID") + private Integer holderUserId; + + // 用于对战模式,游戏完全结束才显示 + // @TableField("is_show") + // private Integer isShow; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/TtCommissionRecord.java b/skins-model/src/main/java/com/ruoyi/domain/entity/TtCommissionRecord.java new file mode 100644 index 0000000..ffe7054 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/TtCommissionRecord.java @@ -0,0 +1,43 @@ +package com.ruoyi.domain.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class TtCommissionRecord { + + /** + * ID + */ + private Integer id; + + /** + * 推广人员ID + */ + private Integer userId; + + /** + * 佣金 + */ + private BigDecimal commission; + + /** + * 汇总时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date summaryTime; + + /** + * 领取状态(0未领取 1已领取) + */ + private String claimStatus; + + /** + * 领取时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date claimTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/TtOrder.java b/skins-model/src/main/java/com/ruoyi/domain/entity/TtOrder.java new file mode 100644 index 0000000..fb20786 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/TtOrder.java @@ -0,0 +1,80 @@ +package com.ruoyi.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_order") +public class TtOrder implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + // 见PartyType + @Excel(name = "第三方") + private String thirdParty; + /** + * 0卡密 1支付宝 2微信 见PayType + */ + @Excel(name = "订单类型") + private String type; + + @Excel(name = "商品ID") + private Integer goodsId; + + @Excel(name = "商品价格") + private BigDecimal goodsPrice; + + @Excel(name = "商品数量") + private Integer goodsNum; + + @Excel(name = "商品总价") + private BigDecimal totalAmount; + + @Excel(name = "订单号") + private String orderId; + + @Excel(name = "外部订单号") + private String outTradeNo; + + @Excel(name = "sign验签密钥") + private String sign; + + @Excel(name = "支付状态") + private String status; + + @Excel(name = "支付跳转链接") + private String payUrl; + + @Excel(name = "百度推广bdVid") + private String bdVid; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/TtOrnament.java b/skins-model/src/main/java/com/ruoyi/domain/entity/TtOrnament.java new file mode 100644 index 0000000..52e261a --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/TtOrnament.java @@ -0,0 +1,123 @@ +package com.ruoyi.domain.entity; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.Objects; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_ornament") +public class TtOrnament implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "饰品唯一名称英文") + @TableId(value = "market_hash_name", type = IdType.INPUT) + private String marketHashName; + + @TableField(value = "id", fill = FieldFill.INSERT) + private Long id; + + @Excel(name = "zbt_id") + @TableField("zbt_id") + private Long zbtId; + + @Excel(name = "yyyouping_id") + @TableField("yyyouping_id") + private Long yyyoupingId; + + @Excel(name = "长名称") + @TableField("name") + private String name; + + @Excel(name = "本网站使用价格(rmb)") + private BigDecimal usePrice; + + @Excel(name = "图片") + private String imageUrl; + + @Override + public int hashCode() { + return Objects.hash(this.marketHashName); + } + + @Excel(name = "在售最低价") + private BigDecimal price; + + @Excel(name = "在售数量") + private Integer quantity; + + @Excel(name = "短名称") + private String shortName; + + @Excel(name = "类别") + private String type; + + @Excel(name = "类别中文名") + private String typeName; + + @Excel(name = "类别hash名") + @TableField("type_hash_name") + private String typeHashName; + + @Excel(name = "品质") + private String quality; + + @Excel(name = "品质hash") + private String qualityHashName; + + @Excel(name = "品质名称") + private String qualityName; + + @Excel(name = "品质颜色") + private String qualityColor; + + @Excel(name = "稀有度") + private String rarity; + + private String rarityHashName; + + @Excel(name = "稀有度名称") + private String rarityName; + + @Excel(name = "稀有度颜色") + private String rarityColor; + + @Excel(name = "外观") + private String exterior; + + @Excel(name = "外观") + private String exteriorHashName; + + @Excel(name = "外观名称") + private String exteriorName; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + private String remark; + + // 0上架 1下架 + private String isPutaway; + + // 0是本网站自定义道具 1否 + private String isProprietaryProperty; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/TtOrnamentsYY.java b/skins-model/src/main/java/com/ruoyi/domain/entity/TtOrnamentsYY.java new file mode 100644 index 0000000..c17a8de --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/TtOrnamentsYY.java @@ -0,0 +1,72 @@ +package com.ruoyi.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_ornament_yy") +public class TtOrnamentsYY implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(value = "id",type = IdType.AUTO) + private Integer id; + + @Excel(name = "哈希名称") + @TableField("hash_name") + private String hashName; + + @Excel(name = "名称") + @TableField("name") + private String name; + + @Excel(name = "类型id") + @TableField("type_id") + private Integer typeId; + + @Excel(name = "type_name") + @TableField("type_name") + private String typeName; + + @Excel(name = "type_hash_name") + @TableField("type_hash_name") + private String typeHashName; + + @Excel(name = "weapon_id") + @TableField("weapon_id") + private Integer weaponId; + + @Excel(name = "weapon_name") + @TableField("weapon_name") + private String weaponName; + + @Excel(name = "weapon_hash_name") + @TableField("weapon_hash_name") + private String weaponHashName; + + // @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + // private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField("update_time") + private Timestamp updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/TtPromotionLevel.java b/skins-model/src/main/java/com/ruoyi/domain/entity/TtPromotionLevel.java new file mode 100644 index 0000000..9ce96c0 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/TtPromotionLevel.java @@ -0,0 +1,57 @@ +package com.ruoyi.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_promotion_level") +public class TtPromotionLevel implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "推广等级名称") + private String name; + + @Excel(name = "推广等级图标") + private String icon; + + @Excel(name = "推广等级达标金额") + private BigDecimal rechargeThreshold; + + @Excel(name = "推广返佣比例") + private BigDecimal commissions; + + @Excel(name = "等级达标奖励红包金额") + private BigDecimal addedBonus; + + @Excel(name = "充值赠送比例") + private BigDecimal rechargeGiftRatio; + + @Excel(name = "描述") + private String description; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/TtPromotionRecord.java b/skins-model/src/main/java/com/ruoyi/domain/entity/TtPromotionRecord.java new file mode 100644 index 0000000..96c8d52 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/TtPromotionRecord.java @@ -0,0 +1,60 @@ +package com.ruoyi.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_promotion_record") +public class TtPromotionRecord implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "下级用户ID") + private Integer subordinateUserId; + + @Excel(name = "下级用户充值金额") + private BigDecimal rechargePrice; + + /** + * 下级用户总消费 + */ + @TableField(exist = false) + private BigDecimal totalConsumption; + + @Excel(name = "返佣金额") + private BigDecimal rebate; + + @Excel(name = "充值记录ID") + private Integer rechargeRecordId; + + private String status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/TtRechargeConfig.java b/skins-model/src/main/java/com/ruoyi/domain/entity/TtRechargeConfig.java new file mode 100644 index 0000000..31eb600 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/TtRechargeConfig.java @@ -0,0 +1,48 @@ +package com.ruoyi.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_recharge_config") +public class TtRechargeConfig implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + // 名称 + private String name; + + @Excel(name = "充值金额") + private BigDecimal price; + + // 0启用,1禁用 + @Excel(name = "状态") + private String status; + + // 产品:金币 + @TableField("product_a") + private BigDecimal productA; + + // 图片信息 + @TableField("picture") + private String picture; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/TtRechargeProd.java b/skins-model/src/main/java/com/ruoyi/domain/entity/TtRechargeProd.java new file mode 100644 index 0000000..0380d7c --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/TtRechargeProd.java @@ -0,0 +1,57 @@ +package com.ruoyi.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_recharge_prod") +public class TtRechargeProd implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + // 名称 + private String name; + + @Excel(name = "充值金额") + private BigDecimal price; + + // 0启用,1禁用 + @Excel(name = "状态") + private String status; + + @Excel(name = "卡密链接") + private String cardLink; + + // 产品:金币 + @TableField("product_a") + private BigDecimal productA; + + // 产品:积分 + @TableField("product_c") + private BigDecimal productC; + + // 图片信息 + @TableField("picture") + private String picture; + + @Excel(name = "交易链接") + private String transactionLink; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/TtUserBlendErcash.java b/skins-model/src/main/java/com/ruoyi/domain/entity/TtUserBlendErcash.java new file mode 100644 index 0000000..86ba971 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/TtUserBlendErcash.java @@ -0,0 +1,86 @@ +package com.ruoyi.domain.entity; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Date; +import java.util.Objects; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_user_blend_ercash") +public class TtUserBlendErcash implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "id") + @TableId(value = "id",type = IdType.AUTO) + private Long id; + + @Excel(name = "user_id") + @TableField("user_id") + private Integer userId; + + @Excel(name = "金币") + @TableField("amount") + private BigDecimal amount; + + @Excel(name = "final_amount") + @TableField("final_amount") + private BigDecimal finalAmount; + + @Excel(name = "积分") + @TableField("credits") + private BigDecimal credits; + + @Excel(name = "final_credits") + @TableField("final_credits") + private BigDecimal finalCredits; + + @Excel(name = "合计") + @TableField("total") + private BigDecimal total; + + /** + * @see com.ruoyi.domain.common.constant.TtAccountRecordSource + */ + @Excel(name = "来源") + @TableField("source") + private Integer source; + + /** + * @see com.ruoyi.domain.common.constant.TtAccountRecordType + */ + @Excel(name = "收支类型") + @TableField("type") + private Integer type; + + @Excel(name = "create_time") + @TableField("create_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Timestamp createTime; + + @Excel(name = "update_time") + @TableField("update_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Timestamp updateTime; + + @Excel(name = "笔记") + @TableField("remark") + private String remark; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/box/TtBoxThirdExplosiveUser.java b/skins-model/src/main/java/com/ruoyi/domain/entity/box/TtBoxThirdExplosiveUser.java new file mode 100644 index 0000000..f254964 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/box/TtBoxThirdExplosiveUser.java @@ -0,0 +1,41 @@ +package com.ruoyi.domain.entity.box; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_box_third_explosive_user") +public class TtBoxThirdExplosiveUser implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "宝箱ID") + private Integer boxId; + + @Excel(name = "参与者_用户ID") + private Integer userId; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/delivery/TtDeliveryRecord.java b/skins-model/src/main/java/com/ruoyi/domain/entity/delivery/TtDeliveryRecord.java new file mode 100644 index 0000000..5a0211f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/delivery/TtDeliveryRecord.java @@ -0,0 +1,84 @@ +package com.ruoyi.domain.entity.delivery; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_delivery_record") +public class TtDeliveryRecord implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "用户背包记录ID") + private Long boxRecordsId; + + @Excel(name = "饰品ID") + @TableField("ornament_id") + private Long ornamentId; + + @Excel(name = "市场唯一hashName") + @TableField("market_hash_name") + private String marketHashName; + + @Excel(name = "饰品价格") + private BigDecimal ornamentsPrice; + + @Excel(name = "网站订单号") + private String outTradeNo; + + /** + * 网站发货模式(1人工发货 2自动发货 3主播号提取) + */ + @Excel(name = "网站发货模式") + private Integer delivery; + + @Excel(name = "实际购买价格") + private BigDecimal buyPrice; + + @Excel(name = "第三方平台订单号") + private String orderId; + + @Excel(name = "第三方发货模式") + private Integer thirdpartyDelivery; + + /** + * 发货订单状态(1待发货 3待收货 10订单完成 11订单取消) + */ + @Excel(name = "发货订单状态") + private Integer status; + + private String message; + + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/exponent/TtExponent.java b/skins-model/src/main/java/com/ruoyi/domain/entity/exponent/TtExponent.java new file mode 100644 index 0000000..d013456 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/exponent/TtExponent.java @@ -0,0 +1,48 @@ +package com.ruoyi.domain.entity.exponent; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_exponent") +public class TtExponent implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "指数结果") + private Integer exponent; + + @Excel(name = "指数生成点") + private String points; + + // 1上升 2下降 + @Excel(name = "") + private Integer forward; + + @Excel(name = "当天第几期") + private Integer number; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/exponent/TtExponentUser.java b/skins-model/src/main/java/com/ruoyi/domain/entity/exponent/TtExponentUser.java new file mode 100644 index 0000000..b464d3b --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/exponent/TtExponentUser.java @@ -0,0 +1,49 @@ +package com.ruoyi.domain.entity.exponent; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_exponent_user") +public class TtExponentUser implements Serializable { + @TableField(exist = false) + private static final long serialVersionUID = 1L; + @TableId(type = IdType.AUTO) + private Integer id; + @Excel(name = "指数ID") + private Integer exponentId; + @Excel(name = "盒子ID") + private Integer boxId; + @Excel(name = "用户ID") + private Integer userId; + @Excel(name = "盒子ID") + private BigDecimal price; + // 1上升 2下降 + @Excel(name = "1上升 2下降") + private Integer forward; + @Excel(name = "0默认,1成功,2失败") + private Integer status; + @Excel(name = "成功开箱结果ID") + private Long boxRecordId; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/exponent/TtExponentUserBox.java b/skins-model/src/main/java/com/ruoyi/domain/entity/exponent/TtExponentUserBox.java new file mode 100644 index 0000000..cf10177 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/exponent/TtExponentUserBox.java @@ -0,0 +1,39 @@ +package com.ruoyi.domain.entity.exponent; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_exponent_user_box") +public class TtExponentUserBox implements Serializable { + @TableField(exist = false) + private static final long serialVersionUID = 1L; + @TableId(type = IdType.AUTO) + private Integer id; + @Excel(name = "盒子ID") + private Integer boxId; + @Excel(name = "用户ID") + private Integer userId; + @Excel(name = "宝箱数量") + private Integer number; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/fight/FightSeat.java b/skins-model/src/main/java/com/ruoyi/domain/entity/fight/FightSeat.java new file mode 100644 index 0000000..6469a10 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/fight/FightSeat.java @@ -0,0 +1,80 @@ +package com.ruoyi.domain.entity.fight; + +import cn.hutool.core.util.ObjectUtil; +import lombok.*; + +import java.math.BigDecimal; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FightSeat { + + private Integer fightId; + + // 座位号 + private Integer code; + + private Integer playerId; + + // 玩家状态(0空位 1入座 2就绪) + private Integer status; + + // 头像 + private String avatar; + + private String nickName; + + private BigDecimal awardTotalPrices; + + public FightSeat(Integer fightId, Integer code) { + this.fightId = fightId; + this.code = code; + this.status = 0; + } + + public FightSeat(Integer code) { + this.code = code; + this.status = 0; + } + + // 入座 + public FightSeat sitDown(Integer playerId) { + if (ObjectUtil.isEmpty(code)) return null; + if (!status.equals(0)) return null; + this.playerId = playerId; + status = 1; + return this; + } + + // 离开座位 + public Boolean sitUp() { + if (ObjectUtil.isEmpty(this.playerId) || ObjectUtil.isEmpty(this.code)) { + return false; + } + if (!this.status.equals(1)) return false; + this.playerId = null; + this.status = 0; + this.nickName = ""; + this.avatar = ""; + return true; + } + + // 准备 + public FightSeat ready() { + if (ObjectUtil.isEmpty(code) || ObjectUtil.isEmpty(playerId)) return null; + if (status.equals(0)) return null; + this.status = 2; + return this; + } + + // 取消准备 + public FightSeat readyCancel() { + if (ObjectUtil.isEmpty(code) || ObjectUtil.isEmpty(playerId)) return null; + if (status.equals(0)) return null; + if (status.equals(1)) return this; + this.status = 1; + return this; + } +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/fight/TtFight.java b/skins-model/src/main/java/com/ruoyi/domain/entity/fight/TtFight.java new file mode 100644 index 0000000..aa7c1fb --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/fight/TtFight.java @@ -0,0 +1,90 @@ +package com.ruoyi.domain.entity.fight; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.domain.entity.fight.FightSeat; +import com.ruoyi.domain.vo.fight.FightBoxVO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_fight",autoResultMap = true) +public class TtFight implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "创建者ID") + private Integer userId; + + @Excel(name = "座位") + @TableField(value = "seats",typeHandler = JacksonTypeHandler.class) + private List seats; + + // 1、欧皇 2、非酋 + @Excel(name = "对战模式") + private String model; + + @Excel(name = "对战人数") + private Integer playerNum; + + /** + * 观战人数 + */ + @TableField(exist = false) + private Integer spectatorNum; + + @Excel(name = "回合数") + private Integer roundNumber; + + @TableField(value = "winner_ids",typeHandler = JacksonTypeHandler.class) + private List winnerIds; + + @Excel(name = "选择宝箱数据") + @TableField(value = "box_data",typeHandler = JacksonTypeHandler.class) + private Map boxData; + + @Excel(name = "创建宝箱价格总数") + private BigDecimal boxPriceTotal; + + // 对战状态(0准备 1进行中 2结束 3超时强制结束) + @Excel(name = "对战状态") + private Integer status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + // 开始时间 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Timestamp beginTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @Excel(name = "备注") + private String remark; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/recorde/TtUserAmountRecords.java b/skins-model/src/main/java/com/ruoyi/domain/entity/recorde/TtUserAmountRecords.java new file mode 100644 index 0000000..0b68387 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/recorde/TtUserAmountRecords.java @@ -0,0 +1,58 @@ +package com.ruoyi.domain.entity.recorde; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_user_amount_records") +public class TtUserAmountRecords implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + // 1、收入 2、支出 + private Integer type; + + // 来源 + private Integer source; + + @Excel(name = "变动金额") + private BigDecimal amount; + + @Excel(name = "最终金额") + private BigDecimal finalAmount; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String remark; + + private Integer pwChildId; + private String pwChildName; + private BigDecimal pwChildAccount; + + @TableField("task_id") + private Integer taskId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/recorde/TtUserCreditsRecords.java b/skins-model/src/main/java/com/ruoyi/domain/entity/recorde/TtUserCreditsRecords.java new file mode 100644 index 0000000..ff7c5ef --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/recorde/TtUserCreditsRecords.java @@ -0,0 +1,58 @@ +package com.ruoyi.domain.entity.recorde; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_user_credits_records") +public class TtUserCreditsRecords implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + private Integer type; + + private Integer source; + + @Excel(name = "变动积分") + private BigDecimal credits; + + @Excel(name = "最终积分") + private BigDecimal finalCredits; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String remark; + + private Integer pwChildId; + + private String pwChildName; + + private BigDecimal pwChildAccount; + + @TableField("task_id") + private Integer taskId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRoll.java b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRoll.java new file mode 100644 index 0000000..1f80056 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRoll.java @@ -0,0 +1,85 @@ +package com.ruoyi.domain.entity.roll; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_roll") +public class TtRoll implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "ROLL房ID") + @TableId(value = "id",type = IdType.AUTO) + private Integer id; + + @Excel(name = "奖池ID") + private Integer jackpotId; + + // 创建者 + private Integer userId; + + // 0官方 1主播 + private String rollType; + + @Excel(name = "Roll房名称") + private String rollName; + + @Excel(name = "Roll房描述") + private String description; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + @Excel(name = "人数限制") + private Integer peopleNum; + + @Excel(name = "Roll房参与密码") + private String rollPassword; + + @Excel(name = "充值门槛") + private BigDecimal minRecharge; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date rechargeStartTime; + + @Excel(name = "排序依据") + private Integer sortBy; + + // 0未开奖 1已开奖 + @Excel(name = "状态") + private String rollStatus; + + @Excel(name = "创建者") + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @TableField(select = false) + private String delFlag; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollBody.java b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollBody.java new file mode 100644 index 0000000..3ff457a --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollBody.java @@ -0,0 +1,13 @@ +package com.ruoyi.domain.entity.roll; + +import lombok.Data; + +@Data +public class TtRollBody { + + private Integer id; + + private String rollName; + + private String rollStatus; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollJackpot.java b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollJackpot.java new file mode 100644 index 0000000..eff9aac --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollJackpot.java @@ -0,0 +1,48 @@ +package com.ruoyi.domain.entity.roll; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_roll_jackpot") +public class TtRollJackpot implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer jackpotId; + + private String jackpotName; + + @TableField("total_price") + private BigDecimal totalPrice; + + private String description; + + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollJackpotOrnaments.java b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollJackpotOrnaments.java new file mode 100644 index 0000000..f7061b3 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollJackpotOrnaments.java @@ -0,0 +1,60 @@ +package com.ruoyi.domain.entity.roll; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_roll_jackpot_ornaments") +public class TtRollJackpotOrnaments implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "Roll房奖池ID") + private Integer jackpotId; + + @Excel(name = "Roll房奖池饰品ID") + private Long ornamentsId; + + @Excel(name = "饰品价格") + private BigDecimal price; + + @Excel(name = "饰品图片") + private String imgUrl; + + @Excel(name = "饰品名称") + private String ornamentName; + + @Excel(name = "饰品级别ID") + @TableField("ornament_level_id") + private Integer ornamentLevelId; + + @Excel(name = "饰品数量") + private Integer ornamentsNum; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollJackpotOrnamentsBody.java b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollJackpotOrnamentsBody.java new file mode 100644 index 0000000..ca76ffe --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollJackpotOrnamentsBody.java @@ -0,0 +1,12 @@ +package com.ruoyi.domain.entity.roll; + +import lombok.Data; + +@Data +public class TtRollJackpotOrnamentsBody { + + private Integer jackpotId; + + private Integer id; + private String itemName; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollUser.java b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollUser.java new file mode 100644 index 0000000..d93b0fe --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollUser.java @@ -0,0 +1,70 @@ +package com.ruoyi.domain.entity.roll; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_roll_user") +public class TtRollUser implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "用户名") + private String userName; + + @Excel(name = "昵称") + private String nickName; + + @Excel(name = "头像") + private String avatar; + + @Excel(name = "Roll房ID") + private Integer rollId; + + @Excel(name = "奖池饰品列表ID") + private Integer jackpotOrnamentsId; + + @Excel(name = "奖池饰品ID") + private Long ornamentsId; + + @Excel(name = "宝箱记录ID") + private Long boxRecordId; + + // 2系统指定获奖者 + private String status; + + @Excel(name = "指定者") + private String designatedBy; + + @TableField("get_prize_way") + private Integer getPrizeWay; + + @JsonFormat(pattern = "yyyy-MM-dd") + private Date joinTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollUserPrize.java b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollUserPrize.java new file mode 100644 index 0000000..2000f11 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtRollUserPrize.java @@ -0,0 +1,67 @@ +package com.ruoyi.domain.entity.roll; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_roll_user_prize") +public class TtRollUserPrize implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "roll用户ID") + @TableField("roll_user_id") + private Integer rollUserId; + + @Excel(name = "Roll房奖池id") + @TableField("roll_jackpot_id") + private Integer rollJackpotId; + + @Excel(name = "奖池饰品ID") + @TableField("roll_jackpot_ornament_id") + private Integer rollJackpotOrnamentId; + + @Excel(name = "饰品ID") + @TableField("ornament_id") + private Long ornamentId; + + @Excel(name = "饰品名称") + @TableField("ornament_name") + private String ornamentName; + + @Excel(name = "饰品图片") + @TableField("img_url") + private String imgUrl; + + @Excel(name = "饰品价格") + @TableField("price") + private BigDecimal price; + + @Excel(name = "奖品数量") + @TableField("number") + private Integer number; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtTimeRoll.java b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtTimeRoll.java new file mode 100644 index 0000000..ccc3256 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtTimeRoll.java @@ -0,0 +1,66 @@ +package com.ruoyi.domain.entity.roll; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_time_roll") +public class TtTimeRoll implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "奖池ID") + private Integer jackpotId; + + @Excel(name = "房间名称") + private String name; + + @Excel(name = "房间描述") + private String description; + + private String rechargeCondition; + + @Excel(name = "充值门槛") + private BigDecimal minRecharge; + + @Excel(name = "排序依据") + private Integer sortBy; + + private String status; + + @Excel(name = "创建者") + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @TableField(select = false) + private String delFlag; + + private Integer jobId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtTimeRollUser.java b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtTimeRollUser.java new file mode 100644 index 0000000..82f19d4 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/roll/TtTimeRollUser.java @@ -0,0 +1,59 @@ +package com.ruoyi.domain.entity.roll; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_time_roll_user") +public class TtTimeRollUser implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "时间Roll房ID") + private Integer timeRollId; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "奖池饰品列表ID") + private Integer jackpotOrnamentsId; + + @Excel(name = "奖池饰品ID") + private Long ornamentsId; + + @Excel(name = "宝箱记录ID") + private Long boxRecordId; + + private String status; + + @Excel(name = "指定者") + private String designatedBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date joinTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @Excel(name = "结束状态", readConverterExp = "0=未开始,1=已结束") + private String endStatus; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/sys/TtPromotionUpdate.java b/skins-model/src/main/java/com/ruoyi/domain/entity/sys/TtPromotionUpdate.java new file mode 100644 index 0000000..d56f396 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/sys/TtPromotionUpdate.java @@ -0,0 +1,39 @@ +package com.ruoyi.domain.entity.sys; + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.sql.Timestamp; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_promotion_update") +public class TtPromotionUpdate { + + @TableId(value = "id",type = IdType.AUTO) + private Integer id; + + @TableField("employee_id") + private Integer employeeId; + + @TableField("boss_id") + private Integer bossId; + + @TableField("create_time") + private Timestamp createTime; + + @TableField("update_time") + private Timestamp updateTime; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/entity/sys/TtUser.java b/skins-model/src/main/java/com/ruoyi/domain/entity/sys/TtUser.java new file mode 100644 index 0000000..6fba141 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/entity/sys/TtUser.java @@ -0,0 +1,157 @@ +package com.ruoyi.domain.entity.sys; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_user") +public class TtUser implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "用户ID") + @TableId(value = "user_id", type = IdType.AUTO) + private Integer userId; + + @Excel(name = "用户账号") + private String userName; + + @Excel(name = "用户昵称") + private String nickName; + + /** + * @see com.ruoyi.domain.common.constant.UserType + */ + private String userType; + + @Excel(name = "用户邮箱") + private String email; + + @Excel(name = "手机号码") + private String phoneNumber; + + @Excel(name = "头像地址") + private String avatar; + + @Excel(name = "密码") + private String password; + + @Excel(name = "账户金额") + private BigDecimal accountAmount; + + @Excel(name = "账户积分") + private BigDecimal accountCredits; + + @Excel(name = "邀请码") + private String invitationCode; + + @Excel(name = "上级ID") + private Integer parentId; + + @Excel(name = "VIP等级") + private Integer vipLevel; + + @Excel(name = "推广等级") + private Integer promotionLevel; + + private String status; + + private String deliveryStatus; + + @Excel(name = "steam账号ID") + private Long steamId; + + @Excel(name = "steam交易链接") + @TableField("transaction_link") + private String transactionLink; + + @Excel(name = "真实姓名") + private String realName; + + @Excel(name = "身份证号码") + private String idNum; + + @Excel(name = "实名认证流程号") + private String certifyId; + + private String isRealCheck; + + /** + * 佣金比例 + */ + private BigDecimal commissionRate; + + @Excel(name = "最后登录IP") + private String loginIp; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "GMT+8") + private Date loginDate; + + private String createBy; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "GMT+8") + private Date createTime; + + private String updateBy; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "GMT+8") + private Date updateTime; + + @Excel(name = "备注") + private String remark; + + //0正常 + @TableField(select = false) + private String delFlag; + + @TableField(select = false) + private String deliveryAddress; + + @TableField(exist = false) + private List children; + + @Override + public boolean equals(Object obj) { + System.out.println("调用equals方法,当前的hashCode为:" + hashCode()); + /** 对象是 null 直接返回 false **/ + if (obj == null) { + return false; + } + /** 对象是当前对象,直接返回 true **/ + if (this == obj) { + return true; + } + /** 判断对象类型是否是User **/ + if (obj instanceof TtUser) { + TtUser vo = (TtUser) obj; + if (vo.getUserId().equals(this.userId)) { + return true; + } + } + return false; + } + + @Override + public int hashCode() { + return this.getUserId().hashCode(); + } +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/ApiForgetPasswordBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/ApiForgetPasswordBody.java new file mode 100644 index 0000000..5b2e56f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/ApiForgetPasswordBody.java @@ -0,0 +1,15 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class ApiForgetPasswordBody { + + private String phoneNumber; + + private String code; + + private String password; + + private String confirmPassword; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/ApiLoginBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/ApiLoginBody.java new file mode 100644 index 0000000..1c3c247 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/ApiLoginBody.java @@ -0,0 +1,15 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Data +public class ApiLoginBody { + + @NotNull(message = "用户名不能为空") + private String username; + + @NotNull(message = "密码不能为空") + private String password; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/ApiLuckyUpgradeBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/ApiLuckyUpgradeBody.java new file mode 100644 index 0000000..d29be23 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/ApiLuckyUpgradeBody.java @@ -0,0 +1,19 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ApiLuckyUpgradeBody { + + private Integer id; + + private String type; + + private String itemName; + + private BigDecimal priceMin = BigDecimal.ZERO; + + private BigDecimal priceMax; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/ApiRegisterBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/ApiRegisterBody.java new file mode 100644 index 0000000..f9c413a --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/ApiRegisterBody.java @@ -0,0 +1,21 @@ +package com.ruoyi.domain.other; + +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +@Data +public class ApiRegisterBody { + + @Length(min = 2,max = 12) + private String nickName; + + private String phoneNumber; + + private String password; + + private String parentInvitationCode; + + private String code; + + private String bdVid; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/ApiShoppingBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/ApiShoppingBody.java new file mode 100644 index 0000000..91395ca --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/ApiShoppingBody.java @@ -0,0 +1,45 @@ +package com.ruoyi.domain.other; + +import io.swagger.annotations.ApiParam; +import lombok.Data; + +import jakarta.validation.constraints.Min; +import java.math.BigDecimal; + +@Data +public class ApiShoppingBody { + + @ApiParam("商品名称") + private String name; + + @ApiParam("类型(1匕首 2手枪 3步枪 4微型冲锋枪 5重型武器 6手套 7印花 8其它)") + private String type; + + @ApiParam("品质(1普通 2StatTrak™ 3纪念品 4★ 5★ StatTrak™ 6闪耀 7金色 8全息 9闪亮)") + private String quality; + + @ApiParam("稀有度") + private String rarity; + + @ApiParam("外观(1崭新出厂 2略有磨损 3久经沙场 4残损不堪 5战痕累累 6无涂装)") + private String exterior; + + @ApiParam("最高价格") + private BigDecimal maxPrice; + + @ApiParam("最低价格") + private BigDecimal minPrice = BigDecimal.ZERO; + + @ApiParam("1价格升序 2价格降序 3更新时间升序 4更新时间降序") + private Integer sortBy; + + @Min(value = 1, message = "每页长度最小为1") + private Integer pageSize; + + @Min(value = 1, message = "页码最小为1") + private Integer pageNum; + + @ApiParam("是否可兑换") + private Boolean isExchange; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/ApiUpdateUserDetailsBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/ApiUpdateUserDetailsBody.java new file mode 100644 index 0000000..403a8de --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/ApiUpdateUserDetailsBody.java @@ -0,0 +1,33 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Data +public class ApiUpdateUserDetailsBody { + + private String nickName; + + private String email; + + private String phoneNumber; + private String code; + + @NotNull + private String oldPassword; + + @NotNull + private String password; + + @NotNull + private String passwordAgain; + + private String avatar; + + private String parentInvitationCode; + + private String transactionLink; + + private String deliveryAddress; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/ApiUserOnline.java b/skins-model/src/main/java/com/ruoyi/domain/other/ApiUserOnline.java new file mode 100644 index 0000000..719c1da --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/ApiUserOnline.java @@ -0,0 +1,23 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class ApiUserOnline { + + private String token; + + private String nickName; + + private String userName; + + private String ipaddr; + + private String loginLocation; + + private String browser; + + private String os; + + private Long loginTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/ApiVerificationCodeLoginBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/ApiVerificationCodeLoginBody.java new file mode 100644 index 0000000..2693552 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/ApiVerificationCodeLoginBody.java @@ -0,0 +1,15 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Data +public class ApiVerificationCodeLoginBody { + + @NotNull(message = "手机号不能为空") + private String phoneNumber; + + @NotNull(message = "验证码不能为空") + private String code; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/ApiVerificationCodeLoginOrRegisterBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/ApiVerificationCodeLoginOrRegisterBody.java new file mode 100644 index 0000000..46a232a --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/ApiVerificationCodeLoginOrRegisterBody.java @@ -0,0 +1,16 @@ +package com.ruoyi.domain.other; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class ApiVerificationCodeLoginOrRegisterBody { + + @NotNull(message = "手机号不能为空") + private String phoneNumber; + + @NotNull(message = "验证码不能为空") + private String code; + + private String parentInvitationCode; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/BoxDataBodyA.java b/skins-model/src/main/java/com/ruoyi/domain/other/BoxDataBodyA.java new file mode 100644 index 0000000..242a52c --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/BoxDataBodyA.java @@ -0,0 +1,13 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class BoxDataBodyA { + private Integer boxId; + private Integer boxNum; + private Integer number; + private String boxName; + private String boxImg01; + private String boxImg02; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/ConfigData.java b/skins-model/src/main/java/com/ruoyi/domain/other/ConfigData.java new file mode 100644 index 0000000..ee17037 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/ConfigData.java @@ -0,0 +1,49 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "sys_config") +public class ConfigData implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Long configId; + + private String configName; + + private String configKey; + + private String configValue; + + private String configType; + + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + private String remark; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/CreateFightBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/CreateFightBody.java new file mode 100644 index 0000000..3d7d211 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/CreateFightBody.java @@ -0,0 +1,31 @@ +package com.ruoyi.domain.other; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.List; +import java.util.Map; + +@Data +public class CreateFightBody { + + @ApiModelProperty("模式(0欧皇 1非酋)") + @NotBlank(message = "模式不能为空") + private String model; + + @ApiModelProperty("玩家人数") + @Max(value = 12, message = "最大值12") + private Integer playerNumber; + + // 所有宝箱 + @NotNull(message = "宝箱数据不能为空") + private Map boxIdAndNumber; + + // private Integer rounds; + // 回合数 + // private Integer createNum = 1; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/CreateOrderParam.java b/skins-model/src/main/java/com/ruoyi/domain/other/CreateOrderParam.java new file mode 100644 index 0000000..86f2d69 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/CreateOrderParam.java @@ -0,0 +1,32 @@ +package com.ruoyi.domain.other; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NonNull; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; + +@Data +public class CreateOrderParam { + + @NotNull(message = "商品id不能为空。") + private Integer goodsId; + + @NotNull(message = "商品价格不能为空。") + private BigDecimal goodsPrice; + + @NotNull(message = "商品数量不能为空。") + @Min(value = 1, message = "最小值1") + private Integer goodsNum; + + @ApiModelProperty("对接qs支付新增:支付方式(alipay/wxpay)") + private String payType; + + @ApiModelProperty("1:原有支付,2:图片支付") + private Integer payWay = 1; + + @ApiModelProperty("百度推广bdvid") + private String bdVid; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/FightBoutData.java b/skins-model/src/main/java/com/ruoyi/domain/other/FightBoutData.java new file mode 100644 index 0000000..ee5e237 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/FightBoutData.java @@ -0,0 +1,17 @@ +package com.ruoyi.domain.other; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class FightBoutData { + + @ApiModelProperty("对战ID") + private Integer fightId; + + @ApiModelProperty("对战回合号") + private Integer boutNum; + + @ApiModelProperty("过期时间") + private Integer expirationTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/JackpotData.java b/skins-model/src/main/java/com/ruoyi/domain/other/JackpotData.java new file mode 100644 index 0000000..2950554 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/JackpotData.java @@ -0,0 +1,34 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class JackpotData { + + private String itemName; + + private BigDecimal usePrice; + + private String imageUrl; + + private String shortName; + + private String typeName; + + private String exteriorName; + + private String level; + + private String levelImg; + + private Integer id; + + private Integer allocatedNum; + + private Integer unAllocatedNum; + + private Integer ornamentsNum; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/OperationalStatistics.java b/skins-model/src/main/java/com/ruoyi/domain/other/OperationalStatistics.java new file mode 100644 index 0000000..cac9640 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/OperationalStatistics.java @@ -0,0 +1,17 @@ +package com.ruoyi.domain.other; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class OperationalStatistics { + + @JsonFormat(pattern = "yyyy-MM-dd") + private Date date; + private BigDecimal rechargePriceTotal; + private BigDecimal deliveryPriceTotal; + private BigDecimal profit; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/ParameterSettingBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/ParameterSettingBody.java new file mode 100644 index 0000000..8876dcf --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/ParameterSettingBody.java @@ -0,0 +1,32 @@ +package com.ruoyi.domain.other; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class ParameterSettingBody { + + private String usePricePremiumRate; + private String exchangePriceRatio; + private String registerRedPacket; + private String maxCompoundNum; + private String compoundMinPrice; + private String compoundMinPremiumRate; + private String compoundMaxPremiumRate; + private String ZBTParities; + private String buyPricePremiumRate; + private String autoDeliveryMinPrice; + private String websiteMaintenance; + private String shoppingMaintenance; + private String fightMaintenance; + private String bindBoxMaintenance; + private String rollMaintenance; + private String compoundMaintenance; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/RealNameAuthenticationBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/RealNameAuthenticationBody.java new file mode 100644 index 0000000..5d0080f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/RealNameAuthenticationBody.java @@ -0,0 +1,10 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class RealNameAuthenticationBody { + + private String realName; + private String idNum; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/ShoppingBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/ShoppingBody.java new file mode 100644 index 0000000..a073377 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/ShoppingBody.java @@ -0,0 +1,18 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ShoppingBody { + + private Integer id; + private String itemName; + private String type; + private String exterior; + private BigDecimal maxPrice; + private BigDecimal minPrice = BigDecimal.ZERO; + private Integer isPutaway; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtAdvertisement.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtAdvertisement.java new file mode 100644 index 0000000..1d9166d --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtAdvertisement.java @@ -0,0 +1,44 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_advertisement") +public class TtAdvertisement implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "标题") + private String title; + + @Excel(name = "图片") + private String picture; + + @Excel(name = "跳转链接") + private String jumpLink; + + @Excel(name = "图片排序") + private Integer sort; + + // 0可用 1禁用 + @Excel(name = "状态") + private String status; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtAnnouncement.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtAnnouncement.java new file mode 100644 index 0000000..484d1eb --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtAnnouncement.java @@ -0,0 +1,26 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * 公告实体类 + */ +@Data +@TableName(value = "tt_announcement") +public class TtAnnouncement { + + private Integer AnnouncementId; + + private String title; + + private String content; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String read; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBanner.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBanner.java new file mode 100644 index 0000000..f4e8280 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBanner.java @@ -0,0 +1,43 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_banner") +public class TtBanner implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "标题") + private String title; + + @Excel(name = "图片") + private String picture; + + @Excel(name = "跳转链接") + private String jumpLink; + + @Excel(name = "图片排序") + private Integer sort; + + // 0可用 1禁用 + @Excel(name = "状态") + private String status; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBonus.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBonus.java new file mode 100644 index 0000000..03c16ac --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBonus.java @@ -0,0 +1,69 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_bonus") +public class TtBonus implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "福利名称") + private String name; + + @Excel(name = "福利描述") + private String description; + + @Excel(name = "封面图片") + private String coverPicture; + + @Excel(name = "福利类型", readConverterExp = "2=充值红包,3=福利宝箱") + private String type; + + @Excel(name = "条件类型") + private String conditionType; + + @Excel(name = "充值门槛") + private BigDecimal rechargeThreshold; + + @Excel(name = "奖励区间") + private String awardSection; + + @Excel(name = "启用状态", readConverterExp = "0=启用,1=禁用") + private String status; + + @Excel(name = "排序依据") + private Integer sortBy; + + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBonusReceiveRecord.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBonusReceiveRecord.java new file mode 100644 index 0000000..b658d1b --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBonusReceiveRecord.java @@ -0,0 +1,68 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_bonus_receive_record") +public class TtBonusReceiveRecord implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "福利类型") + private String type; + + @Excel(name = "福利ID") + private Integer bonusId; + + @Excel(name = "VIP等级ID") + private Integer vipLevelId; + + @Excel(name = "推广等级ID") + private Integer promotionLevelId; + + @Excel(name = "领取人ID") + private Integer userId; + + @Excel(name = "奖励类型") + private String awardType; + + @Excel(name = "奖励ID", readConverterExp = "0=金币") + private Integer awardId; + + @Excel(name = "奖励价值") + private BigDecimal awardPrice; + + @Excel(name = "状态", readConverterExp = "0=未领取,1=已领取") + private String status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "领取时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date receiveTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBox.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBox.java new file mode 100644 index 0000000..7118fcc --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBox.java @@ -0,0 +1,91 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_box") +public class TtBox implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "宝箱ID") + @TableId + private Integer boxId; + + @Excel(name = "宝箱名称") + private String boxName; + + @Excel(name = "宝箱所属分类ID") + private String boxTypeId; + + @Excel(name = "宝箱价格") + private BigDecimal price; + + @Excel(name = "宝箱图片01") + private String boxImg01; + + @Excel(name = "宝箱图片02") + private String boxImg02; + + @Excel(name = "宝箱排序") + private Integer sort; + + // 0:是对战宝箱 + @Excel(name = "是否对战宝箱") + private String isFight; + + /** 宝箱类型 + * 1 普通箱 + * 2 对战箱 + * 3 补偿箱 + * 4 九宫格 + * 5 指数盲盒 + */ + private String boxType; + + /** 补偿值 */ + private Integer compAmount; + + @Excel(name = "宝箱状态") + private String status; + + @Excel(name = "宝箱开启次数") + private Long openNum; + + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + private String remark; + + @Excel(name = "是否首页推荐") + private String isHome; + + @TableField(select = false) + @Excel(name = "删除标志") + private String delFlag; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxA.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxA.java new file mode 100644 index 0000000..834e01a --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxA.java @@ -0,0 +1,22 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +public class TtBoxA { + + private Integer boxId; + private Integer boxTypeId; + private String boxName; + private BigDecimal price; + private String boxImg01; + private String boxImg02; + private String boxType; + private Integer totalOpenOdds; + private List boxOrnamentsList; + private List probabilityDistribution; + private List openProbabilityDistribution; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxBody.java new file mode 100644 index 0000000..40defa3 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxBody.java @@ -0,0 +1,15 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class TtBoxBody { + + private Integer boxId; + + private String boxName; + + private String isFight; + + private String status; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxLevelA.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxLevelA.java new file mode 100644 index 0000000..28bde19 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxLevelA.java @@ -0,0 +1,13 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class TtBoxLevelA { + + private Integer ornamentsLevelId; + + private String level; + + private String probability; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxOpenChance.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxOpenChance.java new file mode 100644 index 0000000..658d999 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxOpenChance.java @@ -0,0 +1,33 @@ +package com.ruoyi.admin.domain; + +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +import jakarta.validation.constraints.NotNull; + +/** + * 开箱机会对象 tt_box_open_chance + * + * @author ruoyi + * @date 2024-07-09 + */ +@Data +public class TtBoxOpenChance extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 道具ID */ + @NotNull(message = "道具ID不能为空") + private Integer ornamentId; + + /** 宝箱ID */ + @NotNull(message = "宝箱ID不能为空") + private Integer boxId; + + /** 开启次数 */ + @NotNull(message = "开启次数不能为空") + private Integer chanceNum; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxOrnaments.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxOrnaments.java new file mode 100644 index 0000000..ebd97f3 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxOrnaments.java @@ -0,0 +1,83 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_box_ornaments") +public class TtBoxOrnaments implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "宝箱ID") + private Integer boxId; + + @Excel(name = "饰品ID") + @TableField("ornament_id") + private Long ornamentId; + + @Excel(name = "市场唯一hash名称") + @TableField("market_hash_name") + private String marketHashName; + + @Excel(name = "zbt ID") + @TableField("ornaments_zbt_id") + private String ornamentsZbtId; + + @Excel(name = "yy ID") + @TableField("ornaments_yy_id") + private String ornamentsYyId; + + @Excel(name = "饰品级别") + private Integer level; + + @Excel(name = "显示数量") + private Integer odds; + + @Excel(name = "真实数量") + private Integer realOdds; + + @Excel(name = "主播数量") + private Integer anchorOdds; + + @Excel(name = "机器人数量") + private Integer robotOdds; + + private Integer compOdds; + + private Integer realCompOdds; + + @Excel(name = "三级爆率") + private Integer thirdExplosiveOdds; + + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + private String remark; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxRecordsBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxRecordsBody.java new file mode 100644 index 0000000..4308d8a --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxRecordsBody.java @@ -0,0 +1,16 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class TtBoxRecordsBody { + + private Long id; + private Integer userId; + private Integer holderUserId; + private Integer ornamentsId; + private String itemName; + private String levelImg; + private String status; + private Integer boxId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxType.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxType.java new file mode 100644 index 0000000..7ad507f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxType.java @@ -0,0 +1,45 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_box_type") +public class TtBoxType implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + private String name; + + private String icon; + + private Integer sort; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @Excel(name = "是否对战分类") + private String isFightType; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxUser.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxUser.java new file mode 100644 index 0000000..0cff7e1 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxUser.java @@ -0,0 +1,28 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class TtBoxUser { + + private Long id; + private Integer boxId; + private Integer userId; + private String userName; + private String nickName; + private String avatar; + private Integer ornamentsId; + private String itemName; + private String shortName; + private BigDecimal usePrice; + private String imageUrl; + private String exteriorName; + private Integer ornamentsLevelId; + private String levelImg; + + // 来源 + private String source; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxUserIdPrice.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxUserIdPrice.java new file mode 100644 index 0000000..4b956da --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxUserIdPrice.java @@ -0,0 +1,19 @@ +package com.ruoyi.domain.other; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtBoxUserIdPrice { + private Integer userId; + private BigDecimal totalPrice; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxVO.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxVO.java new file mode 100644 index 0000000..e3d5d19 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtBoxVO.java @@ -0,0 +1,22 @@ +package com.ruoyi.domain.other; + +import com.ruoyi.domain.vo.TtOrnamentVO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtBoxVO { + private Integer boxTypeId; + private String boxTypeName; + private String icon; + private List BoxList; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtCompRecord.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtCompRecord.java new file mode 100644 index 0000000..702c8a3 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtCompRecord.java @@ -0,0 +1,13 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class TtCompRecord { + + private Integer userId; + + private Integer boxId; + + private Integer compAmount; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtContent.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtContent.java new file mode 100644 index 0000000..6686598 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtContent.java @@ -0,0 +1,54 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_content") +public class TtContent implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "文章类别ID") + private Integer typeId; + + @Excel(name = "文章标题") + private String title; + + @Excel(name = "文章内容") + private String content; + + @Excel(name = "文章绑定链接") + private String link; + + @Excel(name = "浏览量") + private Integer pageView; + + @Excel(name = "状态") + private String status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtContentType.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtContentType.java new file mode 100644 index 0000000..9aa1420 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtContentType.java @@ -0,0 +1,48 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_content_type") +public class TtContentType implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "类别名称") + private String name; + + @Excel(name = "类别别名") + private String alias; + + @Excel(name = "备注") + private String remark; + + @Excel(name = "状态") + private String status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtDeliveryApplyBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtDeliveryApplyBody.java new file mode 100644 index 0000000..102d0ce --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtDeliveryApplyBody.java @@ -0,0 +1,14 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class TtDeliveryApplyBody { + + private String userName; + + private String phoneNumber; + + private String outTradeNo; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtDeliveryRecordBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtDeliveryRecordBody.java new file mode 100644 index 0000000..792d9a8 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtDeliveryRecordBody.java @@ -0,0 +1,30 @@ +package com.ruoyi.domain.other; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +@Data +public class TtDeliveryRecordBody { + + private String userName; + + private String phoneNumber; + + private String outTradeNo; + + private String orderId; + + private Integer userId; + + private String status; + + private String itemName; + + private String levelImg; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private String startTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private String endTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtFightBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtFightBody.java new file mode 100644 index 0000000..73667d9 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtFightBody.java @@ -0,0 +1,11 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class TtFightBody { + + private Integer id; + private String status; + private Integer userId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtFightRankingReward.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtFightRankingReward.java new file mode 100644 index 0000000..6d1c31d --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtFightRankingReward.java @@ -0,0 +1,27 @@ +package com.ruoyi.domain.other; + +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 对战奖励金额对象 tt_fight_ranking_reward + * + * @author ruoyi + * @date 2024-07-04 + */ +@Data +public class TtFightRankingReward extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + private Integer id; + + /** 排名名称 */ + private String name; + + /** 奖励金额 */ + private BigDecimal reward; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtFightResult.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtFightResult.java new file mode 100644 index 0000000..81fac96 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtFightResult.java @@ -0,0 +1,38 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_fight_result") +public class TtFightResult implements Serializable { + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "对战房间ID") + private Integer fightId; + + @Excel(name = "对战结果") + private String fightResult; + + @Excel(name = "结果时间") + private Date createTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtFightUser.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtFightUser.java new file mode 100644 index 0000000..c33fbe5 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtFightUser.java @@ -0,0 +1,54 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_fight_user") +public class TtFightUser implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "对战ID") + private Integer fightId; + + @Excel(name = "参与者_用户ID") + private Integer userId; + + @Excel(name = "加入金额") + private BigDecimal joinPrice; + + @Excel(name = "加入位置编号") + private Integer joinSeatNum; + + // 0,准备 1,进行中 2,结束 + @Excel(name = "对战状态") + private String status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtFirstRecharge.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtFirstRecharge.java new file mode 100644 index 0000000..915f41f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtFirstRecharge.java @@ -0,0 +1,84 @@ +package com.ruoyi.domain.other; + +import java.math.BigDecimal; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 首充赠送对象 tt_first_recharge + * + * @author ruoyi + * @date 2024-06-21 + */ +public class TtFirstRecharge extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + private Integer id; + + /** 充值金额下限 */ + @Excel(name = "充值金额下限") + private BigDecimal minAmount; + + /** 赠送比例 */ + @Excel(name = "赠送比例") + private BigDecimal ratio; + + /** 规则描述 */ + @Excel(name = "规则描述") + private String description; + + public void setId(Integer id) + { + this.id = id; + } + + public Integer getId() + { + return id; + } + public void setMinAmount(BigDecimal minAmount) + { + this.minAmount = minAmount; + } + + public BigDecimal getMinAmount() + { + return minAmount; + } + public void setRatio(BigDecimal ratio) + { + this.ratio = ratio; + } + + public BigDecimal getRatio() + { + return ratio; + } + public void setDescription(String description) + { + this.description = description; + } + + public String getDescription() + { + return description; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("minAmount", getMinAmount()) + .append("ratio", getRatio()) + .append("description", getDescription()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtMessage.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtMessage.java new file mode 100644 index 0000000..48c488a --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtMessage.java @@ -0,0 +1,39 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_message") +public class TtMessage implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "消息内容") + private String message; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtMessageSend.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtMessageSend.java new file mode 100644 index 0000000..fc9138a --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtMessageSend.java @@ -0,0 +1,48 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_message_send") +public class TtMessageSend implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "接收者用户ID") + private Integer recId; + + @Excel(name = "消息内容ID") + private Integer messageId; + + /** + * 1:已读 + */ + @Excel(name = "查看状态") + private String status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date sendTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date readingTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtNotice.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtNotice.java new file mode 100644 index 0000000..0234470 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtNotice.java @@ -0,0 +1,31 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * 通知实体类 + */ +@Data +@TableName("tt_notice") +public class TtNotice { + + private Integer noticeId; + + private Long userId; + + private String title; + + private String content; + + /** + * "1": 已读 + */ + private String read; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtOrnamentsA.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtOrnamentsA.java new file mode 100644 index 0000000..3a377da --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtOrnamentsA.java @@ -0,0 +1,28 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class TtOrnamentsA { + + // private Long id; + // // 平台 + // private Integer partyType; + // 在该平台的id + private Integer ornamentId; + private String name; + private String shortName; + private BigDecimal usePrice; + private String imageUrl; + private String exteriorName; + private Integer ornamentsLevelId; + private String levelImg; + private Integer odds; + private BigDecimal oddsResult; + private Integer compOdds; + private BigDecimal compOddsResult; + private Integer openOdds; + private BigDecimal openOddsResult; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtOrnamentsBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtOrnamentsBody.java new file mode 100644 index 0000000..7de2e39 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtOrnamentsBody.java @@ -0,0 +1,46 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class TtOrnamentsBody { + + private Integer id; + + /** + * 名称 + */ + private String name; + + /** + * 类型 + */ + private String type; + + private String typeName; + + /** + * 外观 + */ + private String exterior; + + /** + * 最高价格 + */ + private BigDecimal maxPrice; + + /** + * 最低价格 + */ + private BigDecimal minPrice; + + private Integer pageNum; + + private Integer pageSize; + + private Integer zbtId; + + private Integer yyyoupingId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtOrnamentsLevel.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtOrnamentsLevel.java new file mode 100644 index 0000000..7f91877 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtOrnamentsLevel.java @@ -0,0 +1,31 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_ornaments_level") +public class TtOrnamentsLevel implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + private String level; + + private String levelImg; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtOrnamentsPrice.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtOrnamentsPrice.java new file mode 100644 index 0000000..dfb9dd6 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtOrnamentsPrice.java @@ -0,0 +1,14 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class TtOrnamentsPrice { + + private Long id; + + private BigDecimal price; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeCard.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeCard.java new file mode 100644 index 0000000..9db496e --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeCard.java @@ -0,0 +1,63 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_recharge_card") +public class TtRechargeCard implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "充值列表ID") + private Integer rechargeListId; + + @Excel(name = "金额") + private BigDecimal price; + + @Excel(name = "卡密") + private String password; + + @Excel(name = "状态") + private String status; + + @Excel(name = "使用者ID") + private Integer useUserId; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date useTime; + + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @TableField(select = false) + @Excel(name = "删除标志") + private String delFlag; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeRankingReward.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeRankingReward.java new file mode 100644 index 0000000..fc32c6e --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeRankingReward.java @@ -0,0 +1,28 @@ +package com.ruoyi.domain.other; + +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 充值奖励金额对象 tt_recharge_ranking_reward + * + * @author ruoyi + * @date 2024-07-04 + */ +@Data +public class TtRechargeRankingReward extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + private Integer id; + + /** 排名名称 */ + private String name; + + /** 奖励金额 */ + private BigDecimal reward; + private String rewardStr; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeRecord.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeRecord.java new file mode 100644 index 0000000..87337e8 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeRecord.java @@ -0,0 +1,66 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_recharge_record") +public class TtRechargeRecord implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "充值记录ID") + @TableId + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "上级用户ID") + private Integer parentId; + + @Excel(name = "到账金额") + private BigDecimal arrivalAmount; + + @Excel(name = "实际支付金额") + private BigDecimal amountActuallyPaid; + + @Excel(name = "最终金额") + private BigDecimal finallyPrice; + + @Excel(name = "订单号") + private String orderId; + + @Excel(name = "外部订单号") + private String outTradeNo; + + private String status; + + private String channelType; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @TableField(select = false) + private String delFlag; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeRecordBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeRecordBody.java new file mode 100644 index 0000000..4fa40c3 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeRecordBody.java @@ -0,0 +1,23 @@ +package com.ruoyi.domain.other; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +@Data +public class TtRechargeRecordBody { + + private Integer userId; + + private Integer parentId; + private String orderId; + private String outTradeNo; + private String channelType; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeRecordVo.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeRecordVo.java new file mode 100644 index 0000000..8d52344 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtRechargeRecordVo.java @@ -0,0 +1,67 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtRechargeRecordVo implements Serializable { + + @Excel(name = "充值记录ID") + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "上级用户ID") + private Integer parentId; + + @Excel(name = "到账金额") + private BigDecimal arrivalAmount; + + @Excel(name = "实际支付金额") + private BigDecimal amountActuallyPaid; + + @Excel(name = "最终金额") + private BigDecimal finallyPrice; + + @Excel(name = "订单号") + private String orderId; + + @Excel(name = "外部订单号") + private String outTradeNo; + + private String status; + + private String channelType; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @TableField(select = false) + private String delFlag; + + private String nickName; + + private String phoneNumber; + + private String deliveryAddress; + + private String picture; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPack.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPack.java new file mode 100644 index 0000000..069ccf2 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPack.java @@ -0,0 +1,110 @@ +package com.ruoyi.domain.other; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 【请填写功能名称】对象 tt_red_pack + * + * @author ruoyi + * @date 2023-06-29 + */ +@Setter +@Getter +public class TtRedPack extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 标题 + */ + @Excel(name = "标题") + private String title; + + /** + * 红包类型 1.充值红包 2.普通红包 + */ + @Excel(name = "红包类型 1.充值红包 2.普通红包") + private String packType; + + /** + * 充值金额 + */ + @Excel(name = "充值金额") + private BigDecimal rechargeLimit; + + /** + * 总数量 0,无限制 + */ + @Excel(name = "总数量 0,无限制") + private Long packCount; + + /** + * 剩余数量 + */ + @Excel(name = "剩余数量") + private Long validCount; + + /** + * 红包最小值 + */ + @Excel(name = "红包最小值") + private BigDecimal valueMin; + + /** + * 红包最大值 + */ + @Excel(name = "红包最大值") + private BigDecimal valueMax; + + /** + * 开始时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date beginTime; + + /** + * 结束时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + /** + * 状态0 已结束 1 进行中 + */ + @Excel(name = "状态0 已结束 1 进行中") + private Long status; + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("title", getTitle()) + .append("packType", getPackType()) + .append("rechargeLimit", getRechargeLimit()) + .append("packCount", getPackCount()) + .append("validCount", getValidCount()) + .append("valueMin", getValueMin()) + .append("valueMax", getValueMax()) + .append("beginTime", getBeginTime()) + .append("endTime", getEndTime()) + .append("status", getStatus()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPacket.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPacket.java new file mode 100644 index 0000000..1dedb45 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPacket.java @@ -0,0 +1,78 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_red_packet") +public class TtRedPacket implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "红包标题") + private String title; + + @Excel(name = "红包描述") + private String description; + + @Excel(name = "发放个数") + private Integer num; + + @Excel(name = "单个红包金额区间") + private String amount; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "红包口令") + private String password; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date openingTime; + + // 有效期限 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date validity; + + // 0,正在使用 1,结束 + private Integer status; + + @Excel(name = "创建者") + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + //0启用 1禁用 + @TableField("use_status") + private Integer useStatus; + + @TableField(select = false) + @TableLogic(value = "0",delval = "1") + private Integer delFlag; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPacketBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPacketBody.java new file mode 100644 index 0000000..abef70b --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPacketBody.java @@ -0,0 +1,21 @@ +package com.ruoyi.domain.other; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtRedPacketBody { + + private Integer id; + + private String title; + + private String status; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPacketRecord.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPacketRecord.java new file mode 100644 index 0000000..89f7539 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPacketRecord.java @@ -0,0 +1,46 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_red_packet_record") +public class TtRedPacketRecord implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "红包ID") + private Integer redPacketId; + + @Excel(name = "领取人ID") + private Integer userId; + + @Excel(name = "领取口令") + private String receivePassword; + + @Excel(name = "领取金额") + private BigDecimal receiveAmount; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date receiveTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPacketRecordBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPacketRecordBody.java new file mode 100644 index 0000000..2490fce --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtRedPacketRecordBody.java @@ -0,0 +1,19 @@ +package com.ruoyi.domain.other; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtRedPacketRecordBody { + + private Integer redPacketId; + private Integer userId; + private String receivePassword; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtReplacementRecord.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtReplacementRecord.java new file mode 100644 index 0000000..1b6038e --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtReplacementRecord.java @@ -0,0 +1,153 @@ +package com.ruoyi.domain.other; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 汰换记录对象 tt_replacement_record + * + * @author junhai + * @date 2023-09-10 + */ +public class TtReplacementRecord extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** id */ + private Long id; + + /** 用户id */ + @Excel(name = "用户id") + private Long uid; + + /** 用户名称 */ + @Excel(name = "用户名称") + private String uname; + + /** 饰品ids */ + @Excel(name = "饰品ids") + private String oids; + + /** 合成饰品的id */ + @Excel(name = "合成饰品的id") + private Long awardOid; + + /** 合成饰品的名称 */ + @Excel(name = "合成饰品的名称") + private String awardOname; + + /** 合成饰品的价格 */ + @Excel(name = "合成饰品的价格") + private BigDecimal awardOprice; + + /** + * 饰品的图片 + */ + @Excel(name = "饰品的图片") + private String awardOimg; + + /** 合成日期 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "合成日期", width = 30, dateFormat = "yyyy-MM-dd") + private Date time; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setUid(Long uid) + { + this.uid = uid; + } + + public Long getUid() + { + return uid; + } + public void setUname(String uname) + { + this.uname = uname; + } + + public String getUname() + { + return uname; + } + public void setOids(String oids) + { + this.oids = oids; + } + + public String getOids() + { + return oids; + } + public void setAwardOid(Long awardOid) + { + this.awardOid = awardOid; + } + + public Long getAwardOid() + { + return awardOid; + } + public void setAwardOname(String awardOname) + { + this.awardOname = awardOname; + } + + public String getAwardOname() + { + return awardOname; + } + public void setAwardOprice(BigDecimal awardOprice) + { + this.awardOprice = awardOprice; + } + + public BigDecimal getAwardOprice() + { + return awardOprice; + } + public void setTime(Date time) + { + this.time = time; + } + + public Date getTime() + { + return time; + } + + public String getAwardOimg() { + return awardOimg; + } + + public void setAwardOimg(String awardOimg) { + this.awardOimg = awardOimg; + } + + @Override + public String toString() { + return "TtReplacementRecord{" + + "id=" + id + + ", uid=" + uid + + ", uname='" + uname + '\'' + + ", oids='" + oids + '\'' + + ", awardOid=" + awardOid + + ", awardOname='" + awardOname + '\'' + + ", awardOprice=" + awardOprice + + ", awardOimg='" + awardOimg + '\'' + + ", time=" + time + + '}'; + } +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtRobotFightGroup.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtRobotFightGroup.java new file mode 100644 index 0000000..08fb417 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtRobotFightGroup.java @@ -0,0 +1,28 @@ +package com.ruoyi.domain.other; + +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; + +/** + * 机器人对战分组对象 tt_robot_fight_group + * + * @author ruoyi + * @date 2024-06-28 + */ +@Data +public class TtRobotFightGroup extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 分组ID */ + private Integer groupId; + + /** 分组名称 */ + private String groupName; + + /** 座位数量 */ + private Integer seatNum; + + /** 对战模式(0欧皇 1非酋) */ + private String model; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtRobotFightGroupBox.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtRobotFightGroupBox.java new file mode 100644 index 0000000..40d9a6f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtRobotFightGroupBox.java @@ -0,0 +1,29 @@ +package com.ruoyi.domain.other; + +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +/** + * 机器人对战分组宝箱对象 tt_robot_fight_group_box + * + * @author ruoyi + * @date 2024-06-28 + */ +@Data +public class TtRobotFightGroupBox extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 分组ID */ + @NotNull(message = "分组ID不能为空") + private Integer groupId; + + /** 宝箱ID */ + @NotNull(message = "宝箱ID不能为空") + private Integer boxId; + + /** 宝箱数量 */ + private Integer boxNum; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtTaskCenter.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtTaskCenter.java new file mode 100644 index 0000000..13ae38f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtTaskCenter.java @@ -0,0 +1,34 @@ +package com.ruoyi.domain.other; + +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 任务中心对象 tt_task_center + * + * @author ruoyi + * @date 2024-05-25 + */ +@Data +public class TtTaskCenter extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 任务ID */ + private Integer taskId; + + /** 任务名称 */ + private String taskName; + + /** 任务描述 */ + private String description; + + /** 任务标识 */ + private String identify; + + /** 任务状态(0停用 1启用) */ + private String status; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtUidTotalRechargeRecord.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtUidTotalRechargeRecord.java new file mode 100644 index 0000000..fbd705e --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtUidTotalRechargeRecord.java @@ -0,0 +1,19 @@ +package com.ruoyi.domain.other; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtUidTotalRechargeRecord { + private Integer userId; + private BigDecimal totalPrice; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeFailOrnaments.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeFailOrnaments.java new file mode 100644 index 0000000..15400e9 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeFailOrnaments.java @@ -0,0 +1,56 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_upgrade_fail_ornaments") +public class TtUpgradeFailOrnaments implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "升级失败奖励列表ID") + @TableId + private Integer id; + + @Excel(name = "升级饰品列表ID") + private Integer upgradeId; + + @Excel(name = "失败奖励饰品ID") + private Long ornamentId; + + @Excel(name = "失败奖励饰品名称") + private String ornamentName; + + @Excel(name = "失败奖励饰品数量") + private Integer ornamentNumber; + + @Excel(name = "饰品级别ID") + private Integer ornamentLevelId; + + @Excel(name = "饰品价格") + private BigDecimal ornamentPrice; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeOrnaments.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeOrnaments.java new file mode 100644 index 0000000..f9a9402 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeOrnaments.java @@ -0,0 +1,74 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_upgrade_ornaments") +public class TtUpgradeOrnaments implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "升级饰品ID") + @TableId + private Integer id; + + @Excel(name = "饰品ID") + private Long ornamentsId; + + @Excel(name = "饰品原价") + @TableField("ornament_price") + private BigDecimal ornamentPrice; + + @Excel(name = "饰品级别ID") + private Integer ornamentsLevelId; + + @Excel(name = "通用幸运区间") + private String luckSection; + + @Excel(name = "本轮出货所需金额") + private BigDecimal amountRequired; + + @Excel(name = "本轮已投金额") + private BigDecimal amountInvested; + + @Excel(name = "主播幸运区间") + private String anchorLuckSection; + + @Excel(name = "本轮出货所需金额") + private BigDecimal anchorAmountRequired; + + @Excel(name = "本轮已投金额") + private BigDecimal anchorAmountInvested; + + // 总投入 + @TableField("total_input") + private BigDecimal totalInput; + + // 0可用 1禁用 + @Excel(name = "状态") + private String status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeOrnamentsBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeOrnamentsBody.java new file mode 100644 index 0000000..92d513c --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeOrnamentsBody.java @@ -0,0 +1,13 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class TtUpgradeOrnamentsBody { + + private String itemName; + + private String type; + + private String status; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeRecord.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeRecord.java new file mode 100644 index 0000000..53cf83b --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeRecord.java @@ -0,0 +1,79 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.domain.common.constant.UserType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_upgrade_record") +public class TtUpgradeRecord implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "升级记录ID") + @TableId + private Long id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "昵称") + private String nickName; + + @Excel(name = "用户类型") + private String userType; + + @Excel(name = "消耗金额") + private BigDecimal amountConsumed; + + @Excel(name = "可升级饰品ID") + @TableField("target_upgrade_id") + private Integer targetUpgradeId; + + @Excel(name = "目标饰品ID") + @TableField("target_ornament_id") + private Long targetOrnamentId; + + @Excel(name = "目标饰品价格") + @TableField("target_ornament_price") + private BigDecimal targetOrnamentPrice; + + @Excel(name = "概率") + @TableField("probability") + private Integer probability; + + @Excel(name = "最终奖励饰品集合") + @TableField("gain_ornament_list") + private String gainOrnamentList; + + @Excel(name = "最终奖励总价") + @TableField("gain_ornaments_price") + private BigDecimal gainOrnamentsPrice; + + @Excel(name = "成败") + @TableField("is_victory") + private Boolean isVictory; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date openTime; + + @TableField(exist = false) + private String userAvatar; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeRecordBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeRecordBody.java new file mode 100644 index 0000000..84ca23d --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtUpgradeRecordBody.java @@ -0,0 +1,11 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class TtUpgradeRecordBody { + + private Integer userId; + + private String targetItemName; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtUserAmountRecordsBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtUserAmountRecordsBody.java new file mode 100644 index 0000000..7393af2 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtUserAmountRecordsBody.java @@ -0,0 +1,41 @@ +package com.ruoyi.domain.other; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import java.util.Date; + +@Data +public class TtUserAmountRecordsBody { + + private Integer userId; + + private String nickName; + + private String phoneNumber; + + // 收入1 支出0 + private Integer type; + + private Integer source; + + // 1金币 2积分 + private Integer moneyType; + + @NotNull(message = "页码不能为空") + @Min(value = 1, message = "最小值1") + private Integer page; + + @NotNull(message = "分页长度不能为空") + @Min(value = 1, message = "最小值1") + private Integer size; + private Integer limit; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtUserAvatar.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtUserAvatar.java new file mode 100644 index 0000000..81aca48 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtUserAvatar.java @@ -0,0 +1,40 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_user_avatar") +public class TtUserAvatar implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "用户头像") + private String avatar; + + @Excel(name = "是否默认", readConverterExp = "0=否,1=是") + private String isDefault; + + @Excel(name = "头像排序") + private Integer sort; + + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtUserBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtUserBody.java new file mode 100644 index 0000000..05f5d95 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtUserBody.java @@ -0,0 +1,27 @@ +package com.ruoyi.domain.other; + +import lombok.Data; + +@Data +public class TtUserBody { + + private Integer userId; + + private String userName; + + private String nickName; + + private String userType; + + private String phoneNumber; + + private String status; + + private String startDate; + + private String endDate; + + private String invitationCode; + + private Integer parentId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtUserCreditsRecordsBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtUserCreditsRecordsBody.java new file mode 100644 index 0000000..8fc3159 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtUserCreditsRecordsBody.java @@ -0,0 +1,25 @@ +package com.ruoyi.domain.other; + +import lombok.Builder; +import lombok.Data; + +@Builder +@Data +public class TtUserCreditsRecordsBody { + + private Integer userId; + + private String nickName; + + private String phoneNumber; + + private Integer type; + + private String source; + + private Integer page; + + private Integer size; + + private Integer limit; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtUserPackSackBody.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtUserPackSackBody.java new file mode 100644 index 0000000..6e42af1 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtUserPackSackBody.java @@ -0,0 +1,20 @@ +package com.ruoyi.domain.other; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TtUserPackSackBody { + + private Integer holderUserId; + private String phoneNumber; + private String status; + private String source; + private Integer fightId; + private Integer rollId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtVipLevel.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtVipLevel.java new file mode 100644 index 0000000..1780ca1 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtVipLevel.java @@ -0,0 +1,55 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_vip_level") +public class TtVipLevel implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "VIP等级名称") + private String name; + + @Excel(name = "VIP等级图标") + private String icon; + + @Excel(name = "VIP等级达标金额") + private BigDecimal rechargeThreshold; + + @Excel(name = "充值加送比例") + private BigDecimal commissions; + + @Excel(name = "等级达标奖励红包金额") + private BigDecimal addedBonus; + + @Excel(name = "描述") + private String description; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtWelfare.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtWelfare.java new file mode 100644 index 0000000..5d0ef89 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtWelfare.java @@ -0,0 +1,31 @@ +package com.ruoyi.domain.other; + +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; + +/** + * 福利列表对象 tt_welfare + * + * @author ruoyi + * @date 2024-05-11 + */ +@Data +public class TtWelfare extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 福利ID */ + private Integer welfareId; + + /** 福利名称 */ + private String welfareName; + + /** 类型 */ + private String type; + + /** VIP等级 */ + private Integer vipLevel; + + /** 箱子ID */ + private Integer boxId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtWelfareMonthlyRecharges.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtWelfareMonthlyRecharges.java new file mode 100644 index 0000000..d0bfc1c --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtWelfareMonthlyRecharges.java @@ -0,0 +1,36 @@ +package com.ruoyi.domain.other; + +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 每月充值福利对象 tt_welfare_monthly_recharges + * + * @author ruoyi + * @date 2024-06-24 + */ +@Data +public class TtWelfareMonthlyRecharges extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 福利ID */ + private Integer welfareId; + + /** 福利名称 */ + private String welfareName; + + /** 福利类型 */ + private String type; + + /** VIP等级 */ + private Integer vipLevel; + + /** 充值金额 */ + private BigDecimal amount; + + /** 福利宝箱ID */ + private Long boxId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/TtYYOrnaments.java b/skins-model/src/main/java/com/ruoyi/domain/other/TtYYOrnaments.java new file mode 100644 index 0000000..ea4dcd8 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/TtYYOrnaments.java @@ -0,0 +1,53 @@ +package com.ruoyi.domain.other; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.sql.Timestamp; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_yy_ornaments") +public class TtYYOrnaments { + + @TableId(value = "id",type = IdType.AUTO) + private String id; + + @TableField("name") + private String name; + + @TableField("hash_name") + private String hashName; + + @TableField("type_id") + private Integer typeId; + + @TableField("type_name") + private String typeName; + + @TableField("type_hash_name") + private String typeHashName; + + @TableField("weapon_id") + private Integer weaponId; + + @TableField("weapon_name") + private String weaponName; + + @TableField("weapon_hash_name") + private String weaponHashName; + + @TableField("update_time") + private Timestamp updateTime; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/other/UpgradeBodyA.java b/skins-model/src/main/java/com/ruoyi/domain/other/UpgradeBodyA.java new file mode 100644 index 0000000..51561c7 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/other/UpgradeBodyA.java @@ -0,0 +1,29 @@ +package com.ruoyi.domain.other; + +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; + +@Data +public class UpgradeBodyA { + + // 饰品id + //@NotNull(message = "饰品id不能为空") + // private Long ornamentId; + + // 幸运升级饰品id + @NotNull(message = "幸运升级饰品id不能为空") + private Integer upgradeOrnamentId; + + // 消费 + @NotNull(message = "消费金额不能为空") + private BigDecimal price; + + // 概率,增量 + @Range(min = 1,max = 100) + private Integer probability; + + // private Integer id; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/param/RedPackRecordParam.java b/skins-model/src/main/java/com/ruoyi/domain/param/RedPackRecordParam.java new file mode 100644 index 0000000..ee80e6e --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/param/RedPackRecordParam.java @@ -0,0 +1,87 @@ +package com.ruoyi.domain.param; + +public class RedPackRecordParam { + + private Integer uId; + /** 用户名 */ + private String uname; + + /** 关联道具id */ + private String email; + + /** 饰品封面 */ + private String phone; + /** 饰品封面 */ + private String key; + + /** 饰品封面 */ + private String name; + + private String origin; + + private Long rId; + + public Integer getuId() { + return uId; + } + + public void setuId(Integer uId) { + this.uId = uId; + } + + public String getUname() { + return uname; + } + + public void setUname(String uname) { + this.uname = uname; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getOrigin() { + return origin; + } + + public void setOrigin(String origin) { + this.origin = origin; + } + + public Long getrId() { + return rId; + } + + public void setrId(Long rId) { + this.rId = rId; + } +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/param/promotion/AnchorDailyTurnoverParam.java b/skins-model/src/main/java/com/ruoyi/domain/param/promotion/AnchorDailyTurnoverParam.java new file mode 100644 index 0000000..055edc3 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/param/promotion/AnchorDailyTurnoverParam.java @@ -0,0 +1,32 @@ +package com.ruoyi.domain.param.promotion; + +import lombok.Data; + +import java.util.List; + +/** + * 主播日流水 + */ +@Data +public class AnchorDailyTurnoverParam { + + /** + * 玩家上级ID + */ + private Integer parentId; + + /** + * 玩家上级昵称 + */ + private String parentNickName; + + private String beginTime; + + private String endTime; + + private Integer num; + + private Integer size; + + private List userIds; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/param/promotion/AnchorRechargeParam.java b/skins-model/src/main/java/com/ruoyi/domain/param/promotion/AnchorRechargeParam.java new file mode 100644 index 0000000..09d3b2d --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/param/promotion/AnchorRechargeParam.java @@ -0,0 +1,21 @@ +package com.ruoyi.domain.param.promotion; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 主播日流水 + */ +@Data +public class AnchorRechargeParam { + + /** + * 主播ID + */ + private Integer parentId; + /** + * 充值金额 + */ + private BigDecimal recharge; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/task/DTO/pWelfareMQData.java b/skins-model/src/main/java/com/ruoyi/domain/task/DTO/pWelfareMQData.java new file mode 100644 index 0000000..6b4631b --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/task/DTO/pWelfareMQData.java @@ -0,0 +1,31 @@ +package com.ruoyi.domain.task.DTO; + +import com.ruoyi.domain.entity.sys.TtUser; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Timestamp; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class pWelfareMQData implements Serializable { + + // 消费者 + private Integer userId; + private TtUser user; + + // 金额 + private BigDecimal account; + + // 消费类型 + private Integer accountType; + + private Timestamp createTime; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/task/TtTask.java b/skins-model/src/main/java/com/ruoyi/domain/task/TtTask.java new file mode 100644 index 0000000..f569b5c --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/task/TtTask.java @@ -0,0 +1,61 @@ +package com.ruoyi.domain.task; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +@TableName("tt_task") +public class TtTask { + + @TableId(value = "id",type = IdType.AUTO) + private Integer id; + + // 描述 + @TableField("task_describe") + private String taskDescribe; + + @TableField("name") + private String name; + + // 任务状态 + @TableField("state") + private Integer state; + + // 任务类型 + @TableField("type") + private Integer type; + + // 目标值类型 + @TableField("target_type") + private Integer targetType; + + // 目标值 + @TableField("target_value") + private Integer targetValue; + + // 奖励类型 + @TableField("award_type") + private Integer awardType; + + // 奖励数量 + @TableField("award_value") + private Integer awardValue; + + // 触发条件 + @TableField("task_condition") + private String taskCondition; + + // 海报地址 + @TableField("placard_url") + private String placardUrl; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/task/TtTaskDoing.java b/skins-model/src/main/java/com/ruoyi/domain/task/TtTaskDoing.java new file mode 100644 index 0000000..b56839b --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/task/TtTaskDoing.java @@ -0,0 +1,48 @@ +package com.ruoyi.domain.task; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.sql.Timestamp; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +@TableName("tt_task_doing") +public class TtTaskDoing { + + @TableId(value = "id",type = IdType.AUTO) + private Integer id; + + // 任务id + @TableField("task_id") + private Integer taskId; + + // 玩家id + @TableField("user_id") + private Integer userId; + + // 任务完成状态 + @TableField("completion_state") + private Integer completionState; + + // 任务进度值 + @TableField("progress") + private Integer progress; + + // 开始时间 + @TableField("begin_time") + private Timestamp beginTime; + + // 完成时间 + @TableField("compete_time") + private Timestamp competeTime; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/task/VO/TtTaskDoingVO.java b/skins-model/src/main/java/com/ruoyi/domain/task/VO/TtTaskDoingVO.java new file mode 100644 index 0000000..a3c1b19 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/task/VO/TtTaskDoingVO.java @@ -0,0 +1,79 @@ +package com.ruoyi.domain.task.VO; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.sql.Timestamp; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class TtTaskDoingVO { + + // 任务id + // @TableField("task_id") + private Integer taskId; + + // 任务实例id + // @TableField("task_id") + private Integer taskDoingId; + + // 玩家id + // @TableField("user_id") + private Integer userId; + + // 描述 + // @TableField("describe") + private String taskDescribe; + + // @TableField("name") + private String name; + + // 任务类型 + // @TableField("type") + private Integer type; + + // 目标值类型 + // @TableField("target_type") + private Integer targetType; + + // 目标值 + // @TableField("target_value") + private Integer targetValue; + + // 奖励类型 + // @TableField("award_type") + private Integer awardType; + + // 奖励数量 + // @TableField("award_value") + private Integer awardValue; + + // 任务完成状态 + // @TableField("completion_state") + private Integer completionState; + + // 开始时间 + // @TableField("begin_time") + private Timestamp beginTime; + + // 完成时间 + // @TableField("compete_time") + private Timestamp competeTime; + + // 任务进度值 + // @TableField("progress") + private Integer progress; + + // 海报地址 + // @TableField("placard_url") + private String placardUrl; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskAwardType.java b/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskAwardType.java new file mode 100644 index 0000000..69a8683 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskAwardType.java @@ -0,0 +1,22 @@ +package com.ruoyi.domain.task.constant; + +// 任务奖励类型 +public enum TaskAwardType { + + // 积分 + CREDITS(1), + + // 其他 + OTHER(2); + + private Integer code; + + TaskAwardType(Integer code){ + this.code = code; + } + + public Integer getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskCompletionState.java b/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskCompletionState.java new file mode 100644 index 0000000..314a075 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskCompletionState.java @@ -0,0 +1,19 @@ +package com.ruoyi.domain.task.constant; + +public enum TaskCompletionState { + + COMPLETION(1), + DOING(2), + TIME_OUT(3), + COMPLETION_PRIZE(4); + + private Integer code; + + TaskCompletionState(Integer code){ + this.code = code; + } + + public Integer getCode(){ + return this.code; + } +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskState.java b/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskState.java new file mode 100644 index 0000000..a25200a --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskState.java @@ -0,0 +1,18 @@ +package com.ruoyi.domain.task.constant; + +public enum TaskState { + + UP(1), + DOWN(2); + + private Integer code; + + TaskState(Integer code){ + this.code = code; + } + + public Integer getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskTargetType.java b/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskTargetType.java new file mode 100644 index 0000000..87bd9b8 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskTargetType.java @@ -0,0 +1,18 @@ +package com.ruoyi.domain.task.constant; + +public enum TaskTargetType { + + CREDITS(1), + DOWNLOAD(2); + + private Integer code; + + TaskTargetType(Integer code){ + this.code = code; + } + + public Integer getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskType.java b/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskType.java new file mode 100644 index 0000000..0ab468f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/task/constant/TaskType.java @@ -0,0 +1,20 @@ +package com.ruoyi.domain.task.constant; + +public enum TaskType { + + COMMON(1), + DAY(2), + WEEK(3), + MONTH(4); + + private Integer code; + + TaskType(Integer code){ + this.code = code; + } + + public Integer getCode(){ + return this.code; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/task/constant/mq/MQMoudle.java b/skins-model/src/main/java/com/ruoyi/domain/task/constant/mq/MQMoudle.java new file mode 100644 index 0000000..22aa0e2 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/task/constant/mq/MQMoudle.java @@ -0,0 +1,19 @@ +package com.ruoyi.domain.task.constant.mq; + +public enum MQMoudle { + + PROMOTION_WELFARE_QUEUE("pWelfareQueue"), + PROMOTION_WELFARE_EXCHANGE("pWelfareExchange"), + PROMOTION_WELFARE_KEY1("pwKey1"); + + private String value; + + MQMoudle(String value){ + this.value = value; + } + + public String getValue(){ + return this.value; + } + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/ApiContentDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/ApiContentDataVO.java new file mode 100644 index 0000000..5668f6d --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/ApiContentDataVO.java @@ -0,0 +1,27 @@ +package com.ruoyi.domain.vo; + +import com.ruoyi.domain.other.TtContent; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class ApiContentDataVO { + + private Integer contentTypeId; + + private String contentTypeName; + + private String contentAlias; + + private List contentList; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/ApiFightListDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/ApiFightListDataVO.java new file mode 100644 index 0000000..2de810f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/ApiFightListDataVO.java @@ -0,0 +1,66 @@ +package com.ruoyi.domain.vo; + +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.ruoyi.domain.entity.fight.FightSeat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class ApiFightListDataVO implements Serializable { + + private Integer id; + + private String model; + + private List seats; + + private Integer roundNumber; + + private Integer playerNum; + + private String boxData; + + private BigDecimal boxPriceTotal; + + private String status; + + private String userData; + + private String createTime; + + private String updateTime; + + + + private List winnerIds; + + private String winnerIdList; + /** + * 开箱饰品总金额 + */ + private BigDecimal totalOrnamentsPrice; + + private Integer userId; + + public List getWinnerIds() { + if (StringUtils.isBlank(winnerIdList)) { + return new ArrayList<>(); + } + return JSONUtil.toList(winnerIdList, Integer.class); + } +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/ApiLuckyOrnamentsDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/ApiLuckyOrnamentsDataVO.java new file mode 100644 index 0000000..a5244b1 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/ApiLuckyOrnamentsDataVO.java @@ -0,0 +1,29 @@ +package com.ruoyi.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ApiLuckyOrnamentsDataVO { + + private Integer id; + + private Long ornamentId; + + private String itemName; + + private BigDecimal usePrice; + + private String imageUrl; + + private String shortName; + + private String typeName; + + private String rarityName; + + private String exteriorName; + + private String levelImg; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/ApiLuckyUpgradeRecordDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/ApiLuckyUpgradeRecordDataVO.java new file mode 100644 index 0000000..245eb3e --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/ApiLuckyUpgradeRecordDataVO.java @@ -0,0 +1,33 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class ApiLuckyUpgradeRecordDataVO { + + private String nickName; + + private String avatar; + + private String probability; + + private BigDecimal amountConsumed; + + private String gainItemName; + + // 获得饰品价值 + private BigDecimal gainOrnamentsPrice; + + private String imageUrl; + + private Boolean isVictory; + + // private String result; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date openTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/ApiMessageDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/ApiMessageDataVO.java new file mode 100644 index 0000000..1dfe5b2 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/ApiMessageDataVO.java @@ -0,0 +1,22 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +@Data +public class ApiMessageDataVO { + + private Integer id; + + private String message; + + private String status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date sendTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date readingTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/ApiShoppingDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/ApiShoppingDataVO.java new file mode 100644 index 0000000..0bf18fd --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/ApiShoppingDataVO.java @@ -0,0 +1,34 @@ +package com.ruoyi.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotEmpty; +import java.math.BigDecimal; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ApiShoppingDataVO { + + private Integer id; + private String itemName; + private BigDecimal usePrice; + private BigDecimal creditsPrice; + private String imageUrl; + private String itemId; + private String shortName; + private String type; + private String typeName; + private String quality; + private String qualityName; + private String rarity; + private String rarityName; + private String rarityColor; + private String exterior; + private String exteriorName; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/AvailableMarketOrnamentVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/AvailableMarketOrnamentVO.java new file mode 100644 index 0000000..001d96c --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/AvailableMarketOrnamentVO.java @@ -0,0 +1,31 @@ +package com.ruoyi.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class AvailableMarketOrnamentVO { + + // 平台 "yy" "zbt" + private Integer platform; + + // yy的 + private Integer templateId; + // zbt的 + private String itemId; + + private Integer delivery; + private String itemName; + private String imageUrl; + private BigDecimal cnyPrice; + private BigDecimal price; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/BoxCacheDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/BoxCacheDataVO.java new file mode 100644 index 0000000..1565b0c --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/BoxCacheDataVO.java @@ -0,0 +1,23 @@ +package com.ruoyi.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class BoxCacheDataVO { + + private String remainingNum; + private Integer todayOpenNum; + private BigDecimal todayArisePriceTotal; + private BigDecimal todayProfit; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/ChildDetailByBossVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/ChildDetailByBossVO.java new file mode 100644 index 0000000..911eb3f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/ChildDetailByBossVO.java @@ -0,0 +1,36 @@ +package com.ruoyi.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class ChildDetailByBossVO { + + // 粉丝id + private Integer userId; + + // 粉丝昵称 + private String nickName; + + // 消费金币 + private BigDecimal amountTotal; + + // 消费积分 + private BigDecimal creditsToTal; + + // 消费合计 + private BigDecimal total; + + // boss收益 + private BigDecimal earnings; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/CommonOrnamentVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/CommonOrnamentVO.java new file mode 100644 index 0000000..3c6e062 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/CommonOrnamentVO.java @@ -0,0 +1,124 @@ +package com.ruoyi.domain.vo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Date; + +/* +通用饰品信息vo + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class CommonOrnamentVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + private Integer yyId; + + // 是表里的itemId(ZBT官网饰品id) + private Integer zbtId; + + // 也是zbt的长名称 + @Excel(name = "名称") + @TableField("name") + private String name; + + @Excel(name = "饰品唯一名称英文") + private String marketHashName; + + @Excel(name = "类型") + private Integer type; + + @Excel(name = "type_name") + @TableField("type_name") + private String typeName; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField("update_time") + private Timestamp updateTime; + + @Excel(name = "本网站使用价格") + private BigDecimal usePrice; + + @Excel(name = "图片") + private String imageUrl; + + @Excel(name = "在售数量") + private Integer quantity; + + + + + // yy + @Excel(name = "type_hash_name") + @TableField("type_hash_name") + private String typeHashName; + + @Excel(name = "weapon_id") + @TableField("weapon_id") + private Integer weaponId; + + @Excel(name = "weapon_name") + @TableField("weapon_name") + private String weaponName; + + @Excel(name = "weapon_hash_name") + @TableField("weapon_hash_name") + private String weaponHashName; + + @Excel(name = "在售最低价") + private BigDecimal price; + + @Excel(name = "短名称") + private String shortName; + + @Excel(name = "品质") + private String quality; + + @Excel(name = "品质名称") + private String qualityName; + + @Excel(name = "品质颜色") + private String qualityColor; + + @Excel(name = "稀有度") + private String rarity; + + @Excel(name = "稀有度名称") + private String rarityName; + + @Excel(name = "稀有度颜色") + private String rarityColor; + + @Excel(name = "外观") + private String exterior; + + @Excel(name = "外观名称") + private String exteriorName; + + private String remark; + + private String isPutaway; + + private String isProprietaryProperty; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/DeliveryApplyVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/DeliveryApplyVO.java new file mode 100644 index 0000000..dfc4d22 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/DeliveryApplyVO.java @@ -0,0 +1,38 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +// 提货申请记录信息 +@Data +public class DeliveryApplyVO { + + private Integer id; + + private Integer userId; + + private String outTradeNo; + + private Long ornamentId; + + private String ornamentName; + + private Integer boxRecordsId; + + private String nickName; + + // // 平台和id + // private Integer partyType; + + // 饰品hash + private String hashName; + + private BigDecimal ornamentsPrice; + private String name; + private String imageUrl; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/FightBoxDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/FightBoxDataVO.java new file mode 100644 index 0000000..ee0d1fa --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/FightBoxDataVO.java @@ -0,0 +1,24 @@ +package com.ruoyi.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class FightBoxDataVO { + + private Integer boxId; + private String boxImg01; + private String boxImg02; + private String boxName; + private BigDecimal price; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/FightResultDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/FightResultDataVO.java new file mode 100644 index 0000000..8c6315d --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/FightResultDataVO.java @@ -0,0 +1,17 @@ +package com.ruoyi.domain.vo; + +import com.ruoyi.domain.entity.TtOrnament; +import lombok.Data; + +import java.util.List; + +@Data +public class FightResultDataVO { + + private Integer fightId; + private Integer round; + private List playerGainsOrnamentsData; + private List winnerGainsOrnamentsData; + private List TtOrnament; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/FightRoomDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/FightRoomDataVO.java new file mode 100644 index 0000000..7e4ba33 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/FightRoomDataVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.domain.vo; + +import com.ruoyi.domain.other.BoxDataBodyA; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +public class FightRoomDataVO { + + List boxDataList; + private Integer fightId; + private String fightStatus; + private Integer boxNumTotal; + private BigDecimal boxPriceTotal; + private Integer userId; + private String userName; + private String nickName; + private String avatar; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/JackpotOrnamentsDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/JackpotOrnamentsDataVO.java new file mode 100644 index 0000000..e09de85 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/JackpotOrnamentsDataVO.java @@ -0,0 +1,25 @@ +package com.ruoyi.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class JackpotOrnamentsDataVO { + //产品名称 + private String itemName; + //成色 + private String exteriorName; + //图片地址 + private String imageUrl; + //等级 + private String level; + //等级图片 + private String levelImg; + //商品名称 + private String shortName; + //类型名称 + private String typeName; + //价格 + private BigDecimal usePrice; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/JoinFightUserDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/JoinFightUserDataVO.java new file mode 100644 index 0000000..811c680 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/JoinFightUserDataVO.java @@ -0,0 +1,15 @@ +package com.ruoyi.domain.vo; + +import lombok.Data; + +@Data +public class JoinFightUserDataVO { + + private Integer fightId; + private Integer userId; + private Integer joinSeatNum; + private String userName; + private String nickName; + private String avatar; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/OnSaleOrnamentVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/OnSaleOrnamentVO.java new file mode 100644 index 0000000..5b0659d --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/OnSaleOrnamentVO.java @@ -0,0 +1,25 @@ +package com.ruoyi.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class OnSaleOrnamentVO { + + private Integer partyType; + private String id; + private Integer delivery; + private String itemName; + private String imageUrl; + private BigDecimal cnyPrice; + private BigDecimal price; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/OpenBoxVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/OpenBoxVO.java new file mode 100644 index 0000000..a773470 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/OpenBoxVO.java @@ -0,0 +1,111 @@ +package com.ruoyi.domain.vo; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class OpenBoxVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Long id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "宝箱ID") + private Integer boxId; + + @Excel(name = "宝箱名称") + private String boxName; + + @Excel(name = "宝箱价格") + private BigDecimal boxPrice; + + @Excel(name = "饰品ID") + @TableField("ornament_id") + private Long ornamentId; + + @Excel(name = "市场唯一hash名称") + @TableField("market_hash_name") + private String marketHashName; + + @Excel(name = "zbt ID") + @TableField("ornaments_zbt_id") + private String ornamentsZbtId; + + @Excel(name = "yy ID") + @TableField("ornaments_yy_id") + private String ornamentsYyId; + + @Excel(name = "饰品名称") + private String ornamenName; + + @Excel(name = "饰品价格") + private BigDecimal ornamentsPrice; + + @Excel(name = "外观名称") + private String exteriorName; + + @Excel(name = "短名称") + private String shortName; + + @Excel(name = "本平台使用价格") + private BigDecimal usePrice; + + @Excel(name = "饰品图片") + private String imageUrl; + + @Excel(name = "饰品级别ID") + private Integer ornamentsLevelId; + + private String levelImg; + + // 0、在背包显示 1、已分解 + @Excel(name = "状态") + private Integer status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + // 0、对战 + @Excel(name = "饰品来源") + private Integer source; + + @Excel(name = "对战ID") + private Integer fightId; + + @Excel(name = "对战模式:回合数") + private Integer fightRoundNumber; + + @Excel(name = "Roll房ID") + private Integer rollId; + + @Excel(name = "持有者_用户ID") + private Integer holderUserId; + + // 用于对战模式,游戏完全结束才显示 + @TableField("is_show") + private Integer isShow; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/PlayerGainsOrnamentsDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/PlayerGainsOrnamentsDataVO.java new file mode 100644 index 0000000..fc836c3 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/PlayerGainsOrnamentsDataVO.java @@ -0,0 +1,19 @@ +package com.ruoyi.domain.vo; + +import lombok.Data; + +import java.util.List; + +@Data +public class PlayerGainsOrnamentsDataVO { + + List ornamentsDataList; + private UserPackSackDataVO ornamentsData; + private Integer userId; + private String userName; + private String nickName; + private String avatar; + private Integer joinSeatNum; + + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/PromotionDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/PromotionDataVO.java new file mode 100644 index 0000000..cc7036d --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/PromotionDataVO.java @@ -0,0 +1,20 @@ +package com.ruoyi.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class PromotionDataVO { + + private BigDecimal rechargeTotal; + private BigDecimal rebateTotal; + private BigDecimal unbalancedPrice; + private BigDecimal balancedPrice; + private BigDecimal todayRechargeTotal; + private BigDecimal todayRebateTotal; + private BigDecimal thisWeekRechargeTotal; + private BigDecimal thisWeekRebateTotal; + private BigDecimal thisMonthRechargeTotal; + private BigDecimal thisMonthRebateTotal; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/RollDetailsDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/RollDetailsDataVO.java new file mode 100644 index 0000000..1ac6761 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/RollDetailsDataVO.java @@ -0,0 +1,54 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.domain.entity.roll.TtRollUser; +import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO; +import com.ruoyi.domain.vo.roll.SimpleRollOrnamentVO; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Data +public class RollDetailsDataVO { + + private String rollName; + + private Boolean hasPW; + // private String pw; + + private String description; + + // 房间类型 + private String rollType; + + private String rollTypeName; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + private Integer currentPeopleNum; + + private Integer peopleNum; + + private Integer ornamentsNum; + + private BigDecimal totalOrnamentsPrice; + + // 状态 + private String rollStatus; + + //充值门槛 + private BigDecimal minRecharge; + + // 参与人员列表 + // private List playerList; + + // 奖池 + private List jackpotOrnamentsDataList; + + // 获奖名单 + // private List openPrizeList; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/RollListDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/RollListDataVO.java new file mode 100644 index 0000000..03db0d9 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/RollListDataVO.java @@ -0,0 +1,70 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class RollListDataVO { + + /** + * ROLL房ID + */ + private Integer id; + + /** + * ROLL房密码 + */ + private String rollPassword; + + /** + * 是否有密码 + */ + private Boolean hasPW; + + /** + * ROLL房类型 + */ + private String rollType; + + /** + * ROLL房类型名称 + */ + private String rollTypeName; + + private String nickName; + + private String avatar; + + private String rollName; + + private String description; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + private Integer currentPeopleNum; + + private Integer peopleNum; + + /** + * ROLL房状态 + */ + private String rollStatus; + + private String ornamentsList; + + /** + * 奖池物品实例数量 + */ + private Integer ornamentsNum; + + private BigDecimal totalOrnamentsPrice; + + /** + * 门槛 + */ + private BigDecimal minRecharge; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/ShoppingDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/ShoppingDataVO.java new file mode 100644 index 0000000..e6a4403 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/ShoppingDataVO.java @@ -0,0 +1,44 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class ShoppingDataVO { + private Integer id; + private String itemName; + private BigDecimal usePrice; + private BigDecimal useCredits; + private String imageUrl; + private String marketHashName; + private String itemId; + private BigDecimal price; + private String shortName; + private String type; + private String typeName; + private String quality; + private String qualityName; + private String qualityColor; + private String rarity; + private String rarityName; + private String rarityColor; + private String exterior; + private String exteriorName; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + private String remark; + private String isPutaway; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/SimpleRollUserVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/SimpleRollUserVO.java new file mode 100644 index 0000000..d3cc0af --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/SimpleRollUserVO.java @@ -0,0 +1,52 @@ +package com.ruoyi.domain.vo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class SimpleRollUserVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "用户名") + private String userName; + + @Excel(name = "昵称") + private String nickName; + + @Excel(name = "头像") + private String avatar; + + @Excel(name = "Roll房ID") + private Integer rollId; + + @Excel(name = "宝箱记录ID") + private Long boxRecordId; + + @JsonFormat(pattern = "yyyy-MM-dd") + private Date joinTime; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/SimpleUserVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/SimpleUserVO.java new file mode 100644 index 0000000..0be7ebd --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/SimpleUserVO.java @@ -0,0 +1,22 @@ +package com.ruoyi.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Data +public class SimpleUserVO { + + private Integer userId; + private Integer rollUserId; + private String nickName; + private String userName; + private Integer userType; + private Integer phoneNumber; + private Integer parentId; + private String avatar; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TeamDetailVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TeamDetailVO.java new file mode 100644 index 0000000..7a10281 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TeamDetailVO.java @@ -0,0 +1,16 @@ +package com.ruoyi.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.sql.Timestamp; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class TeamDetailVO { + private Integer employeeId; + private String employeeNickName; + private Timestamp beginTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtBoxDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtBoxDataVO.java new file mode 100644 index 0000000..0e347b7 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtBoxDataVO.java @@ -0,0 +1,48 @@ +package com.ruoyi.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import com.ruoyi.common.annotation.Excel; +@Data +public class TtBoxDataVO { + + @Excel(name = "宝箱ID") + private Integer boxId; + @Excel(name = "宝箱名称") + private String boxName; + @Excel(name = "宝箱所属分类ID") + private String boxTypeId; + @Excel(name = "宝箱所属分类") + private String boxTypeName; + @Excel(name = "价格") + private BigDecimal price; + private String boxImg01; + private String boxImg02; + @Excel(name = "宝箱顺序") + private Integer sort; + @Excel(name = "是否对战宝箱") + private String isFight; + @Excel(name = "宝箱类型") + private String boxType; + private Integer compAmount; + private String status; + private Long openNum; + private String isHome; + + // 宝箱完全开启所需金额 + @Excel(name = "每轮花费金额") + private BigDecimal amountConsumed; + + // 宝箱内饰品总价值 + @Excel(name = "饰品总价值") + private BigDecimal aggregateAmount; + + // 利润 系统的利润 + @Excel(name = "每轮利润") + private BigDecimal profit; + + // 利润率 系统的利润率 + @Excel(name = "每轮利润率") + private String profitMargin = "100%"; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtBoxOrnamentsDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtBoxOrnamentsDataVO.java new file mode 100644 index 0000000..b0b70af --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtBoxOrnamentsDataVO.java @@ -0,0 +1,38 @@ +package com.ruoyi.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class TtBoxOrnamentsDataVO { + + private Integer id; + private Integer boxId; + private Long ornamentId; + + private String imageUrl; + private String itemName; + private String name; + private BigDecimal usePrice; + private Integer ornamentsLevelId; + + // 等级图片 背景 + private String levelImg; + + private String level; + private Integer odds; + private String oddsPercentum; + private Integer realOdds; + private String realOddsPercentum; + private Integer realRemainingOdds; + private Integer anchorOdds; + private String anchorOddsPercentum; + private Integer robotOdds; + private String robotOddsPercentum; + private Integer compOdds; + private Integer realCompOdds; + private String remark; + private Integer thirdExplosiveOdds; + private String thirdExplosiveOddsPercentum; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtBoxRecordsDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtBoxRecordsDataVO.java new file mode 100644 index 0000000..40034c1 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtBoxRecordsDataVO.java @@ -0,0 +1,30 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class TtBoxRecordsDataVO { + + private Long boxRecordId; + private Integer userId; + private Integer holderUserId; + private Integer boxId; + private String boxName; + private BigDecimal boxPrice; + private Integer ornamentsId; + private String ornamentName; + private String ornamentImgUrl; + private String ornamentLevelImg; + private BigDecimal ornamentsPrice; + private String status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtDeliveryRecordDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtDeliveryRecordDataVO.java new file mode 100644 index 0000000..521c6ef --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtDeliveryRecordDataVO.java @@ -0,0 +1,26 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class TtDeliveryRecordDataVO { + + private Integer id; + private Integer userId; + private String nickName; + private String itemName; + private String imageUrl; + private String outTradeNo; + private BigDecimal buyPrice; + private BigDecimal ornamentsPrice; + private String levelImg; + private Integer thirdpartyDelivery; + private String orderId; + private String status; + private String message; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private String updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtOrnamentVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtOrnamentVO.java new file mode 100644 index 0000000..4851433 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtOrnamentVO.java @@ -0,0 +1,117 @@ +package com.ruoyi.domain.vo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.Objects; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtOrnamentVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(value = "id",type = IdType.AUTO) + private Long id; + + @Excel(name = "zbt_id") + @TableField("zbt_id") + private Long zbtId; + + @Excel(name = "yyyouping_id") + @TableField("yyyouping_id") + private Long yyyoupingId; + + @Excel(name = "长名称") + @TableField("name") + private String name; + + @Excel(name = "本网站使用价格") + private BigDecimal usePrice; + + @Excel(name = "图片") + private String imageUrl; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TtOrnamentVO that = (TtOrnamentVO) o; + return Objects.equals(marketHashName, that.marketHashName); + } + + @Override + public int hashCode() { + return Objects.hash(marketHashName); + } + + @Excel(name = "饰品唯一名称英文") + private String marketHashName; + + // @Excel(name = "在售最低价") + // private BigDecimal price; + + @Excel(name = "在售数量") + private Integer quantity; + + @Excel(name = "短名称") + private String shortName; + + @Excel(name = "类别") + private String type; + + @Excel(name = "类别中文名") + private String typeName; + + @Excel(name = "品质") + private String quality; + + @Excel(name = "品质名称") + private String qualityName; + + @Excel(name = "品质颜色") + private String qualityColor; + + @Excel(name = "稀有度") + private String rarity; + + @Excel(name = "稀有度名称") + private String rarityName; + + @Excel(name = "稀有度颜色") + private String rarityColor; + + @Excel(name = "外观") + private String exterior; + + @Excel(name = "外观名称") + private String exteriorName; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + private String remark; + + private String isPutaway; + + private String isProprietaryProperty; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtRedPacketDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtRedPacketDataVO.java new file mode 100644 index 0000000..2d9e0d0 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtRedPacketDataVO.java @@ -0,0 +1,58 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtRedPacketDataVO { + + private Integer id; + + private String title; + + private String description; + + private Integer num; + + private String amount; + + private String remainingNum; + + private String amountRange; + + private Integer userId; + + private String password; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date openingTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date validity; + + private String status; + + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + private String useStatus; + + private Integer createNum; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtRedPacketRecordDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtRedPacketRecordDataVO.java new file mode 100644 index 0000000..d891a57 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtRedPacketRecordDataVO.java @@ -0,0 +1,36 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtRedPacketRecordDataVO { + + private Integer id; + + private Integer redPacketId; + + private String title; + + private String receivePassword; + + private Integer userId; + + private String nickName; + + private BigDecimal receiveAmount; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date receiveTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtRollJackpotOrnamentsDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtRollJackpotOrnamentsDataVO.java new file mode 100644 index 0000000..4b51385 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtRollJackpotOrnamentsDataVO.java @@ -0,0 +1,37 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class TtRollJackpotOrnamentsDataVO { + + private Integer id; + + private Integer ornamentsId; + + private Integer ornamentsNum; + + private String itemName; + + private String shortName; + + private BigDecimal usePrice; + + private String imageUrl; + + private String ornamentLevelId; + + private String level; + + private String levelImg; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtRollPrizeDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtRollPrizeDataVO.java new file mode 100644 index 0000000..7b5a322 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtRollPrizeDataVO.java @@ -0,0 +1,59 @@ +package com.ruoyi.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtRollPrizeDataVO { + + // roll玩家id + @NotNull(message = "必须指定获奖人。") + private Integer rollUserId; + + /** + * 获奖用户ID + */ + private Integer userId; + + // 奖池物品id + @NotNull(message = "必须指定奖品id。") + private Integer rollJackpotOrnamentId; + + // 数量 + @Min(value = 1,message = "数量最少为1") + private Integer ornamentNum; + + // roll房id + private Integer rollId; + + /** + * 指定人 + */ + private String designatedBy; + + /** + * 获奖用户昵称 + */ + private String nickName; + private String imageUrl; + private Integer jackpotOrnamentsListId; + private Long ornamentsId; + private Integer ornamentsLevelId; + private String itemName; + private String shortName; + private BigDecimal usePrice; + private Integer rollUserPrizeId; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtUpgradeFailOrnamentsDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUpgradeFailOrnamentsDataVO.java new file mode 100644 index 0000000..3592465 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUpgradeFailOrnamentsDataVO.java @@ -0,0 +1,40 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class TtUpgradeFailOrnamentsDataVO { + + private Integer id; + + private Integer upgradeId; + + private Integer ornamentId; + + private BigDecimal usePrice; + + private String itemName; + + private Integer ornamentNumber; + + private String imageUrl; + + private String type; + + private String typeName; + + private Integer ornamentLevelId; + + private String level; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtUpgradeOrnamentsDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUpgradeOrnamentsDataVO.java new file mode 100644 index 0000000..d066dc5 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUpgradeOrnamentsDataVO.java @@ -0,0 +1,50 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class TtUpgradeOrnamentsDataVO { + + private Integer id; + + private Integer ornamentsId; + + private BigDecimal usePrice; + + private String itemName; + + private String imageUrl; + + private String type; + + private String typeName; + + private Integer ornamentsLevelId; + + private String level; + + private String luckSection; + + private BigDecimal amountRequired; + + private BigDecimal amountInvested; + + private String anchorLuckSection; + + private BigDecimal anchorAmountRequired; + + private BigDecimal anchorAmountInvested; + + private String status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAccountRecordsRankVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAccountRecordsRankVO.java new file mode 100644 index 0000000..2e6b2cb --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAccountRecordsRankVO.java @@ -0,0 +1,46 @@ +package com.ruoyi.domain.vo; + +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +//@Accessors(chain = true) +@Builder +public class TtUserAccountRecordsRankVO { + +// @TableId +// private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + private String userName; + + private String nickName; + + private String avatar; + +// private String type; +// +// private String source; + + @Excel(name = "变动金额") + private BigDecimal account; + +// @Excel(name = "最终积分") +// private BigDecimal finalCredits; + +// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +// private Date createTime; + + private String remark; + + private Integer accountRank; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAmountRecords/PWelfareVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAmountRecords/PWelfareVO.java new file mode 100644 index 0000000..c6450cf --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAmountRecords/PWelfareVO.java @@ -0,0 +1,35 @@ +package com.ruoyi.domain.vo.TtUserAmountRecords; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.recorde.TtUserAmountRecords; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class PWelfareVO implements Serializable { + + private static final long serialVersionUID = 1L; + + // 明细 + private Page details; + + // 今日预计收益 + private BigDecimal todayPredict; + + // 历史总收益 + private BigDecimal historyTotal; + + // 时间区间收益 + private BigDecimal timeTotal; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAmountRecords/PersonBlendErcashVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAmountRecords/PersonBlendErcashVO.java new file mode 100644 index 0000000..2f89b34 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAmountRecords/PersonBlendErcashVO.java @@ -0,0 +1,27 @@ +package com.ruoyi.domain.vo.TtUserAmountRecords; + +// 个人综合收支统计 + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class PersonBlendErcashVO { + + private Integer userId; + + private String userName; + + // 金币 + private BigDecimal amount; + // 积分 + private BigDecimal credits; + // 合计 + private BigDecimal total; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAmountRecords/TtUserBlendErcashVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAmountRecords/TtUserBlendErcashVO.java new file mode 100644 index 0000000..714caaa --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAmountRecords/TtUserBlendErcashVO.java @@ -0,0 +1,79 @@ +package com.ruoyi.domain.vo.TtUserAmountRecords; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Timestamp; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtUserBlendErcashVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "id") + @TableId(value = "id",type = IdType.AUTO) + private Long id; + + @Excel(name = "user_id") + @TableField("user_id") + private Integer userId; + + @Excel(name = "金币") + @TableField("amount") + private BigDecimal amount; + + @Excel(name = "final_amount") + @TableField("final_amount") + private BigDecimal finalAmount; + + @Excel(name = "积分") + @TableField("credits") + private BigDecimal credits; + + @Excel(name = "final_credits") + @TableField("final_credits") + private BigDecimal finalCredits; + + @Excel(name = "合计") + @TableField("total") + private BigDecimal total; + + @Excel(name = "来源") + @TableField("source") + private Integer source; + + @Excel(name = "收支类型") + @TableField("type") + private Integer type; + + @Excel(name = "create_time") + @TableField("create_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Timestamp createTime; + + @Excel(name = "update_time") + @TableField("update_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Timestamp updateTime; + + @Excel(name = "笔记") + @TableField("remark") + private String remark; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAmountRecords/UserAmountDetailVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAmountRecords/UserAmountDetailVO.java new file mode 100644 index 0000000..c582abb --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserAmountRecords/UserAmountDetailVO.java @@ -0,0 +1,59 @@ +package com.ruoyi.domain.vo.TtUserAmountRecords; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class UserAmountDetailVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "用户ID") + private Integer userId; + + // 1、收入 2、支出 + private Integer type; + + // 1、金币 2积分 + private Integer moneyType; + + // 来源 + private Integer source; + + @Excel(name = "变动金额") + private BigDecimal amount; + + private BigDecimal credits; + + @Excel(name = "最终金额") + private BigDecimal finalAmount; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String remark; + + private Integer pwChildId; + private String pwChildName; + private BigDecimal pwChildAccount; + + @TableField("task_id") + private Integer taskId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserCreditsRecordsRankVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserCreditsRecordsRankVO.java new file mode 100644 index 0000000..646277c --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserCreditsRecordsRankVO.java @@ -0,0 +1,53 @@ +package com.ruoyi.domain.vo; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +//@Accessors(chain = true) +@Builder +public class TtUserCreditsRecordsRankVO { + +// @TableId +// private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + private String userName; + + private String nickName; + + private String avatar; + +// private String type; +// +// private String source; + + @Excel(name = "变动积分") + private BigDecimal credits; + +// @Excel(name = "最终积分") +// private BigDecimal finalCredits; + +// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") +// private Date createTime; + + private String remark; + + private Integer creditsRank; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserPackSackDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserPackSackDataVO.java new file mode 100644 index 0000000..874f4fc --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserPackSackDataVO.java @@ -0,0 +1,49 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Comparator; +import java.util.Date; + +@Data +public class TtUserPackSackDataVO implements Comparator { + + private Long id; + + private Integer holderUserId; + + private String nickName; + + private String avatar; + + private Integer ornamentsId; + + private String ornamentName; + + private BigDecimal ornamentsPrice; + + private String imageUrl; + + private String status; + + private String source; + + private Integer fightId; + + private Integer rollId; + + private Integer priceRank; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @Override + public int compare(TtUserPackSackDataVO o1, TtUserPackSackDataVO o2) { + return o1.getOrnamentsPrice().compareTo(o2.getOrnamentsPrice()); + } +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserVipLevelVo.java b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserVipLevelVo.java new file mode 100644 index 0000000..639b170 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/TtUserVipLevelVo.java @@ -0,0 +1,28 @@ +package com.ruoyi.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TtUserVipLevelVo { + private Integer userId; + /** + * vip等级 + */ + private Integer vipLevel; + /** + * 当前充值总金额 + */ + private BigDecimal rate; + /** + * 最高vip登录金额 + */ + private BigDecimal maxRate; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/UpgradeResultDataVOA.java b/skins-model/src/main/java/com/ruoyi/domain/vo/UpgradeResultDataVOA.java new file mode 100644 index 0000000..880986e --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/UpgradeResultDataVOA.java @@ -0,0 +1,26 @@ +package com.ruoyi.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class UpgradeResultDataVOA { + + private Long id; + + private BigDecimal ornamentsPrice; + private String itemName; + private String shortName; + private String imageUrl; + private String exteriorName; + private String levelImg; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/UserBERankVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/UserBERankVO.java new file mode 100644 index 0000000..82fa263 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/UserBERankVO.java @@ -0,0 +1,64 @@ +package com.ruoyi.domain.vo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Timestamp; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class UserBERankVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "id") + @TableId(value = "id",type = IdType.AUTO) + private Long id; + + @Excel(name = "user_id") + @TableField("user_id") + private Integer userId; + + @TableField("nick_name") + private String nickName; + + // 头像 + private String avatar; + + @Excel(name = "amount") + @TableField("amount") + private BigDecimal amount; + + private String value; + + @Excel(name = "credits") + @TableField("credits") + private BigDecimal credits; + + @Excel(name = "total") + @TableField("total") + private BigDecimal total; + + @Excel(name = "create_time") + @TableField("create_time") + private Timestamp createTime; + + @TableField("be_rank") + private Integer beRank; + + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/UserPackSackDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/UserPackSackDataVO.java new file mode 100644 index 0000000..5271692 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/UserPackSackDataVO.java @@ -0,0 +1,22 @@ +package com.ruoyi.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class UserPackSackDataVO { + + private Long id; + + private Integer ornamentId; + private BigDecimal ornamentsPrice; + private String itemName; + private String shortName; + private String imageUrl; + private String exteriorName; + + private Integer ornamentsLevelId; + private String levelImg; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/WebsitePropertyDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/WebsitePropertyDataVO.java new file mode 100644 index 0000000..4976dfc --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/WebsitePropertyDataVO.java @@ -0,0 +1,40 @@ +package com.ruoyi.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class WebsitePropertyDataVO { + + private Long id; + + private String name; + + private BigDecimal usePrice; + + private String imageUrl; + + private Integer quantity; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + private String isPutaway; + + private String isProprietaryProperty; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/box/AdminBoxDetailVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/box/AdminBoxDetailVO.java new file mode 100644 index 0000000..cb793c7 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/box/AdminBoxDetailVO.java @@ -0,0 +1,32 @@ +package com.ruoyi.domain.vo.box; + +import com.ruoyi.domain.vo.TtBoxOrnamentsDataVO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class AdminBoxDetailVO { + + // 统计信息 + // 宝箱完全开启所需金额 + private BigDecimal amountConsumed; + + // 宝箱内饰品总价值 + private BigDecimal aggregateAmount; + + // 利润 + private BigDecimal profit; + + // 利润率 + private BigDecimal profitMargin; + + // 宝箱内物品明细 + private List ornamentsDetail; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/box/BoxGlobalData.java b/skins-model/src/main/java/com/ruoyi/domain/vo/box/BoxGlobalData.java new file mode 100644 index 0000000..c542598 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/box/BoxGlobalData.java @@ -0,0 +1,43 @@ +package com.ruoyi.domain.vo.box; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class BoxGlobalData { + + private Integer boxId; + private String boxName; + + // 单价 + private BigDecimal price; + // private Integer openNumber; + + // 通用------------------------------------------------- + // 宝箱完全开启所需金额 + private BigDecimal commonAmountConsumed; + // 宝箱内饰品总价值 + private BigDecimal commonAggregateAmount; + // 利润 + private BigDecimal commonProfit; + // 利润率 + private BigDecimal commonProfitMargin; + + // 主播--------------------------------------------------- + // 宝箱完全开启所需金额 + private BigDecimal anchorAmountConsumed; + // 宝箱内饰品总价值 + private BigDecimal anchorAggregateAmount; + // 利润 + private BigDecimal anchorProfit; + // 利润率 + private BigDecimal anchorProfitMargin; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/box/TtBoxUserVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/box/TtBoxUserVO.java new file mode 100644 index 0000000..9bfe30a --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/box/TtBoxUserVO.java @@ -0,0 +1,12 @@ +package com.ruoyi.domain.vo.box; + +import com.ruoyi.domain.entity.sys.TtUser; +import lombok.Data; + +import java.util.List; + +@Data +public class TtBoxUserVO { + private Integer boxId; + private List users; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/boxRecords/TtBoxRecordsVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/boxRecords/TtBoxRecordsVO.java new file mode 100644 index 0000000..c6e5007 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/boxRecords/TtBoxRecordsVO.java @@ -0,0 +1,110 @@ +package com.ruoyi.domain.vo.boxRecords; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtBoxRecordsVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Long id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "持有者_用户ID") + private Integer holderUserId; + + // 持有者昵称 + private String holderUserNickName; + + // 持有者头像 + private String avatar; + + @Excel(name = "宝箱ID") + private Integer boxId; + + @Excel(name = "宝箱名称") + private String boxName; + + @Excel(name = "宝箱价格") + private BigDecimal boxPrice; + + @Excel(name = "饰品ID") + @TableField("ornament_id") + private Long ornamentId; + + @Excel(name = "饰品名称") + private String ornamentName; + + @Excel(name = "饰品级别ID") + private Integer ornamentsLevelId; + + @Excel(name = "饰品级别图片") + private String ornamentLevelImg; + + @Excel(name = "市场唯一hash名称") + @TableField("market_hash_name") + private String marketHashName; + + @Excel(name = "zbt ID") + @TableField("ornaments_zbt_id") + private String ornamentsZbtId; + + @Excel(name = "yy ID") + @TableField("ornaments_yy_id") + private String ornamentsYyId; + + @Excel(name = "饰品价格") + private BigDecimal ornamentsPrice; + + @Excel(name = "饰品图片") + private String imageUrl; + + + // 0、在背包显示 1、已分解 + @Excel(name = "状态") + private Integer status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + // 0、对战 + @Excel(name = "饰品来源") + private Integer source; + + @Excel(name = "对战ID") + private Integer fightId; + + @Excel(name = "对战模式:回合数") + private Integer fightRoundNumber; + + @Excel(name = "Roll房ID") + private Integer rollId; + + // 用于对战模式,游戏完全结束才显示 + @TableField("is_show") + private Integer isShow; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/client/PackSackGlobalData.java b/skins-model/src/main/java/com/ruoyi/domain/vo/client/PackSackGlobalData.java new file mode 100644 index 0000000..1736aa1 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/client/PackSackGlobalData.java @@ -0,0 +1,20 @@ +package com.ruoyi.domain.vo.client; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class PackSackGlobalData { + + // private String userName; + + // 物品总数 + private Integer totalOrnamentNumber; + + // 物品总价值 + private Integer totalOrnamentPrice; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/delivery/DeliveryRecordVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/delivery/DeliveryRecordVO.java new file mode 100644 index 0000000..5cbe0db --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/delivery/DeliveryRecordVO.java @@ -0,0 +1,87 @@ +package com.ruoyi.domain.vo.delivery; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class DeliveryRecordVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "用户背包记录ID") + private Long boxRecordsId; + + @Excel(name = "饰品ID") + @TableField("ornament_id") + private Long ornamentId; + + @Excel(name = "饰品图片") + private String ornamentName; + + @Excel(name = "饰品图片") + private String ornamentImg; + + @Excel(name = "饰品图片") + private String ornamentLevelImg; + + @Excel(name = "市场唯一hashName") + @TableField("market_hash_name") + private String marketHashName; + + @Excel(name = "饰品价格") + private BigDecimal ornamentsPrice; + + @Excel(name = "网站订单号") + private String outTradeNo; + + //网站发货模式(1人工发货 2自动发货 3主播号提取) + @Excel(name = "网站发货模式") + private Integer delivery; + + @Excel(name = "实际购买价格") + private BigDecimal buyPrice; + + @Excel(name = "第三方平台订单号") + private String orderId; + + @Excel(name = "第三方发货模式") + private Integer thirdpartyDelivery; + + // DeliveryOrderStatus 发货订单状态(0发起提货 1待发货 3待收货 10订单完成 11订单取消) + @Excel(name = "发货订单状态") + private Integer status; + private String message; + + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/fight/AudienceVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/fight/AudienceVO.java new file mode 100644 index 0000000..1db3f64 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/fight/AudienceVO.java @@ -0,0 +1,28 @@ +package com.ruoyi.domain.vo.fight; + +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.fight.TtFight; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@AllArgsConstructor +@Builder +public class AudienceVO { + + private Integer currentRound; + + private List winnerIds; + + private TtFight fight; + + // 开箱结果 + private List fightResult; + + // 宝箱详情 + private List fightBoxVOList; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/fight/FightBoxVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/fight/FightBoxVO.java new file mode 100644 index 0000000..f76ed14 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/fight/FightBoxVO.java @@ -0,0 +1,56 @@ +package com.ruoyi.domain.vo.fight; + +import com.ruoyi.domain.vo.TtBoxOrnamentsDataVO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class FightBoxVO implements Serializable{ + + private static final long serialVersionUID = 1L; + + private Integer boxId; + private Integer number; + private String boxImg01; + private String boxImg02; + private BigDecimal price; + private String boxName; + + // 宝箱所有饰品信息 + private List ornaments; + + public FightBoxVO(Integer boxId,Integer number,String boxImg01,String boxImg02){ + this.boxId = boxId; + this.number = number; + this.boxImg01 = boxImg01; + this.boxImg02 = boxImg02; + } + // + // public FightBoxVO(String number,String boxImg01,String boxImg02){ + // this.number = Integer.valueOf(number); + // this.boxImg01 = boxImg01; + // this.boxImg02 = boxImg02; + // } + + // public FightBoxVO(String json){ + // + // ObjectMapper objectMapper = new ObjectMapper(); + // try { + // FightBoxVO fightBoxVO = objectMapper.readValue(json, FightBoxVO.class); + // this.number = fightBoxVO.getNumber(); + // this.boxImg01 = fightBoxVO.getBoxImg01(); + // this.boxImg02 = fightBoxVO.getBoxImg02(); + // } catch (JsonProcessingException e) { + // System.out.println("FightBoxVO【构造器异常】"); + // } + // } +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/fight/FightResultVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/fight/FightResultVO.java new file mode 100644 index 0000000..16672c1 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/fight/FightResultVO.java @@ -0,0 +1,35 @@ +package com.ruoyi.domain.vo.fight; + +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.fight.TtFight; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class FightResultVO { + + private Long currentRound; + + private List winnerIds; + + private TtFight fight; + + // 开箱结果 + private List fightResult; + + /** + * 开箱饰品总金额 + */ + private BigDecimal totalOrnamentsPrice; + + // 宝箱详情 + private List fightBoxVOList; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/fight/PersonResult.java b/skins-model/src/main/java/com/ruoyi/domain/vo/fight/PersonResult.java new file mode 100644 index 0000000..1a39b89 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/fight/PersonResult.java @@ -0,0 +1,10 @@ +package com.ruoyi.domain.vo.fight; + +import com.ruoyi.domain.entity.TtBoxRecords; + +public class PersonResult { + + private Integer userId; + + private TtBoxRecords openBoxRecord; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/fight/RoundResult.java b/skins-model/src/main/java/com/ruoyi/domain/vo/fight/RoundResult.java new file mode 100644 index 0000000..8088229 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/fight/RoundResult.java @@ -0,0 +1,15 @@ +package com.ruoyi.domain.vo.fight; + +import java.util.List; + +public class RoundResult { + + // 回合数 + private Integer roundNumber; + + // 宝箱id + private Integer boxId; + + // 本回合的开箱结果集合 + private List personResultList; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/fight/TtFightVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/fight/TtFightVO.java new file mode 100644 index 0000000..6aef086 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/fight/TtFightVO.java @@ -0,0 +1,85 @@ +package com.ruoyi.domain.vo.fight; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.fight.FightSeat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtFightVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "创建者ID") + private Integer userId; + + @Excel(name = "座位") + @TableField(value = "seats",typeHandler = JacksonTypeHandler.class) + private List seats; + + // 1、欧皇 2、非酋 + @Excel(name = "对战模式") + private String model; + + @Excel(name = "对战人数") + private Integer playerNum; + + @Excel(name = "回合数") + private Integer roundNumber; + + @TableField(value = "winner_ids",typeHandler = JacksonTypeHandler.class) + private List winnerIds; + + private List fightResult; + + @Excel(name = "选择宝箱数据") + @TableField(value = "box_data",typeHandler = JacksonTypeHandler.class) + private Map boxData; + + @Excel(name = "创建宝箱价格总数") + private BigDecimal boxPriceTotal; + + // 0,准备 1,进行中 2,结束 3,超时强制结束 + @Excel(name = "对战状态") + private Integer status; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + // 开始时间 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Timestamp beginTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @Excel(name = "备注") + private String remark; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/order/TtOrderVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/order/TtOrderVO.java new file mode 100644 index 0000000..22cdc09 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/order/TtOrderVO.java @@ -0,0 +1,75 @@ +package com.ruoyi.domain.vo.order; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtOrderVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "第三方") + private String thirdParty; + /** + * 0卡密 1支付宝 2微信 + */ + @Excel(name = "订单类型") + private String type; + + @Excel(name = "商品ID") + private Integer goodsId; + + @Excel(name = "商品价格") + private BigDecimal goodsPrice; + + @Excel(name = "商品数量") + private Integer goodsNum; + + @Excel(name = "商品总价") + private BigDecimal totalAmount; + + @Excel(name = "订单号") + private String orderId; + + @Excel(name = "外部订单号") + private String outTradeNo; + + @Excel(name = "sign验签密钥") + private String sign; + + @Excel(name = "支付状态") + private String status; + + @Excel(name = "支付跳转链接") + private String payUrl; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/promotion/AnchorDailyTurnoverVo.java b/skins-model/src/main/java/com/ruoyi/domain/vo/promotion/AnchorDailyTurnoverVo.java new file mode 100644 index 0000000..ee6afc6 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/promotion/AnchorDailyTurnoverVo.java @@ -0,0 +1,37 @@ +package com.ruoyi.domain.vo.promotion; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 主播日流水 + */ +@Data +public class AnchorDailyTurnoverVo { + + /** + * 上级ID + */ + private Integer parentId; + + /** + * 上级昵称 + */ + private String parentNickName; + + /** + * 主播下级日消费 + */ + private BigDecimal dailyTotal; + + /** + * 主播下级总消费 + */ + private BigDecimal totalByParent; + + @JsonFormat(pattern = "yyyy-MM-dd") + private Date date; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/promotion/TtPromotionRecordVo.java b/skins-model/src/main/java/com/ruoyi/domain/vo/promotion/TtPromotionRecordVo.java new file mode 100644 index 0000000..01547bb --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/promotion/TtPromotionRecordVo.java @@ -0,0 +1,47 @@ +package com.ruoyi.domain.vo.promotion; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TtPromotionRecordVo { + private Integer userId; + + /** + * 下级用户ID + */ + private Integer subordinateUserId; + + /** + * 下级用户名 + */ + private String subordinateUserName; + + /** + * 下级用户充值金额 + */ + private BigDecimal rechargePrice; + + /** + * 返佣金额 + */ + private BigDecimal rebate; + /** + * 奖励比例 + */ + private BigDecimal commissions; + /** + * 充值时间 + */ + private Date createTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/queryTemplateSaleByCategoryDataVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/queryTemplateSaleByCategoryDataVO.java new file mode 100644 index 0000000..90f6a8f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/queryTemplateSaleByCategoryDataVO.java @@ -0,0 +1,16 @@ +package com.ruoyi.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class queryTemplateSaleByCategoryDataVO { + private List saleTemplateByCategoryResponseList; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/queryTemplateSaleByCategoryVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/queryTemplateSaleByCategoryVO.java new file mode 100644 index 0000000..9f8dacc --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/queryTemplateSaleByCategoryVO.java @@ -0,0 +1,30 @@ +package com.ruoyi.domain.vo; + +//yy有品 批量查询在售商品详情vo + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class queryTemplateSaleByCategoryVO { + private Integer templateId; + private String templateHashName; + private String templateName; + private String iconUrl; + private String exteriorName; + private String rarityName; + private Integer typeId; + private String typeHashName; + private Integer weaponId; + private String weaponHashName; + private BigDecimal minSellPrice; + private BigDecimal referencePrice; + private Integer sellNum; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/roll/RollJackpotOrnamentsByPageVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/roll/RollJackpotOrnamentsByPageVO.java new file mode 100644 index 0000000..d219c07 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/roll/RollJackpotOrnamentsByPageVO.java @@ -0,0 +1,30 @@ +package com.ruoyi.domain.vo.roll; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class RollJackpotOrnamentsByPageVO implements Serializable { + + private List list; + + private Integer total; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/roll/RollJackpotOrnamentsVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/roll/RollJackpotOrnamentsVO.java new file mode 100644 index 0000000..a0ed9f9 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/roll/RollJackpotOrnamentsVO.java @@ -0,0 +1,60 @@ +package com.ruoyi.domain.vo.roll; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class RollJackpotOrnamentsVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "Roll房奖池ID") + private Integer jackpotId; + + @Excel(name = "Roll房奖池饰品ID") + private Long ornamentsId; + + @Excel(name = "饰品价格") + private BigDecimal price; + + @Excel(name = "饰品图片") + private String imgUrl; + + @Excel(name = "饰品名称") + private String ornamentName; + + @Excel(name = "饰品级别ID") + private Integer ornamentLevelId; + + private String ornamentLevelImg; + + @Excel(name = "饰品数量") + private Integer ornamentsNum; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/roll/RollUserPrizeVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/roll/RollUserPrizeVO.java new file mode 100644 index 0000000..8e42b2f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/roll/RollUserPrizeVO.java @@ -0,0 +1,77 @@ +package com.ruoyi.domain.vo.roll; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class RollUserPrizeVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer rollUserPrizeId; + + @Excel(name = "roll用户ID") + @TableField("roll_user_id") + private Integer rollUserId; + + // 用户id + private Integer userId; + + private String nickName; + + // roll房id + private Integer rollId; + + @Excel(name = "Roll房奖池id") + @TableField("roll_jackpot_id") + private Integer rollJackpotId; + + @Excel(name = "奖池饰品ID") + @TableField("roll_jackpot_ornament_id") + private Integer rollJackpotOrnamentId; + + @Excel(name = "饰品ID") + @TableField("ornament_id") + private Long ornamentId; + + @Excel(name = "饰品名称") + @TableField("ornament_name") + private String ornamentName; + + // 饰品图片 + private String ornamentImg; + + @Excel(name = "饰品图片") + @TableField("img_url") + private String imgUrl; + + @Excel(name = "饰品价格") + @TableField("price") + private BigDecimal price; + + @Excel(name = "奖品数量") + @TableField("number") + private Integer number; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/roll/RollUserVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/roll/RollUserVO.java new file mode 100644 index 0000000..06d7b5f --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/roll/RollUserVO.java @@ -0,0 +1,69 @@ +package com.ruoyi.domain.vo.roll; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class RollUserVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "用户名") + private String userName; + + @Excel(name = "昵称") + private String nickName; + + @Excel(name = "头像") + private String avatar; + + @Excel(name = "Roll房ID") + private Integer rollId; + + @Excel(name = "奖池饰品列表ID") + private Integer jackpotOrnamentsId; + + @Excel(name = "奖池饰品ID") + private Long ornamentsId; + + @Excel(name = "宝箱记录ID") + private Long boxRecordId; + + // 2系统指定获奖者 + private String status; + + @Excel(name = "指定者") + private String designatedBy; + + @TableField("get_prize_way") + private Integer getPrizeWay; + + @JsonFormat(pattern = "yyyy-MM-dd") + private Date joinTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/roll/SimpleRollOrnamentVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/roll/SimpleRollOrnamentVO.java new file mode 100644 index 0000000..1504204 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/roll/SimpleRollOrnamentVO.java @@ -0,0 +1,58 @@ +package com.ruoyi.domain.vo.roll; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class SimpleRollOrnamentVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + @Excel(name = "Roll房奖池ID") + private Integer jackpotId; + + @Excel(name = "饰品ID") + private Long ornamentId; + + @Excel(name = "饰品价格") + private BigDecimal price; + + @Excel(name = "饰品图片") + private String imgUrl; + + // 类型 + private String typeName; + + // 外观 + private String exteriorName; + + @Excel(name = "饰品名称") + private String ornamentName; + + @Excel(name = "饰品级别ID") + private Integer ornamentsLevelId; + + @Excel(name = "饰品级别img") + private String ornamentsLevelImg; + + @Excel(name = "饰品数量") + private Integer ornamentsNum; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/sys/SimpleTtUserVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/sys/SimpleTtUserVO.java new file mode 100644 index 0000000..5acf4a5 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/sys/SimpleTtUserVO.java @@ -0,0 +1,124 @@ +package com.ruoyi.domain.vo.sys; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class SimpleTtUserVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "用户ID") + @TableId(value = "user_id",type = IdType.AUTO) + private Integer userId; + + @Excel(name = "用户账号") + private String userName; + + @Excel(name = "用户昵称") + private String nickName; + + //01主播 02普通用户 + private String userType; + + @Excel(name = "用户邮箱") + private String email; + + @Excel(name = "手机号码") + private String phoneNumber; + + @Excel(name = "头像地址") + private String avatar; + + // 充值金额 + private BigDecimal recharge; + + // 金币消费 + private BigDecimal amountConsume; + + // 积分消费 + private BigDecimal creditsConsume; + + // 综合消费 + private BigDecimal beConsume; + + // @Excel(name = "账户金额") + // private BigDecimal accountAmount; + // + // @Excel(name = "账户积分") + // private BigDecimal accountCredits; + + // @Excel(name = "邀请码") + // private String invitationCode; + + @Excel(name = "上级ID") + private Integer parentId; + + @Excel(name = "VIP等级") + private Integer vipLevel; + + @Excel(name = "推广等级") + private Integer promotionLevel; + + private String status; + + private String deliveryStatus; + + // @Excel(name = "steam账号ID") + // private Long steamId; + // + // @Excel(name = "steam交易链接") + // @TableField("transaction_link") + // private String transactionLink; + + // @Excel(name = "真实姓名") + // private String realName; + // + // @Excel(name = "身份证号码") + // private String idNum; + // + // @Excel(name = "实名认证流程号") + // private String certifyId; + // + // private String isRealCheck; + + @Excel(name = "最后登录IP") + private String loginIp; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date loginDate; + + private String createBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String updateBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @Excel(name = "备注") + private String remark; + + @TableField(select = false) + private String delFlag; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/task/SimpleAmountRecordVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/task/SimpleAmountRecordVO.java new file mode 100644 index 0000000..bd447e0 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/task/SimpleAmountRecordVO.java @@ -0,0 +1,60 @@ +package com.ruoyi.domain.vo.task; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class SimpleAmountRecordVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "上级ID") + private Integer parentId; + + // 1、收入 2、支出 + private Integer type; + + // 来源 + private Integer source; + + @Excel(name = "变动金额") + private BigDecimal amount; + + @Excel(name = "最终金额") + private BigDecimal finalAmount; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String remark; + + private Integer pwChildId; + private String pwChildName; + private BigDecimal pwChildAccount; + + @TableField("task_id") + private Integer taskId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/task/SimpleBlendErcashRecordVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/task/SimpleBlendErcashRecordVO.java new file mode 100644 index 0000000..4b0cc03 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/task/SimpleBlendErcashRecordVO.java @@ -0,0 +1,59 @@ +package com.ruoyi.domain.vo.task; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class SimpleBlendErcashRecordVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "上级ID") + private Integer parentId; + + // 1、收入 2、支出 + private Integer type; + + // 来源 + private Integer source; + + @Excel(name = "变动金币") + private BigDecimal amount; + + @Excel(name = "变动积分") + private BigDecimal credits; + + // 合计 + private BigDecimal total; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + private String remark; + +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/task/SimpleCreditsRecordVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/task/SimpleCreditsRecordVO.java new file mode 100644 index 0000000..3cc55ea --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/task/SimpleCreditsRecordVO.java @@ -0,0 +1,60 @@ +package com.ruoyi.domain.vo.task; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class SimpleCreditsRecordVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @TableId + private Integer id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "上级ID") + private Integer parentId; + + private Integer type; + + private Integer source; + + @Excel(name = "变动积分") + private BigDecimal credits; + + @Excel(name = "最终积分") + private BigDecimal finalCredits; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + private String remark; + + private Integer pwChildId; + + private String pwChildName; + + private BigDecimal pwChildAccount; + + @TableField("task_id") + private Integer taskId; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/upgrade/SimpleOrnamentVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/upgrade/SimpleOrnamentVO.java new file mode 100644 index 0000000..b8f4aab --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/upgrade/SimpleOrnamentVO.java @@ -0,0 +1,30 @@ +package com.ruoyi.domain.vo.upgrade; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.Date; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class SimpleOrnamentVO { + private Long ornamentId; + private String ornamentName; + private Integer ornamentNumber; + private String ornamentHashName; + private BigDecimal ornamentPrice; + private String ornamentImgUrl; + private String ornamentLevel; + private String ornamentLevelImg; + private Integer quantity; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/upgrade/UpgradeRecordVO.java b/skins-model/src/main/java/com/ruoyi/domain/vo/upgrade/UpgradeRecordVO.java new file mode 100644 index 0000000..b7de8e2 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/upgrade/UpgradeRecordVO.java @@ -0,0 +1,92 @@ +package com.ruoyi.domain.vo.upgrade; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import jakarta.validation.constraints.NotNull; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class UpgradeRecordVO implements Serializable { + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Excel(name = "升级记录ID") + @TableId + private Long id; + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "昵称") + private String nickName; + + private String avatar; + + @Excel(name = "用户类型") + private String userType; + + @Excel(name = "消耗金额") + private BigDecimal amountConsumed; + + private BigDecimal profit; + + @Excel(name = "可升级饰品ID") + @TableField("target_upgrade_id") + private Integer targetUpgradeId; + + @Excel(name = "目标饰品ID") + @TableField("target_ornament_id") + private Long targetOrnamentId; + + private String targetOrnamentName; + + private String targetOrnamentImg; + + @Excel(name = "目标饰品价格") + @TableField("target_ornament_price") + private BigDecimal targetOrnamentPrice; + + @Excel(name = "概率") + @TableField("probability") + private Integer probability; + + @Excel(name = "最终奖励饰品集合") + @TableField("gain_ornament_list") + private String gainOrnamentList; + + @Excel(name = "最终奖励总价") + @TableField("gain_ornaments_price") + private BigDecimal gainOrnamentsPrice; + + @Excel(name = "成败") + @TableField("is_victory") + private Boolean isVictory; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date openTime; + + // 记录总条数 分页辅助 + private Integer total; + + @NotNull(message = "页码不能为空") + private Integer page; + + @NotNull(message = "分页长度不能为空") + private Integer size; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/user/TtUseChildInfoVo.java b/skins-model/src/main/java/com/ruoyi/domain/vo/user/TtUseChildInfoVo.java new file mode 100644 index 0000000..e30e9f1 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/user/TtUseChildInfoVo.java @@ -0,0 +1,27 @@ +package com.ruoyi.domain.vo.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TtUseChildInfoVo { + /** + * 用户ID + */ + private Integer userId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/user/TtUserInfoVo.java b/skins-model/src/main/java/com/ruoyi/domain/vo/user/TtUserInfoVo.java new file mode 100644 index 0000000..f733f1b --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/user/TtUserInfoVo.java @@ -0,0 +1,40 @@ +package com.ruoyi.domain.vo.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TtUserInfoVo { + private Integer userId; + /** + * 父ID + */ + private Integer parentId; + /** + * 父名称 + */ + private String parentNickName; + /** + * 下级个数 + */ + private Integer childNum; + /** + * 下级总充值金额 + */ + private BigDecimal rechargePrice; + /** + * 充值收益 + */ + private BigDecimal commissionsRate; + /** + * 流水收益 + */ + private BigDecimal turnoverRate; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/user/TtUserVo.java b/skins-model/src/main/java/com/ruoyi/domain/vo/user/TtUserVo.java new file mode 100644 index 0000000..94805a0 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/user/TtUserVo.java @@ -0,0 +1,114 @@ +package com.ruoyi.domain.vo.user; + +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.domain.entity.sys.TtUser; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TtUserVo { + + @Excel(name = "用户ID") + private Integer userId; + + @Excel(name = "用户账号") + private String userName; + + @Excel(name = "用户昵称") + private String nickName; + + //01主播 02普通用户 + private String userType; + + @Excel(name = "用户邮箱") + private String email; + + @Excel(name = "手机号码") + private String phoneNumber; + + @Excel(name = "头像地址") + private String avatar; + + @Excel(name = "密码") + private String password; + + @Excel(name = "账户金额") + private BigDecimal accountAmount; + + @Excel(name = "用户总充值") + private BigDecimal totalPrice; + + @Excel(name = "用户总亏损") + private BigDecimal totalLoss; + + @Excel(name = "账户积分") + private BigDecimal accountCredits; + + @Excel(name = "邀请码") + private String invitationCode; + + @Excel(name = "上级ID") + private Integer parentId; + + @Excel(name = "VIP等级") + private Integer vipLevel; + + @Excel(name = "推广等级") + private Integer promotionLevel; + + private String status; + + private String deliveryStatus; + + @Excel(name = "steam账号ID") + private Long steamId; + + @Excel(name = "steam交易链接") + private String transactionLink; + + @Excel(name = "真实姓名") + private String realName; + + @Excel(name = "身份证号码") + private String idNum; + + @Excel(name = "实名认证流程号") + private String certifyId; + + private String isRealCheck; + + /** + * 佣金比例 + */ + private BigDecimal commissionRate; + + @Excel(name = "最后登录IP") + private String loginIp; + + private Date loginDate; + + private String createBy; + + private Date createTime; + + private String updateBy; + + private Date updateTime; + + @Excel(name = "备注") + private String remark; + + //0正常 + private String delFlag; + + private List children; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/zbt/SellItemInfo.java b/skins-model/src/main/java/com/ruoyi/domain/vo/zbt/SellItemInfo.java new file mode 100644 index 0000000..6541e7d --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/zbt/SellItemInfo.java @@ -0,0 +1,32 @@ +package com.ruoyi.domain.vo.zbt; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class SellItemInfo { + + private Long itemId; + private String itemName; + private String marketHashName; + + + private BigDecimal price; + + // 补贴价 + private BigDecimal subsidyPrice; + + // 上架价格 + private BigDecimal sellerPrice; + + // 人民币价格 + private BigDecimal cnyPrice; + + // // 发货类型 + // private BigDecimal delivery; +} diff --git a/skins-model/src/main/java/com/ruoyi/domain/vo/zbt/SellListData.java b/skins-model/src/main/java/com/ruoyi/domain/vo/zbt/SellListData.java new file mode 100644 index 0000000..e682af0 --- /dev/null +++ b/skins-model/src/main/java/com/ruoyi/domain/vo/zbt/SellListData.java @@ -0,0 +1,22 @@ +package com.ruoyi.domain.vo.zbt; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class SellListData { + + private Integer limit; + private List list; + private String offsetToken; + private Integer page; + private Integer pages; + private String scrollId; + private Integer total; + +} diff --git a/skins-promo/.DS_Store b/skins-promo/.DS_Store new file mode 100644 index 0000000..b387883 Binary files /dev/null and b/skins-promo/.DS_Store differ diff --git a/skins-promo/pom.xml b/skins-promo/pom.xml new file mode 100644 index 0000000..2951474 --- /dev/null +++ b/skins-promo/pom.xml @@ -0,0 +1,40 @@ + + + + ruoyi + com.ruoyi + 4.8.2 + + 4.0.0 + + skins-promo + + + 推广后台模块 + + + + + + com.ruoyi + ruoyi-common + + + + com.ruoyi + ruoyi-framework + + + com.ruoyi + service-thirdparty + 4.8.2 + + + com.ruoyi + service-user + 4.8.2 + + + \ No newline at end of file diff --git a/skins-promo/src/main/java/com/ruoyi/promo/contract/request/GetPurchaseByUserIdRequest.java b/skins-promo/src/main/java/com/ruoyi/promo/contract/request/GetPurchaseByUserIdRequest.java new file mode 100644 index 0000000..ad7b0d1 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/contract/request/GetPurchaseByUserIdRequest.java @@ -0,0 +1,17 @@ +package com.ruoyi.promo.contract.request; + +import lombok.Data; + +@Data +public class GetPurchaseByUserIdRequest { + /** + * 玩家ID + */ + private Integer userId; + + private String beginTime; + + private String endTime; + private Integer pageNum; + private Integer pageSize; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/contract/request/GetSubBranchesRequest.java b/skins-promo/src/main/java/com/ruoyi/promo/contract/request/GetSubBranchesRequest.java new file mode 100644 index 0000000..d6f27c7 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/contract/request/GetSubBranchesRequest.java @@ -0,0 +1,18 @@ +package com.ruoyi.promo.contract.request; + +import lombok.Data; + +@Data +public class GetSubBranchesRequest { + /** + * 主播ID + */ + private Integer userId; + /** + * 玩家id + */ + private Integer playerId; + + private Integer pageNum; + private Integer pageSize; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/contract/request/QueryPromoteWelfareRequest.java b/skins-promo/src/main/java/com/ruoyi/promo/contract/request/QueryPromoteWelfareRequest.java new file mode 100644 index 0000000..764bfd8 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/contract/request/QueryPromoteWelfareRequest.java @@ -0,0 +1,11 @@ +package com.ruoyi.promo.contract.request; + +import lombok.Data; + +@Data +public class QueryPromoteWelfareRequest { + private Integer pageNum; + private Integer pageSize; + private String beginTime; + private String endTime; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/contract/request/RegisterRequest.java b/skins-promo/src/main/java/com/ruoyi/promo/contract/request/RegisterRequest.java new file mode 100644 index 0000000..b6ac5f9 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/contract/request/RegisterRequest.java @@ -0,0 +1,20 @@ +package com.ruoyi.promo.contract.request; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class RegisterRequest { + @NotNull(message = "用户名不能为空") + private String username; + + @NotNull(message = "密码不能为空") + private String password; + + private String contactInformation; + + @NotNull(message = "上级邀请码不能为空") + private String parentInvitationCode; + + private String code; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/contract/request/UpdateInvitationCodeRequest.java b/skins-promo/src/main/java/com/ruoyi/promo/contract/request/UpdateInvitationCodeRequest.java new file mode 100644 index 0000000..69563ae --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/contract/request/UpdateInvitationCodeRequest.java @@ -0,0 +1,13 @@ +package com.ruoyi.promo.contract.request; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class UpdateInvitationCodeRequest { + @NotNull(message = "用户名不能为空") + private String username; + + @NotNull(message = "邀请码不能为空") + private String invitationCode; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/contract/response/GetShareDomainResponse.java b/skins-promo/src/main/java/com/ruoyi/promo/contract/response/GetShareDomainResponse.java new file mode 100644 index 0000000..dd95072 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/contract/response/GetShareDomainResponse.java @@ -0,0 +1,9 @@ +package com.ruoyi.promo.contract.response; + +import lombok.Data; + +@Data +public class GetShareDomainResponse { + private String zhuboShareDomain; + private String zhaoshaoShareDomain; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/contract/response/GetSubBranchesResponse.java b/skins-promo/src/main/java/com/ruoyi/promo/contract/response/GetSubBranchesResponse.java new file mode 100644 index 0000000..dc6127e --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/contract/response/GetSubBranchesResponse.java @@ -0,0 +1,35 @@ +package com.ruoyi.promo.contract.response; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Data +public class GetSubBranchesResponse { + private Integer total; + + private List rows; + + @Data + public static class UserInfo { + private Integer userId; + private String userName; + private String nickName; + //01主播 02普通用户 + private String userType; + /** + * 佣金比例 + */ + private BigDecimal commissionRate; + private Date createTime; + private Date updateTime; + + private BigDecimal totalRecharge; + private BigDecimal totalCost; + private BigDecimal totalNormGameCost; + private BigDecimal totalSpecialGameCost; + } + +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/contract/response/QueryPromotionWelfareResponse.java b/skins-promo/src/main/java/com/ruoyi/promo/contract/response/QueryPromotionWelfareResponse.java new file mode 100644 index 0000000..68f06e0 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/contract/response/QueryPromotionWelfareResponse.java @@ -0,0 +1,19 @@ +package com.ruoyi.promo.contract.response; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +public class QueryPromotionWelfareResponse { + private Integer total; + private List rows; + private BigDecimal totalReward; + + @Data + public static class Record { + private BigDecimal reward; + private String date; + } +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/contract/vo/DailyUserErcashVO.java b/skins-promo/src/main/java/com/ruoyi/promo/contract/vo/DailyUserErcashVO.java new file mode 100644 index 0000000..d762d7a --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/contract/vo/DailyUserErcashVO.java @@ -0,0 +1,13 @@ +package com.ruoyi.promo.contract.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class DailyUserErcashVO { + private String date; + private BigDecimal dailyNormGameCost; + private BigDecimal dailySpecialGameCost; + private BigDecimal dailyRecharge; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/controller/PromoLoginController.java b/skins-promo/src/main/java/com/ruoyi/promo/controller/PromoLoginController.java new file mode 100644 index 0000000..7b0d536 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/controller/PromoLoginController.java @@ -0,0 +1,96 @@ +package com.ruoyi.promo.controller; + +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.ApiLoginBody; +import com.ruoyi.promo.contract.request.RegisterRequest; +import com.ruoyi.promo.contract.request.UpdateInvitationCodeRequest; +import com.ruoyi.promo.contract.response.GetShareDomainResponse; +import com.ruoyi.promo.service.PromoLoginService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Api(tags = "推广后台用户认证") +@RestController +@RequestMapping("/promo") +public class PromoLoginController extends BaseController { + + @Autowired + private PromoLoginService promoLoginService; + + @ApiOperation("推广后台用户登录") + @Anonymous + @PostMapping("/login") + public AjaxResult login(@RequestBody ApiLoginBody apiLoginBody) { + AjaxResult ajax = AjaxResult.success(); + String token = promoLoginService.login(apiLoginBody.getUsername(), apiLoginBody.getPassword()); + ajax.put(Constants.TOKEN, token); + ajax.put("user", null); + return ajax; + } + + @ApiOperation("推广后台用户注册") + @Anonymous + @PostMapping("/register") + public AjaxResult register(@RequestBody RegisterRequest request) { + R result = promoLoginService.register(request); + if (R.isError(result)) { + return AjaxResult.error(result.getMsg()); + } + AjaxResult ajax = AjaxResult.success(); + String token = promoLoginService.login(request.getUsername(), request.getPassword()); + ajax.put(Constants.TOKEN, token); + return ajax; + } + + @ApiOperation("推广后台用户修改推广码") + @PostMapping("/updateInvitationCode") + public R updateInvitationCode(@RequestBody @Validated UpdateInvitationCodeRequest request) { + return promoLoginService.updateInvitationCode(request); + } + + @ApiOperation("获取主播/招商推广域名") + @GetMapping("/getsharedomain") + public R getShareDomain() { + return promoLoginService.getShareDomain(); + } + + + @ApiOperation("获取充值卡列表") + @GetMapping("/card/list") + public PageDataInfo cardList() { + List list = promoLoginService.queryList(); + return getPageData(list); + } + + @ApiOperation("生成充值卡") + @PostMapping("/generateCard/{rechargeListId}/{num}") + @UpdateUserCache + public R> generateCard(@PathVariable("rechargeListId") Integer rechargeListId, + @PathVariable("num") Integer num) { + var loginUser = getLoginUser(); + if (loginUser == null || loginUser.getUserData() == null) { + return R.fail("用户未登录"); + } + if (!UserType.ANCHOR.getCode().equals(loginUser.getUserData().getUserType())) { + return R.fail("无权限生成充值卡"); + } + Long userId = getUserId(); + + List cardList = promoLoginService.generateCard(userId.intValue(), rechargeListId, num); + return R.ok(cardList); + } +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/controller/PromoTurnoverController.java b/skins-promo/src/main/java/com/ruoyi/promo/controller/PromoTurnoverController.java new file mode 100644 index 0000000..81a23cd --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/controller/PromoTurnoverController.java @@ -0,0 +1,236 @@ +package com.ruoyi.promo.controller; + +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.pagehelper.PageInfo; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.utils.DateTimeUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.entity.TtCommissionRecord; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.vo.promotion.AnchorDailyTurnoverVo; +import com.ruoyi.promo.contract.request.GetPurchaseByUserIdRequest; +import com.ruoyi.promo.contract.request.GetSubBranchesRequest; +import com.ruoyi.promo.contract.request.QueryPromoteWelfareRequest; +import com.ruoyi.promo.contract.response.GetSubBranchesResponse; +import com.ruoyi.promo.contract.response.QueryPromotionWelfareResponse; +import com.ruoyi.promo.domain.dto.AnchorDayTurnoverDTO; +import com.ruoyi.promo.domain.param.AnchorDayTurnoverParam; +import com.ruoyi.promo.domain.vo.AnchorDayTurnoverVO; +import com.ruoyi.promo.mapper.PromoTurnoverMapper; +import com.ruoyi.promo.service.PromoTurnoverService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +@Api(tags = "推广流水") +@RestController +@RequestMapping("/promo/turnover") +public class PromoTurnoverController extends BaseController { + + @Autowired + private PromoTurnoverService promoTurnoverService; + + @Autowired + private PromoTurnoverMapper promoTurnoverMapper; + + @Autowired + private TtUserService userService; + + @Autowired + private TtUserMapper userMapper; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + @Anonymous + @GetMapping("/test") + public AjaxResult test(@RequestParam("uid")Long uid) { + if (uid == null) { + uid = getUserId(); + } + userService.updateUserAccount(Math.toIntExact(uid), BigDecimal.valueOf(10), TtAccountRecordSource.P_WELFARE); + return success(); + } + + @ApiOperation("获取实时数据") + @GetMapping("/getRealTimeData") + public AjaxResult getRealTimeData() { + Long userId = SecurityUtils.getUserId(); + return promoTurnoverService.getRealTimeData(userId.intValue()); + } + + @ApiOperation("获取近10天推广数据") + @GetMapping("getLast10DaysPromotionData") + public AjaxResult getLast10DaysPromotionData() { + Long userId = SecurityUtils.getUserId(); + return promoTurnoverService.getLast10DaysPromotionData(userId.intValue()); + } + + @ApiOperation("获取主播推广数据") + @GetMapping("/getAnchorPromotionData") + public TableDataInfo getAnchorPromotionData(AnchorDayTurnoverParam param) { + if (param.getPageNum() == null || param.getPageNum() <= 0) { + param.setPageNum(1); + } + if (param.getPageSize() == null || param.getPageSize() <= 0) { + param.setPageSize(10); + } + + LocalDateTime beginTime = LocalDate.now().atStartOfDay(); + LocalDateTime endTime = LocalDate.now().plusDays(1).atStartOfDay(); + if (StringUtils.isNotEmpty(param.getBeginTime())) { + beginTime = DateTimeUtils.toLocalDateTime(DateTimeUtils.dateTime(DateTimeUtils.YYYY_MM_DD, param.getBeginTime())); + } + if (StringUtils.isNotEmpty(param.getEndTime())) { + endTime = DateTimeUtils.toLocalDateTime(DateTimeUtils.dateTime(DateTimeUtils.YYYY_MM_DD, param.getEndTime())); + } + + var loginUser = getLoginUser(); + if (loginUser == null || loginUser.getUserData() == null) { + TableDataInfo pageDataInfo = new TableDataInfo(); + pageDataInfo.setTotal(0); + pageDataInfo.setCode(HttpStatus.ERROR); + pageDataInfo.setMsg("没登录"); + return pageDataInfo; + } + if (!UserType.BUSINESS_USER.getCode().equals(loginUser.getUserData().getUserType())) { + TableDataInfo pageDataInfo = new TableDataInfo(); + pageDataInfo.setTotal(0); + pageDataInfo.setCode(HttpStatus.ERROR); + pageDataInfo.setMsg("不是招商"); + return pageDataInfo; + } + Long userId = getUserId(); + + + Page pageInfo = new Page<>(param.getPageNum(), param.getPageSize()); + pageInfo.setOptimizeCountSql(false); + + var anchorPage = new LambdaQueryChainWrapper<>(userMapper).eq(TtUser::getParentId, userId) + .eq(param.getParentId() != null, TtUser::getUserId, param.getParentId()) + .eq( StringUtils.isNotEmpty(param.getParentNickName()), TtUser::getNickName, param.getParentNickName()).page(pageInfo); + var anchorList = anchorPage.getRecords(); + if (CollectionUtils.isEmpty(anchorList)) { + TableDataInfo pageDataInfo = new TableDataInfo(); + pageDataInfo.setTotal(0); + pageDataInfo.setCode(HttpStatus.SUCCESS); + pageDataInfo.setMsg("成功"); + return pageDataInfo; + } + + LocalDateTime finalBeginTime = beginTime; + LocalDateTime finalEndTime = endTime; + var data = anchorList.stream().map(anchor -> { + var normalUserList = new LambdaQueryChainWrapper<>(userMapper).eq(TtUser::getParentId, anchor.getUserId()).list(); + AnchorDayTurnoverVO item = new AnchorDayTurnoverVO(); + item.setUserId(anchor.getUserId()); + item.setNickName(anchor.getNickName()); + item.setTotalByParent(BigDecimal.ZERO); + if (CollectionUtils.isEmpty(normalUserList)) { + return item; + } + + var ids = normalUserList.stream().map(TtUser::getUserId).collect(Collectors.toList()); + var records = new LambdaQueryChainWrapper<>(ttUserBlendErcashMapper) + .in(TtUserBlendErcash::getUserId, ids) + .in(TtUserBlendErcash::getSource, TtAccountRecordSource.getGameConsumeCodes()) + .between(TtUserBlendErcash::getCreateTime, finalBeginTime, finalEndTime) + .list(); + if (CollectionUtils.isNotEmpty(records)) { + item.setTotalByParent(records.stream().map(TtUserBlendErcash::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add).abs()); + } + return item; + }).collect(Collectors.toList()); + + TableDataInfo pageDataInfo = new TableDataInfo(); + pageDataInfo.setTotal(anchorPage.getTotal()); + pageDataInfo.setCode(HttpStatus.SUCCESS); + pageDataInfo.setMsg("查询成功"); + pageDataInfo.setRows(data); + return pageDataInfo; + } + + @ApiOperation("获取下级历史数据") + @GetMapping("/getSubBranches") + public AjaxResult getSubBranches(GetSubBranchesRequest request) { + Long userId = getUserId(); + request.setUserId(userId.intValue()); + GetSubBranchesResponse response = promoTurnoverService.getSubBranches(request); + return AjaxResult.success(response); + } + + @ApiOperation("获取日流水记录") + @GetMapping("/getPurchaseByUserId/{userId}") + public TableDataInfo getPurchaseByUserId(GetPurchaseByUserIdRequest request) { + return promoTurnoverService.getPurchaseByUserId(request); + } + + @ApiOperation("获取佣金比例") + @GetMapping("/getCommissionRateByUserId/{userId}") + public AjaxResult getCommissionRateByUserId(@PathVariable("userId") Integer userId) { + BigDecimal commissionRate = promoTurnoverService.getCommissionRateByUserId(userId); + Map map = new HashMap<>(); + map.put("commissionRate", commissionRate); + return AjaxResult.success(map); + } + + @ApiOperation("获取佣金比例") + @PutMapping("/updateCommissionRate") + public AjaxResult updateCommissionRate(@RequestBody TtUser ttUser) { + Integer userId = ttUser.getUserId(); + BigDecimal commissionRate = ttUser.getCommissionRate(); + if (Objects.isNull(userId)) { + return AjaxResult.error("用户ID不能为空"); + } + if (Objects.isNull(commissionRate)) { + return AjaxResult.error("分佣比例不能为空"); + } + if (commissionRate.compareTo(BigDecimal.valueOf(0.1)) > 0) { + return AjaxResult.error("分佣比例不能超过0.1"); + } + if (commissionRate.compareTo(BigDecimal.valueOf(0.01)) < 0) { + return AjaxResult.error("分佣比例不能低于0.01"); + } + int row = promoTurnoverService.updateCommissionRate(userId, commissionRate); + return row >= 1 ? AjaxResult.success("修改成功") : AjaxResult.error("修改失败"); + } + + @ApiOperation("获取佣金列表") + @GetMapping("/getCommissionList") + public AjaxResult getCommissionList() { + Long userId = getUserId(); + List commissionList = promoTurnoverService.getCommissionList(userId.intValue()); + return AjaxResult.success(commissionList); + } + + @ApiOperation("获取主播推广奖励列表") + @GetMapping("/queryPromoteWelfare") + public AjaxResult queryPromoteWelfare(QueryPromoteWelfareRequest request) { + Long userId = getUserId(); + QueryPromotionWelfareResponse response = promoTurnoverService.queryPromoteWelfare(Math.toIntExact(userId), request); + return AjaxResult.success(response); + } +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/domain/dto/AnchorDayTurnoverDTO.java b/skins-promo/src/main/java/com/ruoyi/promo/domain/dto/AnchorDayTurnoverDTO.java new file mode 100644 index 0000000..0e65423 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/domain/dto/AnchorDayTurnoverDTO.java @@ -0,0 +1,38 @@ +package com.ruoyi.promo.domain.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 主播日流水 + */ +@Data +public class AnchorDayTurnoverDTO { + + /** + * 本人ID + */ + private Integer userId; + + /** + * 玩家上级ID + */ + private Integer parentId; + + /** + * 玩家上级昵称 + */ + private String parentNickName; + + /** + * 名下所有玩家ID + */ + private List userIds; + + private List sourceCodes; + + private String beginTime; + + private String endTime; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/domain/dto/TotalUserExpenditureDTO.java b/skins-promo/src/main/java/com/ruoyi/promo/domain/dto/TotalUserExpenditureDTO.java new file mode 100644 index 0000000..c907ca2 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/domain/dto/TotalUserExpenditureDTO.java @@ -0,0 +1,15 @@ +package com.ruoyi.promo.domain.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class TotalUserExpenditureDTO { + private Integer userId; + private String userName; + private String userType; + private Integer parentId; + private BigDecimal commissionRate; + private BigDecimal amount; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/domain/param/AnchorDayTurnoverParam.java b/skins-promo/src/main/java/com/ruoyi/promo/domain/param/AnchorDayTurnoverParam.java new file mode 100644 index 0000000..051b3d4 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/domain/param/AnchorDayTurnoverParam.java @@ -0,0 +1,29 @@ +package com.ruoyi.promo.domain.param; + +import lombok.Data; + +import java.util.List; + +/** + * 主播日流水 + */ +@Data +public class AnchorDayTurnoverParam { + + /** + * 玩家上级ID + */ + private Integer parentId; + + /** + * 玩家上级昵称 + */ + private String parentNickName; + + private String beginTime; + + private String endTime; + + private Integer pageNum; + private Integer pageSize; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/domain/param/GetPlayersTurnoverParam.java b/skins-promo/src/main/java/com/ruoyi/promo/domain/param/GetPlayersTurnoverParam.java new file mode 100644 index 0000000..7ddd863 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/domain/param/GetPlayersTurnoverParam.java @@ -0,0 +1,13 @@ +package com.ruoyi.promo.domain.param; + +import lombok.Data; + +import java.util.List; + +@Data +public class GetPlayersTurnoverParam { + private List userIds; + private List sourceCodes; + private String beginTime; + private String endTime; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/domain/vo/AnchorDayTurnoverVO.java b/skins-promo/src/main/java/com/ruoyi/promo/domain/vo/AnchorDayTurnoverVO.java new file mode 100644 index 0000000..b4e3e2f --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/domain/vo/AnchorDayTurnoverVO.java @@ -0,0 +1,52 @@ +package com.ruoyi.promo.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 主播日流水 + */ +@Data +public class AnchorDayTurnoverVO { + + /** + * 玩家ID + */ + private Integer userId; + + /** + * 上级ID + */ + private Integer parentId; + + /** + * 玩家昵称 + */ + private String nickName; + + /** + * 上级昵称 + */ + private String parentNickName; + + /** + * 主播下级日消费 + */ + private BigDecimal dailyTotal; + + /** + * 主播下级总消费 + */ + private BigDecimal totalByParent; + + /** + * 主播下级日充值 + */ + private BigDecimal dailyRechargeTotal; + + @JsonFormat(pattern = "yyyy-MM-dd") + private Date date; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/domain/vo/DayInviteVO.java b/skins-promo/src/main/java/com/ruoyi/promo/domain/vo/DayInviteVO.java new file mode 100644 index 0000000..7b4f247 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/domain/vo/DayInviteVO.java @@ -0,0 +1,14 @@ +package com.ruoyi.promo.domain.vo; + +import lombok.Data; + +import java.util.Date; + +/** + * 日邀请 + */ +@Data +public class DayInviteVO { + private Date date; + private Integer anchorCount; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/domain/vo/DayTurnoverVO.java b/skins-promo/src/main/java/com/ruoyi/promo/domain/vo/DayTurnoverVO.java new file mode 100644 index 0000000..412211a --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/domain/vo/DayTurnoverVO.java @@ -0,0 +1,15 @@ +package com.ruoyi.promo.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 日流水 + */ +@Data +public class DayTurnoverVO { + private Date date; + private BigDecimal amount; +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/mapper/PromoTurnoverMapper.java b/skins-promo/src/main/java/com/ruoyi/promo/mapper/PromoTurnoverMapper.java new file mode 100644 index 0000000..14ce191 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/mapper/PromoTurnoverMapper.java @@ -0,0 +1,117 @@ +package com.ruoyi.promo.mapper; + +import com.ruoyi.domain.entity.TtCommissionRecord; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.promo.domain.dto.AnchorDayTurnoverDTO; +import com.ruoyi.promo.domain.dto.TotalUserExpenditureDTO; +import com.ruoyi.promo.domain.param.GetPlayersTurnoverParam; +import com.ruoyi.promo.domain.vo.AnchorDayTurnoverVO; +import com.ruoyi.promo.domain.vo.DayInviteVO; +import com.ruoyi.promo.domain.vo.DayTurnoverVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * 推广流水Mapper + */ +@Mapper +public interface PromoTurnoverMapper { + + @Select("select SUM(amount) from tt_user_blend_ercash where user_id = #{userId} and source = #{source}") + BigDecimal queryTotalRewardBySource(@Param("userId")Integer userId, @Param("source")Integer source); + + @Select( + "") + List queryPlayerErcash(@Param("userId") Integer userId, + @Param("beginTime") String beginTime, @Param("endTime") String endTime); + + @Select("select SUM(amount) from tt_user_blend_ercash where user_id = #{userId} and DATE(create_time) = #{date} and amount < 0") + BigDecimal queryDailySum(@Param("userId")Integer userId, @Param("date")String date); + + /** + * 获取直属主播数量 + */ + int getAnchorCount(Integer userId); + + /** + * 获取玩家流水 + */ + BigDecimal getPlayersTurnover(GetPlayersTurnoverParam param); + + /** + * 获取名下主播近10天日流水 + */ + List getLast10DaysTurnover(@Param("userIds") List userIds, + @Param("sourceCodes") List sourceCodes); + + /** + * 获取名下主播近10天日邀请 + */ + List getLast10DaysInvite(Integer userId); + + /** + * 查询名下主播每日流水 + */ + List getAnchorDayTurnover(AnchorDayTurnoverDTO anchorDayTurnoverDTO); + + @Select("SELECT * FROM tt_user WHERE parent_id = #{parentId}") + List findByParentId(@Param("parentId") int parentId); + + @Select("SELECT * FROM tt_user WHERE user_id = #{userId}") + TtUser findById(@Param("userId") int id); + + /** + * 获取消费记录 + */ + List getPurchaseByUserId(Integer userId); + + + @Select("select sum(total) from tt_user_blend_ercash where user_id = #{userId} and type=1 and source = 1 and create_time >= #{startDate} and create_time < #{endDate}") + BigDecimal getDailyTotalRecharge(@Param("userId") Integer userId, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate); + + /** + * 获取佣金比例 + */ + BigDecimal getCommissionRateByUserId(Integer userId); + + /** + * 修改佣金比例 + */ + int updateCommissionRate(@Param("userId") Integer userId, @Param("commissionRate") BigDecimal commissionRate); + + /** + * 获取佣金列表 + */ + List getCommissionList(Integer userId); + + /** + * 获取上个月用户消费额 + */ + List getLastMonthTotalUserExpenditure(); +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/service/PromoLoginService.java b/skins-promo/src/main/java/com/ruoyi/promo/service/PromoLoginService.java new file mode 100644 index 0000000..a7f184f --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/service/PromoLoginService.java @@ -0,0 +1,24 @@ +package com.ruoyi.promo.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.promo.contract.request.RegisterRequest; +import com.ruoyi.promo.contract.request.UpdateInvitationCodeRequest; +import com.ruoyi.promo.contract.response.GetShareDomainResponse; + +import java.util.List; + +public interface PromoLoginService { + + String login(String username, String password); + R register(RegisterRequest request); + + R updateInvitationCode(UpdateInvitationCodeRequest request); + + R getShareDomain(); + + List queryList(); + + List generateCard(Integer userId, Integer rechargeListId, Integer num); +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/service/PromoTurnoverService.java b/skins-promo/src/main/java/com/ruoyi/promo/service/PromoTurnoverService.java new file mode 100644 index 0000000..f291a28 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/service/PromoTurnoverService.java @@ -0,0 +1,66 @@ +package com.ruoyi.promo.service; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.domain.entity.TtCommissionRecord; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.promo.contract.request.GetPurchaseByUserIdRequest; +import com.ruoyi.promo.contract.request.GetSubBranchesRequest; +import com.ruoyi.promo.contract.request.QueryPromoteWelfareRequest; +import com.ruoyi.promo.contract.response.GetSubBranchesResponse; +import com.ruoyi.promo.contract.response.QueryPromotionWelfareResponse; +import com.ruoyi.promo.domain.dto.AnchorDayTurnoverDTO; +import com.ruoyi.promo.domain.vo.AnchorDayTurnoverVO; + +import java.math.BigDecimal; +import java.util.List; + +public interface PromoTurnoverService { + + /** + * 递归全部最下级玩家 + */ + List recursionAllPlayer(Integer userId); + + /** + * 获取实时数据 + */ + AjaxResult getRealTimeData(Integer userId); + + /** + * 获取近10天推广数据 + */ + AjaxResult getLast10DaysPromotionData(Integer userId); + + /** + * 获取名下主播每日流水 + */ + List getAnchorDayTurnover(AnchorDayTurnoverDTO anchorDayTurnoverDTO); + + /** + * 获取下级分支 + */ + GetSubBranchesResponse getSubBranches(GetSubBranchesRequest request); + + /** + * 获取日流水记录 + */ + TableDataInfo getPurchaseByUserId(GetPurchaseByUserIdRequest userId); + + /** + * 获取佣金比例 + */ + BigDecimal getCommissionRateByUserId(Integer userId); + + /** + * 修改佣金比例 + */ + int updateCommissionRate(Integer userId, BigDecimal commissionRate); + + /** + * 获取佣金列表 + */ + List getCommissionList(Integer userId); + + QueryPromotionWelfareResponse queryPromoteWelfare(Integer userId, QueryPromoteWelfareRequest request); +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/service/impl/PromoLoginServiceImpl.java b/skins-promo/src/main/java/com/ruoyi/promo/service/impl/PromoLoginServiceImpl.java new file mode 100644 index 0000000..1a76fc1 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/service/impl/PromoLoginServiceImpl.java @@ -0,0 +1,285 @@ +package com.ruoyi.promo.service.impl; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.NumberUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.TtRechargeCardMapper; +import com.ruoyi.admin.mapper.TtRechargeProdMapper; +import com.ruoyi.admin.mapper.TtRedPacketMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.util.RandomUtils; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.common.utils.uuid.UUID; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtRedPacket; +import com.ruoyi.framework.web.service.SysLoginService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.promo.contract.request.RegisterRequest; +import com.ruoyi.promo.contract.request.UpdateInvitationCodeRequest; +import com.ruoyi.promo.contract.response.GetShareDomainResponse; +import com.ruoyi.promo.service.PromoLoginService; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.mapper.SysConfigMapper; +import com.ruoyi.thirdparty.common.service.ApiSmsService; +import com.ruoyi.user.service.UserIdGenerator; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Random; + +@Service +@Slf4j +public class PromoLoginServiceImpl implements PromoLoginService { + + + @Autowired + private SysLoginService sysLoginService; + + @Autowired + private TokenService tokenService; + + @Autowired + private TtUserMapper ttUserMapper; + + @Autowired + private TtUserService userService; + + @Autowired + private ApiSmsService apiSmsService; + + @Autowired + private UserIdGenerator userIdGenerator; + + @Autowired + private SysConfigMapper sysConfigMapper; + + @Autowired + private TtRechargeCardMapper rechargeCardMapper; + + @Autowired + private TtRechargeProdMapper rechargeListMapper; + + @Autowired + private TtRedPacketMapper redPacketMapper; + + + @Override + public String login(String username, String password) { + // 登录前置校验 + sysLoginService.loginPreCheck(username, password); + // 将前缀提供给ApiUserDetailsServiceImpl.loadUserByUsername,只允许运营类型用户登录 + LoginUser loginUser = sysLoginService.createLoginUser("promo_" + username, password); + // 记录登录信息 + recordLoginInfo(loginUser.getUserId()); + return tokenService.createToken(loginUser); + } + + @Override + public R register(RegisterRequest request) { + if (StringUtils.isEmpty(request.getUsername())) return R.fail("手机号不能为空"); + if (!Validator.isMobile(request.getUsername())) return R.fail("手机号格式错误,请检查手机号是否输入正确!"); + if (StringUtils.isEmpty(request.getPassword())) return R.fail("用户密码不能为空"); + if (StringUtils.isEmpty(request.getParentInvitationCode())) return R.fail("上级邀请码不能为空"); + if (!NumberUtil.isNumber(request.getCode()) || request.getCode().trim().length() != 4) + return R.fail("验证码错误"); + + if (!userService.checkPhoneUnique(TtUser.builder().phoneNumber(request.getUsername()).build())) + return R.fail("注册失败," + "手机号'" + request.getUsername() + "'已被注册!"); + + String phoneNumber = request.getUsername(); + String invitationCode = getInvitationCode(); + String nickName = RandomUtils.getRandomName(new Random().nextInt(2)); + TtUser ttUser = TtUser.builder().build(); + ttUser.setUserName(phoneNumber); + ttUser.setNickName(nickName); + ttUser.setUserType("01"); + ttUser.setPhoneNumber(phoneNumber); + ttUser.setAvatar(""); + ttUser.setPassword(SecurityUtils.encryptPassword(request.getPassword())); + ttUser.setInvitationCode(invitationCode.toLowerCase()); + ttUser.setIsRealCheck("0"); + ttUser.setRemark(request.getContactInformation()); + ttUser.setCreateBy(""); + ttUser.setCreateTime(DateUtils.getNowDate()); + ttUser.setAccountAmount(BigDecimal.ZERO); + ttUser.setAccountCredits(BigDecimal.ZERO); + + var parentInvitationCode = request.getParentInvitationCode(); + TtUser parentUser = new LambdaQueryChainWrapper<>(userService.getBaseMapper()) + .eq(TtUser::getInvitationCode, parentInvitationCode.trim().toUpperCase()) + .eq(TtUser::getDelFlag, "0").one(); + if (Objects.isNull(parentUser) || !UserType.BUSINESS_USER.getCode().equals(parentUser.getUserType())) { + return R.fail("上级邀请码填写错误!"); + } + ttUser.setParentId(parentUser.getUserId()); + + String validateCaptcha = apiSmsService.validateCaptcha(request.getCode().trim(), "ApiRegister_" + phoneNumber); + if (!"success".equals(validateCaptcha)) return R.fail(validateCaptcha); + + try { + var id = userIdGenerator.generateUserId(); + ttUser.setUserId(id); + ttUserMapper.insert(ttUser); + } catch (DuplicateKeyException e) { + return R.fail("手机号'" + request.getUsername() + "'已被注册!"); + } catch (Exception e) { + log.error("用户注册失败", e); + return R.fail("注册失败,请稍后重试"); + } + ttUser.setPassword(null); + return R.ok(ttUser); + } + + @Override + public R updateInvitationCode(UpdateInvitationCodeRequest request) { + // 验证参数 + if (StringUtils.isEmpty(request.getUsername())) return R.fail("用户名不能为空"); + if (StringUtils.isEmpty(request.getInvitationCode())) return R.fail("邀请码不能为空"); + if (request.getInvitationCode().length() != 6) return R.fail("邀请码长度必须为6位"); + + // 查找用户 + TtUser user = new LambdaQueryChainWrapper<>(userService.getBaseMapper()) + .eq(TtUser::getUserName, request.getUsername()) + .eq(TtUser::getDelFlag, "0") + .one(); + + if (Objects.isNull(user)) { + return R.fail("用户不存在"); + } + + // 检查邀请码是否已存在 + TtUser existingUser = new LambdaQueryChainWrapper<>(userService.getBaseMapper()) + .eq(TtUser::getInvitationCode, request.getInvitationCode().toLowerCase()) + .eq(TtUser::getDelFlag, "0") + .ne(TtUser::getUserId, user.getUserId()) // 排除当前用户 + .one(); + + if (Objects.nonNull(existingUser)) { + return R.fail("邀请码已存在,请更换其他邀请码"); + } + + // 更新邀请码 + try { + boolean updated = new LambdaUpdateChainWrapper<>(ttUserMapper) + .eq(TtUser::getUserId, user.getUserId()) + .set(TtUser::getInvitationCode, request.getInvitationCode().toLowerCase()) + .update(); + + + if (!updated) { + return R.fail("更新邀请码失败"); + } + + // 重新获取用户信息 + user = new LambdaQueryChainWrapper<>(userService.getBaseMapper()) + .eq(TtUser::getUserId, user.getUserId()) + .one(); + user.setPassword(null); // 清除密码 + return R.ok(user); + } catch (Exception e) { + log.error("更新邀请码失败", e); + return R.fail("更新邀请码失败,请稍后重试"); + } + } + + @Override + public R getShareDomain() { + GetShareDomainResponse resp = new GetShareDomainResponse(); + SysConfig zhubo = sysConfigMapper.checkConfigKeyUnique("app.zhubo.sharelink"); + SysConfig zhaoshang = sysConfigMapper.checkConfigKeyUnique("app.zhaoshang.sharelink"); + if (zhubo != null) { + resp.setZhuboShareDomain(zhubo.getConfigValue()); + } + if (zhaoshang != null) { + resp.setZhaoshaoShareDomain(zhaoshang.getConfigValue()); + } + return R.ok(resp); + } + + @Override + public List queryList() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.ge(TtRechargeProd::getPrice, BigDecimal.valueOf(100)); + return rechargeListMapper.selectList(wrapper); + } + + @Override + public List generateCard(Integer userId, Integer rechargeListId, Integer num) { + if (num <= 0) return new ArrayList<>(); + TtRechargeProd recharge = rechargeListMapper.selectById(rechargeListId); + if (recharge == null) return new ArrayList<>(); + + BigDecimal totalPrice = recharge.getPrice().multiply(BigDecimal.valueOf(num)); + var r = userService.updateUserCredits(userId, totalPrice.negate(), TtAccountRecordSource.ZHUBO_EXTRACT); + if (R.isError(r)) { + return new ArrayList<>(); + } + + + List cardList = new ArrayList<>(); + List rpList = new ArrayList<>(); + for (int i = 0; i < num; i++) { + + TtRedPacket redPacket = new TtRedPacket(); + redPacket.setNum(1); + redPacket.setUserId(userId); + redPacket.setTitle("福利"); + redPacket.setOpeningTime(DateUtils.getNowDate()); + redPacket.setAmount(String.format("[%s,%s]", recharge.getPrice().toPlainString(), recharge.getPrice().toPlainString())); + redPacket.setPassword(IdUtils.fastSimpleUUID().toUpperCase()); + cardList.add(redPacket.getPassword()); + redPacket.setCreateBy(SecurityUtils.getUsername()); + redPacket.setCreateTime(DateUtils.getNowDate()); + redPacket.setUseStatus(0); + rpList.add(redPacket); + } + + redPacketMapper.insert(rpList); + + return cardList; + } + + public String getInvitationCode() { + while (true) { + try { + String randomInvitationCode = UUID.randomUUID().toString().replace("-", "").substring(0, 6).toUpperCase(); + TtUser ttUser = new LambdaQueryChainWrapper<>(ttUserMapper) + .eq(TtUser::getInvitationCode, randomInvitationCode) + .eq(TtUser::getDelFlag, "0") + .one(); + if (StringUtils.isNull(ttUser)) { + return randomInvitationCode; + } + } catch (Exception e) { + return null; + } + } + } + + /** + * 记录登录信息 + */ + public void recordLoginInfo(Long userId) { + new LambdaUpdateChainWrapper<>(ttUserMapper).eq(TtUser::getUserId, userId) + .set(TtUser::getLoginIp, IpUtils.getIpAddr()) + .set(TtUser::getLoginDate, DateUtils.getNowDate()).update(); + } +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/service/impl/PromoTurnoverServiceImpl.java b/skins-promo/src/main/java/com/ruoyi/promo/service/impl/PromoTurnoverServiceImpl.java new file mode 100644 index 0000000..de0b1aa --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/service/impl/PromoTurnoverServiceImpl.java @@ -0,0 +1,530 @@ +package com.ruoyi.promo.service.impl; + +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.utils.DateTimeUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtCommissionRecord; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.promo.contract.request.GetPurchaseByUserIdRequest; +import com.ruoyi.promo.contract.request.GetSubBranchesRequest; +import com.ruoyi.promo.contract.request.QueryPromoteWelfareRequest; +import com.ruoyi.promo.contract.response.GetSubBranchesResponse; +import com.ruoyi.promo.contract.response.QueryPromotionWelfareResponse; +import com.ruoyi.promo.contract.vo.DailyUserErcashVO; +import com.ruoyi.promo.domain.dto.AnchorDayTurnoverDTO; +import com.ruoyi.promo.domain.param.GetPlayersTurnoverParam; +import com.ruoyi.promo.domain.vo.AnchorDayTurnoverVO; +import com.ruoyi.promo.domain.vo.DayInviteVO; +import com.ruoyi.promo.domain.vo.DayTurnoverVO; +import com.ruoyi.promo.mapper.PromoTurnoverMapper; +import com.ruoyi.promo.service.PromoTurnoverService; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class PromoTurnoverServiceImpl implements PromoTurnoverService { + + @Autowired + private PromoTurnoverMapper promoTurnoverMapper; + + @Autowired + private TtUserBlendErcashMapper userBlendErcashMapper; + + @Autowired + private TtUserMapper userMapper; + + @Override + public AjaxResult getRealTimeData(Integer userId) { + // 递归名下所有玩家 + List allPlayer = recursionAllPlayer(userId); + List userIds = new ArrayList<>(); + for (TtUser ttUser : allPlayer) { + userIds.add(ttUser.getUserId()); + } + Map map = new HashMap<>(); + if (CollectionUtils.isEmpty(userIds)) { + map.put("anchorCount", 0); + map.put("totalTurnover", BigDecimal.ZERO); + map.put("lastMonthTurnover", BigDecimal.ZERO); + map.put("currentMonthTurnover", BigDecimal.ZERO); + map.put("lastWeekTurnover", BigDecimal.ZERO); + map.put("currentWeekTurnover", BigDecimal.ZERO); + map.put("lastDayTurnover", BigDecimal.ZERO); + map.put("currentDayTurnover", BigDecimal.ZERO); + return AjaxResult.success(map); + } + // 设置日期转换为字符串格式 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + // 查询名下主播数 + map.put("anchorCount", promoTurnoverMapper.getAnchorCount(userId)); + // 查询总消费流水 + GetPlayersTurnoverParam param = new GetPlayersTurnoverParam(); + param.setUserIds(userIds); + param.setSourceCodes(new ArrayList<>(TtAccountRecordSource.getWelfarePrizeCodes())); + BigDecimal totalTurnover = promoTurnoverMapper.getPlayersTurnover(param); + map.put("totalTurnover", totalTurnover); + + // 查询上个月消费流水 + Date lastMonthStartTime = getStartOfLastMonth().getTime(); + Date lastMonthEndTime = getEndOfLastMonth().getTime(); + param.setBeginTime(sdf.format(lastMonthStartTime)); + param.setEndTime(sdf.format(lastMonthEndTime)); + BigDecimal lastMonthTurnover = promoTurnoverMapper.getPlayersTurnover(param); + map.put("lastMonthTurnover", lastMonthTurnover); + + // 查询当月消费流水 + Date currentMonthStartTime = getStartOfThisMonth().getTime(); + param.setBeginTime(sdf.format(currentMonthStartTime)); + param.setEndTime(null); + param.setBeginTime(sdf.format(currentMonthStartTime)); + param.setEndTime(null); + BigDecimal currentMonthTurnover = promoTurnoverMapper.getPlayersTurnover(param); + map.put("currentMonthTurnover", currentMonthTurnover); + + // 查询上周消费流水 + Date lastWeekStartTime = getStartOfLastWeek().getTime(); + Date lastWeekEndTime = getEndOfLastWeek().getTime(); + param.setBeginTime(sdf.format(lastWeekStartTime)); + param.setEndTime(sdf.format(lastWeekEndTime)); + BigDecimal lastWeekTurnover = promoTurnoverMapper.getPlayersTurnover(param); + map.put("lastWeekTurnover", lastWeekTurnover); + + // 查询本周消费流水 + Date currentWeekStartTime = getStartOfThisWeek().getTime(); + param.setBeginTime(sdf.format(currentWeekStartTime)); + param.setEndTime(null); + BigDecimal currentWeekTurnover = promoTurnoverMapper.getPlayersTurnover(param); + map.put("currentWeekTurnover", currentWeekTurnover); + + // 查询昨天消费流水 + Date yesterdayStartTime = getStartOfYesterday().getTime(); + Date yesterdayEndTime = getEndOfYesterday().getTime(); + param.setBeginTime(sdf.format(yesterdayStartTime)); + param.setEndTime(sdf.format(yesterdayEndTime)); + BigDecimal lastDayTurnover = promoTurnoverMapper.getPlayersTurnover(param); + map.put("lastDayTurnover", lastDayTurnover); + + // 查询当天消费流水 + Date currentDayStartTime = getStartOfToday().getTime(); + param.setBeginTime(sdf.format(currentDayStartTime)); + param.setEndTime(null); + BigDecimal currentDayTurnover = promoTurnoverMapper.getPlayersTurnover(param); + map.put("currentDayTurnover", currentDayTurnover); + + return AjaxResult.success(map); + } + + @Override + public AjaxResult getLast10DaysPromotionData(Integer userId) { + // 递归名下所有玩家 + List allPlayer = recursionAllPlayer(userId); + List userIds = new ArrayList<>(); + for (TtUser ttUser : allPlayer) { + userIds.add(ttUser.getUserId()); + } + if (CollectionUtils.isEmpty(userIds)) { + return AjaxResult.success(); + } + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + + List last10DaysTurnoverVOList = promoTurnoverMapper.getLast10DaysTurnover(userIds,new ArrayList<>(TtAccountRecordSource.getWelfarePrizeCodes())); + List last10DaysAnchorVOList = promoTurnoverMapper.getLast10DaysInvite(userId); + + List> list = new ArrayList<>(); + for (DayTurnoverVO last10DaysTurnoverVO : last10DaysTurnoverVOList) { + for (DayInviteVO last10DaysAnchorVO : last10DaysAnchorVOList) { + if (last10DaysTurnoverVO.getDate().equals(last10DaysAnchorVO.getDate())) { + Map map = new HashMap<>(); + map.put("date", dateFormat.format(last10DaysTurnoverVO.getDate())); + map.put("last10DaysTurnover", last10DaysTurnoverVO.getAmount()); + map.put("last10DaysAnchorInvite", last10DaysAnchorVO.getAnchorCount()); + list.add(map); + } + } + } + return AjaxResult.success(list); + } + + @Override + public List getAnchorDayTurnover(AnchorDayTurnoverDTO anchorDayTurnoverDTO) { + // 递归名下所有玩家 + Integer userId = anchorDayTurnoverDTO.getUserId(); + List allPlayer = recursionAllPlayer(userId); + List userIds = new ArrayList<>(); + for (TtUser ttUser : allPlayer) { + userIds.add(ttUser.getUserId()); + } + if (CollectionUtils.isEmpty(userIds)) { + return Collections.emptyList(); + } + anchorDayTurnoverDTO.setUserIds(userIds); + anchorDayTurnoverDTO.setSourceCodes(new ArrayList<>(TtAccountRecordSource.getWelfarePrizeCodes())); + + List result = promoTurnoverMapper.getAnchorDayTurnover(anchorDayTurnoverDTO); + for (AnchorDayTurnoverVO anchorDayTurnoverVO : result) { + Date startDate = anchorDayTurnoverVO.getDate(); + Date endDate = DateTimeUtils.addDays(startDate, 1); + BigDecimal dailyTotalRecharge = promoTurnoverMapper.getDailyTotalRecharge(anchorDayTurnoverVO.getUserId(), startDate, endDate); + anchorDayTurnoverVO.setDailyRechargeTotal(dailyTotalRecharge == null ? BigDecimal.ZERO : dailyTotalRecharge); + } + return result; + } + + @Override + public GetSubBranchesResponse getSubBranches(GetSubBranchesRequest request) { + if (request.getPageNum() == null || request.getPageNum() <= 0) { + request.setPageNum(1); + } + if (request.getPageSize() == null || request.getPageSize() <= 0) { + request.setPageSize(10); + } + Long userId = Long.valueOf(request.getUserId()); + var query = new LambdaQueryChainWrapper<>(userMapper).eq(TtUser::getParentId, userId); + + if (request.getPlayerId() != null) { + query.eq(TtUser::getUserId, request.getPlayerId()); + } + + Page page = new Page<>(request.getPageNum(), request.getPageSize()); + page.setOptimizeCountSql(false); + + var pageData = query.page(page); + + var childrenList = pageData.getRecords(); + + if (CollectionUtils.isEmpty(childrenList)) { + GetSubBranchesResponse response = new GetSubBranchesResponse(); + response.setTotal((int) page.getTotal()); + response.setRows(new ArrayList<>()); + return response; + } + + List userids = childrenList.stream().map(TtUser::getUserId).collect(Collectors.toList()); + List ercashList = new LambdaQueryChainWrapper<>(userBlendErcashMapper) + .in(TtUserBlendErcash::getSource, TtAccountRecordSource.getWelfarePrizeCodes()) + .in(TtUserBlendErcash::getUserId, userids).list(); + Map> ercashMap = ercashList.stream() + .collect(Collectors.groupingBy(TtUserBlendErcash::getUserId)); + + List resultList = childrenList.stream().map(user -> { + GetSubBranchesResponse.UserInfo userInfo = new GetSubBranchesResponse.UserInfo(); + BeanUtils.copyProperties(user, userInfo); + + userInfo.setTotalCost(BigDecimal.ZERO); + userInfo.setTotalRecharge(BigDecimal.ZERO); + + if (ercashMap.containsKey(user.getUserId())) { + var records = ercashMap.get(user.getUserId()); + + var totalRecharge = records.stream().filter(v -> { + return Objects.equals(v.getSource(), TtAccountRecordSource.RECHARGE.getCode()); + }).map(v -> v.getAmount().abs()).reduce(BigDecimal.ZERO, BigDecimal::add); + + var allConsumeList = records.stream().filter(v -> { + return Objects.equals(v.getType(), TtAccountRecordType.OUTPUT.getCode()); + }).toList(); + + var totalNormalGameConsume = allConsumeList.stream().filter(v -> { + return TtAccountRecordSource.isNormalGameConsume(v.getSource()); + }).map(v -> v.getAmount().abs()).reduce(BigDecimal.ZERO, BigDecimal::add); + + var totalSpecialGameConsume = allConsumeList.stream().filter(v -> { + return TtAccountRecordSource.isSpecialGameConsume(v.getSource()); + }).map(v -> v.getAmount().abs()).reduce(BigDecimal.ZERO, BigDecimal::add); + + + userInfo.setTotalNormGameCost(totalNormalGameConsume); + userInfo.setTotalSpecialGameCost(totalSpecialGameConsume); + userInfo.setTotalRecharge(totalRecharge); + } + + return userInfo; + }).collect(Collectors.toList()); + + GetSubBranchesResponse response = new GetSubBranchesResponse(); + response.setTotal((int) page.getTotal()); + response.setRows(resultList); + return response; + } + + private Set findAllChild(Integer parentId, Set result) { + + List list = promoTurnoverMapper.findByParentId(parentId); + HashSet childrens = new HashSet<>(list); + + if (childrens.isEmpty()) return result; + + result.addAll(childrens); + for (TtUser child : childrens) { + Set allChild = findAllChild(child.getUserId(), result); + result.addAll(allChild); + } + return result; + } + + @Override + public TableDataInfo getPurchaseByUserId(GetPurchaseByUserIdRequest request) { + if (request.getPageNum() == null || request.getPageNum() <= 0) { + request.setPageNum(1); + } + if (request.getPageSize() == null || request.getPageSize() <= 0) { + request.setPageSize(10); + } + if (request.getBeginTime() == null) { + LocalDateTime endTime = LocalDateTime.now(); + LocalDateTime startTime = endTime.minusDays(30).with(LocalTime.MIN); + request.setBeginTime(DateTimeUtils.date(startTime)); + } + if (request.getEndTime() == null) { + LocalDateTime endTime = LocalDateTime.now(); + request.setEndTime(DateTimeUtils.date(endTime)); + } + // Limit the time range to maximum 30 days + if (request.getBeginTime() != null && request.getEndTime() != null) { + LocalDateTime begin = DateTimeUtils.toLocalDateTime(DateTimeUtils.dateTime(DateTimeUtils.YYYY_MM_DD, request.getBeginTime())); + LocalDateTime end = DateTimeUtils.toLocalDateTime(DateTimeUtils.dateTime(DateTimeUtils.YYYY_MM_DD, request.getEndTime())); + if (ChronoUnit.DAYS.between(begin, end) > 30) { + LocalDateTime adjustedEnd = begin.plusDays(30); + request.setEndTime(DateTimeUtils.date(adjustedEnd)); + } + } + + List days = DateTimeUtils.generateDailyDates(request.getBeginTime(), request.getEndTime()); + days.sort(Comparator.reverseOrder()); + + int total = days.size(); + int start = Math.min((request.getPageNum() - 1) * request.getPageSize(), total - 1); + int end = Math.min(start + request.getPageSize(), total); + start = Math.max(start, 0); + end = Math.max(end, 0); + days = days.subList(start, end); + + + + List resultList = days.stream().map(d -> { + var records = new LambdaQueryChainWrapper<>(userBlendErcashMapper) + .eq(TtUserBlendErcash::getUserId, request.getUserId()) + .apply("DATE(create_time) = {0}", d) + .in(TtUserBlendErcash::getSource, TtAccountRecordSource.getWelfarePrizeCodes()) + .list(); + var totalRecharge = records.stream().filter(v -> { + return Objects.equals(v.getSource(), TtAccountRecordSource.RECHARGE.getCode()); + }).map(v -> v.getAmount().abs()).reduce(BigDecimal.ZERO, BigDecimal::add); + + var allConsumeList = records.stream().filter(v -> { + return Objects.equals(v.getType(), TtAccountRecordType.OUTPUT.getCode()); + }).toList(); + + var totalNormalGameConsume = allConsumeList.stream().filter(v -> { + return TtAccountRecordSource.isNormalGameConsume(v.getSource()); + }).map(v -> v.getAmount().abs()).reduce(BigDecimal.ZERO, BigDecimal::add); + + var totalSpecialGameConsume = allConsumeList.stream().filter(v -> { + return TtAccountRecordSource.isSpecialGameConsume(v.getSource()); + }).map(v -> v.getAmount().abs()).reduce(BigDecimal.ZERO, BigDecimal::add); + + DailyUserErcashVO r = new DailyUserErcashVO(); + r.setDate(d); + r.setDailyNormGameCost(totalNormalGameConsume); + r.setDailySpecialGameCost(totalSpecialGameConsume); + r.setDailyRecharge(totalRecharge); + return r; + }).collect(Collectors.toList()); + + TableDataInfo result = new TableDataInfo(); + result.setCode(200); + result.setRows(resultList); + result.setTotal(total); + return result; + + } + + @Override + public BigDecimal getCommissionRateByUserId(Integer userId) { + return promoTurnoverMapper.getCommissionRateByUserId(userId); + } + + @Override + public int updateCommissionRate(Integer userId, BigDecimal commissionRate) { + return promoTurnoverMapper.updateCommissionRate(userId, commissionRate); + } + + @Override + public List getCommissionList(Integer userId) { + return promoTurnoverMapper.getCommissionList(userId); + } + + @Override + public QueryPromotionWelfareResponse queryPromoteWelfare(Integer userId, QueryPromoteWelfareRequest request) { + Page page = new Page<>(request.getPageNum(), request.getPageSize()); + page.setOptimizeCountSql(false); + + LambdaQueryChainWrapper wrapper = new LambdaQueryChainWrapper<>(userBlendErcashMapper) + .eq(TtUserBlendErcash::getUserId, userId) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.P_WELFARE.getCode()); + + if (request.getBeginTime() != null) { + wrapper.ge(TtUserBlendErcash::getCreateTime, request.getBeginTime()); + } + if (request.getEndTime() != null) { + wrapper.le(TtUserBlendErcash::getCreateTime, request.getEndTime()); + } + wrapper.orderByDesc(TtUserBlendErcash::getCreateTime); + Page pageResult = wrapper.page(page); + + BigDecimal totalReward = promoTurnoverMapper.queryTotalRewardBySource(userId, TtAccountRecordSource.P_WELFARE.getCode()); + if (totalReward == null) { + totalReward = BigDecimal.ZERO; + } + if (CollectionUtils.isEmpty(pageResult.getRecords())) { + QueryPromotionWelfareResponse result = new QueryPromotionWelfareResponse(); + result.setRows(new ArrayList<>()); + result.setTotal(0); + result.setTotalReward(totalReward); + return result; + } + + QueryPromotionWelfareResponse result = new QueryPromotionWelfareResponse(); + result.setRows(pageResult.getRecords().stream().map(v -> { + QueryPromotionWelfareResponse.Record r = new QueryPromotionWelfareResponse.Record(); + r.setReward(v.getAmount()); + r.setDate(DateTimeUtils.dateTime(v.getCreateTime().toLocalDateTime())); + return r; + }).collect(Collectors.toList())); + result.setTotalReward(totalReward); + result.setTotal((int) pageResult.getTotal()); + return result; + } + + /** + * 递归下级分支 + */ + private void buildUserTree(TtUser ttUser) { + // 获取当前用户的子节点 + List children = promoTurnoverMapper.findByParentId(ttUser.getUserId()); + // 设置当前用户的子节点 + ttUser.setChildren(children); + for (TtUser child : children) { + // 如果子节点是主播,继续递归 + if ("01".equals(child.getUserType())) { + buildUserTree(child); + } + } + } + + @Override + public List recursionAllPlayer(Integer userId) { + List players = new ArrayList<>(); + getAllPlayersByAnchorIdRecursive(userId, players); + return players; + } + + /** + * 递归名下所有玩家 + */ + private void getAllPlayersByAnchorIdRecursive(int parentId, List players) { + List users = promoTurnoverMapper.findByParentId(parentId); + for (TtUser user : users) { + if ("02".equals(user.getUserType())) { // 如果是玩家 + players.add(user); + } else if ("01".equals(user.getUserType())) { // 如果是主播,继续递归 + getAllPlayersByAnchorIdRecursive(user.getUserId(), players); + } + } + } + + private Calendar getStartOfLastMonth() { + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.MONTH, -1); + calendar.set(Calendar.DAY_OF_MONTH, 1); + resetTime(calendar); + return calendar; + } + + private Calendar getEndOfLastMonth() { + Calendar calendar = getStartOfLastMonth(); + calendar.add(Calendar.MONTH, 1); + calendar.add(Calendar.DAY_OF_MONTH, -1); + setTimeToEndOfDay(calendar); + return calendar; + } + + private Calendar getStartOfThisMonth() { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.DAY_OF_MONTH, 1); + resetTime(calendar); + return calendar; + } + + private Calendar getStartOfLastWeek() { + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.WEEK_OF_YEAR, -1); + calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek()); + resetTime(calendar); + return calendar; + } + + private Calendar getEndOfLastWeek() { + Calendar calendar = getStartOfLastWeek(); + calendar.add(Calendar.DAY_OF_WEEK, 6); + setTimeToEndOfDay(calendar); + return calendar; + } + + private Calendar getStartOfThisWeek() { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek()); + resetTime(calendar); + return calendar; + } + + private Calendar getStartOfYesterday() { + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DAY_OF_MONTH, -1); + resetTime(calendar); + return calendar; + } + + private Calendar getEndOfYesterday() { + Calendar calendar = getStartOfYesterday(); + setTimeToEndOfDay(calendar); + return calendar; + } + + private Calendar getStartOfToday() { + Calendar calendar = Calendar.getInstance(); + resetTime(calendar); + return calendar; + } + + private void resetTime(Calendar calendar) { + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + } + + private void setTimeToEndOfDay(Calendar calendar) { + calendar.set(Calendar.HOUR_OF_DAY, 23); + calendar.set(Calendar.MINUTE, 59); + calendar.set(Calendar.SECOND, 59); + calendar.set(Calendar.MILLISECOND, 999); + } +} diff --git a/skins-promo/src/main/java/com/ruoyi/promo/task/PromoTask.java b/skins-promo/src/main/java/com/ruoyi/promo/task/PromoTask.java new file mode 100644 index 0000000..a072892 --- /dev/null +++ b/skins-promo/src/main/java/com/ruoyi/promo/task/PromoTask.java @@ -0,0 +1,57 @@ +package com.ruoyi.promo.task; + +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.promo.domain.dto.TotalUserExpenditureDTO; +import com.ruoyi.promo.mapper.PromoTurnoverMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.*; + +@Slf4j +@Component("PromoTask") +public class PromoTask { + + @Autowired + private PromoTurnoverMapper promoTurnoverMapper; + + private Map commissionMap = new HashMap<>(); + + /** + * 计算上月佣金 + */ + public void calculateLastMonthCommission() { + // 汇总所有用户上个月的充值总额 + List lastMonthTotalUserExpenditure = promoTurnoverMapper.getLastMonthTotalUserExpenditure(); + for (TotalUserExpenditureDTO totalUserExpenditureDTO : lastMonthTotalUserExpenditure) { + // 向上递归给各级代理分配佣金 + if (!Objects.isNull(totalUserExpenditureDTO.getParentId())) { + distributeCommission(totalUserExpenditureDTO.getParentId(), + totalUserExpenditureDTO.getCommissionRate(), + totalUserExpenditureDTO.getAmount()); + } + } + } + + /** + * 分配佣金 + */ + private void distributeCommission(Integer parentId, + BigDecimal commissionRate, + BigDecimal amount) { + TtUser ttUser = promoTurnoverMapper.findById(parentId); + BigDecimal parentCommissionRate = ttUser.getCommissionRate(); + BigDecimal commission = parentCommissionRate.multiply(amount).subtract(commissionRate.multiply(amount)); + System.err.println("代理:" + parentId + ",佣金比例:" + parentCommissionRate + ",获得佣金:" + commission); + // 计算并存储该级代理的佣金 + // 将相同ID的代理佣金相加 + // 判断是否还有上级代理,有继续递归 + if (!Objects.isNull(ttUser.getParentId())) { + distributeCommission(ttUser.getParentId(), + ttUser.getCommissionRate(), + amount); + } + } +} diff --git a/skins-promo/src/main/resources/mapper/promo/PromoTurnoverMapper.xml b/skins-promo/src/main/resources/mapper/promo/PromoTurnoverMapper.xml new file mode 100644 index 0000000..c2b54a9 --- /dev/null +++ b/skins-promo/src/main/resources/mapper/promo/PromoTurnoverMapper.xml @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + UPDATE tt_user SET commission_rate = #{commissionRate} WHERE user_id = #{userId} + + + + + + \ No newline at end of file diff --git a/skins-service/.gitignore b/skins-service/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/skins-service/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/skins-service/game-sugar-service/README.md b/skins-service/game-sugar-service/README.md new file mode 100644 index 0000000..602d221 --- /dev/null +++ b/skins-service/game-sugar-service/README.md @@ -0,0 +1,16 @@ +api: Spring项目入口模块,如果是web项目则包含controller定义 + +common: 项目通用类模块 + +contract: 协议、数据模型定义,比如dubbo、grpc的interface等,发布到maven仓库,给其他业务方使用。 + +repository:与数据库交互接口,包含dao层定义和mapper定义 + +service:服务接口定义和实现 + +domain: 领域模型模块,包含实体类、值对象等 + +enums + +model + diff --git a/skins-service/game-sugar-service/doc/data.sql b/skins-service/game-sugar-service/doc/data.sql new file mode 100644 index 0000000..4d94406 --- /dev/null +++ b/skins-service/game-sugar-service/doc/data.sql @@ -0,0 +1 @@ +todo \ No newline at end of file diff --git a/skins-service/game-sugar-service/doc/init.sql b/skins-service/game-sugar-service/doc/init.sql new file mode 100644 index 0000000..72c283e --- /dev/null +++ b/skins-service/game-sugar-service/doc/init.sql @@ -0,0 +1,134 @@ +CREATE TABLE `game_sugar_spin` ( + `gid` bigint NOT NULL AUTO_INCREMENT COMMENT '一次spin的唯一标识符。连续的free_spin算一次spin', + `score` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '总得分', + `count` int NOT NULL DEFAULT '0' COMMENT 'step总数', + `feature` varchar(20) NOT NULL DEFAULT 'normal' COMMENT 'normal:普通spin free:购买的免费旋转 super_free:购买的超级免费旋转', + `extra_free` int NOT NULL DEFAULT '0' COMMENT '本次spin中额外触发的免费spin数量', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`gid`), + KEY `idx_feature` (`feature`), + KEY `idx_score` (`score`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='糖果游戏spin表'; + + +CREATE TABLE `game_sugar_step_info` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '唯一自增id', + `gid` bigint NOT NULL COMMENT '所属的spin id', + `free_spin_id` int NOT NULL COMMENT '所属的free spin id', + `aes` int NOT NULL COMMENT 'step在本次spin中的step序号', + `multipler` varchar(512) DEFAULT '' COMMENT '开始前的倍数状态,偶数个,一对的前面是一维坐标,后面是倍数', + `grid` varchar(128) DEFAULT '' COMMENT '开始前的网格状态', + `symbol_links` varchar(1024) DEFAULT '' COMMENT '符号消除的集群信息,JSON格式', + `score` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '本次step的总倍数。基础倍数 * 级联翻倍', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_gid_aes` (`gid`, `aes`) COMMENT 'gid+aes: 唯一标识一次step', + KEY `idx_gid` (`gid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='糖果游戏旋转步骤详情表'; + +CREATE TABLE `game_sugar_spin_free` ( + `gid` bigint NOT NULL AUTO_INCREMENT COMMENT '一次spin的唯一标识符。连续的free_spin算一次spin', + `score` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '总得分', + `count` int NOT NULL DEFAULT '0' COMMENT 'step总数', + `feature` varchar(20) NOT NULL DEFAULT 'normal' COMMENT 'normal:普通spin free:购买的免费旋转 super_free:购买的超级免费旋转', + `extra_free` int NOT NULL DEFAULT '0' COMMENT '本次spin中额外触发的免费spin数量', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`gid`), + KEY `idx_feature` (`feature`), + KEY `idx_score` (`score`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='糖果游戏spin表'; + + +CREATE TABLE `game_sugar_step_info_free` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '唯一自增id', + `gid` bigint NOT NULL COMMENT '所属的spin id', + `free_spin_id` int NOT NULL COMMENT '所属的free spin id', + `aes` int NOT NULL COMMENT 'step在本次spin中的step序号', + `multipler` varchar(512) DEFAULT '' COMMENT '开始前的倍数状态,偶数个,一对的前面是一维坐标,后面是倍数', + `grid` varchar(128) DEFAULT '' COMMENT '开始前的网格状态', + `symbol_links` varchar(1024) DEFAULT '' COMMENT '符号消除的集群信息,JSON格式', + `score` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '本次step的总倍数。基础倍数 * 级联翻倍', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_gid_aes` (`gid`, `aes`) COMMENT 'gid+aes: 唯一标识一次step', + KEY `idx_gid` (`gid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='糖果游戏旋转步骤详情表'; + +CREATE TABLE `game_sugar_spin_super` ( + `gid` bigint NOT NULL AUTO_INCREMENT COMMENT '一次spin的唯一标识符。连续的free_spin算一次spin', + `score` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '总得分', + `count` int NOT NULL DEFAULT '0' COMMENT 'step总数', + `feature` varchar(20) NOT NULL DEFAULT 'normal' COMMENT 'normal:普通spin free:购买的免费旋转 super_free:购买的超级免费旋转', + `extra_free` int NOT NULL DEFAULT '0' COMMENT '本次spin中额外触发的免费spin数量', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`gid`), + KEY `idx_feature` (`feature`), + KEY `idx_score` (`score`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='糖果游戏spin表'; + + +CREATE TABLE `game_sugar_step_info_super` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '唯一自增id', + `gid` bigint NOT NULL COMMENT '所属的spin id', + `free_spin_id` int NOT NULL COMMENT '所属的free spin id', + `aes` int NOT NULL COMMENT 'step在本次spin中的step序号', + `multipler` varchar(512) DEFAULT '' COMMENT '开始前的倍数状态,偶数个,一对的前面是一维坐标,后面是倍数', + `grid` varchar(128) DEFAULT '' COMMENT '开始前的网格状态', + `symbol_links` varchar(1024) DEFAULT '' COMMENT '符号消除的集群信息,JSON格式', + `score` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '本次step的总倍数。基础倍数 * 级联翻倍', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_gid_aes` (`gid`, `aes`) COMMENT 'gid+aes: 唯一标识一次step', + KEY `idx_gid` (`gid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='糖果游戏旋转步骤详情表'; + +CREATE TABLE `game_sugar_user` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', + `user_id` bigint NOT NULL COMMENT 'user表的id', + `total_bet` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户总下注额', + `total_win` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户总赢得金额', + `count` int NOT NULL DEFAULT '0' COMMENT '总spin次数', + `free_count` int NOT NULL DEFAULT '0' COMMENT '总购买free spin次数', + `super_free_count` int NOT NULL DEFAULT '0' COMMENT '总购买super_free spin次数', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_user_id` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='糖果游戏用户表'; + +CREATE TABLE `game_sugar_win` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', + `date` datetime NOT NULL COMMENT '日期', + `init_bet` decimal(10,2) NOT NULL COMMENT '系统初始下注额', + `total_bet` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户总下注额', + `total_win` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户总赢得金额', + `count` int NOT NULL DEFAULT '0' COMMENT '总spin次数', + `free_count` int NOT NULL DEFAULT '0' COMMENT '总购买free spin次数', + `super_free_count` int NOT NULL DEFAULT '0' COMMENT '总购买super_free spin次数', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_date` (`date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='糖果游戏每日统计表'; + +CREATE TABLE `game_sugar_record` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', + `user_id` bigint NOT NULL COMMENT 'user表的id', + `bet` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户下注额', + `win` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户赢得金额', + `multiplier` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '倍率', + `kind` varchar(12) NOT NULL DEFAULT '' COMMENT '游戏模式', + `status` int NOT NULL DEFAULT 0 COMMENT '状态.0:进行中,1:完成', + `extra_free` int NOT NULL DEFAULT '0' COMMENT '本次spin中额外触发的免费spin数量', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_create_time` (`create_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='糖果游戏用户记录表'; diff --git a/skins-service/game-sugar-service/pom.xml b/skins-service/game-sugar-service/pom.xml new file mode 100644 index 0000000..6623e64 --- /dev/null +++ b/skins-service/game-sugar-service/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + com.ruoyi + skins-service + 4.8.2 + + + game-sugar-service + + + 8 + 8 + UTF-8 + + + + + + com.ruoyi + ruoyi-framework + + + + com.ruoyi + service-admin + 4.8.2 + + + + com.ruoyi + service-user + 4.8.2 + + + + \ No newline at end of file diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/admin/request/AdminSugarUserListRequest.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/admin/request/AdminSugarUserListRequest.java new file mode 100644 index 0000000..cc64749 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/admin/request/AdminSugarUserListRequest.java @@ -0,0 +1,10 @@ +package com.ruoyi.game.sugar.contract.admin.request; + +import lombok.Data; + +@Data +public class AdminSugarUserListRequest { + private Integer pageNum; + private Integer pageSize; + private Long userId; +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/admin/request/QueryUserRecordListRequest.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/admin/request/QueryUserRecordListRequest.java new file mode 100644 index 0000000..d831511 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/admin/request/QueryUserRecordListRequest.java @@ -0,0 +1,12 @@ +package com.ruoyi.game.sugar.contract.admin.request; + +import lombok.Data; + +@Data +public class QueryUserRecordListRequest { + private Integer uid; + private Integer pageNum; + private Integer pageSize; + private String startTime; + private String endTime; +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/admin/request/UpdateMockResultListRequest.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/admin/request/UpdateMockResultListRequest.java new file mode 100644 index 0000000..bbd8aa1 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/admin/request/UpdateMockResultListRequest.java @@ -0,0 +1,13 @@ +package com.ruoyi.game.sugar.contract.admin.request; + +import lombok.Data; + +import java.util.List; + +@Data +public class UpdateMockResultListRequest { + private Integer uid; + private List normalScores; + private List freeScores; + private List superScores; +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/admin/response/QueryGameConfig.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/admin/response/QueryGameConfig.java new file mode 100644 index 0000000..21d41c5 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/admin/response/QueryGameConfig.java @@ -0,0 +1,13 @@ +package com.ruoyi.game.sugar.contract.admin.response; + +import com.ruoyi.game.sugar.model.GameSugarConfig; +import lombok.Data; + +@Data +public class QueryGameConfig { + private GameSugarConfig config; + + // 全局奖池数据 + private double totalWin; + private double totalBet; +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/request/QueryRankRequest.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/request/QueryRankRequest.java new file mode 100644 index 0000000..0a0e894 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/request/QueryRankRequest.java @@ -0,0 +1,12 @@ +package com.ruoyi.game.sugar.contract.api.request; + +import lombok.Data; + +@Data +public class QueryRankRequest { + private Integer pageNum; + private Integer pageSize; + // "real": 实时记录 "multipler": 倍数全服记录 "win": 赢奖全服记录排行 + private String rankType; + private String kind; +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/request/QueryRecordRequest.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/request/QueryRecordRequest.java new file mode 100644 index 0000000..241f51b --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/request/QueryRecordRequest.java @@ -0,0 +1,11 @@ +package com.ruoyi.game.sugar.contract.api.request; + +import lombok.Data; + +@Data +public class QueryRecordRequest { + private Integer pageNum; + private Integer pageSize; + private String kind; + private String rankType; +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/ApiSugarBuySpinResponse.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/ApiSugarBuySpinResponse.java new file mode 100644 index 0000000..bd394e5 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/ApiSugarBuySpinResponse.java @@ -0,0 +1,11 @@ +package com.ruoyi.game.sugar.contract.api.response; + +import lombok.Data; + +@Data +public class ApiSugarBuySpinResponse { + private String credits; + private String balance; + private String cost; + private Boolean isSuperFree; +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/ApiSugarHasFreeSpinResponse.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/ApiSugarHasFreeSpinResponse.java new file mode 100644 index 0000000..ee4095b --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/ApiSugarHasFreeSpinResponse.java @@ -0,0 +1,8 @@ +package com.ruoyi.game.sugar.contract.api.response; + +import lombok.Data; + +@Data +public class ApiSugarHasFreeSpinResponse { + private Boolean hasFreeSpin; +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/ApiSugarSpinResultResponse.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/ApiSugarSpinResultResponse.java new file mode 100644 index 0000000..7976b91 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/ApiSugarSpinResultResponse.java @@ -0,0 +1,18 @@ +package com.ruoyi.game.sugar.contract.api.response; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class ApiSugarSpinResultResponse { + private String balance; + private String credits; + private Integer index; + private Integer remainingFreeSpins; + private Integer extraFreeSpinCount; + // normal:普通旋转模式 standard:购买的免费旋转状态 super:购买的超级免费旋转状态 + private String kind; + private List> data; +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/QueryRankResponse.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/QueryRankResponse.java new file mode 100644 index 0000000..88f5536 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/QueryRankResponse.java @@ -0,0 +1,23 @@ +package com.ruoyi.game.sugar.contract.api.response; + +import lombok.Data; + +import java.util.List; + +@Data +public class QueryRankResponse { + private Integer total; + private List rows; + + @Data + public static class Record { + private String avatar; + private String nickName; + private String cost; + private String kind; + private String status; + private String reward; + private String multiplier; + private String createTime; + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/QueryRecordResponse.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/QueryRecordResponse.java new file mode 100644 index 0000000..508aa0f --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/contract/api/response/QueryRecordResponse.java @@ -0,0 +1,22 @@ +package com.ruoyi.game.sugar.contract.api.response; + +import lombok.Data; + +import java.util.List; + +@Data +public class QueryRecordResponse { + private Integer total; + private List rows; + + @Data + public static class Record { + private String avatar; + private String nickName; + private String cost; + private String kind; + private String status; + private String reward; + private String createTime; + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/controller/AdminSugarController.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/controller/AdminSugarController.java new file mode 100644 index 0000000..b4a555e --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/controller/AdminSugarController.java @@ -0,0 +1,129 @@ +package com.ruoyi.game.sugar.controller; + +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateTimeUtils; +import com.ruoyi.game.sugar.contract.admin.request.AdminSugarUserListRequest; +import com.ruoyi.game.sugar.contract.admin.request.QueryUserRecordListRequest; +import com.ruoyi.game.sugar.contract.admin.request.UpdateMockResultListRequest; +import com.ruoyi.game.sugar.contract.admin.response.QueryGameConfig; +import com.ruoyi.game.sugar.domain.GameSugarRecord; +import com.ruoyi.game.sugar.mapper.ApiGameSugarRecordMapper; +import com.ruoyi.game.sugar.model.GameSugarConfig; +import com.ruoyi.game.sugar.repository.SugarRewardPoolCacheRepository; +import com.ruoyi.game.sugar.service.AdminSugarService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.math3.util.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@Api(tags = "极速永恒/Sugar Rush 1000") +@Slf4j +@RestController +@RequestMapping("/admin/sugar") +public class AdminSugarController extends BaseController { + + @Autowired + private AdminSugarService adminSugarService; + @Autowired + private ApiGameSugarRecordMapper gameSugarRecordMapper; + @Autowired + private SugarRewardPoolCacheRepository sugarRewardPoolCacheRepository; + + // 方法参数是一个具体的类型,则不用加@RequestParam + // 方法参数是一个Map,则需要加@RequestParam + @ApiOperation("糖果用户信息列表") + @GetMapping("/user/list") + public PageDataInfo queryUserList(AdminSugarUserListRequest params) { + // pageNum 从1开始 + Pair>> list = adminSugarService.queryUserList(params); + PageDataInfo> pageDataInfo = getPageData(list.getValue()); + pageDataInfo.setTotal(list.getKey()); + return pageDataInfo; + } + + @ApiOperation("获取糖果游戏配置") + @GetMapping("/gameconfig") + public R getGameConfig() { + GameSugarConfig config = adminSugarService.getGameConfig(); + QueryGameConfig result = new QueryGameConfig(); + result.setConfig(config); + + var cache = sugarRewardPoolCacheRepository.getCache(); + result.setTotalWin(cache.getTotalWin().doubleValue()); + result.setTotalBet(cache.getTotalBet().doubleValue()); + return R.ok(result); + } + + @ApiOperation("更新糖果游戏配置") + @PostMapping("/gameconfig") + public AjaxResult updateGameConfig(@RequestBody GameSugarConfig params) { + String msg = adminSugarService.updateGameConfig(params); + if (msg != null && !msg.isEmpty()) + return AjaxResult.error(msg); + return AjaxResult.success(); + } + + @ApiOperation("糖果每日奖池信息列表") + @GetMapping("/dailywin/list") + public PageDataInfo queryDailyWinList(@RequestParam Map params) { + Pair>> list = adminSugarService.queryDailyWinList(params); + PageDataInfo> pageDataInfo = getPageData(list.getValue()); + pageDataInfo.setTotal(list.getKey()); + return pageDataInfo; + } + + @ApiOperation("糖果主播临时结果列表") + @GetMapping("/mockresult/list") + public R>> getMockResultList(@RequestParam("uid") Integer uid) { + return R.ok(adminSugarService.getMockResultList(uid)); + } + + @ApiOperation("糖果主播临时结果列表") + @PostMapping("/mockresult/list") + public R>> updateMockResultList(@RequestBody UpdateMockResultListRequest request) { + var r = adminSugarService.updateMockResultList(request); + return R.ok(r); + } + + @ApiOperation("游戏记录") + @GetMapping("/user/record/list") + public PageDataInfo queryUserRecordList(QueryUserRecordListRequest params) { + if (params.getPageNum() == null) { + params.setPageNum(1); + } + if (params.getPageSize() == null) { + params.setPageSize(10); + } + + Page page = new Page<>(params.getPageNum(), params.getPageSize()); + var result = new LambdaQueryChainWrapper<>(gameSugarRecordMapper) + .eq(params.getUid() != null, GameSugarRecord::getUserId, params.getUid()) + .ge(params.getStartTime() != null, GameSugarRecord::getCreateTime, params.getStartTime()) + .le(params.getEndTime() != null, GameSugarRecord::getCreateTime, params.getEndTime()) + .orderByDesc(GameSugarRecord::getUpdateTime) + .page(page); + + if (CollectionUtils.isNotEmpty(result.getRecords())) { + result.getRecords().forEach(item -> { + item.setCreateTime(DateTimeUtils.utcToZone8(item.getCreateTime())); + item.setUpdateTime(DateTimeUtils.utcToZone8(item.getUpdateTime())); + }); + } + + PageDataInfo pageDataInfo = new PageDataInfo<>(); + pageDataInfo.setTotal(result.getTotal()); + pageDataInfo.setRows(result.getRecords()); + return pageDataInfo; + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/controller/ApiSugarController.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/controller/ApiSugarController.java new file mode 100644 index 0000000..cff71f0 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/controller/ApiSugarController.java @@ -0,0 +1,288 @@ +package com.ruoyi.game.sugar.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.DateTimeUtils; +import com.ruoyi.common.utils.MoneyUtil; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.game.sugar.contract.api.request.QueryRankRequest; +import com.ruoyi.game.sugar.contract.api.request.QueryRecordRequest; +import com.ruoyi.game.sugar.contract.api.response.*; +import com.ruoyi.game.sugar.domain.GameSugarRecord; +import com.ruoyi.game.sugar.enums.GameSugarRecordStatus; +import com.ruoyi.game.sugar.mapper.ApiGameSugarRecordMapper; +import com.ruoyi.game.sugar.repository.SugarRewardPoolCacheRepository; +import com.ruoyi.game.sugar.service.ApiSugarService; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.user.service.ApiUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Api(tags = "极速永恒/Sugar Rush 1000") +@Slf4j +@RestController +@RequestMapping("/api/sugar") +public class ApiSugarController extends BaseController { + + @Autowired + private ApiSugarService apiSugarService; + @Autowired + private ApiUserService userService; + @Autowired + private ISysConfigService sysConfigService; + + @Autowired + private ApiGameSugarRecordMapper recordMapper; + + @Autowired + private TtUserMapper ttUserMapper; + + @Autowired + private SugarRewardPoolCacheRepository sugarRewardPoolCacheRepository; + + public R checkLogin() { + Long userId; + try { + userId = getUserId(); + if (ObjectUtil.isEmpty(userId)) return R.fail(401, "登录过期,请重新登录。"); + return R.ok(userId); + } catch (Exception e) { + return R.fail("登录过期,请重新登录。"); + } + } + + @ApiOperation("进行一次旋转") + @UpdateUserCache + @GetMapping("/dospin") + public R dospin(@RequestParam("bet") Long bet) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) { + return checkLogin; + } + + Long userId = getUserId(); + var r = userService.checkRecharge(userId.intValue()); + if (R.isError(r)) { + return R.fail(r.getMsg()); + } + return apiSugarService.doSpin(userId, BigDecimal.valueOf(bet)); + } + + @ApiOperation("购买免费旋转") + @UpdateUserCache + @GetMapping("/buy_free_spins") + public R buyFreeSpins(@RequestParam("kind") String kind, @RequestParam("bet") Long bet) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) { + return checkLogin; + } + + Long userId = getUserId(); + var r = userService.checkRecharge(userId.intValue()); + if (R.isError(r)) { + return R.fail(r.getMsg()); + } + return apiSugarService.buyFreeSpins(userId, kind, BigDecimal.valueOf(bet)); + } + + @ApiOperation("检查是否有免费旋转") + @GetMapping("/has_free_spin") + public R hasFreeSpin() { + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) { + return checkLogin; + } + + Long userId = getUserId(); + return apiSugarService.hasFreeSpin(userId); + } + + @ApiOperation("查询记录") + @GetMapping("/records") + public R queryRecords(QueryRecordRequest request) { + if (request.getPageNum() == null || request.getPageNum() <= 0) { + request.setPageNum(1); + } + if (request.getPageSize() == null || request.getPageSize() <= 0) { + request.setPageSize(10); + } + + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) { + return checkLogin; + } + + Long userId = getUserId(); + + + Page page = new Page<>(request.getPageNum(), request.getPageSize()); + var query = new LambdaQueryChainWrapper<>(recordMapper).eq(GameSugarRecord::getUserId, userId); + if (StringUtils.isNotBlank(request.getKind())) { + String t = switch (request.getKind()) { + case "normal" -> ""; + case "free" -> "standard"; + case "super" -> "super"; + default -> null; + }; + if (t != null) { + query.eq(GameSugarRecord::getKind, t); + } + } + + var data = query.orderByDesc(GameSugarRecord::getWin).page(page); + + // 个人记录只查当前用户,直接单条查询用户信息 + TtUser currentUser = ttUserMapper.selectTtUserById((long) userId.intValue()); + + var rows = data.getRecords().stream() + .map(v -> { + QueryRecordResponse.Record item = new QueryRecordResponse.Record(); + if (currentUser != null) { + item.setAvatar(currentUser.getAvatar()); + item.setNickName(currentUser.getNickName()); + } + item.setCost(MoneyUtil.toStr(v.getBet())); + item.setReward(MoneyUtil.toStr(v.getWin())); + item.setKind(switch (v.getKind()) { + case "standard" -> "free"; + case "super" -> "super"; + default -> "normal"; + }); + item.setCreateTime(DateTimeUtils.dateTime(v.getCreateTime(), "MM-dd HH:mm:ss")); + var status = GameSugarRecordStatus.parseCode(v.getStatus()); + if (status == null) { + item.setStatus("未知"); + } else { + item.setStatus(status.getDesc()); + } + return item; + }).collect(Collectors.toList()); + + QueryRecordResponse resp = new QueryRecordResponse(); + resp.setTotal((int) data.getTotal()); + resp.setRows(rows); + return R.ok(resp); + } + + @ApiOperation("查询全服排行记录") + @GetMapping("/rank_records") + public R queryRankRecords(QueryRankRequest request) { + if (request.getPageNum() == null || request.getPageNum() <= 0) { + request.setPageNum(1); + } + if (request.getPageSize() == null || request.getPageSize() <= 0) { + request.setPageSize(10); + } + + if (request.getPageNum() * request.getPageSize() > 100) { + QueryRankResponse resp = new QueryRankResponse(); + resp.setRows(Collections.emptyList()); + resp.setTotal(0); + return R.ok(resp); + } + + + Page page = new Page<>(request.getPageNum(), request.getPageSize()); + var query = new LambdaQueryChainWrapper<>(recordMapper); + if (StringUtils.isNotBlank(request.getKind())) { + String t = switch (request.getKind()) { + case "normal" -> ""; + case "free" -> "standard"; + case "super" -> "super"; + default -> null; + }; + if (t != null) { + query.eq(GameSugarRecord::getKind, t); + } + } + + if (Objects.equals(request.getRankType(), "multipler")) { + query.orderByDesc(GameSugarRecord::getMultiplier); + } else if (Objects.equals(request.getRankType(), "real")) { + query.orderByDesc(GameSugarRecord::getCreateTime); + } else if (Objects.equals(request.getRankType(), "win")) { + query.orderByDesc(GameSugarRecord::getWin); + } + + var data = query.page(page); + if (CollectionUtils.isEmpty(data.getRecords())) { + QueryRankResponse resp = new QueryRankResponse(); + resp.setTotal((int) data.getTotal()); + resp.setRows(Collections.emptyList()); + return R.ok(resp); + } + + //var userIds = data.getRecords().stream().map(GameSugarRecord::getUserId).collect(Collectors.toList()); + var userIds = data.getRecords().stream().map(v -> v.getUserId().intValue()).distinct().collect(Collectors.toList()); + var userMap = ttUserMapper.selectByIds(userIds).stream() + .collect(Collectors.toMap(v -> v.getUserId().longValue(), Function.identity())); + + var rows = data.getRecords().stream() + .filter(v -> { + // 过滤机器人(03),不允许上榜 + var user = userMap.get(v.getUserId()); + return user != null && !"03".equals(user.getUserType()); + }) + .map(v -> { + QueryRankResponse.Record item = new QueryRankResponse.Record(); + if (userMap.containsKey(v.getUserId())) { + item.setAvatar(userMap.get(v.getUserId()).getAvatar()); + item.setNickName(userMap.get(v.getUserId()).getNickName()); + } + item.setCost(MoneyUtil.toStr(v.getBet())); + item.setCost(MoneyUtil.toStr(v.getBet())); + item.setReward(MoneyUtil.toStr(v.getWin())); + item.setKind(switch (v.getKind()) { + case "standard" -> "free"; + case "super" -> "super"; + default -> "normal"; + }); + item.setMultiplier(MoneyUtil.toStr(v.getMultiplier())); + item.setCreateTime(DateTimeUtils.dateTime(v.getCreateTime(), "MM-dd HH:mm:ss")); + var status = GameSugarRecordStatus.parseCode(v.getStatus()); + if (status == null) { + item.setStatus("未知"); + } else { + item.setStatus(status.getDesc()); + } + return item; + }).collect(Collectors.toList()); + + QueryRankResponse resp = new QueryRankResponse(); + resp.setTotal((int) data.getTotal()); + resp.setRows(rows); + return R.ok(resp); + } + @ApiOperation("清空极速永恒奖池缓存(总下注/总返奖)归零") + @PostMapping("/rewardpool/reset") + public R resetRewardPool() { + sugarRewardPoolCacheRepository.resetCache(); + return R.ok(null, "奖池缓存已清空"); + } + +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/domain/GameSugarRecord.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/domain/GameSugarRecord.java new file mode 100644 index 0000000..f1be3e1 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/domain/GameSugarRecord.java @@ -0,0 +1,39 @@ +package com.ruoyi.game.sugar.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 糖果游戏用户记录表 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "game_sugar_record") +public class GameSugarRecord { + @TableId(type = IdType.AUTO) + private Long id; + private Long userId; + private BigDecimal bet; + private BigDecimal win; + private BigDecimal multiplier; + private String kind; + private Integer extraFree; + /** + * @see com.ruoyi.game.sugar.enums.GameSugarRecordStatus + */ + private Integer status; + private LocalDateTime createTime; + private LocalDateTime updateTime; +} \ No newline at end of file diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/enums/GameSugarRecordStatus.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/enums/GameSugarRecordStatus.java new file mode 100644 index 0000000..c5f36d9 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/enums/GameSugarRecordStatus.java @@ -0,0 +1,26 @@ +package com.ruoyi.game.sugar.enums; + +import lombok.Getter; + +@Getter +public enum GameSugarRecordStatus { + RUNING(0, "进行中"), + COMPLETE(1, "完成"); + + private final Integer code; + private final String desc; + + GameSugarRecordStatus(Integer code, String desc) { + this.code = code; + this.desc = desc; + } + + public static GameSugarRecordStatus parseCode(Integer code) { + for (GameSugarRecordStatus status : values()) { + if (status.code.equals(code)) { + return status; + } + } + return null; + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/AdminGameSugarSpinMapper.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/AdminGameSugarSpinMapper.java new file mode 100644 index 0000000..4f95212 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/AdminGameSugarSpinMapper.java @@ -0,0 +1,12 @@ +package com.ruoyi.game.sugar.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +@Mapper +public interface AdminGameSugarSpinMapper { + + @Select("SELECT max(score) FROM game_sugar_spin${type}") + Integer select(@Param("type") String type); +} \ No newline at end of file diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/AdminGameSugarUserMapper.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/AdminGameSugarUserMapper.java new file mode 100644 index 0000000..1a69a94 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/AdminGameSugarUserMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.game.sugar.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.GameSugarUser; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface AdminGameSugarUserMapper extends BaseMapper { +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/AdminGameSugarWinMapper.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/AdminGameSugarWinMapper.java new file mode 100644 index 0000000..76c62f1 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/AdminGameSugarWinMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.game.sugar.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.GameSugarWin; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface AdminGameSugarWinMapper extends BaseMapper { +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/ApiGameSugarRecordMapper.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/ApiGameSugarRecordMapper.java new file mode 100644 index 0000000..479b557 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/ApiGameSugarRecordMapper.java @@ -0,0 +1,10 @@ +package com.ruoyi.game.sugar.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.game.sugar.domain.GameSugarRecord; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ApiGameSugarRecordMapper extends BaseMapper { + +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/ApiGameSugarSpinMapper.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/ApiGameSugarSpinMapper.java new file mode 100644 index 0000000..08f4e8c --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/ApiGameSugarSpinMapper.java @@ -0,0 +1,29 @@ +package com.ruoyi.game.sugar.mapper; + +import com.ruoyi.domain.entity.GameSugarSpin; +import com.ruoyi.domain.entity.GameSugarStepInfo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +@Mapper +public interface ApiGameSugarSpinMapper { + + @Select("SELECT * FROM game_sugar_spin${type} where ${where} ORDER BY score LIMIT 1") + GameSugarSpin selectMinOne(@Param("type") String type, @Param("where")String whereSql); + + + @Select("SELECT * FROM game_sugar_spin${type} where ${where} ORDER BY score LIMIT 5") + List selectMinFive(@Param("type") String type, @Param("where")String whereSql); + + @Select("SELECT * FROM game_sugar_spin${type} where ${where} ORDER BY RAND() LIMIT 1") + GameSugarSpin selectRandomOne(@Param("type") String type, @Param("where")String whereSql); + + @Select("SELECT * FROM game_sugar_spin${type} WHERE gid = #{gid} LIMIT 1") + GameSugarSpin selectByGid(@Param("gid") Long gid, @Param("type") String type); + + @Select("SELECT * FROM game_sugar_step_info${type} WHERE gid = #{gid}") + List selectStepsByGid(@Param("gid") Long gid, @Param("type") String type); +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/ApiGameSugarUserMapper.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/ApiGameSugarUserMapper.java new file mode 100644 index 0000000..c76a4c8 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/ApiGameSugarUserMapper.java @@ -0,0 +1,31 @@ +package com.ruoyi.game.sugar.mapper; + +import com.ruoyi.domain.entity.GameSugarUser; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +@Mapper +public interface ApiGameSugarUserMapper { + + + @Select("SELECT * FROM game_sugar_user where user_id = #{uid}") + GameSugarUser select(@Param("uid") Integer uid); + + /** + * 插入用户数据,如果 user_id 已存在则更新 + */ + @Insert("INSERT INTO game_sugar_user " + + "(user_id, total_bet, total_win, count, free_count, super_free_count) " + + "VALUES " + + "(#{user.userId}, #{user.totalBet}, #{user.totalWin}, #{user.count}, #{user.freeCount}, #{user.superFreeCount}) " + + "ON DUPLICATE KEY UPDATE " + + "total_bet = VALUES(total_bet), " + + "total_win = VALUES(total_win), " + + "count = VALUES(count), " + + "free_count = VALUES(free_count), " + + "super_free_count = VALUES(super_free_count), " + + "update_time = NOW()") + long insertOrUpdate(@Param("user") GameSugarUser user); +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/ApiGameSugarWinMapper.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/ApiGameSugarWinMapper.java new file mode 100644 index 0000000..a8dc2ee --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/mapper/ApiGameSugarWinMapper.java @@ -0,0 +1,47 @@ +package com.ruoyi.game.sugar.mapper; + +import com.ruoyi.domain.entity.GameSugarWin; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Mapper +public interface ApiGameSugarWinMapper { + + @Select("SELECT * FROM game_sugar_win where date = #{date} LIMIT 1") + GameSugarWin select(@Param("date") LocalDateTime dateTime); + + + @Insert("INSERT INTO game_sugar_win " + + "(date, init_bet, total_bet, total_win, count, free_count, super_free_count) " + + "VALUES " + + "(#{record.date}, #{record.initBet}, #{record.totalBet}, #{record.totalWin}, #{record.count}, #{record.freeCount}, #{record.superFreeCount})") + long insert(@Param("record") GameSugarWin record); + + /** + * 通用更新方法:根据传入的字段动态累加 + * 如果某个字段为 null,则不更新该字段 + */ + @Update("UPDATE game_sugar_win " + + "SET count = count + #{count}, " + + "free_count = #{freeCount} + free_count, " + + "super_free_count = #{superFreeCount} + super_free_count " + + "WHERE date = #{date}") + void updateCount(@Param("date") LocalDateTime date, + @Param("count") Integer count, + @Param("freeCount") Integer freeCount, + @Param("superFreeCount") Integer superFreeCount); + + @Update("UPDATE game_sugar_win " + + "SET total_bet = total_bet + #{bet}, " + + "total_win = total_win + #{win} " + + "WHERE date = #{date}") + void updateAmount(@Param("date") LocalDateTime date, + @Param("bet") BigDecimal bet, + @Param("win") BigDecimal win); +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/GameSugarConfig.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/GameSugarConfig.java new file mode 100644 index 0000000..64aacd9 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/GameSugarConfig.java @@ -0,0 +1,11 @@ +package com.ruoyi.game.sugar.model; + +import lombok.Data; + +@Data +public class GameSugarConfig { + private double defaultAwardPool; + private int[] jpPercent; + private double rtp; + private double minProfit; +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarGameCache.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarGameCache.java new file mode 100644 index 0000000..416f519 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarGameCache.java @@ -0,0 +1,33 @@ +package com.ruoyi.game.sugar.model.cache; + +import com.ruoyi.domain.entity.GameSugarStepInfo; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +@Data +public class SugarGameCache implements Serializable { + private BigDecimal bet; + + private BigDecimal accountAmount; + private BigDecimal accountCredits; + + private String kind; + private double globalRtp; // 本局开始时的奖池RTP,用于判断是否触发免费旋转 + private Integer totalFreeSpins; + private Integer freeSpinIndex; + private BigDecimal totalWin; + private GameSpin gameData; + private Long recordId; + + @Data + public static class GameSpin implements Serializable { + private Long gid; + private BigDecimal score; + private List steps; + } + + +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarGameConfigCache.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarGameConfigCache.java new file mode 100644 index 0000000..ad0ea09 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarGameConfigCache.java @@ -0,0 +1,155 @@ +package com.ruoyi.game.sugar.model.cache; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Data +public class SugarGameConfigCache { + private double globalRtp; + private double normalWinBoundary; // 1 + private double freeWinBoundary; // 50 + private double superWinBoundary; // 200 + + // RTP切换阈值(0.6) + private double rtpSwitchThreshold; + + // 奖池 globalRtp >= 0.6 + private List normalAwardItems; + private List freeAwardItems; + private List superAwardItems; + + // globalRtp < 0.6 + private List normalAwardItems2; + private List freeAwardItems2; + private List superAwardItems2; + + @Data + public static class AwardItem { + private double lowRewardRound; + private double highRewardRound; + private double weight; + } + + public static SugarGameConfigCache init() { + SugarGameConfigCache config = new SugarGameConfigCache(); + + // 初始化全局rtp + config.setGlobalRtp(0); + + // 初始化边界值 + config.setNormalWinBoundary(1); + config.setFreeWinBoundary(50); + config.setSuperWinBoundary(200); + + // RTP 切换阈值 + config.setRtpSwitchThreshold(0.6); + + // 配置(globalRtp >= 0.6) + config.setNormalAwardItems(initNormalAwardItems()); + config.setFreeAwardItems(initFreeAwardItems()); + config.setSuperAwardItems(initSuperAwardItems()); + + // 配置(globalRtp < 0.6) + config.setNormalAwardItems2(initNormalAwardItems2()); + config.setFreeAwardItems2(initFreeAwardItems2()); + config.setSuperAwardItems2(initSuperAwardItems2()); + + return config; + } + + // 普通旋转(globalRtp >= 0.6) + private static List initNormalAwardItems() { + List awardItems = new ArrayList<>(); + awardItems.add(makeItem(0, 0, 60)); // 0 倍 60% + awardItems.add(makeItem(0, 0.9, 20)); // 0-0.9 倍 20% + awardItems.add(makeItem(1, 3, 10)); // 1-3 倍 10% + awardItems.add(makeItem(3.1, 5, 4)); // 3.1-5 倍 4% + awardItems.add(makeItem(5.1, 8, 3)); // 5.1-8 倍 3% + awardItems.add(makeItem(8.1, 12, 2)); // 8.1-12 倍 2% + awardItems.add(makeItem(12.1, 16, 1)); // 12.1-16 倍 1% + return awardItems; + } + + // 免费旋转(globalRtp >= 0.6) + private static List initFreeAwardItems() { + List awardItems = new ArrayList<>(); + awardItems.add(makeItem(0, 10, 50)); // 0-10 倍 50% + awardItems.add(makeItem(25, 30, 30)); // 25-30 倍 30% + awardItems.add(makeItem(40, 79, 9)); // 40-79 倍 9% + awardItems.add(makeItem(80, 150, 6)); // 80-150 倍 6% + awardItems.add(makeItem(200, 500, 4)); // 200-500 倍 4% + awardItems.add(makeItem(600, 1500, 1)); // 600-1500 倍 1% + return awardItems; + } + + // 超级旋转(globalRtp >= 0.6) + private static List initSuperAwardItems() { + List awardItems = new ArrayList<>(); + awardItems.add(makeItem(10, 40, 25)); // 10-40 倍 25% + awardItems.add(makeItem(61, 90, 25)); // 61-90 倍 25% + awardItems.add(makeItem(110, 180, 10)); // 110-180 倍 10% + awardItems.add(makeItem(210, 260, 15)); // 210-260 倍 15% + awardItems.add(makeItem(285, 310, 10)); // 285-310 倍 10% + awardItems.add(makeItem(380, 420, 10)); // 380-420 倍 10% + awardItems.add(makeItem(460, 560, 3)); // 460-560 倍 3% + awardItems.add(makeItem(700, 1000, 1)); // 700-1000 倍 1% + awardItems.add(makeItem(1500, 3000, 1)); // 1500-3000 倍 1% + return awardItems; + } + + // 普通旋转(globalRtp < 0.6) + private static List initNormalAwardItems2() { + List awardItems = new ArrayList<>(); + awardItems.add(makeItem(0, 0, 50)); // 0 倍 50% + awardItems.add(makeItem(0, 0.9, 20)); // 0-0.9 倍 20% + awardItems.add(makeItem(1, 3, 15)); // 1-3 倍 15% + awardItems.add(makeItem(4, 5, 6)); // 4-5 倍 6% + awardItems.add(makeItem(6, 8, 4)); // 6-8 倍 4% + awardItems.add(makeItem(10, 12, 3)); // 10-12 倍 3% + awardItems.add(makeItem(13, 15, 1.5));// 13-15 倍 1.5% + awardItems.add(makeItem(30, 40, 0.3));// 30-40 倍 0.3% + awardItems.add(makeItem(50, 60, 0.1));// 50-60 倍 0.1% + awardItems.add(makeItem(60, 80, 0.1));// 60-80 倍 0.1% + return awardItems; + } + + // 免费旋转(globalRtp < 0.6) + private static List initFreeAwardItems2() { + List awardItems = new ArrayList<>(); + awardItems.add(makeItem(0, 10, 42)); // 0-10 倍 42% + awardItems.add(makeItem(31, 39, 30)); // 31-39 倍 30% + awardItems.add(makeItem(50, 81, 11)); // 50-81 倍 11% + awardItems.add(makeItem(101, 151, 8)); // 101-151 倍 8% + awardItems.add(makeItem(201, 460, 6)); // 201-460 倍 6% + awardItems.add(makeItem(500, 600, 2)); // 500-600 倍 2% + awardItems.add(makeItem(600, 1100, 1)); // 600-1100 倍 1% + return awardItems; + } + + // 超级旋转(globalRtp < 0.6) + private static List initSuperAwardItems2() { + List awardItems = new ArrayList<>(); + awardItems.add(makeItem(10, 40, 20)); // 10-40 倍 20% 5 + awardItems.add(makeItem(61, 90, 20)); // 61-90 倍 20% 15.1 + awardItems.add(makeItem(110, 180, 10)); // 110-180 倍 10% 14 + awardItems.add(makeItem(210, 260, 15)); // 210-260 倍 15% 35 + awardItems.add(makeItem(315, 320, 10)); // 315-320 倍 10% 31 + awardItems.add(makeItem(360, 400, 10)); // 360-400 倍 10% 38 + awardItems.add(makeItem(460, 560, 10)); // 460-560 倍 10% 51 + awardItems.add(makeItem(600, 700, 5)); // 600-700 倍 5% 32 + awardItems.add(makeItem(800, 1200, 3)); // 800-1200 倍 3% 30 + awardItems.add(makeItem(1300, 2000, 2)); // 1300-2000 倍 2% + return awardItems; + } + + private static AwardItem makeItem(double low, double high, double weight) { + AwardItem item = new AwardItem(); + item.setLowRewardRound(low); + item.setHighRewardRound(high); + item.setWeight(weight); + return item; + } + +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarGameMockCache.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarGameMockCache.java new file mode 100644 index 0000000..6f3f64e --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarGameMockCache.java @@ -0,0 +1,19 @@ +package com.ruoyi.game.sugar.model.cache; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +@Data +public class SugarGameMockCache implements Serializable { + private List normalScores; + private List freeScores; + private List superScores; + + @Data + public static class MockItem { + private int score; + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarRewardPoolCache.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarRewardPoolCache.java new file mode 100644 index 0000000..499e039 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarRewardPoolCache.java @@ -0,0 +1,11 @@ +package com.ruoyi.game.sugar.model.cache; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class SugarRewardPoolCache { + private BigDecimal totalBet; + private BigDecimal totalWin; +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarUserGameRecordCache.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarUserGameRecordCache.java new file mode 100644 index 0000000..253be20 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/model/cache/SugarUserGameRecordCache.java @@ -0,0 +1,21 @@ +package com.ruoyi.game.sugar.model.cache; + +import com.ruoyi.domain.entity.GameSugarSpin; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +@Data +public class SugarUserGameRecordCache { + + List spinRecords; + + @Data + public static class Record implements Serializable { + private GameSugarSpin spin; + private BigDecimal reward; + private BigDecimal cost; + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/repository/GameConfigCacheRepository.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/repository/GameConfigCacheRepository.java new file mode 100644 index 0000000..deb7a4b --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/repository/GameConfigCacheRepository.java @@ -0,0 +1,24 @@ +package com.ruoyi.game.sugar.repository; + +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.game.sugar.model.cache.SugarGameConfigCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +import static com.ruoyi.admin.config.RedisConstants.SUGAR_GAME_CONFIG_CACHE; + +@Component +public class GameConfigCacheRepository { + @Autowired + private RedisCache redisCache; + + public void setCache(SugarGameConfigCache value) { + redisCache.setCacheObject(SUGAR_GAME_CONFIG_CACHE, value, 7, TimeUnit.DAYS); + } + + public SugarGameConfigCache getCache() { + return redisCache.getCacheObject(SUGAR_GAME_CONFIG_CACHE); + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/repository/SugarCacheRepository.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/repository/SugarCacheRepository.java new file mode 100644 index 0000000..7000581 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/repository/SugarCacheRepository.java @@ -0,0 +1,38 @@ +package com.ruoyi.game.sugar.repository; + +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.game.sugar.model.cache.SugarGameCache; +import com.ruoyi.game.sugar.model.cache.SugarGameMockCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +import static com.ruoyi.admin.config.RedisConstants.SUGAR_GAME_CACHE; +import static com.ruoyi.admin.config.RedisConstants.SUGAR_GAME_MOCK_QUEUE; + +@Component +public class SugarCacheRepository { + @Autowired + private RedisCache redisCache; + + public void setCache(Long uid, SugarGameCache value) { + String cacheKey = SUGAR_GAME_CACHE + uid; + redisCache.setCacheObject(cacheKey, value, 7, TimeUnit.DAYS); + } + + public SugarGameCache getCache(Long uid) { + String cacheKey = SUGAR_GAME_CACHE + uid; + return redisCache.getCacheObject(cacheKey); + } + + public void setMockResultCache(Integer uid, SugarGameMockCache value) { + String cacheKey = SUGAR_GAME_MOCK_QUEUE + uid; + redisCache.setCacheObject(cacheKey, value, 7, TimeUnit.DAYS); + } + + public SugarGameMockCache getMockResultCache(Integer uid) { + String cacheKey = SUGAR_GAME_MOCK_QUEUE + uid; + return redisCache.getCacheObject(cacheKey); + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/repository/SugarRewardPoolCacheRepository.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/repository/SugarRewardPoolCacheRepository.java new file mode 100644 index 0000000..4f1a09c --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/repository/SugarRewardPoolCacheRepository.java @@ -0,0 +1,39 @@ +package com.ruoyi.game.sugar.repository; + +import com.ruoyi.game.sugar.model.cache.SugarRewardPoolCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +import static com.ruoyi.admin.config.RedisConstants.SUGAR_GAME_REWARD_POOL_CACHE; + +@Component +public class SugarRewardPoolCacheRepository { + + @Autowired + public RedisTemplate redisTemplate; + + public void incrementalBet(BigDecimal totalBet) { + redisTemplate.opsForValue().increment(SUGAR_GAME_REWARD_POOL_CACHE + ":bet", totalBet.doubleValue()); + } + + public void incrementalWin(BigDecimal totalWin) { + redisTemplate.opsForValue().increment(SUGAR_GAME_REWARD_POOL_CACHE + ":win", totalWin.doubleValue()); + } + + public SugarRewardPoolCache getCache() { + var totalBetS = redisTemplate.opsForValue().get(SUGAR_GAME_REWARD_POOL_CACHE + ":bet"); + var totalWinS = redisTemplate.opsForValue().get(SUGAR_GAME_REWARD_POOL_CACHE + ":win"); + SugarRewardPoolCache cache = new SugarRewardPoolCache(); + cache.setTotalBet(totalBetS == null ? BigDecimal.ZERO : new BigDecimal(totalBetS.toString())); + cache.setTotalWin(totalWinS == null ? BigDecimal.ZERO : new BigDecimal(totalWinS.toString())); + return cache; + } + // 清空奖池缓存(总下注、总返奖归零) + public void resetCache() { + redisTemplate.delete(SUGAR_GAME_REWARD_POOL_CACHE + ":bet"); + redisTemplate.delete(SUGAR_GAME_REWARD_POOL_CACHE + ":win"); + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/repository/UserRecordCacheRepository.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/repository/UserRecordCacheRepository.java new file mode 100644 index 0000000..134064f --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/repository/UserRecordCacheRepository.java @@ -0,0 +1,26 @@ +package com.ruoyi.game.sugar.repository; + +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.game.sugar.model.cache.SugarUserGameRecordCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +import static com.ruoyi.admin.config.RedisConstants.SUGAR_GAME_USER_RECORD_CACHE; + +@Component +public class UserRecordCacheRepository { + @Autowired + private RedisCache redisCache; + + public void setCache(Long uid, SugarUserGameRecordCache value) { + String cacheKey = SUGAR_GAME_USER_RECORD_CACHE + uid; + redisCache.setCacheObject(cacheKey, value, 7, TimeUnit.DAYS); + } + + public SugarUserGameRecordCache getCache(Long uid) { + String cacheKey = SUGAR_GAME_USER_RECORD_CACHE + uid; + return redisCache.getCacheObject(cacheKey); + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/AdminSugarService.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/AdminSugarService.java new file mode 100644 index 0000000..b30dbe5 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/AdminSugarService.java @@ -0,0 +1,259 @@ +package com.ruoyi.game.sugar.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.common.utils.json.SnakeCaseJsonUtils; +import com.ruoyi.domain.entity.GameSugarUser; +import com.ruoyi.domain.entity.GameSugarWin; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.game.sugar.contract.admin.request.AdminSugarUserListRequest; +import com.ruoyi.game.sugar.contract.admin.request.UpdateMockResultListRequest; +import com.ruoyi.game.sugar.mapper.AdminGameSugarSpinMapper; +import com.ruoyi.game.sugar.mapper.AdminGameSugarUserMapper; +import com.ruoyi.game.sugar.mapper.AdminGameSugarWinMapper; +import com.ruoyi.game.sugar.model.GameSugarConfig; +import com.ruoyi.game.sugar.model.cache.SugarGameMockCache; +import com.ruoyi.game.sugar.repository.SugarCacheRepository; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.mapper.SysConfigMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.commons.math3.util.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class AdminSugarService { + + private static final String EVENT_TAG = "糖果冲刺1000"; + + @Autowired + private AdminGameSugarUserMapper adminGameSugarUserMapper; + + @Autowired + private AdminGameSugarWinMapper adminGameSugarWinMapper; + + + @Autowired + private TtUserMapper ttUserMapper; + + @Autowired + private SysConfigMapper sysConfigMapper; + + @Autowired + private SugarCacheRepository cacheRepository; + + @Autowired + private AdminGameSugarSpinMapper adminGameSugarSpinMapper; + + public Pair>> queryUserList(AdminSugarUserListRequest params) { + Integer pageNum = params.getPageNum(); + Integer pageSize = params.getPageSize(); + if (pageNum == null || pageNum <= 0) pageNum = 1; + if (pageSize == null || pageSize <= 0) pageSize = 10; + + Page page = new Page<>(pageNum, pageSize); + page.setOptimizeCountSql(false); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (params.getUserId() != null) { + wrapper.eq(GameSugarUser::getUserId, params.getUserId()); + } + + adminGameSugarUserMapper.selectList(wrapper); + IPage userListPage = adminGameSugarUserMapper.selectPage(page, wrapper); + List userList = userListPage.getRecords(); + + List> r = userList.stream().map(v -> { + Map user = SnakeCaseJsonUtils.toSnakeCaseMap(v); + user.put("rtp", (v.getTotalBet().compareTo(BigDecimal.ZERO) > 0) ? v.getTotalWin().divide(v.getTotalBet(), 2, RoundingMode.HALF_UP) : 0); + user.put("profit", v.getTotalWin().subtract(v.getTotalBet())); + + Long userId = v.getUserId().longValue(); + TtUser ttUser = ttUserMapper.selectById(userId); + if (ttUser != null) { + user.put("nickname", ttUser.getNickName()); + user.put("avatar", ttUser.getAvatar()); + user.put("account_amount", ttUser.getAccountAmount()); + user.put("account_credits", ttUser.getAccountCredits()); + } + return user; + }).collect(Collectors.toList()); + Integer total = (int) userListPage.getTotal(); + return new Pair<>(total, r); + } + + public GameSugarConfig getGameConfig() { + SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("game.sugar.setting"); + if (sysConfig == null) { + GameSugarConfig config = new GameSugarConfig(); + config.setRtp(0.85); + config.setJpPercent(new int[]{10, 100}); + config.setDefaultAwardPool(0); + config.setMinProfit(0.3); + return config; + } + return SnakeCaseJsonUtils.fromSnakeCaseJson(sysConfig.getConfigValue(), GameSugarConfig.class); + } + + public String updateGameConfig(GameSugarConfig params) { + if (params.getJpPercent().length != 2) { + return "jpPercent长度必须为2"; + } + if (params.getRtp() < 0 || params.getRtp() > 1) { + return "rtp必须在[0,1]之间"; + } + if (params.getMinProfit() < 0 || params.getMinProfit() > 1) { + return "minProfit必须在[0,1]之间"; + } + + SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("game.sugar.setting"); + if (sysConfig == null) { + sysConfig = new SysConfig() {{ + setConfigName("甜蜜糖果1000游戏配置"); + setConfigKey("game.sugar.setting"); + setConfigValue(SnakeCaseJsonUtils.toSnakeCaseJson(params)); + }}; + sysConfigMapper.insertConfig(sysConfig); + } else { + sysConfig.setConfigValue(SnakeCaseJsonUtils.toSnakeCaseJson(params)); + sysConfigMapper.updateConfig(sysConfig); + } + return ""; + } + + public Pair>> queryDailyWinList(Map params) { + int pageNum = NumberUtils.toInt((String) params.get("pageNum"), 1); + int pageSize = NumberUtils.toInt((String) params.get("pageSize"), 10); + + Page page = new Page<>(pageNum, pageSize); + page.setOptimizeCountSql(false); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (params.get("dateRange[0]") != null) { + wrapper.ge(GameSugarWin::getDate, LocalDateTime.parse(params.get("dateRange[0]").toString())); + } + if (params.get("dateRange[1]") != null) { + wrapper.le(GameSugarWin::getDate, LocalDateTime.parse(params.get("dateRange[1]").toString())); + } + wrapper.orderByDesc(GameSugarWin::getUpdateTime); + + IPage userListPage = adminGameSugarWinMapper.selectPage(page, wrapper); + List userList = userListPage.getRecords(); + + List> r = userList.stream().map(v -> { + Map user = SnakeCaseJsonUtils.toSnakeCaseMap(v); + user.put("date", v.getDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + user.put("rtp", (v.getTotalBet().compareTo(BigDecimal.ZERO) > 0) ? v.getTotalWin().divide(v.getTotalBet(), 2, RoundingMode.HALF_UP) : 0); + user.put("profit", v.getTotalBet().subtract(v.getTotalWin()).subtract(v.getInitBet())); + return user; + }).collect(Collectors.toList()); + Integer total = (int) userListPage.getTotal(); + return new Pair<>(total, r); + } + + public Map> getMockResultList(Integer uid) { + var cache = cacheRepository.getMockResultCache(uid); + if (cache == null) { + return new HashMap<>(); + } + var result = new HashMap>(); + if (CollectionUtils.isNotEmpty(cache.getNormalScores())) + result.put("", cache.getNormalScores().stream().map(SugarGameMockCache.MockItem::getScore).collect(Collectors.toList())); + if (CollectionUtils.isNotEmpty(cache.getFreeScores())) + result.put("_free", cache.getFreeScores().stream().map(SugarGameMockCache.MockItem::getScore).collect(Collectors.toList())); + if (CollectionUtils.isNotEmpty(cache.getSuperScores())) + result.put("_super", cache.getSuperScores().stream().map(SugarGameMockCache.MockItem::getScore).collect(Collectors.toList())); + + return result; + } + + public Map> updateMockResultList(UpdateMockResultListRequest request) { + List types = Arrays.asList("", "_free", "_super"); + List maxScores = types.stream().map(type -> { + return adminGameSugarSpinMapper.select(type); + }).toList(); + + var cache = cacheRepository.getMockResultCache(request.getUid()); + if (cache == null) { + cache = new SugarGameMockCache(); + } + if (CollectionUtils.isNotEmpty(request.getNormalScores())) { + List newScores = new ArrayList<>(); + Integer maxScore = maxScores.get(0); + for (Integer score : request.getNormalScores()) { + if (score > maxScore) { + score = maxScore; + } + newScores.add(score); + } + var r = newScores.stream().map(v -> { + SugarGameMockCache.MockItem item = new SugarGameMockCache.MockItem(); + item.setScore(v); + return item; + }).collect(Collectors.toList()); + cache.setNormalScores(r); + } else { + cache.setNormalScores(new ArrayList<>()); + } + if (CollectionUtils.isNotEmpty(request.getFreeScores())) { + List newScores = new ArrayList<>(); + Integer maxScore = maxScores.get(1); + for (Integer score : request.getFreeScores()) { + if (score > maxScore) { + score = maxScore; + } + newScores.add(score); + } + var r = newScores.stream().map(v -> { + SugarGameMockCache.MockItem item = new SugarGameMockCache.MockItem(); + item.setScore(v); + return item; + }).collect(Collectors.toList()); + cache.setFreeScores(r); + } else { + cache.setFreeScores(new ArrayList<>()); + } + if (CollectionUtils.isNotEmpty(request.getSuperScores())) { + List newScores = new ArrayList<>(); + Integer maxScore = maxScores.get(2); + for (Integer score : request.getSuperScores()) { + if (score > maxScore) { + score = maxScore; + } + newScores.add(score); + } + var r = newScores.stream().map(v -> { + SugarGameMockCache.MockItem item = new SugarGameMockCache.MockItem(); + item.setScore(v); + return item; + }).collect(Collectors.toList()); + cache.setSuperScores(r); + } else { + cache.setSuperScores(new ArrayList<>()); + } + + cacheRepository.setMockResultCache(request.getUid(), cache); + + + var result = new HashMap>(); + if (CollectionUtils.isNotEmpty(cache.getNormalScores())) + result.put("", cache.getNormalScores().stream().map(SugarGameMockCache.MockItem::getScore).collect(Collectors.toList())); + if (CollectionUtils.isNotEmpty(cache.getFreeScores())) + result.put("_free", cache.getFreeScores().stream().map(SugarGameMockCache.MockItem::getScore).collect(Collectors.toList())); + if (CollectionUtils.isNotEmpty(cache.getSuperScores())) + result.put("_super", cache.getSuperScores().stream().map(SugarGameMockCache.MockItem::getScore).collect(Collectors.toList())); + + return result; + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/ApiSugarService.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/ApiSugarService.java new file mode 100644 index 0000000..45eb2a9 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/ApiSugarService.java @@ -0,0 +1,670 @@ +package com.ruoyi.game.sugar.service; + +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.model.UpdateUserAccountBo; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.redis.config.RedisLock; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.MoneyUtil; +import com.ruoyi.common.utils.json.SnakeCaseJsonUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.entity.GameSugarSpin; +import com.ruoyi.domain.entity.GameSugarStepInfo; +import com.ruoyi.domain.entity.GameSugarUser; +import com.ruoyi.domain.entity.GameSugarWin; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.game.sugar.contract.api.response.ApiSugarBuySpinResponse; +import com.ruoyi.game.sugar.contract.api.response.ApiSugarHasFreeSpinResponse; +import com.ruoyi.game.sugar.contract.api.response.ApiSugarSpinResultResponse; +import com.ruoyi.game.sugar.domain.GameSugarRecord; +import com.ruoyi.game.sugar.enums.GameSugarRecordStatus; +import com.ruoyi.game.sugar.mapper.ApiGameSugarRecordMapper; +import com.ruoyi.game.sugar.mapper.ApiGameSugarSpinMapper; +import com.ruoyi.game.sugar.mapper.ApiGameSugarUserMapper; +import com.ruoyi.game.sugar.mapper.ApiGameSugarWinMapper; +import com.ruoyi.game.sugar.model.GameSugarConfig; +import com.ruoyi.game.sugar.model.cache.SugarGameCache; +import com.ruoyi.game.sugar.model.cache.SugarGameConfigCache; +import com.ruoyi.game.sugar.model.cache.SugarGameMockCache; +import com.ruoyi.game.sugar.repository.GameConfigCacheRepository; +import com.ruoyi.game.sugar.repository.SugarCacheRepository; +import com.ruoyi.game.sugar.repository.SugarRewardPoolCacheRepository; +import com.ruoyi.game.sugar.repository.UserRecordCacheRepository; +import com.ruoyi.game.sugar.service.sugar.*; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.mapper.SysConfigMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.math3.util.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.interceptor.TransactionAspectSupport; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static com.ruoyi.admin.config.RedisConstants.COMMON_LOCK; + +@Service +@Slf4j +public class ApiSugarService { + + private static final String EVENT_TAG = "糖果冲刺1000"; + + @Autowired + private RedisLock redisLock; + + @Autowired + private SugarCacheRepository sugarCacheRepository; + + @Autowired + private UserRecordCacheRepository userRecordCacheRepository; + + @Autowired + private GameConfigCacheRepository gameConfigCacheRepository; + + @Autowired + private TtUserMapper userMapper; + + @Autowired + private ApiGameSugarSpinMapper gameSugarSpinMapper; + + @Autowired + private SelectNormalGameLogic selectNormalGameLogic; + + @Autowired + private SelectFreeGameLogic selectFreeGameLogic; + + @Autowired + private SelectSuperFreeGameLogic selectSuperFreeGameLogic; + + @Autowired + private PostGameLogicManager postGameLogicManager; + + + @Autowired + private ApiGameSugarUserMapper apiGameSugarUserMapper; + + @Autowired + private ApiGameSugarWinMapper apiGameSugarWinMapper; + + @Autowired + private SysConfigMapper sysConfigMapper; + + @Autowired + private TtUserService userService; + + @Autowired + private ApiGameSugarRecordMapper apiGameSugarRecordMapper; + + @Autowired + private SugarRewardPoolCacheRepository rewardPoolCacheRepository; + + + // 符号映射 + private static final Map SYMBOL_MAPPING = new HashMap() {{ + put("S", 1); + put("A", 3); + put("B", 4); + put("C", 5); + put("D", 6); + put("E", 7); + put("F", 8); + put("G", 9); + }}; + + // 符号映射 + private static final Map FREE_SPIN_MAPPING = new HashMap() {{ + put(3, 10); + put(4, 12); + put(5, 15); + put(6, 20); + put(7, 30); + }}; + + + @Transactional(rollbackFor = Throwable.class) + public R doSpin(Long userId, BigDecimal bet) { + if (bet == null || bet.compareTo(BigDecimal.ZERO) <= 0) { + return R.fail("下注金额不正确"); + } + + Boolean lock = redisLock.tryLock(COMMON_LOCK + userId, 3L, 7L, TimeUnit.SECONDS); + if (!Boolean.TRUE.equals(lock)) { + return R.fail("请求频繁,加锁失败"); + } + try { + + // 获取用户缓存 + SugarGameCache cache = sugarCacheRepository.getCache(userId); + + UpdateUserAccountBo updateUserAccountBo; + if (cache == null) { + // 扣减余额 + R updateResult = userService.updateUserAccount(userId.intValue(), bet.negate(), TtAccountRecordSource.SUGAR_SPIN_NORMAL); + if (R.isError(updateResult)) { + return R.fail(updateResult.getMsg()); + } + updateUserAccountBo = updateResult.getData(); + + // 更新统计数据 + GameSugarUser sugarUser = apiGameSugarUserMapper.select(userId.intValue()); + if (sugarUser == null) { + sugarUser = initUser(userId); + } + sugarUser.setTotalBet(sugarUser.getTotalBet().add(bet)); + apiGameSugarUserMapper.insertOrUpdate(sugarUser); + if (apiGameSugarWinMapper.select(DateUtils.getTodayAtZero()) == null) { + initWin(); + } + apiGameSugarWinMapper.updateAmount(DateUtils.getTodayAtZero(), bet, BigDecimal.ZERO); + rewardPoolCacheRepository.incrementalBet(bet); + + // 初始化缓存 + cache = new SugarGameCache(); + cache.setKind("normal"); + cache.setAccountAmount(updateUserAccountBo.getAccountAmount()); + cache.setAccountCredits(updateUserAccountBo.getAccountCredits()); + cache.setBet(bet); + cache.setTotalFreeSpins(0); + cache.setFreeSpinIndex(0); + cache.setTotalWin(new BigDecimal("0")); + + // 选择游戏 + SugarGameCache.GameSpin gameData = selectGame("", userId, bet, bet); + cache.setGameData(gameData); + cache.setRecordId(initRecord(userId, "", bet)); + + log.info("开始新spin: uid={}, gid={}, score={}", userId, gameData.getGid(), gameData.getScore()); + } + bet = cache.getBet(); + + // 处理免费旋转 + Integer freeSpinIndex = cache.getFreeSpinIndex() + 1; + cache.setFreeSpinIndex(freeSpinIndex); + + // 获取当前免费旋转的步骤 + List currentSteps = cache.getGameData().getSteps().stream() + .filter(step -> step.getFreeSpinId().equals(freeSpinIndex)) + .collect(Collectors.toList()); + + int extraFreeCount = 0; + if (CollectionUtils.isNotEmpty(currentSteps)) { + int count = (int) currentSteps.get(currentSteps.size() - 1).getGrid().chars() + .filter(c -> c == 'S').count(); + Integer freeSpins = FREE_SPIN_MAPPING.get(count); + if (freeSpins != null) { + extraFreeCount = freeSpins; + cache.setTotalFreeSpins(cache.getTotalFreeSpins() + freeSpins); + } + } + + // 转换并计算总赢 + Pair>> pair = convert(currentSteps, cache.getTotalWin(), bet); + BigDecimal totalWin = pair.getFirst(); + List> steps = pair.getSecond(); + cache.setTotalWin(totalWin); + + // 保存缓存更新 + sugarCacheRepository.setCache(userId, cache); + + // 检查是否结束, 结束则进行结算 + if (CollectionUtils.isEmpty(steps)) { + BigDecimal score = cache.getGameData().getScore(); + BigDecimal win = score.multiply(bet); + + // 增加余额 + if (win.compareTo(BigDecimal.ZERO) <= 0) { + win = new BigDecimal("0.01"); + } + R updateResult = userService.updateUserAccount(userId.intValue(), win, TtAccountRecordSource.SUGAR_SPIN_REWARD); + if (R.isError(updateResult)) { + return R.fail(updateResult.getMsg()); + } + updateUserAccountBo = updateResult.getData(); + cache.setAccountAmount(updateUserAccountBo.getAccountAmount()); + cache.setAccountCredits(updateUserAccountBo.getAccountCredits()); + + if ("normal".equals(cache.getKind()) && score.compareTo(new BigDecimal("30")) >= 0) { + String triggeredKind = score.compareTo(new BigDecimal("200")) >= 0 ? "super" : "standard"; + String triggeredType = "super".equals(triggeredKind) ? "_super" : "_free"; + log.info("普通旋转触发免费旋转: uid={}, score={}, triggeredKind={}", userId, score, triggeredKind); + + sugarCacheRepository.setCache(userId, null); + + BigDecimal finalWin = win; + Long recordId = cache.getRecordId(); + AsyncManager.me().run(() -> { + GameSugarUser sugarUser = apiGameSugarUserMapper.select(userId.intValue()); + sugarUser.setTotalWin(sugarUser.getTotalWin().add(finalWin)); + apiGameSugarUserMapper.insertOrUpdate(sugarUser); + new LambdaUpdateChainWrapper<>(apiGameSugarRecordMapper) + .eq(GameSugarRecord::getId, recordId) + .set(GameSugarRecord::getStatus, GameSugarRecordStatus.COMPLETE.getCode()) + .set(GameSugarRecord::getWin, finalWin) + .setSql("multiplier = win / bet") + .update(); + apiGameSugarWinMapper.updateAmount(DateUtils.getTodayAtZero(), BigDecimal.ZERO, finalWin); + rewardPoolCacheRepository.incrementalWin(finalWin); + }); + + // 初始化免费旋转缓存 + SugarGameCache freeCache = new SugarGameCache(); + freeCache.setKind(triggeredKind); + freeCache.setBet(bet); + freeCache.setAccountAmount(updateUserAccountBo.getAccountAmount()); + freeCache.setAccountCredits(updateUserAccountBo.getAccountCredits()); + freeCache.setTotalFreeSpins(0); + freeCache.setFreeSpinIndex(0); + freeCache.setTotalWin(BigDecimal.ZERO); + SugarGameCache.GameSpin freeGameData = selectGame(triggeredType, userId, bet, BigDecimal.ZERO); + freeCache.setGameData(freeGameData); + freeCache.setRecordId(initRecord(userId, triggeredKind, BigDecimal.ZERO)); + sugarCacheRepository.setCache(userId, freeCache); + + ApiSugarSpinResultResponse triggeredResult = new ApiSugarSpinResultResponse(); + triggeredResult.setKind(triggeredKind); + triggeredResult.setBalance(MoneyUtil.toStr(updateUserAccountBo.getAccountAmount())); + triggeredResult.setCredits(MoneyUtil.toStr(updateUserAccountBo.getAccountCredits())); + triggeredResult.setIndex(freeSpinIndex - 1); + triggeredResult.setRemainingFreeSpins(0); + triggeredResult.setExtraFreeSpinCount(0); + triggeredResult.setData(steps); + return R.ok(triggeredResult); + } + // 清除缓存 + sugarCacheRepository.setCache(userId, null); + + // 更新统计数据 + BigDecimal finalWin = win; + Long recordId = cache.getRecordId(); + AsyncManager.me().run(() -> { + GameSugarUser sugarUser = apiGameSugarUserMapper.select(userId.intValue()); + sugarUser.setTotalWin(sugarUser.getTotalWin().add(finalWin)); + apiGameSugarUserMapper.insertOrUpdate(sugarUser); + + new LambdaUpdateChainWrapper<>(apiGameSugarRecordMapper) + .eq(GameSugarRecord::getId, recordId) + .set(GameSugarRecord::getStatus, GameSugarRecordStatus.COMPLETE.getCode()) + .set(GameSugarRecord::getWin, finalWin) + .setSql("multiplier = win / bet") + .update(); + + apiGameSugarWinMapper.updateAmount(DateUtils.getTodayAtZero(), BigDecimal.ZERO, finalWin); + + rewardPoolCacheRepository.incrementalWin(finalWin); + }); + } + + // 构建返回结果 + ApiSugarSpinResultResponse result = new ApiSugarSpinResultResponse(); + result.setKind(cache.getKind()); + result.setBalance(MoneyUtil.toStr(cache.getAccountAmount())); + result.setCredits(MoneyUtil.toStr(cache.getAccountCredits())); + result.setIndex(freeSpinIndex - 1); + result.setRemainingFreeSpins(Math.max(0, cache.getTotalFreeSpins() + 1 - freeSpinIndex)); + result.setExtraFreeSpinCount(extraFreeCount); + result.setData(steps); + + return R.ok(result); + } catch (Exception e) { + TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); + log.error("错误", e); + return R.ok(); + } finally { + redisLock.unlock(COMMON_LOCK + userId); + } + } + + private GameSugarUser initUser(Long userId) { + GameSugarUser sugarUser = new GameSugarUser(); + sugarUser.setUserId(userId.intValue()); + sugarUser.setTotalWin(BigDecimal.ZERO); + sugarUser.setTotalBet(BigDecimal.ZERO); + sugarUser.setCount(0); + sugarUser.setFreeCount(0); + sugarUser.setSuperFreeCount(0); + apiGameSugarUserMapper.insertOrUpdate(sugarUser); + return sugarUser; + } + + public GameSugarConfig getGameConfig() { + SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("game.sugar.setting"); + if (sysConfig == null) { + GameSugarConfig config = new GameSugarConfig(); + config.setRtp(0.8); + config.setJpPercent(new int[]{10, 100}); + config.setDefaultAwardPool(0); + config.setMinProfit(0.3); + return config; + } + return SnakeCaseJsonUtils.fromSnakeCaseJson(sysConfig.getConfigValue(), GameSugarConfig.class); + } + + private GameSugarWin initWin() { + GameSugarConfig config = getGameConfig(); + + GameSugarWin sugarWin = new GameSugarWin(); + sugarWin.setDate(DateUtils.getTodayAtZero()); + sugarWin.setTotalWin(BigDecimal.ZERO); + sugarWin.setInitBet(BigDecimal.valueOf(config.getDefaultAwardPool())); + sugarWin.setTotalBet(BigDecimal.valueOf(config.getDefaultAwardPool())); + sugarWin.setCount(0); + sugarWin.setFreeCount(0); + sugarWin.setSuperFreeCount(0); + apiGameSugarWinMapper.insert(sugarWin); + return sugarWin; + } + + private SugarGameCache.GameSpin selectGame(String type, Long userId, BigDecimal bet, BigDecimal cost) { + StrategyContext context = new StrategyContext(); + + GameSugarUser sugarUser = apiGameSugarUserMapper.select(userId.intValue()); + if (sugarUser == null) { + sugarUser = initUser(userId); + } + + SugarGameConfigCache cache = gameConfigCacheRepository.getCache(); + if (cache == null) { + cache = SugarGameConfigCache.init(); + } + context.setGameConfigCache(cache); + + GameSugarConfig gameConfig = getGameConfig(); + + var rewardPool = rewardPoolCacheRepository.getCache(); + + context.setType(type); + context.setBet(bet); + context.setCost(cost); + context.setSugarUser(sugarUser); + context.setSysConfig(gameConfig); + context.setRewardPool(rewardPool); + + String whereSql = ""; + if (Objects.equals(type, "")) { + sugarUser.setCount(sugarUser.getCount() + 1); + apiGameSugarWinMapper.updateCount(DateUtils.getTodayAtZero(), 1, 0, 0); + + whereSql = selectNormalGameLogic.where(context); + } else if (Objects.equals(type, "_free")) { + sugarUser.setCount(sugarUser.getCount() + 1); + sugarUser.setFreeCount(sugarUser.getFreeCount() + 1); + apiGameSugarWinMapper.updateCount(DateUtils.getTodayAtZero(), 1, 1, 0); + + whereSql = selectFreeGameLogic.where(context); + } else if (Objects.equals(type, "_super")) { + sugarUser.setCount(sugarUser.getCount() + 1); + sugarUser.setSuperFreeCount(sugarUser.getSuperFreeCount() + 1); + apiGameSugarWinMapper.updateCount(DateUtils.getTodayAtZero(), 1, 0, 1); + + whereSql = selectSuperFreeGameLogic.where(context); + } + apiGameSugarUserMapper.insertOrUpdate(sugarUser); + + + if (StringUtils.isBlank(whereSql)) { + whereSql = " 1=1 "; + } + + log.info(EVENT_TAG + " uid={} whereSql={}", userId, whereSql); + + GameSugarSpin spin = null; + var mockResultCache = sugarCacheRepository.getMockResultCache(Math.toIntExact(userId)); + if (mockResultCache != null) { + List r = null; + if (type.equals("_free")) { + r = mockResultCache.getFreeScores(); + } + if (type.isEmpty()) { + r = mockResultCache.getNormalScores(); + } + if (type.equals("_super")) { + r = mockResultCache.getSuperScores(); + } + if (r != null && !r.isEmpty()) { + whereSql = " score >= " + r.removeFirst().getScore(); + spin = gameSugarSpinMapper.selectMinOne(type, whereSql); + log.info(EVENT_TAG + " uid={} use mock result.sql={}", userId, whereSql); + sugarCacheRepository.setMockResultCache(Math.toIntExact(userId), mockResultCache); + } + } + if (spin == null) { + spin = gameSugarSpinMapper.selectRandomOne(type, whereSql); + if (spin == null) { + // 随机选择一个游戏 + spin = gameSugarSpinMapper.selectRandomOne(type, "score <= 10"); + } + } + + // 获取游戏步骤 + List steps = gameSugarSpinMapper.selectStepsByGid(spin.getGid(), type); + steps = steps.stream().peek(v -> { + v.setMultipler2(SnakeCaseJsonUtils.parseSnakeCaseList(v.getMultipler(), Integer.class)); + v.setSymbolLinks2(SnakeCaseJsonUtils.parseSnakeCaseList(v.getSymbolLinks(), + GameSugarStepInfo.SymbolLink.class)); + }).collect(Collectors.toList()); + + SugarGameCache.GameSpin result = new SugarGameCache.GameSpin(); + result.setGid(spin.getGid()); + result.setScore(spin.getScore()); + result.setSteps(steps); + return result; + } + + @Transactional(rollbackFor = Throwable.class) + public R buyFreeSpins(Long userId, String kind, BigDecimal bet) { + if (bet == null || bet.compareTo(BigDecimal.ZERO) <= 0) { + return R.fail("下注金额不正确"); + } + if (kind == null || !Arrays.asList("standard", "super").contains(kind)) { + return R.fail("类型不正确"); + } + Boolean lock = redisLock.tryLock(COMMON_LOCK + userId, 3L, 7L, TimeUnit.SECONDS); + if (!Boolean.TRUE.equals(lock)) { + return R.fail("请求频繁,加锁失败"); + } + + try { + // 检查用户是否正在进行免费旋转 + SugarGameCache cache = sugarCacheRepository.getCache(userId); + + if (cache != null) { + return R.fail("免费旋转进行中,请先完成当前免费旋转"); + } + + // 根据类型处理 + BigDecimal requiredAmount = null; + String type = ""; + TtAccountRecordSource t = null; + if ("standard".equals(kind)) { + requiredAmount = bet.multiply(new BigDecimal(50)); + type = "_free"; + t = TtAccountRecordSource.SUGAR_BUY_FREE_SPIN; + } else { + requiredAmount = bet.multiply(new BigDecimal(200)); + type = "_super"; + t = TtAccountRecordSource.SUGAR_BUY_SUPER_SPIN; + } + + // 检查余额和扣减余额 + R updateResult = userService.updateUserAccount(userId.intValue(), requiredAmount.negate(), t); + if (R.isError(updateResult)) { + return R.fail(updateResult.getMsg()); + } + + GameSugarUser sugarUser = apiGameSugarUserMapper.select(userId.intValue()); + if (sugarUser == null) { + sugarUser = initUser(userId); + } + sugarUser.setTotalBet(sugarUser.getTotalBet().add(requiredAmount)); + apiGameSugarUserMapper.insertOrUpdate(sugarUser); + if (apiGameSugarWinMapper.select(DateUtils.getTodayAtZero()) == null) { + initWin(); + } + apiGameSugarWinMapper.updateAmount(DateUtils.getTodayAtZero(), requiredAmount, BigDecimal.ZERO); + rewardPoolCacheRepository.incrementalBet(requiredAmount); + + + // 初始化缓存 + cache = new SugarGameCache(); + cache.setAccountAmount(updateResult.getData().getAccountAmount()); + cache.setAccountCredits(updateResult.getData().getAccountCredits()); + cache.setKind(kind); + cache.setBet(bet); + cache.setTotalFreeSpins(0); + cache.setFreeSpinIndex(0); + cache.setTotalWin(BigDecimal.ZERO); + + // 选择标准免费游戏 + SugarGameCache.GameSpin gameData = selectGame(type, userId, bet, requiredAmount); + cache.setGameData(gameData); + var recordId = initRecord(userId, kind, requiredAmount); + cache.setRecordId(recordId); + + // 保存到缓存 + sugarCacheRepository.setCache(userId, cache); + log.info("用户{}购买了{}免费旋转,下注:{},扣除金额:{},gid={},score={}", userId, kind, bet, + requiredAmount, gameData.getGid(), gameData.getScore()); + + ApiSugarBuySpinResponse response = new ApiSugarBuySpinResponse(); + response.setCost(MoneyUtil.toStr(requiredAmount)); + response.setBalance(MoneyUtil.toStr(updateResult.getData().getAccountAmount())); + response.setCredits(MoneyUtil.toStr(updateResult.getData().getAccountCredits())); + response.setIsSuperFree("super".equals(kind)); + + return R.ok(response); + } catch (Exception e) { + TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); + return R.fail("内部错误"); + } finally { + redisLock.unlock(COMMON_LOCK + userId); + } + } + + private Long initRecord(Long userId, String kind, BigDecimal bet) { + GameSugarRecord record = new GameSugarRecord(); + record.setUserId(userId); + record.setKind(kind); + record.setBet(bet); + record.setWin(BigDecimal.ZERO); + record.setExtraFree(0); + record.setStatus(GameSugarRecordStatus.RUNING.getCode()); + + apiGameSugarRecordMapper.insert(record); + + return record.getId(); + } + + /** + * 转换步骤数据 + * + * @param steps 步骤列表 + * @param totalWin 当前总赢取金额 + * @param bet 下注金额 + * @return 转换结果 + */ + public Pair>> convert(List steps, BigDecimal totalWin, BigDecimal bet) { + List> result = new ArrayList<>(); + if (CollectionUtils.isEmpty(steps)) { + return new Pair<>(totalWin, result); + } + + for (GameSugarStepInfo step : steps) { + // 转换grid为符号映射值 + List s = step.getGrid().chars().mapToObj(c -> (char) c) + .map(symbol -> SYMBOL_MAPPING.getOrDefault(symbol + "", 0)) + .collect(Collectors.toList()); + + // 转换multipler为slm格式 + List> slm = new ArrayList<>(); + List multipler = step.getMultipler2(); + for (int i = 0; i < multipler.size(); i += 2) { + if (i + 1 < multipler.size()) { + slm.add(Arrays.asList(multipler.get(i), multipler.get(i + 1))); + } + } + + // 处理symbol_links + List>> symbolMultipler = new ArrayList<>(); + List> symbolWin = new ArrayList<>(); + List> symbolMark = new ArrayList<>(); + + for (GameSugarStepInfo.SymbolLink link : step.getSymbolLinks2()) { + // 处理s_mark + List markItem = new ArrayList<>(); + markItem.add(SYMBOL_MAPPING.getOrDefault(link.getSymbol(), 0)); + + // 添加坐标列表 + List coordinates = new ArrayList<>(link.getLoc()); + markItem.add(coordinates); + + // 添加奖励金额 + markItem.add(bet.multiply(BigDecimal.valueOf(link.getScore())) + .setScale(2, RoundingMode.HALF_UP).toPlainString()); + symbolMark.add(markItem); + + // 处理sm和swin + if (link.getTotalMulti() > 1) { + // 处理symbol_multipler (sm) + List> multiItem = new ArrayList<>(); + List linkMultipler = link.getMultipler(); + for (int i = 0; i < linkMultipler.size(); i += 2) { + if (i + 1 < linkMultipler.size()) { + multiItem.add(Arrays.asList( + linkMultipler.get(i), + linkMultipler.get(i + 1) + )); + } + } + symbolMultipler.add(multiItem); + + // 处理symbol_win (swin) + symbolWin.add(Arrays.asList( + bet.multiply(BigDecimal.valueOf(link.getBaseScore())), + link.getTotalMulti() + )); + } + } + + // 计算总赢取金额 + totalWin = totalWin.add(bet.multiply(step.getScore())); + + // 构建步骤结果 + Map stepResult = new LinkedHashMap<>(); + stepResult.put("tw", totalWin.setScale(2, RoundingMode.HALF_UP).toPlainString()); + stepResult.put("w", step.getScore().multiply(bet) + .setScale(2, RoundingMode.HALF_UP).toPlainString()); + stepResult.put("slm", slm); + stepResult.put("sm", symbolMultipler); + stepResult.put("swin", symbolWin); + stepResult.put("s_mark", symbolMark); + stepResult.put("s", s); + + result.add(stepResult); + } + + return new Pair<>(totalWin, result); + } + + public R hasFreeSpin(Long userId) { + // 检查用户是否正在进行免费旋转 + SugarGameCache cache = sugarCacheRepository.getCache(userId); + + if (cache != null) { + ApiSugarHasFreeSpinResponse resp = new ApiSugarHasFreeSpinResponse(); + resp.setHasFreeSpin(true); + return R.ok(resp); + } + ApiSugarHasFreeSpinResponse resp = new ApiSugarHasFreeSpinResponse(); + resp.setHasFreeSpin(false); + return R.ok(resp); + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/PostGameLogicManager.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/PostGameLogicManager.java new file mode 100644 index 0000000..74da9fc --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/PostGameLogicManager.java @@ -0,0 +1,32 @@ +package com.ruoyi.game.sugar.service.sugar; + +import com.ruoyi.domain.entity.GameSugarSpin; +import com.ruoyi.game.sugar.mapper.ApiGameSugarSpinMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.RoundingMode; + +@Component +public class PostGameLogicManager { + @Autowired + private ApiGameSugarSpinMapper gameSugarSpinMapper; + + public GameSugarSpin postGameLogic(StrategyContext context, GameSugarSpin spin) { + if (spin.getScore().doubleValue() >= 20) { + return spin; + } + if (context.getCost().doubleValue() < 100) { + return spin; + } else if (context.getCost().doubleValue() <= 150) { + String whereSql = " score between 20 and 70 "; + spin = gameSugarSpinMapper.selectRandomOne(context.getType(), whereSql); + } else { + double maxScore = context.getCost().divide(context.getBet(), RoundingMode.HALF_UP).doubleValue() / 2; + double minScore = maxScore / 1.5; + String whereSql = String.format(" score between %.2f and %.2f ", minScore, maxScore); + spin = gameSugarSpinMapper.selectRandomOne(context.getType(), whereSql); + } + return spin; + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/SelectFreeGameLogic.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/SelectFreeGameLogic.java new file mode 100644 index 0000000..f3da75a --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/SelectFreeGameLogic.java @@ -0,0 +1,59 @@ +package com.ruoyi.game.sugar.service.sugar; + +import com.ruoyi.common.utils.RandomUtil; +import com.ruoyi.game.sugar.model.cache.SugarGameConfigCache; +import com.ruoyi.game.sugar.service.sugar.strategy.MaxScoreStrategy; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Comparator; + +@Component +@Slf4j +public class SelectFreeGameLogic { + @Autowired + private SelectGameCommonLogic selectGameCommonLogic; + + private static final String EVENT_TAG = "糖果冲刺1000免费旋转选择策略"; + + + public String where(StrategyContext context) { + // todo + var userTag = selectGameCommonLogic.getUserTag(context); + var rewardPool = context.getRewardPool(); + BigDecimal totalBet = rewardPool.getTotalBet(); + BigDecimal totalWin = rewardPool.getTotalWin(); + double globalRtp = 0.9; + if (totalBet.compareTo(BigDecimal.ZERO) > 0) { + globalRtp = totalWin.doubleValue() / totalBet.doubleValue(); + } + context.getGameConfigCache().setGlobalRtp(globalRtp); + + var table = selectGameCommonLogic.calcCurrentProbabilityTable(context); + table = table.stream().sorted(Comparator.comparing(SugarGameConfigCache.AwardItem::getLowRewardRound)).toList(); + var indexes = new ArrayList(); + for (int i = 0; i < table.size(); i++) { + indexes.add(i); + } + var weights = table.stream().map(SugarGameConfigCache.AwardItem::getWeight).toList(); + log.info("使用的奖励表: {}", table); + log.info("weights: {}", weights); + var rewardIndex = RandomUtil.choices(indexes, weights, 1).getFirst(); + log.info("rewardIndex: {}", rewardIndex); + var rewardItem = table.get(rewardIndex); + var minScore = rewardItem.getLowRewardRound(); + var maxScore = rewardItem.getHighRewardRound(); + if (minScore == maxScore && rewardIndex < table.size() - 1) { + maxScore = table.get(rewardIndex + 1).getLowRewardRound(); + } + + context.getControlParam().setMinScore(minScore); + context.getControlParam().setMaxScore(maxScore); + new MaxScoreStrategy().calcScore(context); + + return SelectGameCommonLogic.applyParam(log, EVENT_TAG, context); + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/SelectGameCommonLogic.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/SelectGameCommonLogic.java new file mode 100644 index 0000000..69dc77b --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/SelectGameCommonLogic.java @@ -0,0 +1,95 @@ +package com.ruoyi.game.sugar.service.sugar; + + +import cn.hutool.core.lang.Pair; +import com.ruoyi.game.sugar.model.cache.SugarGameConfigCache; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +@Component +@Slf4j +public class SelectGameCommonLogic { + + + public UserTag getUserTag(StrategyContext context) { + UserTag result = UserTag.NEWBIE; + + // 获取用户信息 + BigDecimal totalBet = context.getSugarUser().getTotalBet(); + int betCount = context.getSugarUser().getCount(); + + // 判定新人奖池条件:投注额小于5000或投注次数小于100次 + if (totalBet.compareTo(new BigDecimal("5000")) < 0 || betCount < 100) { + return UserTag.NEWBIE; + } + + // 判定老人奖池条件:投注额大于等于1w或投注次数大于300次 + if (totalBet.compareTo(new BigDecimal("10000")) >= 0 || betCount > 300) { + return UserTag.OLDIE; + } + + // 判定中等奖池条件:投注额大于4999小于1w或投注次数小于300次 + if ((totalBet.compareTo(new BigDecimal("4999")) > 0 && totalBet.compareTo(new BigDecimal("10000")) < 0) || betCount < 300) { + return UserTag.MIDDLE; + } + + return result; + } + + public List calcCurrentProbabilityTable(StrategyContext context) { + SugarGameConfigCache cache = context.getGameConfigCache(); + double globalRtp = cache.getGlobalRtp(); + boolean useTable2 = globalRtp < cache.getRtpSwitchThreshold(); + + log.info("globalRtp={}, threshold={}, useTable2={}", globalRtp, cache.getRtpSwitchThreshold(), useTable2); + + List awardItems = getAwardItems(context, cache, useTable2); + + return new ArrayList<>(awardItems); + } + + private static List getAwardItems( + StrategyContext context, SugarGameConfigCache cache, boolean useTable2) { + + return switch (context.getType()) { + case "" -> useTable2 ? cache.getNormalAwardItems2() : cache.getNormalAwardItems(); + case "_free" -> useTable2 ? cache.getFreeAwardItems2() : cache.getFreeAwardItems(); + case "_super" -> useTable2 ? cache.getSuperAwardItems2() : cache.getSuperAwardItems(); + default -> cache.getNormalAwardItems(); + }; + } + + public static String applyParam(Logger log, String EVENT_TAG, StrategyContext context) { + // 校验 + StrategyContext.ControlParam param = context.getControlParam(); + if (param.getMinScore() > param.getMaxScore()) { + log.error("{} minScore is greater than maxScore.minScore={},maxScore={}", + EVENT_TAG, param.getMinScore(), param.getMaxScore()); + param.setMinScore(-1); + } + + String whereSql = ""; + if (param.getMinScore() >= 0) { + whereSql += " and score >= " + param.getMinScore(); + } + if (param.getMaxScore() >= 0) { + whereSql += " and score < " + param.getMaxScore(); + } + if (param.isJP()) { + whereSql += " and extra_free > 0"; + } else if (param.isNotJP()) { + whereSql += " and extra_free = 0"; + } + if (whereSql.startsWith(" and")) { + whereSql = whereSql.substring(4); + } + return whereSql; + } +} \ No newline at end of file diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/SelectNormalGameLogic.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/SelectNormalGameLogic.java new file mode 100644 index 0000000..51c830e --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/SelectNormalGameLogic.java @@ -0,0 +1,66 @@ +package com.ruoyi.game.sugar.service.sugar; + +import com.ruoyi.common.utils.RandomUtil; +import com.ruoyi.common.utils.json.SnakeCaseJsonUtils; +import com.ruoyi.game.sugar.model.cache.SugarGameConfigCache; +import com.ruoyi.game.sugar.service.sugar.strategy.MaxScoreStrategy; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Comparator; + +@Component +@Slf4j +public class SelectNormalGameLogic { + @Autowired + private SelectGameCommonLogic selectGameCommonLogic; + + private static final String EVENT_TAG = "糖果冲刺1000普通选择策略"; + + + public String where(StrategyContext context) { + // todo + var userTag = selectGameCommonLogic.getUserTag(context); + var rewardPool = context.getRewardPool(); + BigDecimal totalBet = rewardPool.getTotalBet(); + BigDecimal totalWin = rewardPool.getTotalWin(); + double globalRtp = 0.9; + if (totalBet.compareTo(BigDecimal.ZERO) > 0) { + globalRtp = totalWin.doubleValue() / totalBet.doubleValue(); + } + context.getGameConfigCache().setGlobalRtp(globalRtp); + + var table = selectGameCommonLogic.calcCurrentProbabilityTable(context); + table = table.stream().sorted(Comparator.comparing(SugarGameConfigCache.AwardItem::getLowRewardRound)).toList(); + var indexes = new ArrayList(); + for (int i = 0; i < table.size(); i++) { + indexes.add(i); + } + var weights = table.stream().map(SugarGameConfigCache.AwardItem::getWeight).toList(); + log.info("使用的奖励表: {}", SnakeCaseJsonUtils.toSnakeCaseJson(table)); + log.info("weights: {}", SnakeCaseJsonUtils.toSnakeCaseJson(weights)); + var rewardIndex = RandomUtil.choices(indexes, weights, 1).getFirst(); + log.info("rewardIndex: {}", rewardIndex); + var rewardItem = table.get(rewardIndex); + var minScore = rewardItem.getLowRewardRound(); + var maxScore = rewardItem.getHighRewardRound(); + if (minScore == maxScore && rewardIndex < table.size() - 1) { + maxScore = table.get(rewardIndex + 1).getLowRewardRound(); + } + + context.getControlParam().setMinScore(minScore); + context.getControlParam().setMaxScore(maxScore); + new MaxScoreStrategy().calcScore(context); + + if (minScore >= 30) { + context.getControlParam().setJP(true); + } else { + context.getControlParam().setNotJP(true); + } + + return SelectGameCommonLogic.applyParam(log, EVENT_TAG, context); + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/SelectSuperFreeGameLogic.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/SelectSuperFreeGameLogic.java new file mode 100644 index 0000000..518c1f5 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/SelectSuperFreeGameLogic.java @@ -0,0 +1,61 @@ +package com.ruoyi.game.sugar.service.sugar; + +import com.ruoyi.common.utils.RandomUtil; +import com.ruoyi.game.sugar.model.cache.SugarGameConfigCache; +import com.ruoyi.game.sugar.service.sugar.strategy.MaxScoreStrategy; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +@Component +@Slf4j +public class SelectSuperFreeGameLogic { + @Autowired + private SelectGameCommonLogic selectGameCommonLogic; + + private static final String EVENT_TAG = "糖果冲刺1000超级免费旋转选择策略"; + public static List strategies = new ArrayList() {{ + add(new MaxScoreStrategy()); + }}; + + public String where(StrategyContext context) { + var userTag = selectGameCommonLogic.getUserTag(context); + var rewardPool = context.getRewardPool(); + BigDecimal totalBet = rewardPool.getTotalBet(); + BigDecimal totalWin = rewardPool.getTotalWin(); + double globalRtp = 0.9; + if (totalBet.compareTo(BigDecimal.ZERO) > 0) { + globalRtp = totalWin.doubleValue() / totalBet.doubleValue(); + } + context.getGameConfigCache().setGlobalRtp(globalRtp); + + var table = selectGameCommonLogic.calcCurrentProbabilityTable(context); + table = table.stream().sorted(Comparator.comparing(SugarGameConfigCache.AwardItem::getLowRewardRound)).toList(); + var indexes = new ArrayList(); + for (int i = 0; i < table.size(); i++) { + indexes.add(i); + } + var weights = table.stream().map(SugarGameConfigCache.AwardItem::getWeight).toList(); + log.info("使用的奖励表: {}", table); + log.info("weights: {}", weights); + var rewardIndex = RandomUtil.choices(indexes, weights, 1).getFirst(); + log.info("rewardIndex: {}", rewardIndex); + var rewardItem = table.get(rewardIndex); + var minScore = rewardItem.getLowRewardRound(); + var maxScore = rewardItem.getHighRewardRound(); + if (minScore == maxScore && rewardIndex < table.size() - 1) { + maxScore = table.get(rewardIndex + 1).getLowRewardRound(); + } + + context.getControlParam().setMinScore(minScore); + context.getControlParam().setMaxScore(maxScore); + new MaxScoreStrategy().calcScore(context); + + return SelectGameCommonLogic.applyParam(log, EVENT_TAG, context); + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/Strategy.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/Strategy.java new file mode 100644 index 0000000..a8a3b53 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/Strategy.java @@ -0,0 +1,5 @@ +package com.ruoyi.game.sugar.service.sugar; + +public interface Strategy { + public void calcScore(StrategyContext context); +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/StrategyContext.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/StrategyContext.java new file mode 100644 index 0000000..f596734 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/StrategyContext.java @@ -0,0 +1,43 @@ +package com.ruoyi.game.sugar.service.sugar; + +import com.ruoyi.domain.entity.GameSugarUser; +import com.ruoyi.game.sugar.model.GameSugarConfig; +import com.ruoyi.game.sugar.model.cache.SugarGameConfigCache; +import com.ruoyi.game.sugar.model.cache.SugarRewardPoolCache; +import com.ruoyi.game.sugar.model.cache.SugarUserGameRecordCache; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class StrategyContext { + private String type; + private BigDecimal bet; + private BigDecimal cost; + private GameSugarUser sugarUser; + private SugarRewardPoolCache rewardPool; + private GameSugarConfig sysConfig; + private SugarUserGameRecordCache recordCache; + private SugarGameConfigCache gameConfigCache; + + private ControlParam controlParam; + + public StrategyContext() { + ControlParam param = new ControlParam(); + param.setMinScore(-1); + param.setMaxScore(-1); + param.setJP(false); + param.setNotJP(false); + param.setRandom(false); + this.controlParam = param; + } + + @Data + public static class ControlParam { + private double minScore; + private double maxScore; + private boolean isRandom; + private boolean isJP; + private boolean notJP; + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/UserTag.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/UserTag.java new file mode 100644 index 0000000..e2670bf --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/UserTag.java @@ -0,0 +1,14 @@ +package com.ruoyi.game.sugar.service.sugar; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public enum UserTag { + NEWBIE("new"), // 新人奖池 + MIDDLE("middle"), // 中等奖池 + OLDIE("old"), // 老人奖池 + + ; + + private String tag; +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/strategy/DynamicRtpStrategy.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/strategy/DynamicRtpStrategy.java new file mode 100644 index 0000000..a867260 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/strategy/DynamicRtpStrategy.java @@ -0,0 +1,102 @@ +package com.ruoyi.game.sugar.service.sugar.strategy; + +import com.ruoyi.common.utils.RandomUtil; +import com.ruoyi.domain.entity.GameSugarUser; +import com.ruoyi.game.sugar.model.GameSugarConfig; +import com.ruoyi.game.sugar.service.sugar.Strategy; +import com.ruoyi.game.sugar.service.sugar.StrategyContext; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Arrays; + +@Slf4j +public class DynamicRtpStrategy implements Strategy { + @Data + private static class RewardConfig { + private String name; + private double weight; + private double value; + + public RewardConfig(String name, double weight, double value) { + this.name = name; + this.weight = weight; + this.value = value; + } + } + + // 为了简单,每种奖的价值就是它的概率 + private static final RewardConfig[] REWARD_CONFIGS = { + new RewardConfig("空奖", 56.76, 25), // score=[0,0] + new RewardConfig("小奖", 15.46, 25), // score=(0,0.5] + new RewardConfig("中奖", 27.11, 25), // score=(0.5,1.5.0] + new RewardConfig("大奖", 0.68, 25), // score=(1.5,+] + }; + + @Override + public void calcScore(StrategyContext context) { + GameSugarUser user = context.getSugarUser(); + BigDecimal totalBet = user.getTotalBet(); + BigDecimal totalWin = user.getTotalWin(); + + GameSugarConfig sugarConfig = context.getSysConfig(); + + // 还未下过注 + if (totalBet.compareTo(BigDecimal.ZERO) <= 0) { + return; + } + + double rtp = totalWin.divide(totalBet, 4, RoundingMode.HALF_UP).doubleValue(); + double targetRtp = sugarConfig.getRtp(); + if (rtp > 0.5) { + return; + } + // 计算当前rtp与目标rtp的差值 +// double error = rtp - targetRtp; +// log.info("DynamicRtpStrategy rtp={},targetRtp={},error={}", rtp, targetRtp, error); +// +// // 误差小于30%,不做处理 +// if (Math.abs(error) < 0.3) { +// return; +// } +// if (error > 1) { +// error = 1; +// } +// +// double safe_error = Math.max(Math.min(error, 1.0), -1.0); +// // 调整的灵敏度系数 +// double learning_factor = 0.01; +// // 如果error是正数,则说明当前rtp高于目标rtp,要减少一些高价值符号的权重 +// // 如果error是负数,则说明当前rtp小于目标rtp,要增加一些高价值符号的权重 +// double minWeight = 0.1; + + RewardConfig[] rewardConfigs = Arrays.copyOf(REWARD_CONFIGS, REWARD_CONFIGS.length); +// for (RewardConfig config : rewardConfigs) { +// double adjustment_factor = -1 * safe_error * config.getWeight() * config.value * learning_factor; +// double newWeight = config.getWeight() + adjustment_factor; +// if (newWeight < minWeight) { +// newWeight = minWeight; +// } +// config.setWeight(newWeight); +// } + +// RewardConfig config = RandomUtil.choices(rewardConfigs, Arrays.stream(rewardConfigs).mapToDouble(RewardConfig::getWeight).toArray(), 1).get(0); +// switch (config.getName()) { +// case "空奖": +// context.setMaxScore(0); +// break; +// case "小奖": +// context.setMaxScore(0.5); +// break; +// case "中奖": +// context.setMinScore(0.5); +// context.setMaxScore(2); +// break; +// case "大奖": +// context.setMinScore(1.5); +// break; +// } + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/strategy/JpStrategy.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/strategy/JpStrategy.java new file mode 100644 index 0000000..67d7877 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/strategy/JpStrategy.java @@ -0,0 +1,43 @@ +package com.ruoyi.game.sugar.service.sugar.strategy; + +import com.ruoyi.common.utils.RandomUtil; +import com.ruoyi.domain.entity.GameSugarUser; +import com.ruoyi.game.sugar.model.GameSugarConfig; +import com.ruoyi.game.sugar.service.sugar.Strategy; +import com.ruoyi.game.sugar.service.sugar.StrategyContext; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class JpStrategy implements Strategy { + @Override + public void calcScore(StrategyContext context) { + GameSugarUser user = context.getSugarUser(); + BigDecimal totalBet = user.getTotalBet(); + BigDecimal totalWin = user.getTotalWin(); + + GameSugarConfig sugarConfig = context.getSysConfig(); + + // 还未下过注 + if (totalBet.compareTo(BigDecimal.ZERO) <= 0) { + return; + } + + int r = RandomUtil.randInt(1, sugarConfig.getJpPercent()[1]); + if (r > sugarConfig.getJpPercent()[0]) { + return; + } + // 命中额外JP,则限制MaxScore +// context.setJP(true); +// double rtp = totalWin.divide(totalBet, 4, RoundingMode.HALF_UP).doubleValue(); +// if (rtp > context.getSysConfig().getRtp()) { +// if (context.getMaxScore() > 5 || context.getMaxScore() < 0) { +// context.setMaxScore(5); +// } +// } else { +// if (context.getMaxScore() > 10 || context.getMaxScore() < 0) { +// context.setMaxScore(10); +// } +// } + } +} diff --git a/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/strategy/MaxScoreStrategy.java b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/strategy/MaxScoreStrategy.java new file mode 100644 index 0000000..dfbad78 --- /dev/null +++ b/skins-service/game-sugar-service/src/main/java/com/ruoyi/game/sugar/service/sugar/strategy/MaxScoreStrategy.java @@ -0,0 +1,34 @@ +package com.ruoyi.game.sugar.service.sugar.strategy; + +import com.ruoyi.game.sugar.model.GameSugarConfig; +import com.ruoyi.game.sugar.service.sugar.Strategy; +import com.ruoyi.game.sugar.service.sugar.StrategyContext; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class MaxScoreStrategy implements Strategy { + @Override + public void calcScore(StrategyContext context) { + var win = context.getRewardPool(); + BigDecimal totalBet = win.getTotalBet(); + BigDecimal totalWin = win.getTotalWin(); + + GameSugarConfig sugarConfig = context.getSysConfig(); + + double minProfit = sugarConfig.getMinProfit(); + + // 假设系统要保留30%的奖励,minProfit=0.3, 那么可供抽奖的奖池就是total*0.7 + // 返回的倍数*下注数一定小于可用奖池,防止奖池抽空 + BigDecimal profitMargin = (totalBet.subtract(totalWin)).multiply(BigDecimal.valueOf(1 - minProfit)); + BigDecimal maxScore = profitMargin.divide(context.getBet(), 2, RoundingMode.HALF_UP); + + if (maxScore.compareTo(BigDecimal.ZERO) < 0) maxScore = BigDecimal.ZERO; + + if (context.getControlParam().getMaxScore() > maxScore.doubleValue() + || context.getControlParam().getMaxScore() < 0) { + context.getControlParam().setMaxScore(maxScore.doubleValue()); + } + + } +} diff --git a/skins-service/game-wheel-service/doc/data.sql b/skins-service/game-wheel-service/doc/data.sql new file mode 100644 index 0000000..4d94406 --- /dev/null +++ b/skins-service/game-wheel-service/doc/data.sql @@ -0,0 +1 @@ +todo \ No newline at end of file diff --git a/skins-service/game-wheel-service/doc/init.sql b/skins-service/game-wheel-service/doc/init.sql new file mode 100644 index 0000000..0ff3458 --- /dev/null +++ b/skins-service/game-wheel-service/doc/init.sql @@ -0,0 +1,60 @@ +CREATE TABLE `game_wheel_round` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '唯一自增id', + `date` varchar(10) NOT NULL COMMENT '所属日期', + `round_id` bigint NOT NULL COMMENT 'round id', + `start_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '开始时间', + `end_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '结束时间', + `phase_times` varchar(128) NOT NULL DEFAULT '' COMMENT '每个阶段的时间点', + `total_bet` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '本期总下注数', + `total_win` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '本期玩家总赢数', + `symbols` varchar(64) NOT NULL DEFAULT '' COMMENT '转盘上的符号', + `result` varchar(64) NOT NULL COMMENT '开出的奖的集合', + `is_manual` int NOT NULL DEFAULT 0 COMMENT '是否后台开奖', + `status` varchar(16) NOT NULL DEFAULT '' COMMENT '状态', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_date_round_id` (`date`, `round_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转盘每期表'; + +CREATE TABLE `game_wheel_round_bet` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '唯一自增id', + `round_id` bigint NOT NULL COMMENT 'round id', + `user_id` bigint NOT NULL DEFAULT 0 COMMENT '用户id', + `bet` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '下注数', + `bet_symbol` varchar(4) NOT NULL DEFAULT '' COMMENT '压注目标', + `win` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '赔付结果', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_round_user_symbol` (`round_id`, `user_id`, `bet_symbol`), + KEY `idx_user_id` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转盘下注表'; + +CREATE TABLE `game_wheel_user` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', + `user_id` bigint NOT NULL COMMENT 'user表的id', + `total_bet` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户总下注额', + `total_win` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '用户总赢得金额', + `count` int NOT NULL DEFAULT '0' COMMENT '总下注次数', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_user_id` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='转盘用户表'; + + +ALTER TABLE sys_config MODIFY COLUMN config_value VARCHAR(1024); + +游戏配置数据: +``` +{ + // 核心配置 + "symbols": [1,2,3,4,5,6,7,8,7,6,5,4,3,2,1], // 转盘格子配置 + "buffer_ratio": 0.9, // 一次旋转,最多可用的奖池比例 + "rtp": 0.85, // 一次旋转最多返回给玩家的总投注比例 + // 下面是体验相关,不影响利润 + "special_effects": [{"name": "系统赢", "ratio": 0.7},{"name": "免费旋转1次", "ratio": 0.2},{"name": "免费旋转2次", "ratio": 0.1}] +} +``` + diff --git a/skins-service/game-wheel-service/pom.xml b/skins-service/game-wheel-service/pom.xml new file mode 100644 index 0000000..c619528 --- /dev/null +++ b/skins-service/game-wheel-service/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + com.ruoyi + skins-service + 4.8.2 + + + + 幸运转盘 + + + game-wheel-service + + + 8 + 8 + UTF-8 + + + + + + com.ruoyi + ruoyi-framework + + + + com.ruoyi + service-admin + 4.8.2 + + + + com.ruoyi + service-user + 4.8.2 + + + + \ No newline at end of file diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/cache/WheelRewardPoolCacheRepository.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/cache/WheelRewardPoolCacheRepository.java new file mode 100644 index 0000000..0151c0f --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/cache/WheelRewardPoolCacheRepository.java @@ -0,0 +1,34 @@ +package com.ruoyi.game.wheel.cache; +import com.ruoyi.game.wheel.model.cache.WheelRewardPoolCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +import static com.ruoyi.admin.config.RedisConstants.WHEEL_GAME_REWARD_POOL_CACHE; +@Component +public class WheelRewardPoolCacheRepository { + @Autowired + public RedisTemplate redisTemplate; + + /** 封盘时将本局总下注全额注入奖池统计 */ + public void incrementalBet(BigDecimal totalBet) { + redisTemplate.opsForValue().increment(WHEEL_GAME_REWARD_POOL_CACHE + ":bet", totalBet.doubleValue()); + } + + /** 开奖后将本局实际总返奖全额记录到奖池统计 */ + public void incrementalWin(BigDecimal totalWin) { + redisTemplate.opsForValue().increment(WHEEL_GAME_REWARD_POOL_CACHE + ":win", totalWin.doubleValue()); + } + + /** 读取奖池当前累计数据 */ + public WheelRewardPoolCache getCache() { + var totalBetS = redisTemplate.opsForValue().get(WHEEL_GAME_REWARD_POOL_CACHE + ":bet"); + var totalWinS = redisTemplate.opsForValue().get(WHEEL_GAME_REWARD_POOL_CACHE + ":win"); + WheelRewardPoolCache cache = new WheelRewardPoolCache(); + cache.setTotalBet(totalBetS == null ? BigDecimal.ZERO : new BigDecimal(totalBetS.toString())); + cache.setTotalWin(totalWinS == null ? BigDecimal.ZERO : new BigDecimal(totalWinS.toString())); + return cache; + } +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/cache/WheelRoundCacheRepository.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/cache/WheelRoundCacheRepository.java new file mode 100644 index 0000000..1de3175 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/cache/WheelRoundCacheRepository.java @@ -0,0 +1,30 @@ +package com.ruoyi.game.wheel.cache; + +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.json.SnakeCaseJsonUtils; +import com.ruoyi.game.wheel.constants.CacheConstants; +import com.ruoyi.game.wheel.model.cache.WheelRoundCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +@Component +public class WheelRoundCacheRepository { + @Autowired + private RedisCache redisCache; + + public WheelRoundCache getCache() { + String s = redisCache.getCacheObject(CacheConstants.WHEEL_ROUND); + return SnakeCaseJsonUtils.fromSnakeCaseJson(s, WheelRoundCache.class); + } + + public void setCache(WheelRoundCache cache) { + if (cache == null) { + redisCache.setCacheObject(CacheConstants.WHEEL_ROUND, null, 1, TimeUnit.DAYS); + } else { + String json = SnakeCaseJsonUtils.toSnakeCaseJson(cache); + redisCache.setCacheObject(CacheConstants.WHEEL_ROUND, json, 1, TimeUnit.DAYS); + } + } +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/constants/CacheConstants.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/constants/CacheConstants.java new file mode 100644 index 0000000..8cab6b3 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/constants/CacheConstants.java @@ -0,0 +1,9 @@ +package com.ruoyi.game.wheel.constants; + +public class CacheConstants { + private static final String PREFIX = "ruoyi:wheel:"; + + public static final String WHEEL_ROUND = PREFIX + "round"; + + public static final String WHEEL_LOCK = PREFIX + "lock"; +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/admin/response/QueryWheelGameConfig.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/admin/response/QueryWheelGameConfig.java new file mode 100644 index 0000000..f71e465 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/admin/response/QueryWheelGameConfig.java @@ -0,0 +1,11 @@ +package com.ruoyi.game.wheel.contract.admin.response; + +import com.ruoyi.game.wheel.domain.GameWheelGameConfig; +import lombok.Data; + +@Data +public class QueryWheelGameConfig { + private GameWheelGameConfig config; + private double totalBet; + private double totalWin; +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/request/DoBetRequest.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/request/DoBetRequest.java new file mode 100644 index 0000000..74a294d --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/request/DoBetRequest.java @@ -0,0 +1,9 @@ +package com.ruoyi.game.wheel.contract.api.request; + +import lombok.Data; + +@Data +public class DoBetRequest { + private Integer bet; + private String symbol; +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/request/GetHistoryBetRequest.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/request/GetHistoryBetRequest.java new file mode 100644 index 0000000..3645dd0 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/request/GetHistoryBetRequest.java @@ -0,0 +1,9 @@ +package com.ruoyi.game.wheel.contract.api.request; + +import lombok.Data; + +@Data +public class GetHistoryBetRequest { + private Integer pageNum; + private Integer pageSize; +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/request/GetRoundHistoryRequest.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/request/GetRoundHistoryRequest.java new file mode 100644 index 0000000..f762b3f --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/request/GetRoundHistoryRequest.java @@ -0,0 +1,9 @@ +package com.ruoyi.game.wheel.contract.api.request; + +import lombok.Data; + +@Data +public class GetRoundHistoryRequest { + private Integer pageNum; + private Integer pageSize; +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/DoBetResponse.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/DoBetResponse.java new file mode 100644 index 0000000..bf32e22 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/DoBetResponse.java @@ -0,0 +1,13 @@ +package com.ruoyi.game.wheel.contract.api.response; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class DoBetResponse { + private Long roundId; + private String cost; + private String balance; + private String credits; +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/GetGameConfigResponse.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/GetGameConfigResponse.java new file mode 100644 index 0000000..66626ff --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/GetGameConfigResponse.java @@ -0,0 +1,20 @@ +package com.ruoyi.game.wheel.contract.api.response; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.List; + +@Data +public class GetGameConfigResponse { + private String symbols; + private List symbolScores; + private List bets; + + @Data + @AllArgsConstructor + public static class SymbolScore { + private String symbol; + private Integer score; + } +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/GetHistoryBetResponse.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/GetHistoryBetResponse.java new file mode 100644 index 0000000..a4c089d --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/GetHistoryBetResponse.java @@ -0,0 +1,34 @@ +package com.ruoyi.game.wheel.contract.api.response; + +import lombok.Data; + +import java.util.List; + +@Data +public class GetHistoryBetResponse { + /** + * 总记录数 + */ + private long total; + + /** + * 列表数据 + */ + private List rows; + + @Data + public static class RoundItem { + private Long roundId; + private String totalBet; + private String endTime; + private List items; + } + + @Data + public static class Item { + private String bet; + // 下注信息 + private String betSymbol; + private String win; + } +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/GetRoundHistoryResponse.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/GetRoundHistoryResponse.java new file mode 100644 index 0000000..9e429fe --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/GetRoundHistoryResponse.java @@ -0,0 +1,26 @@ +package com.ruoyi.game.wheel.contract.api.response; + +import lombok.Data; + +import java.util.List; + +@Data +public class GetRoundHistoryResponse { + + /** + * 总记录数 + */ + private long total; + + /** + * 列表数据 + */ + private List rows; + + @Data + public static class Item { + private Integer roundId; + private List resultSymbols; + private String endTime; + } +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/GetRoundInfoResponse.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/GetRoundInfoResponse.java new file mode 100644 index 0000000..7acb5d6 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/contract/api/response/GetRoundInfoResponse.java @@ -0,0 +1,48 @@ +package com.ruoyi.game.wheel.contract.api.response; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.List; + +@Data +public class GetRoundInfoResponse { + private Long roundId; + private Long serverTime; + // 本局转盘开始时间 + private Long startTime; + // 本句转盘下注结束时间 + private Long bettedTime; + // 开始旋转时间。目前bettedTime=spinTime + private Long spinTime; + // 结束时间 + private Long endTime; + /** + * @see com.ruoyi.game.wheel.enums.WheelRoundStatusEnum + */ + private String status; + private String result; + + // 总返奖 + private String totalWin; + // 每个符号的下注统计 + private List symbolBets; + // 请求用户的下注信息和开奖结果 + private List userResults; + + @Data + @AllArgsConstructor + public static class SymbolBet { + private String symbol; + private String totalBet; + private String userCount; + } + + @Data + @AllArgsConstructor + public static class UserResult { + private String bet; + private String betSymbol; + private String win; + } +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/controller/AdminWheelController.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/controller/AdminWheelController.java new file mode 100644 index 0000000..6d4c321 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/controller/AdminWheelController.java @@ -0,0 +1,33 @@ +package com.ruoyi.game.wheel.controller; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.game.wheel.domain.GameWheelGameConfig; +import com.ruoyi.game.wheel.service.AdminWheelService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +@Api(tags = "幸运转盘") +@Slf4j +@RestController +@RequestMapping("/admin/wheel") +public class AdminWheelController extends BaseController { + + @Autowired + private AdminWheelService adminWheelService; + + @ApiOperation("获取糖果游戏配置") + @GetMapping("/gameconfig") + public AjaxResult getGameConfig() { + return adminWheelService.getGameConfig(); + } + + @ApiOperation("更新糖果游戏配置") + @PostMapping("/gameconfig") + public AjaxResult updateGameConfig(@RequestBody GameWheelGameConfig request) { + return adminWheelService.updateGameConfig(request); + } +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/controller/ApiWheelController.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/controller/ApiWheelController.java new file mode 100644 index 0000000..7184364 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/controller/ApiWheelController.java @@ -0,0 +1,98 @@ +package com.ruoyi.game.wheel.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.game.wheel.contract.api.request.DoBetRequest; +import com.ruoyi.game.wheel.contract.api.request.GetHistoryBetRequest; +import com.ruoyi.game.wheel.contract.api.request.GetRoundHistoryRequest; +import com.ruoyi.game.wheel.contract.api.response.DoBetResponse; +import com.ruoyi.game.wheel.contract.api.response.GetGameConfigResponse; +import com.ruoyi.game.wheel.contract.api.response.GetHistoryBetResponse; +import com.ruoyi.game.wheel.contract.api.response.GetRoundHistoryResponse; +import com.ruoyi.game.wheel.contract.api.response.GetRoundInfoResponse; +import com.ruoyi.game.wheel.service.ApiWheelService; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.user.service.ApiUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "幸运转盘") +@Slf4j +@RestController +@RequestMapping("/api/wheel") +public class ApiWheelController extends BaseController { + + @Autowired + private ApiWheelService apiWheelService; + + @Autowired + private ApiUserService userService; + + @Autowired + private ISysConfigService sysConfigService; + + public Long checkLogin() { + Long userId; + try { + userId = getUserId(); + if (ObjectUtil.isEmpty(userId)) return 0L; + return userId; + } catch (Exception e) { + return 0L; + } + } + + @ApiOperation("获取游戏配置") + @GetMapping("/game_config") + public R getGameConfig() { + return apiWheelService.getGameConfig(); + } + + @ApiOperation("投注") + @GetMapping("/dobet") + @UpdateUserCache + public R doBet(DoBetRequest request) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + Long userId = checkLogin(); + if (userId == 0L) return R.fail("登录过期,请重新登录。"); + + var r = userService.checkRecharge(userId.intValue()); + if (R.isError(r)) { + return R.fail(r.getMsg()); + } + return apiWheelService.doBet(request, userId); + } + + @ApiOperation("获取最新一期游戏状态") + @GetMapping("/last_round_info") + public R getLastRoundInfo() { + Long userId = checkLogin(); + if (userId == 0L) return R.fail("登录过期,请重新登录。"); + return apiWheelService.getLastRoundInfo(userId); + } + + @ApiOperation("获取玩家的游戏历史记录") + @GetMapping("/history_bet_info") + public R getHistoryBetInfo(GetHistoryBetRequest request) { + Long userId = checkLogin(); + if (userId == 0L) return R.fail("登录过期,请重新登录。"); + return apiWheelService.getHistoryBetInfo(request, userId); + } + + @ApiOperation("获取转盘游戏历史记录") + @GetMapping("/round_history_bet_info") + public R getRoundHistoryBetInfo(GetRoundHistoryRequest request) { + return apiWheelService.getRoundHistoryInfo(request); + } + +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/GameWheelGameConfig.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/GameWheelGameConfig.java new file mode 100644 index 0000000..6df5ff1 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/GameWheelGameConfig.java @@ -0,0 +1,56 @@ +package com.ruoyi.game.wheel.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.List; + +@Data +public class GameWheelGameConfig { + // 玩法配置 + // 是否关闭游戏 false + private boolean switchOff; + // 可选的下注数 + private List bets; + // 每个符号的赔率设置 + private List symbolScores; + // ABCDEFGFEDCBA 转盘格子配置 + private String symbols; + + // 下注期55分钟 + private int bettingDuration = 10; + // 封盘期5秒 + private int bettedDuration = 0; + // 开奖期15秒。这是最少要求,如果命中免费多次开奖,则时间进行累加 + private int animationDuration = 5; + + // 盈利配置 + // 一次旋转,最多可用的奖池比例 0.9 total_bet * buff_ratio = reward_pool + private double bufferRatio; + // 一次旋转最多返回给玩家的总投注比例 0.85 reward_pool < total_bet * rtp + private double rtp; + + // 下面是体验相关,不影响盈利 + // 命中特殊效果的概率。 + private double specialEffectRatio; + // 每种特殊效果的概率。 + // [{"name": "系统赢", "ratio": 0.7},{"name": "免费旋转1次", "ratio": 0.2},{"name": "免费旋转2次", "ratio": 0.1}] + private List specialEffects; + + + + @Data + @AllArgsConstructor + public static class SpecialEffect { + private String name; + private double weight; + } + + @Data + @AllArgsConstructor + public static class SymbolScore { + private String symbol; + // 符号赔率 + private Integer score; + } +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/GameWheelRound.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/GameWheelRound.java new file mode 100644 index 0000000..26b1938 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/GameWheelRound.java @@ -0,0 +1,44 @@ +package com.ruoyi.game.wheel.domain; + + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ruoyi.game.wheel.domain.handler.WheelRoundPhaseTimesTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "game_wheel_round", autoResultMap = true) +public class GameWheelRound implements Serializable { + + private Long id; + private String date; + private Long roundId; + private LocalDateTime startTime; + private LocalDateTime endTime; + @TableField(typeHandler = WheelRoundPhaseTimesTypeHandler.class) + private List phaseTimes; + private String symbols; + // 下面的字段只做统计用,不参与业务逻辑 + private BigDecimal totalBet; + private BigDecimal totalWin; + // 旋转的结果,包含中间旋转结果,每个结果用`,`分割。如S2,6,9,表示命中额外抽奖两次,6,9,表示命中额外抽奖一次 + private String result; + private Integer isManual; + private String status; + + private LocalDateTime create_time; + private LocalDateTime update_time; +} \ No newline at end of file diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/GameWheelRoundBet.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/GameWheelRoundBet.java new file mode 100644 index 0000000..734fd33 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/GameWheelRoundBet.java @@ -0,0 +1,32 @@ +package com.ruoyi.game.wheel.domain; + + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "game_wheel_round_bet") +public class GameWheelRoundBet implements Serializable { + + private Long id; + private Long roundId; + private Long userId; + private BigDecimal bet; + private String betSymbol; + private BigDecimal win; + + private LocalDateTime createTime; + private LocalDateTime updateTime; +} \ No newline at end of file diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/GameWheelUser.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/GameWheelUser.java new file mode 100644 index 0000000..66bc703 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/GameWheelUser.java @@ -0,0 +1,31 @@ +package com.ruoyi.game.wheel.domain; + + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "game_wheel_user") +public class GameWheelUser implements Serializable { + + private Long id; + private Long userId; + private BigDecimal totalBet; + private BigDecimal totalWin; + private Integer count; + + private LocalDateTime create_time; + private LocalDateTime update_time; +} \ No newline at end of file diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/handler/WheelRoundPhaseTimesTypeHandler.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/handler/WheelRoundPhaseTimesTypeHandler.java new file mode 100644 index 0000000..98f2fa8 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/domain/handler/WheelRoundPhaseTimesTypeHandler.java @@ -0,0 +1,14 @@ +package com.ruoyi.game.wheel.domain.handler; + +import com.ruoyi.common.typehandler.JsonObjectHandler; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; + +import java.util.List; + + +@MappedJdbcTypes(JdbcType.VARCHAR) +@MappedTypes(List.class) +public class WheelRoundPhaseTimesTypeHandler extends JsonObjectHandler> { +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/enums/SpecialEffectEnum.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/enums/SpecialEffectEnum.java new file mode 100644 index 0000000..57a48f4 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/enums/SpecialEffectEnum.java @@ -0,0 +1,25 @@ +package com.ruoyi.game.wheel.enums; + + +public enum SpecialEffectEnum { + SystemWin("系统赢"), + FreeSpin1("免费旋转1次"), + FreeSpin2("免费旋转2次"), + FreeSpin3("免费旋转3次"); + + private String desc; + + SpecialEffectEnum(String desc) { + this.desc = desc; + } + + @Override + public String toString() { + return this.name().toLowerCase(); + } + + + public boolean equals(String other) { + return this.desc.equals(other); + } +} \ No newline at end of file diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/enums/WheelRoundStatusEnum.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/enums/WheelRoundStatusEnum.java new file mode 100644 index 0000000..626a875 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/enums/WheelRoundStatusEnum.java @@ -0,0 +1,14 @@ +package com.ruoyi.game.wheel.enums; + + +public enum WheelRoundStatusEnum { + BETTING, + @Deprecated + BETED, + SPINED; + + @Override + public String toString() { + return this.name().toLowerCase(); + } +} \ No newline at end of file diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/mapper/ApiGameWheelRoundBetMapper.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/mapper/ApiGameWheelRoundBetMapper.java new file mode 100644 index 0000000..de12a84 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/mapper/ApiGameWheelRoundBetMapper.java @@ -0,0 +1,41 @@ +package com.ruoyi.game.wheel.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.game.wheel.domain.GameWheelRoundBet; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +@Mapper +public interface ApiGameWheelRoundBetMapper extends BaseMapper { + @Select("select * from game_wheel_round_bet where round_id = #{roundId}") + List selectByRoundId(@Param("roundId") Long roundId); + + @Select("select * from game_wheel_round_bet where round_id = #{roundId} and bet_symbol = #{symbol}") + List selectByRoundIdAndTarget(@Param("roundId") Long roundId, @Param("symbol") String symbol); + + @Select("select * from game_wheel_round_bet where round_id = #{roundId} and user_id = #{uid} limit 1") + GameWheelRoundBet selectByRoundIdAndUid(@Param("roundId") Long roundId, @Param("uid") Long uid); + + @Select( + "SELECT rbt.round_id " + + "FROM game_wheel_round_bet rbt " + + "LEFT JOIN game_wheel_round rt ON rbt.round_id = rt.round_id " + + "WHERE rbt.user_id = #{userId} AND rt.date = #{date} " + + "GROUP BY rbt.round_id " + + "ORDER BY rbt.round_id DESC " + + "LIMIT #{offset}, #{pageSize}") + List selectHistoryBetRound(@Param("userId") Long userId, @Param("date") String date, @Param("offset") Integer offset, @Param("pageSize") Integer pageSize); + + @Select("" + ) + List selectHistoryBetInfo(@Param("userId") Long userId, @Param("roundIds") List roundIds); +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/mapper/ApiGameWheelRoundMapper.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/mapper/ApiGameWheelRoundMapper.java new file mode 100644 index 0000000..bd505ab --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/mapper/ApiGameWheelRoundMapper.java @@ -0,0 +1,21 @@ +package com.ruoyi.game.wheel.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.game.wheel.domain.GameWheelRound; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + + +@Mapper +public interface ApiGameWheelRoundMapper extends BaseMapper { + @Select("SELECT MAX(round_id) FROM game_wheel_round where `date` = #{today}") + public Long getTodayLastRoundId(@Param("today") String today); + + default GameWheelRound selectByRoundId(@Param("roundId") Long roundId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(GameWheelRound::getRoundId, roundId); + return selectOne(wrapper); + } +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/mapper/ApiGameWheelUserMapper.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/mapper/ApiGameWheelUserMapper.java new file mode 100644 index 0000000..4597de0 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/mapper/ApiGameWheelUserMapper.java @@ -0,0 +1,21 @@ +package com.ruoyi.game.wheel.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.game.wheel.domain.GameWheelUser; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +import java.math.BigDecimal; + +@Mapper +public interface ApiGameWheelUserMapper extends BaseMapper { + @Select("select * from game_wheel_user where user_id = #{userId}") + GameWheelUser getByUserId(@Param("userId") Long userId); + + @Update("update game_wheel_user set total_bet = total_bet + #{totalBet}, total_win = total_win + #{totalWin}, count = count + #{count} where user_id = #{userId}") + int updateIncrement(@Param("totalBet") BigDecimal totalBet, + @Param("totalWin") BigDecimal totalWin, + @Param("count") Integer count, @Param("userId") Long userId); +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/model/cache/WheelRewardPoolCache.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/model/cache/WheelRewardPoolCache.java new file mode 100644 index 0000000..d421bce --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/model/cache/WheelRewardPoolCache.java @@ -0,0 +1,16 @@ +package com.ruoyi.game.wheel.model.cache; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class WheelRewardPoolCache { + /** + * 疯狂转盘奖池缓存 + * totalBet:累计总投注 + * totalWin:累计总返奖 + */ + private BigDecimal totalBet; + private BigDecimal totalWin; +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/model/cache/WheelRoundCache.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/model/cache/WheelRoundCache.java new file mode 100644 index 0000000..be2d9b9 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/model/cache/WheelRoundCache.java @@ -0,0 +1,27 @@ +package com.ruoyi.game.wheel.model.cache; + +import com.ruoyi.game.wheel.domain.GameWheelRound; +import com.ruoyi.game.wheel.domain.GameWheelRoundBet; +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +@Data +public class WheelRoundCache implements Serializable { + private Long lastRoundId; + private GameWheelRound round; + + private Map betMap; + + public static String betKey(Long roundId, Long userId, String symbol) { + return String.format("%s_%s_%s", roundId, userId, symbol.toLowerCase()); + } + + public static String betKey(GameWheelRoundBet bet) { + if (bet == null) { + return ""; + } + return String.format("%s_%s_%s", bet.getRoundId(), bet.getUserId(), bet.getBetSymbol().toLowerCase()); + } +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/service/AdminWheelService.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/service/AdminWheelService.java new file mode 100644 index 0000000..37054fb --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/service/AdminWheelService.java @@ -0,0 +1,51 @@ +package com.ruoyi.game.wheel.service; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.game.wheel.cache.WheelRewardPoolCacheRepository; +import com.ruoyi.game.wheel.contract.admin.response.QueryWheelGameConfig; +import com.ruoyi.game.wheel.domain.GameWheelGameConfig; +import com.ruoyi.game.wheel.model.cache.WheelRewardPoolCache; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@Service +@Slf4j +public class AdminWheelService { + + private static final String EVENT_TAG = "幸运转盘"; + + + @Autowired + private CommonWheelService commonWheelService; + + @Autowired + private WheelRewardPoolCacheRepository wheelRewardPoolCacheRepository; + + @ApiOperation("获取糖果游戏配置") + @GetMapping("/gameconfig") + public AjaxResult getGameConfig() { + GameWheelGameConfig config = commonWheelService.getGameConfig(); + + QueryWheelGameConfig result = new QueryWheelGameConfig(); + result.setConfig(config); + + // 读取奖池 Redis 数据,与极速永恒保持一致 + WheelRewardPoolCache cache = wheelRewardPoolCacheRepository.getCache(); + result.setTotalBet(cache.getTotalBet().doubleValue()); + result.setTotalWin(cache.getTotalWin().doubleValue()); + + return AjaxResult.success(result); + } + + @ApiOperation("更新糖果游戏配置") + @PostMapping("/gameconfig") + public AjaxResult updateGameConfig(@RequestBody GameWheelGameConfig params) { + commonWheelService.updateGameConfig(params); + return AjaxResult.success(); + } +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/service/ApiWheelService.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/service/ApiWheelService.java new file mode 100644 index 0000000..50ceab3 --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/service/ApiWheelService.java @@ -0,0 +1,745 @@ +package com.ruoyi.game.wheel.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.model.UpdateUserAccountBo; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.redis.config.RedisLock; +import com.ruoyi.common.utils.DateTimeUtils; +import com.ruoyi.common.utils.MoneyUtil; +import com.ruoyi.common.utils.RandomUtil; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.game.wheel.cache.WheelRoundCacheRepository; +import com.ruoyi.game.wheel.constants.CacheConstants; +import com.ruoyi.game.wheel.contract.api.request.DoBetRequest; +import com.ruoyi.game.wheel.contract.api.request.GetHistoryBetRequest; +import com.ruoyi.game.wheel.contract.api.request.GetRoundHistoryRequest; +import com.ruoyi.game.wheel.contract.api.response.*; +import com.ruoyi.game.wheel.domain.GameWheelGameConfig; +import com.ruoyi.game.wheel.domain.GameWheelRound; +import com.ruoyi.game.wheel.domain.GameWheelRoundBet; +import com.ruoyi.game.wheel.domain.GameWheelUser; +import com.ruoyi.game.wheel.enums.SpecialEffectEnum; +import com.ruoyi.game.wheel.enums.WheelRoundStatusEnum; +import com.ruoyi.game.wheel.mapper.ApiGameWheelRoundBetMapper; +import com.ruoyi.game.wheel.mapper.ApiGameWheelRoundMapper; +import com.ruoyi.game.wheel.mapper.ApiGameWheelUserMapper; +import com.ruoyi.game.wheel.model.cache.WheelRoundCache; +import com.ruoyi.game.wheel.cache.WheelRewardPoolCacheRepository; +import com.ruoyi.game.wheel.model.cache.WheelRewardPoolCache; +import com.ruoyi.thirdparty.common.service.ApiNoticeService; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.interceptor.TransactionAspectSupport; + +import java.math.BigDecimal; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@Service +@Slf4j +public class ApiWheelService { + + private static final String EVENT_TAG = "幸运转盘"; + private ScheduledExecutorService scheduler; + + private static GameWheelGameConfig gameConfig; + + @Autowired + private RedisLock redisLock; + + @Autowired + private WheelRoundCacheRepository wheelRoundCacheRepository; + + @Autowired + private CommonWheelService commonWheelService; + @Autowired + private ApiGameWheelRoundMapper wheelRoundMapper; + @Autowired + private ApiGameWheelRoundBetMapper wheelRoundBetMapper; + @Autowired + private ApiGameWheelUserMapper wheelUserMapper; + + @Autowired + private TtUserMapper ttUserMapper; + + @Autowired + private TtUserService userService; + + @Autowired + private WheelRewardPoolCacheRepository wheelRewardPoolCacheRepository; + + @Autowired + private ApiNoticeService apiNoticeService; + + + @PostConstruct + public void init() { + // 创建单线程的定时任务执行器 + this.scheduler = Executors.newSingleThreadScheduledExecutor(); + + // 开启一个线程执行开始新一局的逻辑 + AsyncManager.me().run(() -> { + newRound(); + }); + } + + @PreDestroy + public void destroy() { + if (scheduler != null) { + scheduler.shutdown(); + } + } + + private boolean getLock() { + return redisLock.tryLock(CacheConstants.WHEEL_LOCK, 3, 5, TimeUnit.SECONDS); + } + + private void newRound() { + log.info("{} 开始新的转盘游戏", EVENT_TAG); + + if (!getLock()) { + log.info("{} newRound,获取锁失败,{} 秒后重试", EVENT_TAG, gameConfig.getBettingDuration()); + scheduler.schedule(this::newRound, gameConfig.getBettingDuration(), TimeUnit.SECONDS); + return; + } + try { + while (true) { + // 新的一局,检查更新游戏配置 + gameConfig = commonWheelService.getGameConfig(); + if (gameConfig.isSwitchOff()) { + log.debug("{} 游戏已关闭,{} 秒后重新开始", EVENT_TAG, 10); + wheelRoundCacheRepository.setCache(null); + Thread.sleep(Duration.ofSeconds(10)); + continue; + } + + String today = DateTimeUtils.dateTime(); + WheelRoundCache cache = wheelRoundCacheRepository.getCache(); + if (cache == null) { + cache = initCache(today); + } else { + // 检查是否跨天 + if (cache.getRound() != null && !today.equals(cache.getRound().getDate())) { + cache.setLastRoundId(0L); + } + } + + cache.setBetMap(new HashMap<>()); + cache.setLastRoundId(cache.getLastRoundId() + 1); + GameWheelRound round = createGameWheelRound(cache.getLastRoundId(), today); + if (round == null) { + continue; + } + cache.setRound(round); + wheelRoundCacheRepository.setCache(cache); + break; + } + } catch (Exception e) { + log.error("newRound错误", e); + } finally { + // 等待进入封盘期 + scheduler.schedule(this::drawReward, gameConfig.getBettingDuration(), TimeUnit.SECONDS); + redisLock.unlock(CacheConstants.WHEEL_LOCK); + } + + } + + private WheelRoundCache initCache(String today) { + WheelRoundCache cache = new WheelRoundCache(); + Long roundId = wheelRoundMapper.getTodayLastRoundId(today); + if (roundId == null) { + roundId = 0L; + } + cache.setLastRoundId(roundId); + return cache; + } + + private Collection getAllBets(WheelRoundCache cache) { + return cache.getBetMap().values(); + } + + // 进入开奖期 + private void drawReward() { + if (!getLock()) { + log.info("{} drawReward,获取锁失败,{} 秒后重试", EVENT_TAG, gameConfig.getBettedDuration()); + scheduler.schedule(this::drawReward, gameConfig.getBettedDuration(), TimeUnit.SECONDS); + return; + } + + try { + WheelRoundCache cache = wheelRoundCacheRepository.getCache(); + cache.getRound().setStatus(WheelRoundStatusEnum.SPINED.toString()); + + // 开奖 + Collection bets = getAllBets(cache); + BigDecimal totalBet = calculateTotalBet(bets); + cache.getRound().setTotalBet(totalBet); + /*List result = this.doSpin(cache.getRound(), bets, totalBet); + + cache.getRound().setEndTime(LocalDateTime.now().plusSeconds( + (long) gameConfig.getAnimationDuration() * result.size())); + cache.getRound().setResult(String.join(",", result));*/ + // 封盘后将本局总下注全额注入奖池统计 + if (totalBet.compareTo(BigDecimal.ZERO) > 0) { + wheelRewardPoolCacheRepository.incrementalBet(totalBet); + } + List result = this.doSpin(cache.getRound(), bets, totalBet); + + cache.getRound().setEndTime(LocalDateTime.now().plusSeconds( + (long) gameConfig.getAnimationDuration() * result.size())); + cache.getRound().setResult(String.join(",", result)); + + // 进行赔付 + this.processResult(cache, result); + + wheelRoundCacheRepository.setCache(cache); + wheelRoundMapper.updateById(cache.getRound()); + } catch (Exception e) { + log.error("drawReward错误", e); + } finally { + redisLock.unlock(CacheConstants.WHEEL_LOCK); + // 等待一个前端播放动画时间, 开启下一局 + scheduler.schedule(this::newRound, gameConfig.getAnimationDuration(), TimeUnit.SECONDS); + } + } + + private List selectBySymbol(WheelRoundCache cache, String symbol) { + return getAllBets(cache).stream(). + filter(v -> v.getBetSymbol().equals(symbol)).collect(Collectors.toList()); + } + + private void processResult(WheelRoundCache cache, List result) { + GameWheelRound round = cache.getRound(); + + for (String spinResult : result) { + int target = NumberUtils.toInt(spinResult, -1); + // 特殊效果不处理 + if (target < 0) { + continue; + } + + String symbol = String.valueOf(round.getSymbols().charAt(target)); + GameWheelGameConfig.SymbolScore score = gameConfig.getSymbolScores().stream() + .filter(s -> s.getSymbol().equals(symbol)).findFirst().orElse(null); + if (score == null) { + continue; + } + + double s = score.getScore(); + List betsOnTarget = selectBySymbol(cache, symbol); + if (CollectionUtils.isEmpty(betsOnTarget)) { + continue; + } + + for (GameWheelRoundBet bet : betsOnTarget) { + BigDecimal amount = bet.getBet().multiply(BigDecimal.valueOf(s)); + bet.setWin(amount); + } + } + + // 存储 + BigDecimal min = new BigDecimal("0.01"); + BigDecimal totalWin = BigDecimal.ZERO; + for (GameWheelRoundBet bet : getAllBets(cache)) { + BigDecimal amount = bet.getWin(); + if (amount.compareTo(BigDecimal.ZERO) > 0) { + totalWin = totalWin.add(amount); + userService.updateUserAccount(bet.getUserId().intValue(), amount, + TtAccountRecordSource.LUCKLY_WHEEL_REWARD); + } else { + bet.setWin(min); + userService.updateUserAccount(bet.getUserId().intValue(), min, + TtAccountRecordSource.LUCKLY_WHEEL_REWARD); + } + wheelRoundBetMapper.insert(bet); + wheelUserMapper.updateIncrement(BigDecimal.ZERO, amount, 0, bet.getUserId()); + } + round.setTotalWin(totalWin); + // 将本局实际总返奖写入奖池统计 + if (totalWin.compareTo(BigDecimal.ZERO) > 0) { + wheelRewardPoolCacheRepository.incrementalWin(totalWin); + } + + // ===== 新增:疯狂转盘结果通知(过滤机器人用户)===== + AsyncManager.me().run(() -> sendWheelResultNotice(cache)); + // ===== 新增疯狂转盘结果通知结束 ===== + } + + /** + * 疯狂转盘结果通知(过滤机器人用户) + * 示例:张三 疯狂转盘本局结果:投入 50 元,获得回报 120 元。 + */ + private void sendWheelResultNotice(WheelRoundCache cache) { + try { + Collection allBets = getAllBets(cache); + if (allBets.isEmpty()) return; + + // 获取所有下注用户ID,过滤机器人 + List betUserIds = allBets.stream() + .map(GameWheelRoundBet::getUserId) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + List realUsers = new LambdaQueryChainWrapper<>(ttUserMapper) + .in(TtUser::getUserId, betUserIds) + .ne(TtUser::getUserType, UserType.ROBOT_USER.getCode()) + .list(); + + // 合并同一用户的多次下注(bet 和 win 分别累加) + Map betByUser = new HashMap<>(); + for (GameWheelRoundBet bet : allBets) { + if (bet.getUserId() == null) continue; + betByUser.merge(bet.getUserId(), bet, (a, b) -> { + a.setBet(a.getBet().add(b.getBet())); + a.setWin(a.getWin().add(b.getWin())); + return a; + }); + } + + for (TtUser realUser : realUsers) { + Long userId = Long.valueOf(realUser.getUserId()); + GameWheelRoundBet userBet = betByUser.get(userId); + if (userBet == null) continue; + BigDecimal betAmount = userBet.getBet() != null ? userBet.getBet() : BigDecimal.ZERO; + BigDecimal winAmount = userBet.getWin() != null ? userBet.getWin() : BigDecimal.ZERO; + String content = realUser.getNickName() + " 疯狂转盘本局结果:投入 " + betAmount + " 元,获得回报 " + winAmount + " 元。"; + apiNoticeService.sendNotice(userId, "疯狂转盘结果通知", content); + } + } catch (Exception e) { + log.error("疯狂转盘通知推送失败,error={}", e.getMessage(), e); + } + } + + private BigDecimal calculateTotalBet(Collection bets) { + return bets.stream() + .map(GameWheelRoundBet::getBet) + .reduce(BigDecimal.ZERO, BigDecimal::add); + } + + private BigDecimal payoutIf(Collection bets, String symbol) { + BigDecimal totalBet = bets.stream().filter(bet -> Objects.equals(bet.getBetSymbol(), symbol)) + .map(GameWheelRoundBet::getBet) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + return gameConfig.getSymbolScores().stream() + .filter(s -> s.getSymbol().equals(String.valueOf(symbol))).map(GameWheelGameConfig.SymbolScore::getScore) + .map(score -> totalBet.multiply(BigDecimal.valueOf(score))) + .findFirst().orElse(BigDecimal.valueOf(1_0000_0000)); + } + + private Integer getRandomScotterIndex(String symbol) { + List indices = new ArrayList<>(); + for (int i = 0; i < symbol.length(); i++) { + if (symbol.charAt(i) != 'S') indices.add(i); + } + return RandomUtil.choice(indices); + } + + private List doSpin(GameWheelRound round, Collection bets, BigDecimal totalBet) { + List result = new ArrayList<>(); + + // 无人下注,踢出特殊符号,随机开奖 + if (totalBet.compareTo(BigDecimal.ZERO) == 0) { + List validTargets = IntStream.range(0, round.getSymbols().length()) + .filter(target -> round.getSymbols().charAt(target) != 'S') + .boxed() + .collect(Collectors.toList()); + + int target = RandomUtil.choice(validTargets); + return Collections.singletonList(String.valueOf(target)); + } + + // 计算每个选区的净利润 + List> candidates = new ArrayList<>(); + for (int num = 0; num < gameConfig.getSymbols().length(); num++) { + char symbol = gameConfig.getSymbols().charAt(num); + // 特殊符号不计算赔付 + if (symbol == 'S') { + continue; + } + BigDecimal payout = payoutIf(bets, String.valueOf(symbol)); + BigDecimal profit = totalBet.subtract(payout); + candidates.add(Pair.of(num, profit)); + } + + // 判断是否能命中特殊效果 + String specialEffectName = ""; + if (RandomUtil.random() < gameConfig.getSpecialEffectRatio()) { + // 命中特殊效果 + double[] weights = gameConfig.getSpecialEffects().stream().mapToDouble(GameWheelGameConfig.SpecialEffect::getWeight).toArray(); + GameWheelGameConfig.SpecialEffect specialEffect = RandomUtil.choices(gameConfig.getSpecialEffects(), weights, 1).get(0); + specialEffectName = specialEffect.getName(); + } + + if (SpecialEffectEnum.SystemWin.equals(specialEffectName)) { + Integer index = getRandomScotterIndex(round.getSymbols()); + result.add(index + ":S0"); + return result; + } + int spinCount = 1; + if (SpecialEffectEnum.FreeSpin1.equals(specialEffectName)) { + Integer index = getRandomScotterIndex(round.getSymbols()); + result.add(index + ":S1"); + spinCount = 1; + } + if (SpecialEffectEnum.FreeSpin2.equals(specialEffectName)) { + Integer index = getRandomScotterIndex(round.getSymbols()); + result.add(index + ":S2"); + spinCount = 2; + } + if (SpecialEffectEnum.FreeSpin3.equals(specialEffectName)) { + Integer index = getRandomScotterIndex(round.getSymbols()); + result.add(index + ":S3"); + spinCount = 3; + } + + /*for (int i = 0; i < spinCount; i++) { + List> profitableCandidates = candidates.stream() + .filter(v -> v.getRight().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList()); + if (profitableCandidates.isEmpty()) { + // 没有盈利的选区, 选择一个亏本最小的开 + Pair minLoss = candidates.stream() + .min(Comparator.comparing(Pair::getRight)).orElse(null); + assert minLoss != null; + result.add(minLoss.getLeft() + ""); + } + + // 有盈利的选区, 随机选择一个开 + result.add(RandomUtil.choice(profitableCandidates).getLeft() + ""); + }*/ + /*for (int i = 0; i < spinCount; i++) { + // 筛选出赔付率 < 0.8 的格子(即赔付 < 总下注 * 0.8,对系统盈利) + BigDecimal rtpCap = totalBet.multiply(BigDecimal.valueOf(0.8));//BigDecimal.valueOf(gameConfig.getRtp())系统配置0.85暂时不改 + List> rtpSafeCandidates = candidates.stream() + .filter(v -> { + // 计算赔付金额 + BigDecimal payout = totalBet.subtract(v.getRight()); + // 判断赔付金额是否小于 rtpCap(rpt上限 总下注 * 0.8) + return payout.compareTo(rtpCap) < 0; + }) + .collect(Collectors.toList()); + + if (!rtpSafeCandidates.isEmpty()) { + // 有多个赔率 < 0.8 的格子,随机选一个(不一定选赔付最小的) + result.add(RandomUtil.choice(rtpSafeCandidates).getLeft() + ""); + } else { + // 所有格子赔付都 >= 0.8,选赔付最小的(亏损最少) + Pair minLoss = candidates.stream() + .max(Comparator.comparing(Pair::getRight)).orElse(null); + assert minLoss != null; + result.add(minLoss.getLeft() + ""); + } + }*/ + // ===== 奖池 RTP 逻辑===== + // 读取全局奖池累计数据 + WheelRewardPoolCache poolCache = wheelRewardPoolCacheRepository.getCache(); + BigDecimal poolTotalBet = poolCache.getTotalBet(); + BigDecimal poolTotalWin = poolCache.getTotalWin(); + + // 奖池余额 = 累计总投注 - 累计总返奖 + BigDecimal jackpotBalance = poolTotalBet.subtract(poolTotalWin); + if (jackpotBalance.compareTo(BigDecimal.ZERO) < 0) { + jackpotBalance = BigDecimal.ZERO; + } + + // 本局赔付上限 = 奖池余额 * 0.8 + BigDecimal payoutCap = jackpotBalance.multiply(BigDecimal.valueOf(0.8)); + + for (int i = 0; i < spinCount; i++) { + // 筛选赔付 <= 上限 + List> safeCandidates = candidates.stream() + .filter(v -> { + // v.getRight() 是 profit = totalBet - payout,所以 payout = totalBet - profit + BigDecimal payout = totalBet.subtract(v.getRight()); + return payout.compareTo(payoutCap) <= 0; + }) + .collect(Collectors.toList()); + + if (!safeCandidates.isEmpty()) { + // 有满足条件的格子,随机选一个 + result.add(RandomUtil.choice(safeCandidates).getLeft() + ""); + } else { + // 全部格子赔付都超过 payoutCap,选赔率最小的(profit 最大 = 系统亏损最少) + Pair minPayout = candidates.stream() + .max(Comparator.comparing(Pair::getRight)).orElse(null); + assert minPayout != null; + result.add(minPayout.getLeft() + ""); + } + } + return result; + + } + + private GameWheelRound createGameWheelRound(long roundId, String today) { + + GameWheelRound round = new GameWheelRound(); + round.setRoundId(roundId); + round.setDate(today); + round.setStartTime(LocalDateTime.now()); + round.setPhaseTimes(Arrays.asList( + DateTimeUtils.now() + gameConfig.getBettingDuration(), // 开始封盘 + DateTimeUtils.now() + gameConfig.getBettingDuration() + gameConfig.getBettedDuration()) // 开始开奖 + ); + round.setEndTime(LocalDateTime.now().plusSeconds(gameConfig.getBettingDuration() + + gameConfig.getBettedDuration() + + gameConfig.getAnimationDuration() + )); + round.setSymbols(gameConfig.getSymbols()); + round.setTotalBet(BigDecimal.ZERO); + round.setTotalWin(BigDecimal.ZERO); + round.setResult(""); + round.setIsManual(0); + round.setStatus(WheelRoundStatusEnum.BETTING.toString()); + + try { + wheelRoundMapper.insert(round); + } catch (Exception e) { + log.error("{} 创建新局失败", EVENT_TAG, e); + wheelRoundCacheRepository.setCache(null); + return null; + } + + + return round; + } + + public R getGameConfig() { + GameWheelGameConfig config = commonWheelService.getGameConfig(); + GetGameConfigResponse resp = new GetGameConfigResponse(); + resp.setSymbols(config.getSymbols()); + resp.setBets(config.getBets()); + resp.setSymbolScores(config.getSymbolScores().stream() + .map(s -> new GetGameConfigResponse.SymbolScore(s.getSymbol(), s.getScore())) + .collect(Collectors.toList())); + return R.ok(resp); + } + + // 实时更新ttUser|流水和wheelUser信息 + public R doBet(DoBetRequest request, Long uid) { + if (request.getBet() == null || request.getSymbol() == null) { + return R.fail("参数错误"); + } + if (!gameConfig.getBets().contains(request.getBet())) { + return R.fail("下注金额错误"); + } + if (!gameConfig.getSymbols().contains(request.getSymbol())) { + return R.fail("下注符号错误"); + } + + WheelRoundCache cache = wheelRoundCacheRepository.getCache(); + if (cache == null || cache.getRound() == null) { + return R.fail("游戏未开始"); + } + if (!cache.getRound().getStatus().equals(WheelRoundStatusEnum.BETTING.toString())) { + return R.fail("游戏不在投注期"); + } + + Boolean lock = getLock(); + if (!Boolean.TRUE.equals(lock)) { + return R.fail("请求频繁,加锁失败"); + } + try { + R updateResult = userService.updateUserAccount(uid.intValue(), + BigDecimal.valueOf(request.getBet()).negate(), + TtAccountRecordSource.LUCKLY_WHEEL_SPIN); + if (R.isError(updateResult)) { + return R.fail(updateResult.getMsg()); + } + + GameWheelRoundBet bet = cache.getBetMap().get(WheelRoundCache.betKey(cache.getRound().getRoundId(), + uid, request.getSymbol())); + if (bet != null) { + // 追加下注 + bet.setBet(bet.getBet().add(BigDecimal.valueOf(request.getBet()))); + } else { + bet = new GameWheelRoundBet(); + bet.setRoundId(cache.getRound().getRoundId()); + bet.setUserId(uid); + bet.setBet(BigDecimal.valueOf(request.getBet())); + bet.setBetSymbol(request.getSymbol()); + bet.setWin(BigDecimal.ZERO); + } + cache.getBetMap().put(WheelRoundCache.betKey(bet), bet); + wheelRoundCacheRepository.setCache(cache); + + if (wheelUserMapper.getByUserId(uid) == null) { + initWheelUser(uid); + } + wheelUserMapper.updateIncrement(bet.getBet(), BigDecimal.ZERO, 1, uid); + + return R.ok(new DoBetResponse(cache.getRound().getRoundId(), request.getBet().toString(), + MoneyUtil.toStr(updateResult.getData().getAccountAmount()), + MoneyUtil.toStr(updateResult.getData().getAccountCredits())) + ); + } catch (Exception e) { + TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); + log.error("错误", e); + return R.fail("内部错误,请联系管理员"); + } finally { + redisLock.unlock(CacheConstants.WHEEL_LOCK); + } + } + + private void initWheelUser(Long uid) { + GameWheelUser user = new GameWheelUser(); + user.setUserId(uid); + user.setTotalBet(BigDecimal.ZERO); + user.setTotalWin(BigDecimal.ZERO); + user.setCount(0); + wheelUserMapper.insert(user); + } + + public R getLastRoundInfo(Long uid) { + WheelRoundCache cache = wheelRoundCacheRepository.getCache(); + if (cache == null || cache.getRound() == null) { + return R.fail("游戏未开始"); + } + return R.ok(buildRoundResp(cache, cache.getRound(), uid)); + } + + private GetRoundInfoResponse buildRoundResp(WheelRoundCache cache, GameWheelRound round, Long uid) { + GetRoundInfoResponse resp = new GetRoundInfoResponse(); + resp.setRoundId(round.getRoundId()); + resp.setServerTime(DateTimeUtils.now()); + resp.setStartTime(DateTimeUtils.toSeconds(round.getStartTime())); + resp.setEndTime(DateTimeUtils.toSeconds(round.getEndTime())); + resp.setBettedTime(round.getPhaseTimes().get(0)); + resp.setSpinTime(round.getPhaseTimes().get(1)); + resp.setStatus(round.getStatus()); + resp.setResult(round.getResult()); + resp.setTotalWin(MoneyUtil.toStr(round.getTotalWin())); + + // 统计每个符号的下注数据 + Map> symbolMap = cache.getBetMap().values().stream().collect(Collectors.groupingBy(GameWheelRoundBet::getBetSymbol)); + List symbolBets = new ArrayList<>(); + symbolMap.forEach((symbol, bets) -> { + String totalBet = MoneyUtil.toStr(bets.stream().map(GameWheelRoundBet::getBet).reduce(BigDecimal.ZERO, BigDecimal::add)); + symbolBets.add(new GetRoundInfoResponse.SymbolBet(symbol, totalBet, String.valueOf(bets.size()))); + }); + resp.setSymbolBets(symbolBets); + + List bets = cache.getBetMap().values().stream().filter(v -> v.getUserId().equals(uid)) + .map(bet -> { + return new GetRoundInfoResponse.UserResult( + MoneyUtil.toStr(bet.getBet()), + bet.getBetSymbol(), + MoneyUtil.toStr(bet.getWin())); + }) + .collect(Collectors.toList()); + resp.setUserResults(bets); + return resp; + } + + public R getHistoryBetInfo(GetHistoryBetRequest request, Long userId) { + if (request.getPageNum() == null || request.getPageNum() <= 0) { + request.setPageNum(1); + } + if (request.getPageSize() == null || request.getPageSize() <= 0) { + request.setPageSize(10); + } + List roundIds = wheelRoundBetMapper.selectHistoryBetRound(userId, DateTimeUtils.dateTime(), (request.getPageNum() - 1) * request.getPageSize(), request.getPageSize()); + + if (CollectionUtils.isEmpty(roundIds)) { + GetHistoryBetResponse resp = new GetHistoryBetResponse(); + resp.setRows(new ArrayList<>()); + resp.setTotal(0); + return R.ok(resp); + } + + List rounds = wheelRoundBetMapper.selectHistoryBetInfo(userId, roundIds); + Map> roundMap = rounds.stream().collect(Collectors.groupingBy(GameWheelRoundBet::getRoundId)); + + GetHistoryBetResponse resp = new GetHistoryBetResponse(); + resp.setTotal(rounds.size() + 1); + resp.setRows(new ArrayList<>()); + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd HH:mm:ss"); + for (Integer roundId : roundIds) { + GetHistoryBetResponse.RoundItem item = new GetHistoryBetResponse.RoundItem(); + item.setRoundId(roundId.longValue()); + item.setEndTime(roundMap.get(roundId.longValue()).get(0).getUpdateTime().format(formatter)); + + List betitems = new ArrayList<>(); + BigDecimal totalBet = BigDecimal.ZERO; + for (GameWheelRoundBet betItem : roundMap.get(roundId.longValue())) { + GetHistoryBetResponse.Item i = new GetHistoryBetResponse.Item(); + i.setBet(MoneyUtil.toStr(betItem.getBet())); + totalBet = totalBet.add(betItem.getBet()); + i.setBetSymbol(betItem.getBetSymbol()); + i.setWin(MoneyUtil.toStr(betItem.getWin())); + betitems.add(i); + } + item.setTotalBet(MoneyUtil.toStr(totalBet)); + item.setItems(betitems); + resp.getRows().add(item); + } + + return R.ok(resp); + } + + public R getRoundHistoryInfo(GetRoundHistoryRequest request) { + if (request.getPageNum() == null || request.getPageNum() <= 0) { + request.setPageNum(1); + } + if (request.getPageSize() == null || request.getPageSize() <= 0) { + request.setPageSize(10); + } + + Page page = new Page<>(request.getPageNum(), request.getPageSize()); + page.setOptimizeCountSql(false); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + // 只获取今天的游戏回合数据 + LocalDate today = LocalDate.now(); + LocalDateTime startOfDay = today.atStartOfDay(); + LocalDateTime endOfDay = today.plusDays(1).atStartOfDay().minusSeconds(1); + wrapper.between(GameWheelRound::getStartTime, startOfDay, endOfDay); + wrapper.orderByDesc(GameWheelRound::getStartTime); + IPage listPage = wheelRoundMapper.selectPage(page, wrapper); + List userList = listPage.getRecords(); + + + GetRoundHistoryResponse response = new GetRoundHistoryResponse(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd HH:mm:ss"); + response.setRows(userList.stream().map(v -> { + GetRoundHistoryResponse.Item item = new GetRoundHistoryResponse.Item(); + item.setRoundId(v.getRoundId().intValue()); + item.setEndTime(v.getEndTime().format(formatter)); + item.setResultSymbols(new ArrayList<>()); + if (StringUtils.isNotBlank(v.getResult())) { + for (String i : v.getResult().split(",")) { + int index = NumberUtils.toInt(i.split(":")[0], 0); + item.getResultSymbols().add(String.valueOf(v.getSymbols().charAt(index))); + } + } + + return item; + }).collect(Collectors.toList())); + response.setTotal(listPage.getTotal()); + + return R.ok(response); + } +} diff --git a/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/service/CommonWheelService.java b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/service/CommonWheelService.java new file mode 100644 index 0000000..183807f --- /dev/null +++ b/skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/service/CommonWheelService.java @@ -0,0 +1,76 @@ +package com.ruoyi.game.wheel.service; + + +import com.ruoyi.common.utils.json.SnakeCaseJsonUtils; +import com.ruoyi.game.wheel.domain.GameWheelGameConfig; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.mapper.SysConfigMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Arrays; + +@Service +@Slf4j +public class CommonWheelService { + @Autowired + private SysConfigMapper sysConfigMapper; + + public GameWheelGameConfig getGameConfig() { + SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("game.wheel.setting"); + if (sysConfig == null) { + GameWheelGameConfig config = new GameWheelGameConfig(); + config.setSwitchOff(false); + config.setBets(Arrays.asList(5, 10, 20, 50, 100, 200, 500)); + config.setSymbolScores(Arrays.asList( + new GameWheelGameConfig.SymbolScore("S", 0), + new GameWheelGameConfig.SymbolScore("A", 10), + new GameWheelGameConfig.SymbolScore("B", 10), + new GameWheelGameConfig.SymbolScore("C", 10), + new GameWheelGameConfig.SymbolScore("D", 10), + new GameWheelGameConfig.SymbolScore("E", 10), + new GameWheelGameConfig.SymbolScore("F", 10), + new GameWheelGameConfig.SymbolScore("G", 10) + )); + config.setSymbols("SSAABBCCDDEEFFGG"); + config.setBufferRatio(0.9); + config.setRtp(0.85); + + config.setSpecialEffectRatio(0.05); + config.setSpecialEffects(Arrays.asList( + new GameWheelGameConfig.SpecialEffect("系统赢", 0.5), + new GameWheelGameConfig.SpecialEffect("免费旋转1次", 0.25), + new GameWheelGameConfig.SpecialEffect("免费旋转2次", 0.15), + new GameWheelGameConfig.SpecialEffect("免费旋转3次", 0.10) + )); + + + sysConfig = new SysConfig(); + sysConfig.setConfigName("幸运转盘游戏配置"); + sysConfig.setConfigKey("game.wheel.setting"); + sysConfig.setConfigValue(SnakeCaseJsonUtils.toSnakeCaseJson(config)); + sysConfig.setConfigType("N"); + + sysConfigMapper.insertConfig(sysConfig); + return config; + } + return SnakeCaseJsonUtils.fromSnakeCaseJson(sysConfig.getConfigValue(), GameWheelGameConfig.class); + } + + public void updateGameConfig(GameWheelGameConfig config) { + SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("game.wheel.setting"); + if (sysConfig == null) { + sysConfig = new SysConfig(); + sysConfig.setConfigName("幸运转盘游戏配置"); + sysConfig.setConfigKey("game.wheel.setting"); + sysConfig.setConfigType("N"); + sysConfig.setConfigValue(SnakeCaseJsonUtils.toSnakeCaseJson(config)); + sysConfigMapper.insertConfig(sysConfig); + } else { + sysConfig.setConfigValue(SnakeCaseJsonUtils.toSnakeCaseJson(config)); + sysConfigMapper.updateConfig(sysConfig); + } + + } +} diff --git a/skins-service/pom.xml b/skins-service/pom.xml new file mode 100644 index 0000000..8b5be74 --- /dev/null +++ b/skins-service/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + com.ruoyi + ruoyi + 4.8.2 + + + skins-service + pom + + service-user + service-thirdparty + service-admin + service-playingmethod + service-task + game-sugar-service + game-wheel-service + + + + 8 + 8 + UTF-8 + + + + + + com.ruoyi + ruoyi-common + + + + com.ruoyi + ruoyi-system + + + + com.ruoyi + skins-model + ${ruoyi.version} + + + + + io.springfox + springfox-boot-starter + ${swagger.version} + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/pom.xml b/skins-service/service-admin/pom.xml new file mode 100644 index 0000000..6097adc --- /dev/null +++ b/skins-service/service-admin/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + com.ruoyi + skins-service + 4.8.2 + + + service-admin + + + 8 + 8 + UTF-8 + + + + + com.ruoyi + ruoyi-quartz + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/cahe/BoxMockCacheRepository.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/cahe/BoxMockCacheRepository.java new file mode 100644 index 0000000..ab9feab --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/cahe/BoxMockCacheRepository.java @@ -0,0 +1,26 @@ +package com.ruoyi.admin.cahe; + +import com.ruoyi.admin.model.BoxMockCache; +import com.ruoyi.common.core.redis.RedisCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +@Component +public class BoxMockCacheRepository { + + private static final String BOX_MOCK_CACHE_KEY = "ruoyi:box:mock:"; + + @Autowired + private RedisCache redisCache; + + //将用户的临时爆率存储在redis中,存储七天 + public void setMockResultCache(Integer uid, BoxMockCache value) { + redisCache.setCacheObject(BOX_MOCK_CACHE_KEY + uid, value, 7, TimeUnit.DAYS); + } + //根据用户的id,从redis中取出用户的临时爆率 + public BoxMockCache getMockResultCache(Integer uid) { + return redisCache.getCacheObject(BOX_MOCK_CACHE_KEY + uid); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/config/RedisConstants.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/config/RedisConstants.java new file mode 100644 index 0000000..89ba14f --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/config/RedisConstants.java @@ -0,0 +1,51 @@ +package com.ruoyi.admin.config; + +public class RedisConstants { + // 整个应用的通用uid锁 + public static final String COMMON_LOCK = "ruoyi:common:lock_"; + public static final String SUGAR_GAME_CACHE = "ruoyi:sugar:"; + public static final String SUGAR_GAME_MOCK_QUEUE = "ruoyi:sugar:mock:"; + public static final String SUGAR_GAME_USER_RECORD_CACHE = "ruoyi:sugar:user:record:"; + public static final String SUGAR_GAME_CONFIG_CACHE = "ruoyi:sugar:config"; + public static final String SUGAR_GAME_REWARD_POOL_CACHE = "ruoyi:sugar:reward:pool"; + public static final String WHEEL_GAME_REWARD_POOL_CACHE = "ruoyi:wheel:reward:pool"; + + + /** + * 开箱奖池 + * 奖池key: baseKey:boxId:poolType + * + * @see com.ruoyi.admin.enums.BoxPoolType + */ + public static final String BASE_POOL_KEY = "prize_pool:"; + + // 对战模式 + public static final String JOIN_FIGHT_LOCK = "join_fight:lock_"; + public static final String JOIN_FIGHT_BEGIN_LOCK = "join_fight:lock_begin"; + public static final String JOIN_FIGHT_SEAT_READY_LOCK = "join_fight:lock_seat_ready_"; + public static final String JOIN_FIGHT_END_LOCK = "join_fight:lock_end"; + + + // roll房间 + public static final String JOIN_ROLL_LOCK = "join_roll:lock_"; + + + public static final String RECEIVE_RED_PACKET_LOCK = "receive_red_packet:lock_"; + public static final String CARD_PAY_LOCK = "CARD_PAY:lock_"; + + + // 幸运升级 + public static final String UPGRADE_RANGE = "upgrade_range:"; // 概率区间 '业务key:饰品id:用户类型' + public static final String UPGRADE_RANGE_FIXED = "upgrade_range_fixed:"; // 固定概率 '业务key:饰品id:用户类型' + + + /** + * 抽奖box的奖品空间 + * open_box_goods_apace: + * box_id: + * odds_key: + * valua + */ + public static final String OPEN_BOX_GOODS_SPACE = "open_box_goods_apace:"; + public static final String OPEN_BOX_LOTTERY = "open_box_lottery:"; +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/ShoppingController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/ShoppingController.java new file mode 100644 index 0000000..8f3d70a --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/ShoppingController.java @@ -0,0 +1,48 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.admin.service.ShoppingService; +import com.ruoyi.domain.other.ShoppingBody; +import com.ruoyi.domain.vo.ShoppingDataVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +@Api(tags = "管理端 商城管理") +@RestController +@RequestMapping("/admin/shopping") +public class ShoppingController extends BaseController { + + private final ShoppingService shoppingService; + private final TtOrnamentService ornamentsService; + + public ShoppingController(ShoppingService shoppingService, + TtOrnamentService ornamentsService) { + this.shoppingService = shoppingService; + this.ornamentsService = ornamentsService; + } + + @ApiOperation("商品列表") + @GetMapping("/list") + public PageDataInfo list(ShoppingBody shoppingBody) { + startPage(); + return shoppingService.list(shoppingBody); + } + + @PostMapping("/batchPutAwayOrSoldOut/{status}") + public R batchPutAwayOrSoldOut(@RequestBody List ornamentsList, + @PathVariable("status") String status) { + if (StringUtils.isNull(ornamentsList) || ornamentsList.isEmpty()) return R.fail(); + ornamentsList = ornamentsList.stream().peek(ttOrnaments -> ttOrnaments.setIsPutaway(status)).collect(Collectors.toList()); + if (ornamentsService.updateBatchById(ornamentsList, 1)) return R.ok(true); + return R.fail(false); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtAdvertisementController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtAdvertisementController.java new file mode 100644 index 0000000..1e6c457 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtAdvertisementController.java @@ -0,0 +1,58 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtAdvertisementService; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.other.TtAdvertisement; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/admin/advertisement") +public class TtAdvertisementController extends BaseController { + + private final TtAdvertisementService ttAdvertisementService; + + public TtAdvertisementController(TtAdvertisementService ttAdvertisementService) { + this.ttAdvertisementService = ttAdvertisementService; + } + + @GetMapping("/list") + public PageDataInfo list() { + startPage(); + List list = ttAdvertisementService.list(); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtAdvertisement ttAdvertisement = ttAdvertisementService.getById(id); + // ttBanner.setPicture(""); + return R.ok(ttAdvertisement); + } + + @PostMapping + public AjaxResult add(@RequestBody TtAdvertisement ttAdvertisement) { + if (StringUtils.isEmpty(ttAdvertisement.getPicture())) ttAdvertisement.setPicture(""); + else ttAdvertisement.setPicture(RuoYiConfig.getDomainName() + ttAdvertisement.getPicture()); + return toAjax(ttAdvertisementService.save(ttAdvertisement)); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtAdvertisement ttAdvertisement) { + String msg = ttAdvertisementService.updatePicById(ttAdvertisement); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + return toAjax(ttAdvertisementService.removeByIds(Arrays.asList(ids))); + } + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtAnnouncementController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtAnnouncementController.java new file mode 100644 index 0000000..a8d4d65 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtAnnouncementController.java @@ -0,0 +1,71 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtAnnouncementService; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.domain.other.TtAnnouncement; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@RestController +@RequestMapping("/admin/announcement") +public class TtAnnouncementController extends BaseController { + + @Autowired + private TtAnnouncementService ttAnnouncementService; + + /** + * 获取公告列表 + */ + @GetMapping("/list") + public PageDataInfo list() { + startPage(); + List announcementList = ttAnnouncementService.getAnnouncementList(); + return getPageData(announcementList); + } + + /** + * 获取公告详情 + */ + @GetMapping("/{announcementId}") + public AjaxResult getAnnouncementByAnnouncementId(@PathVariable Integer announcementId) { + Long userId = SecurityUtils.getUserId(); + TtAnnouncement ttAnnouncement = ttAnnouncementService.getAnnouncementByAnnouncementId(userId, announcementId); + return AjaxResult.success(ttAnnouncement); + } + + /** + * 新增公告 + */ + @PostMapping + public AjaxResult addAnnouncement(@RequestBody TtAnnouncement ttAnnouncement) { + return ttAnnouncementService.addAnnouncement(ttAnnouncement) > 0 ? AjaxResult.success("新增成功") : AjaxResult.error("新增失败"); + } + + /** + * 修改公告 + */ + @PutMapping + public AjaxResult editAnnouncement(@RequestBody TtAnnouncement ttAnnouncement) { + return ttAnnouncementService.editAnnouncement(ttAnnouncement) > 0 ? AjaxResult.success("修改成功") : AjaxResult.error("修改失败"); + } + + /** + * 删除公告 + */ + @DeleteMapping("/{announcementId}") + public AjaxResult removeAnnouncementByAnnouncementId(@PathVariable Integer announcementId) { + return ttAnnouncementService.removeAnnouncementByAnnouncementId(announcementId) > 0 ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败"); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBannerController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBannerController.java new file mode 100644 index 0000000..787c11c --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBannerController.java @@ -0,0 +1,57 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.other.TtBanner; +import com.ruoyi.admin.service.TtBannerService; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/admin/banner") +public class TtBannerController extends BaseController { + + private final TtBannerService bannerService; + + public TtBannerController(TtBannerService bannerService) { + this.bannerService = bannerService; + } + + @GetMapping("/list") + public PageDataInfo list() { + startPage(); + List list = bannerService.list(); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtBanner ttBanner = bannerService.getById(id); + // ttBanner.setPicture(""); + return R.ok(ttBanner); + } + + @PostMapping + public AjaxResult add(@RequestBody TtBanner ttBanner) { + if (StringUtils.isEmpty(ttBanner.getPicture())) ttBanner.setPicture(""); + else ttBanner.setPicture(RuoYiConfig.getDomainName() + ttBanner.getPicture()); + return toAjax(bannerService.save(ttBanner)); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtBanner ttBanner) { + String msg = bannerService.updateBannerById(ttBanner); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + return toAjax(bannerService.removeByIds(Arrays.asList(ids))); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBonusController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBonusController.java new file mode 100644 index 0000000..e11b91e --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBonusController.java @@ -0,0 +1,67 @@ +package com.ruoyi.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.domain.other.TtBonus; +import com.ruoyi.admin.service.TtBonusService; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/admin/bonus") +public class TtBonusController extends BaseController { + + private final TtBonusService bonusService; + + public TtBonusController(TtBonusService bonusService) { + this.bonusService = bonusService; + } + + @GetMapping("/list") + public PageDataInfo list(String type){ + startPage(); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotEmpty(type)) wrapper.eq(TtBonus::getType, type); + List list = bonusService.list(wrapper); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtBonus bonus = bonusService.getById(id); + bonus.setCoverPicture(""); + return R.ok(bonus); + } + + @PostMapping + public AjaxResult add(@RequestBody TtBonus ttBonus) { + if (StringUtils.isEmpty(ttBonus.getCoverPicture())) ttBonus.setCoverPicture(""); + else ttBonus.setCoverPicture(RuoYiConfig.getDomainName() + ttBonus.getCoverPicture()); + ttBonus.setCreateBy(getUsername()); + ttBonus.setCreateTime(DateUtils.getNowDate()); + return toAjax(bonusService.save(ttBonus)); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtBonus ttBonus) { + ttBonus.setUpdateBy(getUsername()); + ttBonus.setUpdateTime(DateUtils.getNowDate()); + String msg = bonusService.updateBonusById(ttBonus); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + return toAjax(bonusService.removeByIds(Arrays.asList(ids))); + } + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxController.java new file mode 100644 index 0000000..02034eb --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxController.java @@ -0,0 +1,147 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.domain.body.BoxMockRequest; +import com.ruoyi.admin.model.BoxMockCache; +import com.ruoyi.admin.service.BoxMockService; +import com.ruoyi.admin.service.TtBoxService; +import com.ruoyi.admin.util.core.fight.LotteryMachine; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtBoxBody; +import com.ruoyi.domain.vo.BoxCacheDataVO; +import com.ruoyi.domain.vo.TtBoxDataVO; +import com.ruoyi.domain.vo.box.TtBoxUserVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +@Api(tags = "管理端 宝箱管理") +@RestController +@RequestMapping("/admin/box") +public class TtBoxController extends BaseController { + + @Autowired + private TtBoxService boxService; + + @Autowired + private LotteryMachine lotteryMachine; + + @Autowired + private BoxMockService boxMockService; + + @GetMapping("/test") + public AjaxResult test() { + return success(); + } + + @ApiOperation("获取宝箱列表") + @GetMapping("/list") + public PageDataInfo list(TtBoxBody ttBoxBody) { + startPage(); + return boxService.selectTtBoxList(ttBoxBody); + } + + @GetMapping(value = "/{boxId}") + public R getInfo(@PathVariable("boxId") Long boxId) { + TtBox ttBox = boxService.getById(boxId); + return R.ok(ttBox); + } + + @PostMapping + public AjaxResult add(@RequestBody TtBox ttBox) { + ttBox.setCreateBy(getUsername()); + ttBox.setCreateTime(DateUtils.getNowDate()); + return toAjax(boxService.save(ttBox)); + } + + @ApiOperation("修改宝箱") + @PutMapping + public AjaxResult edit(@RequestBody TtBoxDataVO ttBoxDataVO) { + String msg = boxService.updateTtBoxById(ttBoxDataVO); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @ApiOperation("清空当前奖池") + @GetMapping("clearPrizePool/{boxId}") + public R clearPrizePool(@PathVariable("boxId") Integer boxId) { + + lotteryMachine.clearBoxPrizePool(boxId); + + return R.ok("清空当前奖池成功。"); + } + + @DeleteMapping("/{boxIds}") + public AjaxResult remove(@PathVariable Long[] boxIds) { + return toAjax(boxService.removeByIds(Arrays.asList(boxIds))); + } + + @GetMapping("/resetBox/{boxId}") + public AjaxResult resetBox(@PathVariable Integer boxId) { + boxService.isReplenishment(boxId); + return AjaxResult.success(); + } + + @GetMapping("/statisticsBoxData/{boxId}") + public R statisticsBoxData(@PathVariable Integer boxId, @RequestParam(required = false) Date date) { + BoxCacheDataVO boxCacheData = boxService.statisticsBoxData(boxId, date); + return R.ok(boxCacheData); + } + + @ApiOperation("添加宝箱三级爆率玩家范围") + @PostMapping("/createTtBoxThirdExplosiveUsers") + public AjaxResult createTtBoxThirdExplosiveUsers(@RequestBody TtBoxUserVO ttBoxUserVO) { + String msg = boxService.createTtBoxThirdExplosiveUsers(ttBoxUserVO); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @ApiOperation("查询宝箱三级爆率玩家范围") + @GetMapping("/queryTtBoxThirdExplosiveUsers/{boxId}") + public AjaxResult queryBoxUsers(@PathVariable Integer boxId) { + return boxService.queryTtBoxThirdExplosiveUsers(boxId); + } + + @ApiOperation("删除宝箱三级爆率玩家范围") + @PostMapping("/deleteTtBoxThirdExplosiveUsers") + public AjaxResult deleteTtBoxThirdExplosiveUsers(@RequestBody TtBoxUserVO ttBoxUserVO) { + return boxService.deleteTtBoxThirdExplosiveUsers(ttBoxUserVO); + } + + @PostMapping("/export") + public void export(HttpServletResponse response, TtBoxBody ttBoxBody) { + var list = boxService.selectTtBoxList(ttBoxBody); + ExcelUtil util = new ExcelUtil<>(TtBoxDataVO.class); + util.exportExcel(response, list.getRows(), "宝箱列表"); + } + + @ApiOperation("查询开箱临时爆率队列") + @GetMapping("/mockresult/list") + public R> getBoxMockResult(@RequestParam("uid") Integer uid) { + return R.ok(boxMockService.getMockResultList(uid)); + } + + @ApiOperation("设置开箱临时爆率队列") + @PostMapping("/mockresult/list") + public R> updateBoxMockResult(@RequestBody BoxMockRequest request) { + return R.ok(boxMockService.updateMockResultList(request)); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxOpenChanceController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxOpenChanceController.java new file mode 100644 index 0000000..c65cba0 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxOpenChanceController.java @@ -0,0 +1,91 @@ +package com.ruoyi.admin.controller; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.admin.domain.TtBoxOpenChance; +import com.ruoyi.admin.service.TtBoxOpenChanceService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 开箱机会Controller + * + * @author ruoyi + * @date 2024-07-09 + */ +@RestController +@RequestMapping("/admin/chance") +public class TtBoxOpenChanceController extends BaseController +{ + @Autowired + private TtBoxOpenChanceService ttBoxOpenChanceService; + + /** + * 查询开箱机会列表 + */ + @PreAuthorize("@ss.hasPermi('admin:chance:list')") + @GetMapping("/list") + public TableDataInfo list(TtBoxOpenChance ttBoxOpenChance) + { + startPage(); + List list = ttBoxOpenChanceService.selectTtBoxOpenChanceList(ttBoxOpenChance); + return getDataTable(list); + } + + /** + * 获取开箱机会详细信息 + */ + @PreAuthorize("@ss.hasPermi('admin:chance:query')") + @GetMapping(value = "/{ornamentId}") + public AjaxResult getInfo(@PathVariable("ornamentId") Integer ornamentId) + { + return success(ttBoxOpenChanceService.selectTtBoxOpenChanceByOrnamentId(ornamentId)); + } + + /** + * 新增开箱机会 + */ + @PreAuthorize("@ss.hasPermi('admin:chance:add')") + @Log(title = "开箱机会", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody TtBoxOpenChance ttBoxOpenChance) + { + return toAjax(ttBoxOpenChanceService.insertTtBoxOpenChance(ttBoxOpenChance)); + } + + /** + * 修改开箱机会 + */ + @PreAuthorize("@ss.hasPermi('admin:chance:edit')") + @Log(title = "开箱机会", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody TtBoxOpenChance ttBoxOpenChance) + { + return toAjax(ttBoxOpenChanceService.updateTtBoxOpenChance(ttBoxOpenChance)); + } + + /** + * 删除开箱机会 + */ + @PreAuthorize("@ss.hasPermi('admin:chance:remove')") + @Log(title = "开箱机会", businessType = BusinessType.DELETE) + @DeleteMapping("/{ornamentIds}") + public AjaxResult remove(@PathVariable Integer[] ornamentIds) + { + return toAjax(ttBoxOpenChanceService.deleteTtBoxOpenChanceByOrnamentIds(ornamentIds)); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxOrnamentsController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxOrnamentsController.java new file mode 100644 index 0000000..a167875 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxOrnamentsController.java @@ -0,0 +1,107 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.other.TtBoxOrnaments; +import com.ruoyi.admin.service.TtBoxOrnamentsService; +import com.ruoyi.domain.vo.TtBoxOrnamentsDataVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.constraints.NotEmpty; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +@Api(tags = "管理端 宝箱物品") +@RestController +@RequestMapping("/admin/boxOrnaments") +public class TtBoxOrnamentsController extends BaseController { + + private final TtBoxOrnamentsService boxOrnamentsService; + + public TtBoxOrnamentsController(TtBoxOrnamentsService boxOrnamentsService) { + this.boxOrnamentsService = boxOrnamentsService; + } + + @ApiOperation("宝箱物品详情") + @GetMapping("/list/{boxId}") + public PageDataInfo list(@PathVariable("boxId") Integer boxId) { + startPage(); + List list = boxOrnamentsService.selectTtBoxOrnamentsList(boxId); + return getPageData(list); + } + + @ApiOperation("宝箱统计数据") + @GetMapping("/globalData/{boxId}") + public R globalData(@PathVariable("boxId") Integer boxId) { + return boxOrnamentsService.globalData(boxId); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtBoxOrnaments ttBoxOrnaments = boxOrnamentsService.getById(id); + return R.ok(ttBoxOrnaments); + } + + @PostMapping + public AjaxResult add(@RequestBody TtBoxOrnaments ttBoxOrnaments) { + ttBoxOrnaments.setCreateBy(getUsername()); + ttBoxOrnaments.setCreateTime(DateUtils.getNowDate()); + String msg = boxOrnamentsService.saveBoxOrnaments(ttBoxOrnaments); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + /** + * 修改宝箱物品 + * + * @param ttBoxOrnamentsDataVO + * @return + */ + @PutMapping + public AjaxResult edit(@RequestBody TtBoxOrnamentsDataVO ttBoxOrnamentsDataVO) { + String msg = boxOrnamentsService.updateBoxOrnamentsById(ttBoxOrnamentsDataVO); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @DeleteMapping("/{boxId}/{ids}") + public AjaxResult remove(@PathVariable Integer boxId, @PathVariable Long[] ids) { + String msg = boxOrnamentsService.removeBoxOrnamentsByIds(boxId, Arrays.asList(ids)); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @GetMapping("/getProfitMargin/{boxId}") + public AjaxResult getProfitMargin(@PathVariable("boxId") Integer boxId) { + return boxOrnamentsService.getProfitMargin(boxId); + } + + @AllArgsConstructor + @NoArgsConstructor + @Data + @Builder + public static class batchAddParam{ + private Integer boxId; + private Integer partyType; + // private List ornamentsIds; + + @NotEmpty(message = "物品id不能为空") + private List ornamentIds; + } + // 宝箱填货 + @ApiOperation("宝箱填货") + @PostMapping("/batchAdd") + public AjaxResult batchAdd(@RequestBody batchAddParam param) { + return boxOrnamentsService.batchAdd(param); + // return StringUtils.isEmpty(msg) ? AjaxResult.success("批量填货成功,请手动修改饰品数量!") : AjaxResult.error(msg); + } + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxRecordsController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxRecordsController.java new file mode 100644 index 0000000..ba626d7 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxRecordsController.java @@ -0,0 +1,30 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.domain.other.TtBoxRecordsBody; +import com.ruoyi.domain.vo.TtBoxRecordsDataVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.page.PageDataInfo; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/admin/boxRecords") +public class TtBoxRecordsController extends BaseController { + + private final TtBoxRecordsService boxRecordsService; + + public TtBoxRecordsController(TtBoxRecordsService boxRecordsService) { + this.boxRecordsService = boxRecordsService; + } + + @GetMapping("/list") + public PageDataInfo list(TtBoxRecordsBody ttBoxRecordsBody) { + startPage(); + List list = boxRecordsService.selectBoxRecordsList(ttBoxRecordsBody); + return getPageData(list); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxTypeController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxTypeController.java new file mode 100644 index 0000000..6d5e517 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtBoxTypeController.java @@ -0,0 +1,67 @@ +package com.ruoyi.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.domain.other.TtBoxType; +import com.ruoyi.admin.service.TtBoxTypeService; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/admin/boxType") +@Slf4j +public class TtBoxTypeController extends BaseController { + + private final TtBoxTypeService boxTypeService; + + public TtBoxTypeController(TtBoxTypeService boxTypeService) { + this.boxTypeService = boxTypeService; + } + + @GetMapping("/list") + public PageDataInfo list(@RequestParam(required = false) String isFightType) { + startPage(); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotEmpty(isFightType)) wrapper.eq(TtBoxType::getIsFightType, isFightType); + List list = boxTypeService.list(wrapper); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtBoxType boxType = boxTypeService.getById(id); + boxType.setIcon(""); + return R.ok(boxType); + } + + @PostMapping + public AjaxResult add(@RequestBody TtBoxType ttBoxType) { + if (StringUtils.isEmpty(ttBoxType.getIcon())) ttBoxType.setIcon(""); + else ttBoxType.setIcon(RuoYiConfig.getDomainName() + ttBoxType.getIcon()); + ttBoxType.setCreateTime(DateUtils.getNowDate()); + return toAjax(boxTypeService.save(ttBoxType)); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtBoxType ttBoxType) { + ttBoxType.setUpdateTime(DateUtils.getNowDate()); + String msg = boxTypeService.updateBoxTypeById(ttBoxType); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + return toAjax(boxTypeService.removeByIds(Arrays.asList(ids))); + } + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtContentController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtContentController.java new file mode 100644 index 0000000..836c385 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtContentController.java @@ -0,0 +1,53 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.other.TtContent; +import com.ruoyi.admin.service.TtContentService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/admin/content") +public class TtContentController extends BaseController { + + private final TtContentService contentService; + + public TtContentController(TtContentService contentService) { + this.contentService = contentService; + } + + @GetMapping("/list") + public PageDataInfo list(TtContent ttContent) { + startPage(); + List list = contentService.queryList(ttContent); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Long id) { + return R.ok(contentService.getById(id)); + } + + @PostMapping + public AjaxResult add(@RequestBody TtContent ttContent) { + ttContent.setCreateTime(DateUtils.getNowDate()); + return toAjax(contentService.save(ttContent)); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtContent ttContent) { + ttContent.setUpdateTime(DateUtils.getNowDate()); + return toAjax(contentService.updateById(ttContent)); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + return toAjax(contentService.removeByIds(Arrays.asList(ids))); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtContentTypeController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtContentTypeController.java new file mode 100644 index 0000000..92cdae9 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtContentTypeController.java @@ -0,0 +1,53 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.other.TtContentType; +import com.ruoyi.admin.service.TtContentTypeService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/admin/contentType") +public class TtContentTypeController extends BaseController { + + private final TtContentTypeService contentTypeService; + + public TtContentTypeController(TtContentTypeService contentTypeService) { + this.contentTypeService = contentTypeService; + } + + @GetMapping("/list") + public PageDataInfo list(TtContentType ttContentType) { + startPage(); + List list = contentTypeService.queryList(ttContentType); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Long id) { + return R.ok(contentTypeService.getById(id)); + } + + @PostMapping + public AjaxResult add(@RequestBody TtContentType ttContentType) { + ttContentType.setCreateTime(DateUtils.getNowDate()); + return toAjax(contentTypeService.save(ttContentType)); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtContentType ttContentType) { + ttContentType.setUpdateTime(DateUtils.getNowDate()); + return toAjax(contentTypeService.updateById(ttContentType)); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + return toAjax(contentTypeService.removeByIds(Arrays.asList(ids))); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtDeliveryRecordController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtDeliveryRecordController.java new file mode 100644 index 0000000..ebd9567 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtDeliveryRecordController.java @@ -0,0 +1,52 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtDeliveryRecordService; +import com.ruoyi.domain.other.TtDeliveryApplyBody; +import com.ruoyi.domain.vo.DeliveryApplyVO; +import com.ruoyi.domain.other.TtDeliveryRecordBody; +import com.ruoyi.domain.vo.TtDeliveryRecordDataVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Api(tags = "管理端 发货管理") +@RestController +@RequestMapping("/admin/deliverGoods") +public class TtDeliveryRecordController extends BaseController { + + private final TtDeliveryRecordService deliveryRecordService; + + public TtDeliveryRecordController(TtDeliveryRecordService deliveryRecordService) { + this.deliveryRecordService = deliveryRecordService; + } + + @ApiOperation("发货申请列表") + @GetMapping("/getDeliveryApplyList") + public PageDataInfo getDeliveryApplyList(TtDeliveryApplyBody deliveryApplyBody) { + startPage(); + List list = deliveryRecordService.getDeliveryApplyList(deliveryApplyBody); + return getPageData(list); + } + + @ApiOperation("退回发货申请") + @PostMapping("/deliveryFail") + public R deliveryFail(@RequestParam("deliveryRecordId") Integer deliveryRecordId, @RequestParam("message") String message) { + String msg = deliveryRecordService.deliveryFail(deliveryRecordId, message); + return StringUtils.isEmpty(msg) ? R.ok(true, "操作成功!") : R.fail(false, msg); + } + + @ApiOperation("发货记录申请") + @GetMapping("/getDeliveryRecordList") + public PageDataInfo getDeliveryRecordList(TtDeliveryRecordBody deliveryRecordBody) { + startPage(); + List list = deliveryRecordService.getDeliveryRecordList(deliveryRecordBody); + return getPageData(list); + } + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtFightController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtFightController.java new file mode 100644 index 0000000..746b1c2 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtFightController.java @@ -0,0 +1,39 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.admin.service.TtFightService; +import com.ruoyi.domain.vo.FightBoxDataVO; +import com.ruoyi.domain.other.TtFightBody; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.page.PageDataInfo; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/admin/fight") +public class TtFightController extends BaseController { + + private final TtFightService fightService; + + public TtFightController(TtFightService fightService) { + this.fightService = fightService; + } + + @GetMapping("/list") + public PageDataInfo list(TtFightBody ttFightBody) { + startPage(); + List list = fightService.selectFightList(ttFightBody); + return getPageData(list); + } + + @GetMapping("/getFightBoxList/{fightId}") + public PageDataInfo getFightBoxList(@PathVariable("fightId") Integer fightId) { + startPage(); + List list = fightService.selectFightBoxList(fightId); + return getPageData(list); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtFightRankingRewardController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtFightRankingRewardController.java new file mode 100644 index 0000000..ab324fd --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtFightRankingRewardController.java @@ -0,0 +1,109 @@ +package com.ruoyi.admin.controller; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; + +import com.ruoyi.common.utils.StringUtils; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.domain.other.TtFightRankingReward; +import com.ruoyi.admin.service.TtFightRankingRewardService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 对战奖励金额Controller + * + * @author ruoyi + * @date 2024-07-04 + */ +@RestController +@RequestMapping("/admin/reward") +public class TtFightRankingRewardController extends BaseController +{ + @Autowired + private TtFightRankingRewardService ttFightRankingRewardService; + + /** + * 查询对战奖励金额列表 + */ + @PreAuthorize("@ss.hasPermi('admin:reward:list')") + @GetMapping("/list") + public TableDataInfo list(TtFightRankingReward ttFightRankingReward) + { + startPage(); + List list = ttFightRankingRewardService.selectTtFightRankingRewardList(ttFightRankingReward); + return getDataTable(list); + } + + /** + * 获取对战奖励金额详细信息 + */ + @PreAuthorize("@ss.hasPermi('admin:reward:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Integer id) + { + return success(ttFightRankingRewardService.selectTtFightRankingRewardById(id)); + } + + /** + * 新增对战奖励金额 + */ + // @PreAuthorize("@ss.hasPermi('admin:reward:add')") + // @Log(title = "对战奖励金额", businessType = BusinessType.INSERT) + // @PostMapping + // public AjaxResult add(@RequestBody TtFightRankingReward ttFightRankingReward) + // { + // return toAjax(ttFightRankingRewardService.insertTtFightRankingReward(ttFightRankingReward)); + // } + + @PreAuthorize("@ss.hasPermi('admin:reward:add')") + @Log(title = "生成奖励金额", businessType = BusinessType.INSERT) + @PostMapping("/generateRankingReward") + public AjaxResult generatePromotionLevel() { + String msg = ttFightRankingRewardService.generateRankingReward(); + return StringUtils.isEmpty(msg) ? AjaxResult.success("生成成功,请修改参数!") : AjaxResult.error(msg); + } + + /** + * 修改对战奖励金额 + */ + @PreAuthorize("@ss.hasPermi('admin:reward:edit')") + @Log(title = "对战奖励金额", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody TtFightRankingReward ttFightRankingReward) + { + return toAjax(ttFightRankingRewardService.updateTtFightRankingReward(ttFightRankingReward)); + } + + /** + * 删除对战奖励金额 + */ + // @PreAuthorize("@ss.hasPermi('admin:reward:remove')") + // @Log(title = "对战奖励金额", businessType = BusinessType.DELETE) + // @DeleteMapping("/{ids}") + // public AjaxResult remove(@PathVariable Integer[] ids) + // { + // return toAjax(ttFightRankingRewardService.deleteTtFightRankingRewardByIds(ids)); + // } + + @PreAuthorize("@ss.hasPermi('admin:reward:remove')") + @Log(title = "重置奖励金额", businessType = BusinessType.DELETE) + @DeleteMapping("/truncateRankingReward") + public AjaxResult truncateRankingReward() { + ttFightRankingRewardService.truncateRankingReward(); + return AjaxResult.success("重置成功"); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtFightResultController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtFightResultController.java new file mode 100644 index 0000000..8a82f3e --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtFightResultController.java @@ -0,0 +1,33 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtFightResultService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.vo.FightResultDataVO; +import com.ruoyi.domain.vo.fight.FightResultVO; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/admin/fightResult") +public class TtFightResultController { + + private final TtFightResultService fightResultService; + + public TtFightResultController(TtFightResultService fightResultService) { + this.fightResultService = fightResultService; + } + + // @GetMapping("/getFightResult/{fightId}") + // public R getFightResult(@PathVariable("fightId") Integer fightId) { + // FightResultDataVO fightResultDataVO = fightResultService.getFightResult(fightId); + // return R.ok(fightResultDataVO); + // } + + @GetMapping("/getFightResult/{fightId}") + public R getFightResult(@PathVariable("fightId") Integer fightId) { + FightResultVO fightResultVO = fightResultService.getFightResult(fightId); + return R.ok(fightResultVO); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtFirstRechargeController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtFirstRechargeController.java new file mode 100644 index 0000000..99f3e5b --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtFirstRechargeController.java @@ -0,0 +1,104 @@ +package com.ruoyi.admin.controller; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.domain.other.TtFirstRecharge; +import com.ruoyi.admin.service.TtFirstRechargeService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 首充赠送Controller + * + * @author ruoyi + * @date 2024-06-21 + */ +@RestController +@RequestMapping("/admin/recharge") +public class TtFirstRechargeController extends BaseController +{ + @Autowired + private TtFirstRechargeService ttFirstRechargeService; + + /** + * 查询首充赠送列表 + */ + @PreAuthorize("@ss.hasPermi('admin:recharge:list')") + @GetMapping("/list") + public TableDataInfo list(TtFirstRecharge ttFirstRecharge) + { + startPage(); + List list = ttFirstRechargeService.selectTtFirstRechargeList(ttFirstRecharge); + return getDataTable(list); + } + + /** + * 导出首充赠送列表 + */ + @PreAuthorize("@ss.hasPermi('admin:recharge:export')") + @Log(title = "首充赠送", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, TtFirstRecharge ttFirstRecharge) + { + List list = ttFirstRechargeService.selectTtFirstRechargeList(ttFirstRecharge); + ExcelUtil util = new ExcelUtil(TtFirstRecharge.class); + util.exportExcel(response, list, "首充赠送数据"); + } + + /** + * 获取首充赠送详细信息 + */ + @PreAuthorize("@ss.hasPermi('admin:recharge:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Integer id) + { + return success(ttFirstRechargeService.selectTtFirstRechargeById(id)); + } + + /** + * 新增首充赠送 + */ + @PreAuthorize("@ss.hasPermi('admin:recharge:add')") + @Log(title = "首充赠送", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody TtFirstRecharge ttFirstRecharge) + { + return toAjax(ttFirstRechargeService.insertTtFirstRecharge(ttFirstRecharge)); + } + + /** + * 修改首充赠送 + */ + @PreAuthorize("@ss.hasPermi('admin:recharge:edit')") + @Log(title = "首充赠送", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody TtFirstRecharge ttFirstRecharge) + { + return toAjax(ttFirstRechargeService.updateTtFirstRecharge(ttFirstRecharge)); + } + + /** + * 删除首充赠送 + */ + @PreAuthorize("@ss.hasPermi('admin:recharge:remove')") + @Log(title = "首充赠送", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) + { + return toAjax(ttFirstRechargeService.deleteTtFirstRechargeByIds(ids)); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtMessageController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtMessageController.java new file mode 100644 index 0000000..3b91c67 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtMessageController.java @@ -0,0 +1,67 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.other.TtMessage; +import com.ruoyi.admin.service.TtMessageService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/admin/message") +public class TtMessageController extends BaseController { + + private final TtMessageService messageService; + + public TtMessageController(TtMessageService messageService) { + this.messageService = messageService; + } + + @GetMapping("/list") + public PageDataInfo list(TtMessage ttMessage) { + startPage(); + List list = messageService.queryList(ttMessage); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + return R.ok(messageService.getById(id)); + } + + @PostMapping + public AjaxResult add(@RequestBody TtMessage ttMessage) { + ttMessage.setCreateTime(DateUtils.getNowDate()); + return toAjax(messageService.save(ttMessage)); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtMessage ttMessage) { + ttMessage.setUpdateTime(DateUtils.getNowDate()); + return toAjax(messageService.updateById(ttMessage)); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + String msg = messageService.delByIds(Arrays.asList(ids)); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @PostMapping("/singleMessage/{messageId}/{userIds}") + public AjaxResult singleMessage(@PathVariable("messageId") Integer messageId, @PathVariable Integer[] userIds){ + String msg = messageService.singleMessage(messageId, Arrays.asList(userIds)); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @PostMapping("/massMessaging/{messageId}") + public AjaxResult massMessaging(@PathVariable("messageId") Integer messageId){ + String msg = messageService.massMessaging(messageId); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOrderController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOrderController.java new file mode 100644 index 0000000..939dafe --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOrderController.java @@ -0,0 +1,33 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtOrderService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.domain.dto.sys.OrderQueryCondition; +import com.ruoyi.domain.entity.TtOrder; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Api(tags = "管理端 订单管理") +@RestController +@RequestMapping("/admin/order") +public class TtOrderController extends BaseController { + + private final TtOrderService ttOrderService; + + public TtOrderController(TtOrderService ttOrderService) { + this.ttOrderService = ttOrderService; + } + + @ApiOperation("网站支付订单查询") + @PostMapping("/list") + public R list(@RequestBody @Validated OrderQueryCondition condition) { + return ttOrderService.adminList(condition); + } + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOrnamentsController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOrnamentsController.java new file mode 100644 index 0000000..1043a24 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOrnamentsController.java @@ -0,0 +1,72 @@ +package com.ruoyi.admin.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.other.TtOrnamentsBody; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.domain.other.TtOrnamentsPrice; +import com.ruoyi.domain.vo.TtOrnamentVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +@Api(tags = "管理端 饰品管理") +@RestController +@RequestMapping("/admin/ornaments") +public class TtOrnamentsController extends BaseController { + + @Autowired + private TtOrnamentService ornamentsService; + + @Autowired + private TtOrnamentService ttOrnamentService; + + // 饰品列表多条件筛选 + @ApiOperation("饰品列表多条件筛选") + @GetMapping("/list") + public R list(TtOrnamentsBody param) { + Page ttOrnamentVOPage = ttOrnamentService.listByParam(param); + return R.ok(ttOrnamentVOPage); + } + + // TODO: 2024/3/29 !!!!!! + // 导出饰品数据 + @Log(title = "饰品数据", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, TtOrnamentsBody ttOrnamentsBody) { + List list = ttOrnamentService.listByParam(ttOrnamentsBody).getRecords(); + ExcelUtil util = new ExcelUtil<>(TtOrnamentVO.class); + util.exportExcel(response, list, "饰品数据数据"); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Long id) { + return R.ok(ornamentsService.getById(id)); + } + + @ApiOperation("饰品发放") + @PostMapping("/grantOrnaments/{userId}/{ornamentsId}/{num}") + public AjaxResult grantOrnaments(@PathVariable("userId") Integer userId, + @PathVariable("ornamentsId") Long ornamentsId, + @RequestParam(name = "ornamentsLevelId", required = false) Integer ornamentsLevelId, + @PathVariable("num") Integer num) { + return ornamentsService.grantOrnaments(userId, ornamentsId, ornamentsLevelId, num); + } + + @PutMapping("/updateOrnamentPrice") + public AjaxResult updateOrnamentPrice(@RequestBody TtOrnamentsPrice ttOrnamentsPrice) { + return ornamentsService.updateOrnamentPrice(ttOrnamentsPrice.getId(), ttOrnamentsPrice.getPrice()); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOrnamentsLevelController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOrnamentsLevelController.java new file mode 100644 index 0000000..2faf4cf --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtOrnamentsLevelController.java @@ -0,0 +1,59 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.other.TtOrnamentsLevel; +import com.ruoyi.admin.service.TtOrnamentsLevelService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Api(tags = "管理端 饰品等级信息") +@RestController +@RequestMapping("/admin/ornamentsLevel") +public class TtOrnamentsLevelController extends BaseController { + + private final TtOrnamentsLevelService ornamentsLevelService; + + public TtOrnamentsLevelController(TtOrnamentsLevelService ornamentsLevelService) { + this.ornamentsLevelService = ornamentsLevelService; + } + + @ApiOperation("获取所有饰品等级背景") + @GetMapping("/list") + public PageDataInfo list() { + startPage(); + List list = ornamentsLevelService.list(); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtOrnamentsLevel ornamentsLevel = ornamentsLevelService.getById(id); + ornamentsLevel.setLevelImg(""); + return R.ok(ornamentsLevel); + } + + @PostMapping("generateOrnamentsLevel/{num}") + public AjaxResult generateOrnamentsLevel(@PathVariable("num") Integer num) { + String msg = ornamentsLevelService.generateOrnamentsLevel(num); + return StringUtils.isEmpty(msg) ? AjaxResult.success("生成成功,请修改参数!") : AjaxResult.error(msg); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtOrnamentsLevel ttOrnamentsLevel) { + String msg = ornamentsLevelService.updateOrnamentsLevelById(ttOrnamentsLevel); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @PostMapping + public AjaxResult truncateOrnamentsLevel() { + ornamentsLevelService.truncateOrnamentsLevel(); + return AjaxResult.success("重置成功,请重新设置!"); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtPromoTurnoverController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtPromoTurnoverController.java new file mode 100644 index 0000000..a184c96 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtPromoTurnoverController.java @@ -0,0 +1,47 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtPromoTurnoverService; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.domain.param.promotion.AnchorDailyTurnoverParam; +import com.ruoyi.domain.param.promotion.AnchorRechargeParam; +import com.ruoyi.domain.vo.promotion.AnchorDailyTurnoverVo; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +@RestController +@RequestMapping("/admin/promo") +public class TtPromoTurnoverController extends BaseController { + + @Autowired + private TtPromoTurnoverService ttPromoTurnoverService; + + @ApiOperation("获取主播推广数据") + @GetMapping("/getAnchorPromotionData") + public PageDataInfo getAnchorPromotionData(AnchorDailyTurnoverParam param) { + List list = ttPromoTurnoverService.getAnchorDailyTurnover(param); + PageDataInfo pageDataInfo = new PageDataInfo(); + pageDataInfo.setTotal(list.size()); + pageDataInfo.setCode(HttpStatus.SUCCESS); + pageDataInfo.setMsg("查询成功"); + int num = param.getNum() != null ? param.getNum() : 1; + int size = param.getSize() != null ? param.getSize() : 10; + int fromIndex = (num - 1) * size; + int toIndex = Math.min(fromIndex + size, list.size()); + pageDataInfo.setRows(list.subList(fromIndex, toIndex)); + return pageDataInfo; + } + + @ApiOperation("主播充值") + @PostMapping("/recharge") + public AjaxResult recharge(@RequestBody AnchorRechargeParam param) { + return ttPromoTurnoverService.recharge(param); + } + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtPromotionLevelController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtPromotionLevelController.java new file mode 100644 index 0000000..9acbbcb --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtPromotionLevelController.java @@ -0,0 +1,57 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.entity.TtPromotionLevel; +import com.ruoyi.admin.service.TtPromotionLevelService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/admin/promotionLevel") +public class TtPromotionLevelController extends BaseController { + + private final TtPromotionLevelService promotionLevelService; + + public TtPromotionLevelController(TtPromotionLevelService promotionLevelService) { + this.promotionLevelService = promotionLevelService; + } + + @GetMapping("/list") + public PageDataInfo list() { + startPage(); + List list = promotionLevelService.list(); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtPromotionLevel ttPromotionLevel = promotionLevelService.getById(id); + ttPromotionLevel.setIcon(""); + return R.ok(ttPromotionLevel); + } + + @PostMapping("generatePromotionLevel/{num}") + public AjaxResult generatePromotionLevel(@PathVariable("num") Integer num) { + String msg = promotionLevelService.generateVipLevel(num); + return StringUtils.isEmpty(msg) ? AjaxResult.success("生成成功,请修改参数!") : AjaxResult.error(msg); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtPromotionLevel ttPromotionLevel) { + ttPromotionLevel.setUpdateTime(DateUtils.getNowDate()); + String msg = promotionLevelService.updatePromotionLevelById(ttPromotionLevel); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @PostMapping + public AjaxResult truncateVipLevel() { + promotionLevelService.truncatePromotionLevel(); + return AjaxResult.success("重置成功,请重新设置!"); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtPromotionRecordController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtPromotionRecordController.java new file mode 100644 index 0000000..c685fa1 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtPromotionRecordController.java @@ -0,0 +1,38 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.entity.TtPromotionRecord; +import com.ruoyi.admin.service.TtPromotionRecordService; +import com.ruoyi.domain.vo.PromotionDataVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/admin/promotionRecord") +public class TtPromotionRecordController extends BaseController { + + private final TtPromotionRecordService promotionRecordService; + + public TtPromotionRecordController(TtPromotionRecordService promotionRecordService) { + this.promotionRecordService = promotionRecordService; + } + + @GetMapping("/getPromotionRecord") + public PageDataInfo getPromotionRecord(TtPromotionRecord ttPromotionRecord){ + startPage(); + List list = promotionRecordService.getPromotionRecord(ttPromotionRecord); + return getPageData(list); + } + + @GetMapping("/statisticsPromotionData/{userId}") + public R statisticsPromotionData(@PathVariable("userId") Integer userId){ + PromotionDataVO data = promotionRecordService.statisticsPromotionData(userId); + return R.ok(data); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeCardController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeCardController.java new file mode 100644 index 0000000..110721b --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeCardController.java @@ -0,0 +1,40 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.other.TtRechargeCard; +import com.ruoyi.admin.service.TtRechargeCardService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +@RestController +@RequestMapping("/admin/rechargeCard") +public class TtRechargeCardController extends BaseController { + + private final TtRechargeCardService rechargeCardService; + + public TtRechargeCardController(TtRechargeCardService rechargeCardService) { + this.rechargeCardService = rechargeCardService; + } + + @GetMapping("/list") + public PageDataInfo list(TtRechargeCard ttRechargeCard) { + startPage(); + List list = rechargeCardService.queryList(ttRechargeCard); + return getPageData(list); + } + + @PostMapping("/export") + public void export(HttpServletResponse response, TtRechargeCard ttRechargeCard) { + rechargeCardService.export(response, ttRechargeCard); + } + + @PostMapping("/generateCard/{rechargeListId}/{num}") + public R> generateCard(@PathVariable("rechargeListId") Integer rechargeListId, @PathVariable("num") Integer num) { + List cardList = rechargeCardService.generateCard(rechargeListId, num); + return R.ok(cardList); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeConfigController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeConfigController.java new file mode 100644 index 0000000..4857eb9 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeConfigController.java @@ -0,0 +1,57 @@ +package com.ruoyi.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.service.TtRechargeConfigService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.entity.TtRechargeConfig; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/admin/rechargeConfig") +public class TtRechargeConfigController extends BaseController { + + private final TtRechargeConfigService rechargeConfigService; + + public TtRechargeConfigController(TtRechargeConfigService rechargeConfigService) { + this.rechargeConfigService = rechargeConfigService; + } + + @GetMapping("/list") + public PageDataInfo list(@RequestParam(required = false) String status) { + startPage(); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotEmpty(status)) wrapper.eq(TtRechargeConfig::getStatus, status); + List list = rechargeConfigService.list(wrapper); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtRechargeConfig rechargeConfig = rechargeConfigService.getById(id); + return R.ok(rechargeConfig); + } + + @PostMapping + public AjaxResult add(@RequestBody TtRechargeConfig ttRechargeConfig) { + return toAjax(rechargeConfigService.save(ttRechargeConfig)); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtRechargeConfig ttRechargeConfig) { + return toAjax(rechargeConfigService.updateById(ttRechargeConfig)); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + return toAjax(rechargeConfigService.removeByIds(Arrays.asList(ids))); + } + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeListController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeListController.java new file mode 100644 index 0000000..1441612 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeListController.java @@ -0,0 +1,59 @@ +package com.ruoyi.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.admin.service.TtRechargeProdService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/admin/rechargeList") +public class TtRechargeListController extends BaseController { + + private final TtRechargeProdService rechargeListService; + + public TtRechargeListController(TtRechargeProdService rechargeListService) { + this.rechargeListService = rechargeListService; + } + + @GetMapping("/list") + public PageDataInfo list(@RequestParam(required = false) String status) { + startPage(); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotEmpty(status)) wrapper.eq(TtRechargeProd::getStatus, status); + List list = rechargeListService.list(wrapper); + return getPageData(list); + } + + @PreAuthorize("@ss.hasPermi('admin:rechargeList:query')") + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtRechargeProd rechargeList = rechargeListService.getById(id); + return R.ok(rechargeList); + } + + @PostMapping + public AjaxResult add(@RequestBody TtRechargeProd ttRechargeProd) { + return toAjax(rechargeListService.save(ttRechargeProd)); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtRechargeProd ttRechargeProd) { + return toAjax(rechargeListService.updateById(ttRechargeProd)); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + return toAjax(rechargeListService.removeByIds(Arrays.asList(ids))); + } + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeRankingRewardController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeRankingRewardController.java new file mode 100644 index 0000000..6d42ba7 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeRankingRewardController.java @@ -0,0 +1,95 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtRechargeRankingRewardService; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.other.TtRechargeRankingReward; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 充值奖励金额Controller + * + * @author ruoyi + * @date 2024-07-04 + */ +@RestController +@RequestMapping("/admin/rechargeRankingReward") +public class TtRechargeRankingRewardController extends BaseController +{ + @Autowired + private TtRechargeRankingRewardService ttRechargeRankingRewardService; + + /** + * 查询充值奖励金额列表 + */ + @GetMapping("/list") + public TableDataInfo list(TtRechargeRankingReward ttRechargeRankingReward) + { + startPage(); + List list = ttRechargeRankingRewardService.selectTtRechargeRankingRewardList(ttRechargeRankingReward); + return getDataTable(list); + } + + /** + * 获取充值奖励金额详细信息 + */ + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Integer id) + { + return success(ttRechargeRankingRewardService.selectTtRechargeRankingRewardById(id)); + } + + /** + * 新增充值奖励金额 + */ + // @PreAuthorize("@ss.hasPermi('admin:reward:add')") + // @Log(title = "充值奖励金额", businessType = BusinessType.INSERT) + // @PostMapping + // public AjaxResult add(@RequestBody TtRechargeRankingReward ttRechargeRankingReward) + // { + // return toAjax(ttRechargeRankingRewardService.insertTtRechargeRankingReward(ttRechargeRankingReward)); + // } + + @Log(title = "生成奖励金额", businessType = BusinessType.INSERT) + @PostMapping("/generateRankingReward") + public AjaxResult generatePromotionLevel() { + String msg = ttRechargeRankingRewardService.generateRankingReward(); + return StringUtils.isEmpty(msg) ? AjaxResult.success("生成成功,请修改参数!") : AjaxResult.error(msg); + } + + /** + * 修改充值奖励金额 + */ + @Log(title = "充值奖励金额", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody TtRechargeRankingReward ttRechargeRankingReward) + { + return toAjax(ttRechargeRankingRewardService.updateTtRechargeRankingReward(ttRechargeRankingReward)); + } + + /** + * 删除充值奖励金额 + */ + // @PreAuthorize("@ss.hasPermi('admin:reward:remove')") + // @Log(title = "充值奖励金额", businessType = BusinessType.DELETE) + // @DeleteMapping("/{ids}") + // public AjaxResult remove(@PathVariable Integer[] ids) + // { + // return toAjax(ttRechargeRankingRewardService.deleteTtRechargeRankingRewardByIds(ids)); + // } + + @Log(title = "重置奖励金额", businessType = BusinessType.DELETE) + @DeleteMapping("/truncateRankingReward") + public AjaxResult truncateRankingReward() { + ttRechargeRankingRewardService.truncateRankingReward(); + return AjaxResult.success("重置成功"); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeRecordController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeRecordController.java new file mode 100644 index 0000000..1c8359a --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRechargeRecordController.java @@ -0,0 +1,158 @@ +package com.ruoyi.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.mapper.TtOrderMapper; +import com.ruoyi.admin.mapper.TtRechargeConfigMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.TtRechargeConfig; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtRechargeRecord; +import com.ruoyi.admin.service.TtRechargeRecordService; +import com.ruoyi.domain.other.TtRechargeRecordBody; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.domain.other.TtRechargeRecordVo; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.servlet.http.HttpServletResponse; + +import java.math.BigDecimal; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/admin/rechargeRecord") +public class TtRechargeRecordController extends BaseController { + + private final TtRechargeRecordService rechargeRecordService; + + public TtRechargeRecordController(TtRechargeRecordService rechargeRecordService) { + this.rechargeRecordService = rechargeRecordService; + } + + @Autowired + private com.ruoyi.admin.mapper.TtUserBlendErcashMapper ttUserBlendErcashMapper; + + @Autowired + private TtUserMapper ttUserMapper; + + @Autowired + private TtOrderMapper ttOrderMapper; + + @Autowired + private TtRechargeConfigMapper ttRechargeConfigMapper; + + @GetMapping("/list") + public PageDataInfo list(TtRechargeRecordBody rechargeRecordBody) { + startPage(); + List list = rechargeRecordService.queryList(rechargeRecordBody); + if (CollectionUtils.isNotEmpty(list)) { + Set userIds = new HashSet<>(); + Set orderIds = new HashSet<>(); + for (TtRechargeRecord record : list) { + userIds.add(record.getUserId()); + orderIds.add(record.getOrderId()); + } + List ttUsers = ttUserMapper.selectBatchIds(userIds); + Map userMap = ttUsers.stream().collect(Collectors.toMap(TtUser::getUserId, Function.identity())); + List orders = new LambdaQueryChainWrapper<>(ttOrderMapper) + .in(TtOrder::getOrderId, orderIds).list(); + Map orderPicture = new HashMap<>(); + if (CollectionUtils.isNotEmpty(orders)) { + Set goodIds = orders.stream().map(TtOrder::getGoodsId).collect(Collectors.toSet()); + List configs = new LambdaQueryChainWrapper<>(ttRechargeConfigMapper).in(TtRechargeConfig::getId, goodIds).list(); + Map configMap = configs.stream().collect(Collectors.toMap(TtRechargeConfig::getId, Function.identity())); + for (TtOrder order : orders) { + TtRechargeConfig config = configMap.get(order.getGoodsId()); + if (config != null) { + orderPicture.put(order.getOrderId(), config.getPicture()); + } + } + } + + List vos = new ArrayList<>(); + for (TtRechargeRecord record : list) { + TtRechargeRecordVo vo = new TtRechargeRecordVo(); + BeanUtils.copyProperties(record, vo); + vos.add(vo); + TtUser ttUser = userMap.get(record.getUserId()); + if (ttUser == null) { + continue; + } + vo.setNickName(ttUser.getNickName()); + vo.setPhoneNumber(ttUser.getPhoneNumber()); + vo.setDeliveryAddress(ttUser.getDeliveryAddress()); + vo.setPicture(orderPicture.getOrDefault(vo.getOrderId(), "")); + } + return getPageData(vos); + } else { + return getPageData(new ArrayList<>()); + } + } + + @PostMapping("/export") + public void export(HttpServletResponse response, TtRechargeRecordBody rechargeRecordBody) { + List list = rechargeRecordService.queryList(rechargeRecordBody); + ExcelUtil util = new ExcelUtil<>(TtRechargeRecord.class); + util.exportExcel(response, list, "充值记录信息列表"); + } + + @GetMapping("/homeStats") + public com.ruoyi.common.core.domain.R homeStats() { + java.time.LocalDate today = java.time.LocalDate.now(); + java.sql.Timestamp startOfDay = java.sql.Timestamp.valueOf(today.atStartOfDay()); + java.sql.Timestamp endOfDay = java.sql.Timestamp.valueOf(today.plusDays(1).atStartOfDay()); + + // 今日总充值:source = 1 TtAccountRecordSource.RECHARGE.getCode() + LambdaQueryWrapper rechargeWrapper = + Wrappers.lambdaQuery() + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode()) + .ge(TtUserBlendErcash::getCreateTime, startOfDay) + .lt(TtUserBlendErcash::getCreateTime, endOfDay); + BigDecimal todayRecharge = ttUserBlendErcashMapper.selectList(rechargeWrapper).stream() + .map(TtUserBlendErcash::getAmount) + .filter(java.util.Objects::nonNull) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + // 今日总投注:游戏消费(经典盲盒/对战/roll房/逐梦前行/指数盲盒/极速永恒×3/幸运转盘/黄金装备) TtAccountRecordSource.getGameBetCodes() + List betSources = new ArrayList<>(TtAccountRecordSource.getGameBetCodes()); + LambdaQueryWrapper betWrapper = + Wrappers.lambdaQuery() + .in(TtUserBlendErcash::getSource, betSources) + .ge(TtUserBlendErcash::getCreateTime, startOfDay) + .lt(TtUserBlendErcash::getCreateTime, endOfDay); + BigDecimal todayBet = ttUserBlendErcashMapper.selectList(betWrapper).stream() + .map(r -> r.getAmount() != null ? r.getAmount().abs() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + // 今日总赢奖:游戏奖励(经典盲盒/对战/roll房/逐梦前行/极速永恒/幸运转盘/黄金装备) TtAccountRecordSource.getGameRewardCodes() + List rewardSources = new ArrayList<>(TtAccountRecordSource.getGameRewardCodes()); + LambdaQueryWrapper rewardWrapper = + Wrappers.lambdaQuery() + .in(TtUserBlendErcash::getSource, rewardSources) + .ge(TtUserBlendErcash::getCreateTime, startOfDay) + .lt(TtUserBlendErcash::getCreateTime, endOfDay); + BigDecimal todayReward = ttUserBlendErcashMapper.selectList(rewardWrapper).stream() + .map(r -> r.getAmount() != null ? r.getAmount().abs() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + java.util.Map result = new java.util.HashMap<>(); + result.put("todayRecharge", todayRecharge); + result.put("todayBet", todayBet); + result.put("todayReward", todayReward); + return com.ruoyi.common.core.domain.R.ok(result); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRedPackController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRedPackController.java new file mode 100644 index 0000000..6217f6d --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRedPackController.java @@ -0,0 +1,106 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtRedPackService; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.domain.other.TtRedPack; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 【请填写功能名称】Controller + * + * @author ruoyi + * @date 2023-06-29 + */ +@RestController +@RequestMapping("/admin/redPack") +public class TtRedPackController extends BaseController +{ + @Autowired + private TtRedPackService ttRedPackService; + + /** + * 查询【请填写功能名称】列表 + */ + @PreAuthorize("@ss.hasPermi('skinsback:ttRedPack:list')") + @GetMapping("/list") + public TableDataInfo list(TtRedPack ttRedPack) + { + startPage(); + List list = ttRedPackService.selectTtRedPackList(ttRedPack); + return getDataTable(list); + } + @PreAuthorize("@ss.hasPermi('skinsback:ttRedPack:listAll')") + @GetMapping("/listAll") + public TableDataInfo listAll(TtRedPack ttRedPack) + { + List list = ttRedPackService.selectTtRedPackList(ttRedPack); + return getDataTable(list); + } + /** + * 导出【请填写功能名称】列表 + */ + @PreAuthorize("@ss.hasPermi('skinsback:ttRedPack:export')") + @Log(title = "【请填写功能名称】", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, TtRedPack ttRedPack) + { + List list = ttRedPackService.selectTtRedPackList(ttRedPack); + ExcelUtil util = new ExcelUtil(TtRedPack.class); + util.exportExcel(response, list, "【请填写功能名称】数据"); + } + + /** + * 获取【请填写功能名称】详细信息 + */ + @PreAuthorize("@ss.hasPermi('skinsback:ttRedPack:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(ttRedPackService.selectTtRedPackById(id)); + } + + /** + * 新增【请填写功能名称】 + */ + @PreAuthorize("@ss.hasPermi('skinsback:ttRedPack:add')") + @Log(title = "【请填写功能名称】", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody TtRedPack ttRedPack) + { + ttRedPack.setValidCount(ttRedPack.getPackCount()); + ttRedPack.setStatus(0l); + return toAjax(ttRedPackService.insertTtRedPack(ttRedPack)); + } + + /** + * 修改【请填写功能名称】 + */ + @PreAuthorize("@ss.hasPermi('skinsback:ttRedPack:edit')") + @Log(title = "【请填写功能名称】", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody TtRedPack ttRedPack) + { + return toAjax(ttRedPackService.updateTtRedPack(ttRedPack)); + } + + /** + * 删除【请填写功能名称】 + */ + @PreAuthorize("@ss.hasPermi('skinsback:ttRedPack:remove')") + @Log(title = "【请填写功能名称】", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(ttRedPackService.deleteTtRedPackByIds(ids)); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRedPacketController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRedPacketController.java new file mode 100644 index 0000000..79192a7 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRedPacketController.java @@ -0,0 +1,67 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtRedPacketService; +import com.ruoyi.domain.other.TtRedPacket; +import com.ruoyi.domain.other.TtRedPacketBody; +import com.ruoyi.domain.vo.TtRedPacketDataVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@Api(tags = "管理端 红包") +@RestController +@RequestMapping("/admin/redPacket") +public class TtRedPacketController extends BaseController { + + private final TtRedPacketService redPacketService; + + public TtRedPacketController(TtRedPacketService redPacketService) { + this.redPacketService = redPacketService; + } + + @GetMapping("/list") + public PageDataInfo list(TtRedPacketBody ttRedPacketBody) { + startPage(); + List list = redPacketService.queryList(ttRedPacketBody); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtRedPacketDataVO data = redPacketService.queryList(TtRedPacketBody.builder().id(id).build()).get(0); + return R.ok(data); + } + + @ApiOperation("生成红包") + @PostMapping + public AjaxResult add(@RequestBody TtRedPacketDataVO ttRedPacketDataVO) { + List passwordList = redPacketService.insertRedPacket(ttRedPacketDataVO); + return StringUtils.isNotNull(passwordList) ? AjaxResult.success("生成成功!", passwordList) : AjaxResult.error("生成红包异常"); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtRedPacket redPacket) { + redPacket.setUpdateBy(getUsername()); + redPacket.setUpdateTime(DateUtils.getNowDate()); + return redPacketService.updateRedPacketById(redPacket); + // return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + + boolean b = redPacketService.removeByIds(Arrays.asList(ids)); + + if (b) return AjaxResult.success(); + return AjaxResult.error(); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRedPacketRecordController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRedPacketRecordController.java new file mode 100644 index 0000000..ef16ead --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRedPacketRecordController.java @@ -0,0 +1,30 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtRedPacketRecordService; +import com.ruoyi.domain.other.TtRedPacketRecordBody; +import com.ruoyi.domain.vo.TtRedPacketRecordDataVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.page.PageDataInfo; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/admin/redPacketRecord") +public class TtRedPacketRecordController extends BaseController { + + private final TtRedPacketRecordService redPacketRecordService; + + public TtRedPacketRecordController(TtRedPacketRecordService redPacketRecordService) { + this.redPacketRecordService = redPacketRecordService; + } + + @GetMapping("/getRedPacketRecordList") + public PageDataInfo getRedPacketRecordList(TtRedPacketRecordBody ttRedPacketRecordBody) { + startPage(); + List list = redPacketRecordService.queryList(ttRedPacketRecordBody); + return getPageData(list); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtReplacementRecordController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtReplacementRecordController.java new file mode 100644 index 0000000..c45094d --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtReplacementRecordController.java @@ -0,0 +1,98 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtReplacementRecordService; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.domain.other.TtReplacementRecord; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 汰换记录Controller + * + * @author junhai + * @date 2023-09-10 + */ +@RestController +@RequestMapping("/admin/replacementRecord") +public class TtReplacementRecordController extends BaseController +{ + @Autowired + private TtReplacementRecordService ttReplacementRecordService; + + /** + * 查询汰换记录列表 + */ + @PreAuthorize("@ss.hasPermi('skinsback:replacementRecord:list')") + @GetMapping("/list") + public TableDataInfo list(TtReplacementRecord ttReplacementRecord) + { + startPage(); + List list = ttReplacementRecordService.selectTtReplacementRecordList(ttReplacementRecord); + return getDataTable(list); + } + + /** + * 导出汰换记录列表 + */ + @PreAuthorize("@ss.hasPermi('skinsback:replacementRecord:export')") + @Log(title = "汰换记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, TtReplacementRecord ttReplacementRecord) + { + List list = ttReplacementRecordService.selectTtReplacementRecordList(ttReplacementRecord); + ExcelUtil util = new ExcelUtil(TtReplacementRecord.class); + util.exportExcel(response, list, "汰换记录数据"); + } + + /** + * 获取汰换记录详细信息 + */ + @PreAuthorize("@ss.hasPermi('skinsback:replacementRecord:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(ttReplacementRecordService.selectTtReplacementRecordById(id)); + } + + /** + * 新增汰换记录 + */ + @PreAuthorize("@ss.hasPermi('skinsback:replacementRecord:add')") + @Log(title = "汰换记录", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody TtReplacementRecord ttReplacementRecord) + { + return toAjax(ttReplacementRecordService.insertTtReplacementRecord(ttReplacementRecord)); + } + + /** + * 修改汰换记录 + */ + @PreAuthorize("@ss.hasPermi('skinsback:replacementRecord:edit')") + @Log(title = "汰换记录", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody TtReplacementRecord ttReplacementRecord) + { + return toAjax(ttReplacementRecordService.updateTtReplacementRecord(ttReplacementRecord)); + } + + /** + * 删除汰换记录 + */ + @PreAuthorize("@ss.hasPermi('skinsback:replacementRecord:remove')") + @Log(title = "汰换记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(ttReplacementRecordService.deleteTtReplacementRecordByIds(ids)); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRobotFightGroupBoxController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRobotFightGroupBoxController.java new file mode 100644 index 0000000..b0f457e --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRobotFightGroupBoxController.java @@ -0,0 +1,100 @@ +package com.ruoyi.admin.controller; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.domain.other.TtRobotFightGroupBox; +import com.ruoyi.admin.service.TtRobotFightGroupBoxService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 机器人对战分组宝箱Controller + * + * @author ruoyi + * @date 2024-06-28 + */ +@RestController +@RequestMapping("/admin/group/box") +public class TtRobotFightGroupBoxController extends BaseController +{ + @Autowired + private TtRobotFightGroupBoxService ttRobotFightGroupBoxService; + + /** + * 查询机器人对战分组宝箱列表 + */ + @PreAuthorize("@ss.hasPermi('admin:box:list')") + @GetMapping("/list/{groupId}") + public TableDataInfo list( + @PathVariable("groupId") Integer groupId, + TtRobotFightGroupBox ttRobotFightGroupBox + ) + { + startPage(); + List list = ttRobotFightGroupBoxService + .selectTtRobotFightGroupBoxList(groupId, ttRobotFightGroupBox); + return getDataTable(list); + } + + /** + * 获取机器人对战分组宝箱详细信息 + */ + @PreAuthorize("@ss.hasPermi('admin:box:query')") + @GetMapping(value = "/{groupId}/{boxId}") + public AjaxResult getInfo( + @PathVariable("groupId") Integer groupId, + @PathVariable("boxId") Integer boxId + ) + { + return success(ttRobotFightGroupBoxService.selectTtRobotFightGroupBoxByBoxId(groupId, boxId)); + } + + /** + * 新增机器人对战分组宝箱 + */ + @PreAuthorize("@ss.hasPermi('admin:box:add')") + @Log(title = "机器人对战分组宝箱", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody TtRobotFightGroupBox ttRobotFightGroupBox) + { + // TODO 检查该分组下是否已存在该宝箱 + return toAjax(ttRobotFightGroupBoxService.insertTtRobotFightGroupBox(ttRobotFightGroupBox)); + } + + /** + * 修改机器人对战分组宝箱 + */ + @PreAuthorize("@ss.hasPermi('admin:box:edit')") + @Log(title = "机器人对战分组宝箱", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody TtRobotFightGroupBox ttRobotFightGroupBox) + { + return toAjax(ttRobotFightGroupBoxService.updateTtRobotFightGroupBox(ttRobotFightGroupBox)); + } + + /** + * 删除机器人对战分组宝箱 + */ + @PreAuthorize("@ss.hasPermi('admin:box:remove')") + @Log(title = "机器人对战分组宝箱", businessType = BusinessType.DELETE) + @DeleteMapping("/{groupId}/{boxIds}") + public AjaxResult remove(@PathVariable Integer groupId, @PathVariable Integer[] boxIds) + { + return toAjax(ttRobotFightGroupBoxService.deleteTtRobotFightGroupBoxByGroupIds(groupId, boxIds)); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRobotFightGroupController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRobotFightGroupController.java new file mode 100644 index 0000000..46b92b8 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRobotFightGroupController.java @@ -0,0 +1,118 @@ +package com.ruoyi.admin.controller; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; + +import com.ruoyi.admin.mapper.TtRobotFightGroupBoxMapper; +import com.ruoyi.admin.service.TtRobotFightGroupBoxService; +import com.ruoyi.domain.other.TtRobotFightGroupBox; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.domain.other.TtRobotFightGroup; +import com.ruoyi.admin.service.TtRobotFightGroupService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 机器人对战分组Controller + * + * @author ruoyi + * @date 2024-06-28 + */ +@RestController +@RequestMapping("/admin/group") +public class TtRobotFightGroupController extends BaseController +{ + @Autowired + private TtRobotFightGroupService ttRobotFightGroupService; + + @Autowired + private TtRobotFightGroupBoxMapper ttRobotFightGroupBoxMapper; + + /** + * 查询机器人对战分组列表 + */ + @GetMapping("/list") + public TableDataInfo list(TtRobotFightGroup ttRobotFightGroup) + { + startPage(); + List list = ttRobotFightGroupService.selectTtRobotFightGroupList(ttRobotFightGroup); + return getDataTable(list); + } + + /** + * 导出机器人对战分组列表 + */ + @Log(title = "机器人对战分组", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, TtRobotFightGroup ttRobotFightGroup) + { + List list = ttRobotFightGroupService.selectTtRobotFightGroupList(ttRobotFightGroup); + ExcelUtil util = new ExcelUtil(TtRobotFightGroup.class); + util.exportExcel(response, list, "机器人对战分组数据"); + } + + /** + * 获取机器人对战分组详细信息 + */ + @GetMapping(value = "/{groupId}") + public AjaxResult getInfo(@PathVariable("groupId") Integer groupId) + { + return success(ttRobotFightGroupService.selectTtRobotFightGroupByGroupId(groupId)); + } + + /** + * 新增机器人对战分组 + */ + @Log(title = "机器人对战分组", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody TtRobotFightGroup ttRobotFightGroup) + { + if (ttRobotFightGroup.getSeatNum() != 2 && ttRobotFightGroup.getSeatNum() != 3) { + return AjaxResult.error("座位数量只能为2或3"); + } + return toAjax(ttRobotFightGroupService.insertTtRobotFightGroup(ttRobotFightGroup)); + } + + /** + * 修改机器人对战分组 + */ + @Log(title = "机器人对战分组", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody TtRobotFightGroup ttRobotFightGroup) + { + if (ttRobotFightGroup.getSeatNum() != 2 && ttRobotFightGroup.getSeatNum() != 3) { + return AjaxResult.error("座位数量只能为2或3"); + } + return toAjax(ttRobotFightGroupService.updateTtRobotFightGroup(ttRobotFightGroup)); + } + + /** + * 删除机器人对战分组 + */ + @Log(title = "机器人对战分组", businessType = BusinessType.DELETE) + @DeleteMapping("/{groupIds}") + public AjaxResult remove(@PathVariable Integer[] groupIds) + { + for (Integer groupId : groupIds) { + int i = ttRobotFightGroupBoxMapper.hasChildByGroupId(groupId); + if (i > 0) { + return AjaxResult.error("删除失败,分组" + i + "不为空"); + } + } + + return toAjax(ttRobotFightGroupService.deleteTtRobotFightGroupByGroupIds(groupIds)); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollController.java new file mode 100644 index 0000000..283ba32 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollController.java @@ -0,0 +1,180 @@ +package com.ruoyi.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.domain.body.AddRobotBody; +import com.ruoyi.admin.service.TtRollService; +import com.ruoyi.admin.service.TtRollUserService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.domain.dto.roll.GetRollPrizePool; +import com.ruoyi.domain.dto.roll.InviteRollUser; +import com.ruoyi.domain.entity.roll.TtRoll; +import com.ruoyi.domain.entity.roll.TtRollBody; +import com.ruoyi.domain.entity.roll.TtRollUser; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.vo.TtRollPrizeDataVO; +import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsByPageVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.Data; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@Api(tags = "管理端 roll房") +@RestController +@RequestMapping("/admin/roll") +public class TtRollController extends BaseController { + + private final TtRollService rollService; + private final TtRollUserService rollUserService; + + public TtRollController(TtRollService rollService, + TtRollUserService rollUserService) { + this.rollService = rollService; + this.rollUserService = rollUserService; + } + + @GetMapping("/list") + public PageDataInfo list(TtRollBody ttRollBody) { + startPage(); + List list = rollService.queryList(ttRollBody); + return getPageData(list); + } + + /** + * 导出信息 + * @param response + * @param ttRollBody + */ + @PostMapping("/export") + public void export(HttpServletResponse response, TtRollBody ttRollBody) { + List list = rollService.queryList(ttRollBody); + ExcelUtil util = new ExcelUtil<>(TtRoll.class); + util.exportExcel(response, list, "Roll房信息列表"); + } + + @GetMapping(value = "/{rollId}") + public R getInfo(@PathVariable("rollId") Long rollId) { + TtRoll roll = rollService.getById(rollId); + return R.ok(roll); + } + + // 创建roll房 + @ApiOperation("创建roll房") + @PostMapping + public AjaxResult add(@RequestBody TtRoll ttRoll) { + + ttRoll.setCreateBy(getUsername()); + ttRoll.setCreateTime(DateUtils.getNowDate()); + return rollService.createRoll(ttRoll); + + } + + // roll房拉人 + @ApiOperation("roll房拉人") + @PostMapping("/inviteRollUser") + public R inviteRollUser(@RequestBody InviteRollUser InviteRollUser) { + + return rollService.inviteRollUser(InviteRollUser); + + } + + // 获取roll房成员信息 + @ApiOperation("获取roll房成员信息") + @GetMapping("getRollUsers/{rollId}") + public AjaxResult getRollUsers(@PathVariable("rollId") Integer rollId) { + return rollService.getRollUsers(rollId); + } + + + @ApiOperation("编辑roll房") + @PutMapping + public AjaxResult edit(@RequestBody TtRoll ttRoll) { + + ttRoll.setUpdateBy(getUsername()); + ttRoll.setUpdateTime(DateUtils.getNowDate()); + + return rollService.updateRollById(ttRoll); + } + + @ApiOperation("删除roll房") + @DeleteMapping("/remove/{rollId}") + public AjaxResult remove(@PathVariable Long rollId) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(TtRollUser::getRollId, rollId); + List list = rollUserService.list(wrapper); + TtRoll ttRoll = rollService.getById(rollId); + ttRoll.setUpdateBy(getUsername()); + ttRoll.setUpdateTime(DateUtils.getNowDate()); + rollService.updateById(ttRoll); + return toAjax(rollService.removeById(rollId)); + } + + + // @ApiOperation("roll房奖品列表") + // @PostMapping("/getRollPrizeList") + // public R> getRollPrizeList(@RequestBody RollPrizesCondition condition) { + // + // List list = rollService.getRollPrizeList(condition); + // + // return R.ok(list); + // + // } + @ApiOperation("roll房奖品列表") + @GetMapping("/getRollPrizeList/{rollId}") + public R> getRollPrizeList(@PathVariable("rollId") Integer rollId) { + + List list = rollService.getRollPrizeList(rollId); + + return R.ok(list); + + } + + @ApiOperation("获取 roll房奖池详情") + @PostMapping("/getRollPrizePool") + public R getRollPrizePool(@RequestBody @Validated GetRollPrizePool param) { + return rollService.getRollPrizePool(param); + } + + @ApiOperation("roll房的指定获奖人名单") + @GetMapping("/rollWinners/{rollId}") + public R rollWinners(@PathVariable Integer rollId) { + return rollUserService.rollWinners(rollId); + } + + @ApiOperation("指定roll房winner") + @PostMapping("/namedWinner") + public AjaxResult namedWinner(@RequestBody @Validated TtRollPrizeDataVO param) { + return rollService.namedWinner(param); + } + + @ApiOperation("取消指定roll房奖品") + @DeleteMapping("/cancelNamedWinner") + public R cancelNamedWinner(@RequestBody List rollUserPrizeIds) { + return rollService.cancelNamedWinner(rollUserPrizeIds); + } + + + @ApiOperation("获取ROLL房用户") + @GetMapping("/getRollUser/{rollId}") + public AjaxResult getRollUser(@PathVariable("rollId") Integer rollId) { + List rollUserList = rollService.getRollUser(rollId); + return success(rollUserList); + } + + @ApiOperation("加入机器人") + @PostMapping("/addRobot") + public AjaxResult addRobot(@RequestBody AddRobotBody addRobotBody) { + return rollService.addRobot(addRobotBody); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollJackpotController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollJackpotController.java new file mode 100644 index 0000000..a922bcf --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollJackpotController.java @@ -0,0 +1,70 @@ +package com.ruoyi.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.domain.entity.roll.TtRollJackpot; +import com.ruoyi.admin.service.TtRollJackpotService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.List; + +@Api(tags = "管理端 roll奖池") +@RestController +@RequestMapping("/admin/rollJackpot") +public class TtRollJackpotController extends BaseController { + + private final TtRollJackpotService rollJackpotService; + + public TtRollJackpotController(TtRollJackpotService rollJackpotService) { + this.rollJackpotService = rollJackpotService; + } + + @ApiOperation("奖池列表") + @GetMapping("/list") + public PageDataInfo list(@RequestParam(value = "jackpotName", required = false) String jackpotName) { + startPage(); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotEmpty(jackpotName)) { + wrapper.like(TtRollJackpot::getJackpotName, jackpotName); + } + List list = rollJackpotService.list(wrapper); + return getPageData(list); + } + + @GetMapping(value = "/{jackpotId}") + public R getInfo(@PathVariable("jackpotId") Long jackpotId) { + TtRollJackpot rollJackpot = rollJackpotService.getById(jackpotId); + return R.ok(rollJackpot); + } + + @ApiOperation("添加奖池") + @PostMapping + public AjaxResult add(@RequestBody TtRollJackpot ttRollJackpot) { + ttRollJackpot.setCreateBy(getUsername()); + ttRollJackpot.setCreateTime(DateUtils.getNowDate()); + ttRollJackpot.setTotalPrice(BigDecimal.ZERO); + return toAjax(rollJackpotService.save(ttRollJackpot)); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtRollJackpot ttRollJackpot) { + ttRollJackpot.setUpdateBy(getUsername()); + ttRollJackpot.setUpdateTime(DateUtils.getNowDate()); + return toAjax(rollJackpotService.updateById(ttRollJackpot)); + } + + @DeleteMapping("/remove/{jackpotId}") + public AjaxResult remove(@PathVariable Long jackpotId) { + String msg = rollJackpotService.removeRollJackpotById(jackpotId); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollJackpotOrnamentsController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollJackpotOrnamentsController.java new file mode 100644 index 0000000..53adec1 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtRollJackpotOrnamentsController.java @@ -0,0 +1,80 @@ +package com.ruoyi.admin.controller; + +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.domain.dto.rollJackpotOrnament.RollJOEdit; +import com.ruoyi.domain.entity.roll.TtRoll; +import com.ruoyi.domain.entity.roll.TtRollJackpotOrnaments; +import com.ruoyi.admin.service.TtRollJackpotOrnamentsService; +import com.ruoyi.admin.service.TtRollService; +import com.ruoyi.domain.entity.roll.TtRollJackpotOrnamentsBody; +import com.ruoyi.domain.vo.TtRollJackpotOrnamentsDataVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@Api(tags = "管理端 roll奖池物品管理") +@RestController +@RequestMapping("/admin/rollJackpotOrnaments") +public class TtRollJackpotOrnamentsController extends BaseController { + + private final TtRollService rollService; + private final TtRollJackpotOrnamentsService rollJackpotOrnamentsService; + + public TtRollJackpotOrnamentsController(TtRollService rollService, + TtRollJackpotOrnamentsService rollJackpotOrnamentsService) { + this.rollService = rollService; + this.rollJackpotOrnamentsService = rollJackpotOrnamentsService; + } + + @ApiOperation("物品列表") + @GetMapping("/list") + public PageDataInfo list(TtRollJackpotOrnamentsBody rollJackpotOrnamentsBody) { + startPage(); + List list = rollJackpotOrnamentsService.queryList(rollJackpotOrnamentsBody); + return getPageData(list); + } + + @GetMapping(value = "/{rollJackpotOrnamentsId}") + public R getInfo(@PathVariable("rollJackpotOrnamentsId") Long rollJackpotOrnamentsId) { + TtRollJackpotOrnaments rollJackpotOrnaments = rollJackpotOrnamentsService.getById(rollJackpotOrnamentsId); + return R.ok(rollJackpotOrnaments); + } + + @PostMapping + public AjaxResult add(@RequestBody TtRollJackpotOrnaments ttRollJackpotOrnaments) { + String msg = rollJackpotOrnamentsService.insertRollJackpotOrnaments(ttRollJackpotOrnaments); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @ApiOperation("修改奖池道具") + @PutMapping + public AjaxResult edit(@RequestBody TtRollJackpotOrnaments rollJOEdit) { + String msg = rollJackpotOrnamentsService.updateRollJackpotOrnamentsById(rollJOEdit); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @DeleteMapping("/{jackpotId}/{rollJackpotOrnamentsIds}") + public AjaxResult remove(@PathVariable("jackpotId") Integer jackpotId, + @PathVariable("rollJackpotOrnamentsIds") Long[] rollJackpotOrnamentsIds) { + List rollList = new LambdaQueryChainWrapper<>(rollService.getBaseMapper()).eq(TtRoll::getJackpotId, jackpotId) + .eq(TtRoll::getRollStatus, "0").eq(TtRoll::getDelFlag, "0").list(); + if (StringUtils.isNotNull(rollList) && !rollList.isEmpty()) + return AjaxResult.error("该奖池正在被进行中的Roll房使用,禁止删除奖品!"); + return toAjax(rollJackpotOrnamentsService.removeByIds(Arrays.asList(rollJackpotOrnamentsIds))); + } + + @ApiOperation("奖池批量填货") + @PostMapping("/batchAdd/{rollJackpotId}/{OrnamentsIds}") + public AjaxResult batchAdd(@PathVariable Integer rollJackpotId, @PathVariable Long[] OrnamentsIds) { + String msg = rollJackpotOrnamentsService.batchAdd(rollJackpotId, Arrays.asList(OrnamentsIds)); + return StringUtils.isEmpty(msg) ? AjaxResult.success("批量填货成功,请手动修改饰品数量!") : AjaxResult.error(msg); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtTaskCenterController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtTaskCenterController.java new file mode 100644 index 0000000..34c802a --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtTaskCenterController.java @@ -0,0 +1,94 @@ +package com.ruoyi.admin.controller; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.domain.other.TtTaskCenter; +import com.ruoyi.admin.service.TtTaskCenterService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 任务中心Controller + * + * @author ruoyi + * @date 2024-05-25 + */ +@RestController +@RequestMapping("/admin/taskCenter") +public class TtTaskCenterController extends BaseController +{ + @Autowired + private TtTaskCenterService ttTaskCenterService; + + /** + * 查询任务列表 + */ + @PreAuthorize("isAuthenticated()") + @GetMapping("/list") + public TableDataInfo list(TtTaskCenter ttTaskCenter) + { + startPage(); + List list = ttTaskCenterService.selectTtTaskCenterList(ttTaskCenter); + return getDataTable(list); + } + + /** + * 获取任务详细信息 + */ + @PreAuthorize("isAuthenticated()") + @GetMapping(value = "/{taskId}") + public AjaxResult getInfo(@PathVariable("taskId") Integer taskId) + { + return success(ttTaskCenterService.selectTtTaskCenterByTaskId(taskId)); + } + + /** + * 新增任务 + */ + @PreAuthorize("isAuthenticated()") + @Log(title = "任务中心", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody TtTaskCenter ttTaskCenter) + { + ttTaskCenter.setCreateBy(getUsername()); + return toAjax(ttTaskCenterService.insertTtTaskCenter(ttTaskCenter)); + } + + /** + * 修改任务 + */ + @PreAuthorize("isAuthenticated()") + @Log(title = "任务中心", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody TtTaskCenter ttTaskCenter) + { + ttTaskCenter.setUpdateBy(getUsername()); + return toAjax(ttTaskCenterService.updateTtTaskCenter(ttTaskCenter)); + } + + /** + * 删除任务 + */ + @PreAuthorize("isAuthenticated()") + @Log(title = "任务中心", businessType = BusinessType.DELETE) + @DeleteMapping("/{taskIds}") + public AjaxResult remove(@PathVariable Integer[] taskIds) + { + return error("任务创建后无法删除"); + // return toAjax(ttTaskCenterService.deleteTtTaskCenterByTaskIds(taskIds)); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtTimeRollController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtTimeRollController.java new file mode 100644 index 0000000..53b8361 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtTimeRollController.java @@ -0,0 +1,81 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.entity.roll.TtTimeRoll; +import com.ruoyi.admin.service.TtTimeRollService; +import com.ruoyi.domain.vo.TtRollPrizeDataVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import org.quartz.SchedulerException; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/admin/timeRoll") +public class TtTimeRollController extends BaseController { + + private final TtTimeRollService timeRollService; + + public TtTimeRollController(TtTimeRollService timeRollService) { + this.timeRollService = timeRollService; + } + + @GetMapping("/list") + public PageDataInfo list(){ + startPage(); + List list = timeRollService.list(); + return getPageData(list); + } + + @GetMapping(value = "/{timeRollId}") + public R getInfo(@PathVariable("timeRollId") Integer timeRollId) { + TtTimeRoll timeRoll = timeRollService.getById(timeRollId); + return R.ok(timeRoll); + } + + @PostMapping + public AjaxResult add(@RequestBody TtTimeRoll ttTimeRoll) throws SchedulerException, TaskException { + ttTimeRoll.setCreateBy(getUsername()); + ttTimeRoll.setCreateTime(DateUtils.getNowDate()); + String msg = timeRollService.insertTimeRoll(ttTimeRoll); + return StringUtils.isEmpty(msg) ? AjaxResult.success("新增成功!") : AjaxResult.error(msg); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtTimeRoll ttTimeRoll) { + ttTimeRoll.setUpdateBy(getUsername()); + ttTimeRoll.setUpdateTime(DateUtils.getNowDate()); + return toAjax(timeRollService.updateById(ttTimeRoll)); + } + + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody TtTimeRoll ttTimeRoll) throws SchedulerException { + String msg = timeRollService.changeStatus(ttTimeRoll); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @DeleteMapping("/remove/{id}") + public AjaxResult remove(@PathVariable Integer id) throws SchedulerException { + return timeRollService.removeTimeRollById(id); + } + + @GetMapping("/getTimeRollPrizeList/{id}") + public R> getTimeRollPrizeList(@PathVariable("id") Integer id) { + List list = timeRollService.getTimeRollPrizeList(id); + List sortedList = list.stream().sorted((a, b) -> b.getUsePrice().compareTo(a.getUsePrice())).collect(Collectors.toList()); + return R.ok(sortedList); + } + + @PostMapping("/namedWinner") + public AjaxResult namedWinner(@RequestBody TtRollPrizeDataVO rollPrizeData) { + String msg = timeRollService.namedWinner(rollPrizeData); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUpgradeFailOrnamentsController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUpgradeFailOrnamentsController.java new file mode 100644 index 0000000..3cfc07f --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUpgradeFailOrnamentsController.java @@ -0,0 +1,83 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtUpgradeFailOrnamentsService; +import com.ruoyi.domain.other.TtUpgradeFailOrnaments; +import com.ruoyi.domain.vo.TtUpgradeFailOrnamentsDataVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +@Api(tags = "管理端 失败物品奖励") +@RestController +@RequestMapping("/admin/upgradeFailOrnaments") +public class TtUpgradeFailOrnamentsController extends BaseController { + + private final TtUpgradeFailOrnamentsService upgradeFailOrnamentsService; + + public TtUpgradeFailOrnamentsController(TtUpgradeFailOrnamentsService upgradeFailOrnamentsService) { + this.upgradeFailOrnamentsService = upgradeFailOrnamentsService; + } + + @AllArgsConstructor + @NoArgsConstructor + @Data + public static class listParam { + @NotNull(message = "参与升级物品id不能为空") + private Integer upgradeOrnamentId; + } + @ApiOperation("失败奖励列表") + @PostMapping("/list") + public PageDataInfo list(@RequestBody listParam param) { + startPage(); + List list = upgradeFailOrnamentsService.queryList(param); + return getPageData(list); + } + + @AllArgsConstructor + @NoArgsConstructor + @Data + public static class BatchAddParam{ + @NotNull(message = "升级物品id不能为空") + private Integer upgradeId; +// @NotEmpty(message = "失败物品列表不能为空") + private Map ornamentsInfo; + + @NotEmpty(message = "失败物品列表不能为空") + private List ornamentsIds; + + //是否应用所有 + private Boolean useAllFlag; + } + @ApiOperation("批量添加失败奖励") + @PostMapping("/batchAdd") + public AjaxResult batchAdd(@RequestBody @Validated BatchAddParam param) { + return upgradeFailOrnamentsService.batchAdd(param); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtUpgradeFailOrnamentsDataVO ttUpgradeFailOrnamentsDataVO) { + String msg = upgradeFailOrnamentsService.updateUpgradeFailOrnamentsById(ttUpgradeFailOrnamentsDataVO); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + return toAjax(upgradeFailOrnamentsService.removeByIds(Arrays.asList(ids))); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUpgradeOrnamentsController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUpgradeOrnamentsController.java new file mode 100644 index 0000000..06f7ec0 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUpgradeOrnamentsController.java @@ -0,0 +1,81 @@ +package com.ruoyi.admin.controller; + +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.domain.other.TtUpgradeFailOrnaments; +import com.ruoyi.domain.other.TtUpgradeOrnaments; +import com.ruoyi.admin.service.TtUpgradeFailOrnamentsService; +import com.ruoyi.admin.service.TtUpgradeOrnamentsService; +import com.ruoyi.domain.other.TtUpgradeOrnamentsBody; +import com.ruoyi.domain.vo.TtUpgradeOrnamentsDataVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Api(tags = "管理端 幸运升级") +@RestController +@RequestMapping("/admin/upgradeOrnaments") +public class TtUpgradeOrnamentsController extends BaseController { + + private final TtUpgradeFailOrnamentsService upgradeFailOrnamentsService; + private final TtUpgradeOrnamentsService upgradeOrnamentsService; + + public TtUpgradeOrnamentsController(TtUpgradeFailOrnamentsService upgradeFailOrnamentsService, + TtUpgradeOrnamentsService upgradeOrnamentsService) { + this.upgradeFailOrnamentsService = upgradeFailOrnamentsService; + this.upgradeOrnamentsService = upgradeOrnamentsService; + } + + @ApiOperation("获取参与幸运升级的物品") + @GetMapping("/list") + public PageDataInfo list(TtUpgradeOrnamentsBody ttUpgradeOrnamentsBody){ + startPage(); + List list = upgradeOrnamentsService.queryList(ttUpgradeOrnamentsBody); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtUpgradeOrnaments ttUpgradeOrnaments = upgradeOrnamentsService.getById(id); + return R.ok(ttUpgradeOrnaments); + } + + @ApiOperation("批量填货") + @PostMapping("/batchAdd/{ornamentsIds}") + public AjaxResult batchAdd(@PathVariable Long[] ornamentsIds) { + String msg = upgradeOrnamentsService.batchAdd(Arrays.asList(ornamentsIds)); + return StringUtils.isEmpty(msg) ? AjaxResult.success("批量上架成功,请手动修改幸运区间!") : AjaxResult.error(msg); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtUpgradeOrnamentsDataVO ttUpgradeOrnamentsDataVO) { + String msg = upgradeOrnamentsService.updateUpgradeOrnamentsById(ttUpgradeOrnamentsDataVO); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + List upgradeFailOrnamentsList = new LambdaQueryChainWrapper<>(upgradeFailOrnamentsService.getBaseMapper()) + .in(TtUpgradeFailOrnaments::getUpgradeId, Arrays.asList(ids)).list(); + List list = upgradeFailOrnamentsList.stream().map(TtUpgradeFailOrnaments::getId).collect(Collectors.toList()); + upgradeFailOrnamentsService.removeByIds(list); + return toAjax(upgradeOrnamentsService.removeByIds(Arrays.asList(ids))); + } + + @GetMapping("/getUpgradeProfitStatistics/{id}") + public R getUpgradeProfitStatistics(@PathVariable("id") Integer id){ + Map map = upgradeOrnamentsService.getUpgradeProfitStatistics(id); + return R.ok(map); + } + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUpgradeRecordController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUpgradeRecordController.java new file mode 100644 index 0000000..0c39cc2 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUpgradeRecordController.java @@ -0,0 +1,36 @@ +package com.ruoyi.admin.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.admin.service.TtUpgradeRecordService; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.upgrade.UpgradeCondition; +import com.ruoyi.domain.other.TtUpgradeRecord; +import com.ruoyi.domain.other.TtUpgradeRecordBody; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.domain.vo.upgrade.UpgradeRecordVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Api(tags = "管理端 幸运升级日志") +@RestController +@RequestMapping("/admin/upgradeRecord") +public class TtUpgradeRecordController extends BaseController { + + private final TtUpgradeRecordService upgradeRecordService; + + public TtUpgradeRecordController(TtUpgradeRecordService upgradeRecordService) { + this.upgradeRecordService = upgradeRecordService; + } + + @ApiOperation("幸运升级日志") + @PostMapping("/list") + public R> list(@RequestBody @Validated UpgradeCondition param){ + return upgradeRecordService.adminGetLog(param); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserAmountRecordsController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserAmountRecordsController.java new file mode 100644 index 0000000..7dc6109 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserAmountRecordsController.java @@ -0,0 +1,33 @@ +package com.ruoyi.admin.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.recorde.TtUserAmountRecords; +import com.ruoyi.admin.service.TtUserAmountRecordsService; +import com.ruoyi.domain.other.TtUserAmountRecordsBody; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.page.PageDataInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Api(tags = "管理端 金币账户") +@RestController +@RequestMapping("/admin/userAmountRecords") +public class TtUserAmountRecordsController extends BaseController { + + private final TtUserAmountRecordsService userAmountRecordsService; + + public TtUserAmountRecordsController(TtUserAmountRecordsService userAmountRecordsService) { + this.userAmountRecordsService = userAmountRecordsService; + } + + @ApiOperation("获取列表") + @PostMapping("/list") + public R list(@RequestBody @Validated TtUserAmountRecordsBody ttUserAmountRecordsBody) { + return userAmountRecordsService.queryList(ttUserAmountRecordsBody); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserAvatarController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserAvatarController.java new file mode 100644 index 0000000..cb57af5 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserAvatarController.java @@ -0,0 +1,59 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.other.TtUserAvatar; +import com.ruoyi.admin.service.TtUserAvatarService; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/admin/userAvatar") +@Slf4j +public class TtUserAvatarController extends BaseController { + + private final TtUserAvatarService userAvatarService; + + public TtUserAvatarController(TtUserAvatarService userAvatarService) { + this.userAvatarService = userAvatarService; + } + + @GetMapping("/list") + public PageDataInfo list() { + startPage(); + List list = userAvatarService.list(); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtUserAvatar ttUserAvatar = userAvatarService.getById(id); + ttUserAvatar.setAvatar(""); + return R.ok(ttUserAvatar); + } + + @PostMapping + public AjaxResult add(@RequestBody TtUserAvatar ttUserAvatar) { + if (StringUtils.isEmpty(ttUserAvatar.getAvatar())) ttUserAvatar.setAvatar(""); + else ttUserAvatar.setAvatar(RuoYiConfig.getDomainName() + ttUserAvatar.getAvatar()); + return toAjax(userAvatarService.save(ttUserAvatar)); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtUserAvatar ttUserAvatar) { + String msg = userAvatarService.updateUserAvatarById(ttUserAvatar); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + return toAjax(userAvatarService.removeByIds(Arrays.asList(ids))); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserBlendErcashController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserBlendErcashController.java new file mode 100644 index 0000000..88f5c89 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserBlendErcashController.java @@ -0,0 +1,32 @@ +package com.ruoyi.admin.controller; + + +import com.ruoyi.admin.service.TtUserBlendErcashService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.userRecord.BlendErcashCondition; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "管理端 收支明细") +@RestController +@RequestMapping("/admin/blendErcash") +@Slf4j +public class TtUserBlendErcashController extends BaseController { + + @Autowired + private TtUserBlendErcashService ttUserBlendErcashService; + + @ApiOperation("收支明细") + @PostMapping("/list") + public R list(@RequestBody @Validated BlendErcashCondition condition) { + return ttUserBlendErcashService.byCondition(condition); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserController.java new file mode 100644 index 0000000..bc00f80 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserController.java @@ -0,0 +1,183 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.ApiUserOnline; +import com.ruoyi.domain.other.TtUserBody; +import com.ruoyi.domain.other.TtUserPackSackBody; +import com.ruoyi.domain.vo.TtUserPackSackDataVO; +import com.ruoyi.domain.vo.user.TtUserVo; +import io.swagger.annotations.ApiOperation; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/admin/user") +public class TtUserController extends BaseController { + + private final TtUserService userService; + private final RedisCache redisCache; + private final TtBoxRecordsService boxRecordsService; + + public TtUserController(TtUserService ttUserService, + RedisCache redisCache, + TtBoxRecordsService boxRecordsService) { + this.userService = ttUserService; + this.redisCache = redisCache; + this.boxRecordsService = boxRecordsService; + } + + @Autowired + private TtUserMapper ttUserMapper; + + @GetMapping("/list") + public PageDataInfo list(TtUserBody ttUserBody) { + startPage(); + Pair> list = userService.queryList(ttUserBody); + list.getValue().forEach(ttUser -> ttUser.setPassword("")); + PageDataInfo pageDataInfo = getPageData(list.getValue()); + pageDataInfo.setTotal(list.getKey()); + return pageDataInfo; + } + + @PostMapping("/export") + public void export(HttpServletResponse response, TtUserBody ttUserBody) { + Pair> list = userService.queryList(ttUserBody); + ExcelUtil util = new ExcelUtil<>(TtUserVo.class); + util.exportExcel(response, list.getValue(), "用户信息列表"); + } + + @GetMapping(value = "/{userId}") + public R getInfo(@PathVariable("userId") Long userId) { + TtUser user = userService.getById(userId); + user.setPassword(null); + if (user.getRemark() != null && user.getRemark().length() > 5) user.setRemark(user.getRemark().substring(5)); + return R.ok(user); + } + + @ApiOperation("生成账号") + @PostMapping("/generateAccount/{num}") + public void generateAccount(HttpServletResponse response, @PathVariable("num") Integer num) { + List userList = userService.getAccountList(num); + userService.generateAccount(response, userList); + } + + @ApiOperation("生成账号") + @GetMapping("/generateAccountByPhoneNum") + public R generateAccountByPhoneNum(HttpServletResponse response, @RequestParam("phoneNum") String phoneNum, + @RequestParam("password") String password) { + TtUser user = userService.getAccountList(phoneNum, password); + return R.ok(user); + } + + @ApiOperation("生成机器人") + @PostMapping("/generateRobot/{num}") + public void generateRobot(HttpServletResponse response, @PathVariable("num") Integer num) { + List userList = userService.getRobotList(num); + userService.generateAccount(response, userList); + } + + @ApiOperation("修改用户信息") + @PutMapping + public AjaxResult edit(@RequestBody TtUser ttUser) { + ttUser.setUpdateBy(getUsername()); + ttUser.setUpdateTime(DateUtils.getNowDate()); + return userService.updateUserById(ttUser); + } + + @ApiOperation("修改邀请码") + @PostMapping("/editInvitationCode") + public AjaxResult editInvitationCode(@RequestBody TtUser ttUser) { + ttUser.setUpdateBy(getUsername()); + ttUser.setUpdateTime(DateUtils.getNowDate()); + return userService.updateUserInvitationCodeById(ttUser); + } + + @DeleteMapping("/{userIds}") + public AjaxResult remove(@PathVariable Long[] userIds) { + return toAjax(userService.removeByIds(Arrays.asList(userIds))); + } + + @GetMapping("/online/list") + public PageDataInfo list(String ipaddr, String userName) { + startPage(); + Collection redisKeys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); + List userOnlineList = redisKeys.stream().map(redisKey -> { + LoginUser user = redisCache.getCacheObject(redisKey); + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) { + return userService.selectOnlineByInfo(ipaddr, userName, user); + } else if (StringUtils.isNotEmpty(ipaddr)) { + return userService.selectOnlineByIpaddr(ipaddr, user); + } else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) { + return userService.selectOnlineByUserName(userName, user); + } else { + return userService.loginUserToUserOnline(user); + } + }).collect(Collectors.toList()); + Collections.reverse(userOnlineList); + userOnlineList.removeAll(Collections.singleton(null)); + return getPageData(userOnlineList); + } + + @GetMapping("/getPackSack") + public PageDataInfo getPackSack(TtUserPackSackBody ttUserPackSackBody) { + startPage(); + List list = userService.getPackSack(ttUserPackSackBody); + return getPageData(list); + } + + @PostMapping("/removeUserPackSackData/{ids}") + public AjaxResult removeUserPackSackData(@PathVariable Long[] ids) { + List boxRecordsList = boxRecordsService.getBaseMapper().selectBatchIds(Arrays.asList(ids)); + boxRecordsList = boxRecordsList.stream().peek(ttBoxRecords -> { + ttBoxRecords.setStatus(TtboxRecordStatus.ADMIN_DELETE.getCode()); + ttBoxRecords.setUpdateTime(DateUtils.getNowDate()); + }).collect(Collectors.toList()); + return toAjax(boxRecordsService.updateBatchById(boxRecordsList, 1)); + } + + @GetMapping("/getUserProfitStatistics/{userId}") + public R getUserProfitStatistics(@PathVariable("userId") Integer userId) { + Map map = userService.getUserProfitStatistics(userId); + return R.ok(map); + } + + @ApiOperation("重置用户余额和积分") + @PostMapping("/resetBalance/{userId}") + public AjaxResult resetUserBalance(@PathVariable("userId") Integer userId){ + TtUser ttUser = new TtUser(); + ttUser.setUserId(userId); + ttUser.setAccountAmount(BigDecimal.ZERO); + ttUser.setAccountCredits(BigDecimal.ZERO); + ttUser.setUpdateBy(getUsername()); + ttUser.setUpdateTime(DateUtils.getNowDate()); + return userService.updateUserById(ttUser); + } + + @ApiOperation("真删除用户(数据库删除,不可恢复)") + @DeleteMapping("/forceDelete/{userId}") + public AjaxResult forceDelete(@PathVariable("userId") Integer userId) { + ttUserMapper.removeByUserId(userId); + return AjaxResult.success("用户已彻底删除"); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserCreditsRecordsController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserCreditsRecordsController.java new file mode 100644 index 0000000..ba237c4 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtUserCreditsRecordsController.java @@ -0,0 +1,30 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.entity.recorde.TtUserCreditsRecords; +import com.ruoyi.admin.service.TtUserCreditsRecordsService; +import com.ruoyi.domain.other.TtUserCreditsRecordsBody; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.page.PageDataInfo; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/admin/userCreditsRecords") +public class TtUserCreditsRecordsController extends BaseController { + + private final TtUserCreditsRecordsService userCreditsRecordsService; + + public TtUserCreditsRecordsController(TtUserCreditsRecordsService userCreditsRecordsService) { + this.userCreditsRecordsService = userCreditsRecordsService; + } + + @GetMapping("/list") + public PageDataInfo list(TtUserCreditsRecordsBody ttUserCreditsRecordsBody) { + startPage(); + List list = userCreditsRecordsService.queryList(ttUserCreditsRecordsBody); + return getPageData(list); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtVipLevelController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtVipLevelController.java new file mode 100644 index 0000000..e597d37 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtVipLevelController.java @@ -0,0 +1,57 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.domain.other.TtVipLevel; +import com.ruoyi.admin.service.TtVipLevelService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/admin/vipLevel") +public class TtVipLevelController extends BaseController { + + private final TtVipLevelService vipLevelService; + + public TtVipLevelController(TtVipLevelService vipLevelService) { + this.vipLevelService = vipLevelService; + } + + @GetMapping("/list") + public PageDataInfo list() { + startPage(); + List list = vipLevelService.list(); + return getPageData(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtVipLevel ttVipLevel = vipLevelService.getById(id); + ttVipLevel.setIcon(""); + return R.ok(ttVipLevel); + } + + @PostMapping("generateVipLevel/{num}") + public AjaxResult generateVipLevel(@PathVariable("num") Integer num) { + String msg = vipLevelService.generateVipLevel(num); + return StringUtils.isEmpty(msg) ? AjaxResult.success("生成成功,请修改参数!") : AjaxResult.error(msg); + } + + @PutMapping + public AjaxResult edit(@RequestBody TtVipLevel ttVipLevel) { + ttVipLevel.setUpdateTime(DateUtils.getNowDate()); + String msg = vipLevelService.updateVipLevelById(ttVipLevel); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @PostMapping + public AjaxResult truncateVipLevel() { + vipLevelService.truncateVipLevel(); + return AjaxResult.success("重置成功,请重新设置!"); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtWelfareController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtWelfareController.java new file mode 100644 index 0000000..266e575 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtWelfareController.java @@ -0,0 +1,99 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtWelfareService; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.domain.other.TtWelfare; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 福利列表Controller + * + * @author ruoyi + * @date 2024-05-11 + */ +@RestController +@RequestMapping("/admin/welfare") +public class TtWelfareController extends BaseController +{ + @Autowired + private TtWelfareService ttWelfareService; + + /** + * 查询福利列表 + */ + @PreAuthorize("isAuthenticated()") + @GetMapping("/list") + public TableDataInfo list(TtWelfare ttWelfare) + { + startPage(); + List list = ttWelfareService.selectTtWelfareList(ttWelfare); + return getDataTable(list); + } + + /** + * 导出福利列表 + */ + @PreAuthorize("isAuthenticated()") + @Log(title = "福利列表", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, TtWelfare ttWelfare) + { + List list = ttWelfareService.selectTtWelfareList(ttWelfare); + ExcelUtil util = new ExcelUtil(TtWelfare.class); + util.exportExcel(response, list, "福利列表数据"); + } + + /** + * 获取福利列表详细信息 + */ + @PreAuthorize("isAuthenticated()") + @GetMapping(value = "/{welfareId}") + public AjaxResult getInfo(@PathVariable("welfareId") Integer welfareId) + { + return success(ttWelfareService.selectTtWelfareByWelfareId(welfareId)); + } + + /** + * 新增福利列表 + */ + @PreAuthorize("isAuthenticated()") + @Log(title = "福利列表", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody TtWelfare ttWelfare) + { + return toAjax(ttWelfareService.insertTtWelfare(ttWelfare)); + } + + /** + * 修改福利列表 + */ + @PreAuthorize("isAuthenticated()") + @Log(title = "福利列表", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody TtWelfare ttWelfare) + { + return toAjax(ttWelfareService.updateTtWelfare(ttWelfare)); + } + + /** + * 删除福利列表 + */ + @PreAuthorize("isAuthenticated()") + @Log(title = "福利列表", businessType = BusinessType.DELETE) + @DeleteMapping("/{welfareIds}") + public AjaxResult remove(@PathVariable Integer[] welfareIds) + { + return toAjax(ttWelfareService.deleteTtWelfareByWelfareIds(welfareIds)); + } +} + diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtWelfareMonthlyRechargesController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtWelfareMonthlyRechargesController.java new file mode 100644 index 0000000..a9c109c --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/TtWelfareMonthlyRechargesController.java @@ -0,0 +1,104 @@ +package com.ruoyi.admin.controller; + +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.domain.other.TtWelfareMonthlyRecharges; +import com.ruoyi.admin.service.ITtWelfareMonthlyRechargesService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 每月充值福利Controller + * + * @author ruoyi + * @date 2024-06-24 + */ +@RestController +@RequestMapping("/admin/recharges") +public class TtWelfareMonthlyRechargesController extends BaseController +{ + @Autowired + private ITtWelfareMonthlyRechargesService ttWelfareMonthlyRechargesService; + + /** + * 查询每月充值福利列表 + */ + @PreAuthorize("@ss.hasPermi('admin:recharges:list')") + @GetMapping("/list") + public TableDataInfo list(TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges) + { + startPage(); + List list = ttWelfareMonthlyRechargesService.selectTtWelfareMonthlyRechargesList(ttWelfareMonthlyRecharges); + return getDataTable(list); + } + + /** + * 导出每月充值福利列表 + */ + @PreAuthorize("@ss.hasPermi('admin:recharges:export')") + @Log(title = "每月充值福利", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges) + { + List list = ttWelfareMonthlyRechargesService.selectTtWelfareMonthlyRechargesList(ttWelfareMonthlyRecharges); + ExcelUtil util = new ExcelUtil(TtWelfareMonthlyRecharges.class); + util.exportExcel(response, list, "每月充值福利数据"); + } + + /** + * 获取每月充值福利详细信息 + */ + @PreAuthorize("@ss.hasPermi('admin:recharges:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Integer id) + { + return success(ttWelfareMonthlyRechargesService.selectTtWelfareMonthlyRechargesById(id)); + } + + /** + * 新增每月充值福利 + */ + @PreAuthorize("@ss.hasPermi('admin:recharges:add')") + @Log(title = "每月充值福利", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges) + { + return toAjax(ttWelfareMonthlyRechargesService.insertTtWelfareMonthlyRecharges(ttWelfareMonthlyRecharges)); + } + + /** + * 修改每月充值福利 + */ + @PreAuthorize("@ss.hasPermi('admin:recharges:edit')") + @Log(title = "每月充值福利", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges) + { + return toAjax(ttWelfareMonthlyRechargesService.updateTtWelfareMonthlyRecharges(ttWelfareMonthlyRecharges)); + } + + /** + * 删除每月充值福利 + */ + @PreAuthorize("@ss.hasPermi('admin:recharges:remove')") + @Log(title = "每月充值福利", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) + { + return toAjax(ttWelfareMonthlyRechargesService.deleteTtWelfareMonthlyRechargesByIds(ids)); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/WebsitePropertyController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/WebsitePropertyController.java new file mode 100644 index 0000000..bcd052f --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/WebsitePropertyController.java @@ -0,0 +1,77 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.admin.service.WebsitePropertyService; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.domain.dto.queryCondition.OrnamentCondition; +import com.ruoyi.domain.vo.WebsitePropertyDataVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@Api(tags = "管理端 道具管理") +@RestController +@RequestMapping("/admin/websiteProperty") +@Slf4j +public class WebsitePropertyController extends BaseController { + + private final WebsitePropertyService websitePropertyService; + private final TtOrnamentService ornamentsService; + + public WebsitePropertyController(WebsitePropertyService websitePropertyService, + TtOrnamentService ornamentsService) { + this.websitePropertyService = websitePropertyService; + this.ornamentsService = ornamentsService; + } + + // @ApiOperation("获取道具列表") + // @GetMapping("/list") + // public PageDataInfo list() { + // startPage(); + // List list = websitePropertyService.list(); + // return getPageData(Arrays.asList()); + // } + + @ApiOperation("获取道具列表") + @GetMapping("/list") + public TableDataInfo list(OrnamentCondition condition) { + startPage(); + List list = ornamentsService.byCondition(condition); + return getDataTable(list); + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + WebsitePropertyDataVO websitePropertyDataVO = websitePropertyService.getById(id); + websitePropertyDataVO.setImageUrl(""); + return R.ok(websitePropertyDataVO); + } + + @PostMapping + public AjaxResult add(@RequestBody WebsitePropertyDataVO websitePropertyDataVO) { + String msg = websitePropertyService.save(websitePropertyDataVO); + return StringUtils.isEmpty(msg) ? AjaxResult.success("新增网站道具成功!") : AjaxResult.error(msg); + } + + @PutMapping + public AjaxResult edit(@RequestBody WebsitePropertyDataVO websitePropertyDataVO) { + String msg = websitePropertyService.updateWebsitePropertyById(websitePropertyDataVO); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } + + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + // return toAjax(ornamentsService.removeByIds(Arrays.asList(ids))); + return toAjax(websitePropertyService.deleteWebsitePropertyByIds(ids)); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/WebsiteSetupController.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/WebsiteSetupController.java new file mode 100644 index 0000000..df9a852 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/WebsiteSetupController.java @@ -0,0 +1,40 @@ +package com.ruoyi.admin.controller; + +import com.ruoyi.admin.service.WebsiteSetupService; +import com.ruoyi.domain.other.OperationalStatistics; +import com.ruoyi.domain.other.ParameterSettingBody; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/admin/websiteSetup") +public class WebsiteSetupController extends BaseController { + + private final WebsiteSetupService websiteSetupService; + + public WebsiteSetupController(WebsiteSetupService websiteSetupService) { + this.websiteSetupService = websiteSetupService; + } + + @GetMapping("getOperationalStatistics") + public R> getOperationalStatistics() { + List list = websiteSetupService.getOperationalStatistics(); + return R.ok(list); + } + + @PostMapping("/getParameterSetting") + public R getParameterSetting() { + return R.ok(websiteSetupService.getParameterSetting()); + } + + @PostMapping("/updateParameterSetting") + public AjaxResult updateParameterSetting(@RequestBody ParameterSettingBody parameterSettingBody) { + String msg = websiteSetupService.updateParameterSetting(parameterSettingBody); + return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/domain/body/AddRobotBody.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/domain/body/AddRobotBody.java new file mode 100644 index 0000000..453baf9 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/domain/body/AddRobotBody.java @@ -0,0 +1,14 @@ +package com.ruoyi.admin.domain.body; + +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Data +public class AddRobotBody { + + @NotNull(message = "ROLL房ID不能为空") + private Integer rollId; + + private Integer robotNum; +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/domain/body/BoxMockRequest.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/domain/body/BoxMockRequest.java new file mode 100644 index 0000000..5266f51 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/domain/body/BoxMockRequest.java @@ -0,0 +1,24 @@ +package com.ruoyi.admin.domain.body; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +//设置开箱临时爆率请求参数 +@Data +public class BoxMockRequest { + private Integer uid; + + //临时爆率列表 + private List items; + + @Data + public static class MockItem implements Serializable { + //最低价格(包含) + private BigDecimal lowPrice; + //最高价格(包含) + private BigDecimal highPrice; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/enums/BoxPoolType.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/enums/BoxPoolType.java new file mode 100644 index 0000000..fc89537 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/enums/BoxPoolType.java @@ -0,0 +1,33 @@ +package com.ruoyi.admin.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@AllArgsConstructor +@Getter +public enum BoxPoolType { + ANCHOR_PLAYER("01", "主播用户奖池"), + NORMAL_PLAYER("02", "普通用户奖池"), + COMPENSATION("03", "补偿奖池"), + THIRD_EXPLOSIVE("04", "三级爆率奖池"), + ROBOT_PLAYER("05", "机器人用户奖池"); + private final String id; + private final String desc; + + public static List allTypes() { + return Arrays.stream(values()).collect(Collectors.toList()); + } + + public static BoxPoolType fromId(String id) { + for (BoxPoolType type : values()) { + if (type.getId().equals(id)) { + return type; + } + } + return null; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/ConfigMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/ConfigMapper.java new file mode 100644 index 0000000..c3a0786 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/ConfigMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.ConfigData; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ConfigMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtAdvertisementMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtAdvertisementMapper.java new file mode 100644 index 0000000..2ba699d --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtAdvertisementMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtAdvertisement; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtAdvertisementMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtAnnouncementMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtAnnouncementMapper.java new file mode 100644 index 0000000..e387505 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtAnnouncementMapper.java @@ -0,0 +1,20 @@ +package com.ruoyi.admin.mapper; + +import com.ruoyi.domain.other.TtAnnouncement; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface TtAnnouncementMapper { + + List getAnnouncementList(); + + TtAnnouncement getAnnouncementByAnnouncementId(Integer announcementId); + + int addAnnouncement(TtAnnouncement ttAnnouncement); + + int editAnnouncement(TtAnnouncement ttAnnouncement); + + int removeAnnouncementByAnnouncementId(Integer announcementId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBannerMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBannerMapper.java new file mode 100644 index 0000000..83c87cc --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBannerMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtBanner; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtBannerMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBonusMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBonusMapper.java new file mode 100644 index 0000000..5ea8570 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBonusMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtBonus; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtBonusMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBonusReceiveRecordMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBonusReceiveRecordMapper.java new file mode 100644 index 0000000..550d749 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBonusReceiveRecordMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtBonusReceiveRecord; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtBonusReceiveRecordMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxMapper.java new file mode 100644 index 0000000..8dd76fc --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxMapper.java @@ -0,0 +1,18 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtBoxBody; +import com.ruoyi.domain.vo.TtBoxDataVO; +import com.ruoyi.domain.vo.box.BoxGlobalData; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface TtBoxMapper extends BaseMapper { + List selectTtBoxList(TtBoxBody ttBoxBody); + + BoxGlobalData globalData(@Param("boxId") Integer boxId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxOpenChanceMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxOpenChanceMapper.java new file mode 100644 index 0000000..f777333 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxOpenChanceMapper.java @@ -0,0 +1,61 @@ +package com.ruoyi.admin.mapper; + +import java.util.List; +import com.ruoyi.admin.domain.TtBoxOpenChance; + +/** + * 开箱机会Mapper接口 + * + * @author ruoyi + * @date 2024-07-09 + */ +public interface TtBoxOpenChanceMapper +{ + /** + * 查询开箱机会 + * + * @param ornamentId 开箱机会主键 + * @return 开箱机会 + */ + public TtBoxOpenChance selectTtBoxOpenChanceByOrnamentId(Integer ornamentId); + + /** + * 查询开箱机会列表 + * + * @param ttBoxOpenChance 开箱机会 + * @return 开箱机会集合 + */ + public List selectTtBoxOpenChanceList(TtBoxOpenChance ttBoxOpenChance); + + /** + * 新增开箱机会 + * + * @param ttBoxOpenChance 开箱机会 + * @return 结果 + */ + public int insertTtBoxOpenChance(TtBoxOpenChance ttBoxOpenChance); + + /** + * 修改开箱机会 + * + * @param ttBoxOpenChance 开箱机会 + * @return 结果 + */ + public int updateTtBoxOpenChance(TtBoxOpenChance ttBoxOpenChance); + + /** + * 删除开箱机会 + * + * @param ornamentId 开箱机会主键 + * @return 结果 + */ + public int deleteTtBoxOpenChanceByOrnamentId(Integer ornamentId); + + /** + * 批量删除开箱机会 + * + * @param ornamentIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteTtBoxOpenChanceByOrnamentIds(Integer[] ornamentIds); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxOrnamentsMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxOrnamentsMapper.java new file mode 100644 index 0000000..31979ed --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxOrnamentsMapper.java @@ -0,0 +1,24 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtBoxOrnaments; +import com.ruoyi.domain.vo.TtBoxOrnamentsDataVO; +import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface TtBoxOrnamentsMapper extends BaseMapper { + + List selectBoxIdList(); + + List selectTtBoxOrnamentsList(Integer boxId); + + List ornametBelongbox(Integer integer); + + List simpleBoxDetail(Integer boxId); + + List allOrnId(@Param("boxId") Integer boxId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxRecordsMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxRecordsMapper.java new file mode 100644 index 0000000..8f0fd0b --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxRecordsMapper.java @@ -0,0 +1,71 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.dto.boxRecords.TtBoxRecordsNum; +import com.ruoyi.domain.dto.boxRecords.queryCondition; +import com.ruoyi.domain.dto.packSack.DecomposeLogCondition; +import com.ruoyi.domain.dto.roll.GetRollOpenPrizeParam; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.other.TtBoxRecordsBody; +import com.ruoyi.domain.other.TtBoxUserIdPrice; +import com.ruoyi.domain.vo.TtBoxRecordsDataVO; +import com.ruoyi.domain.vo.TtUserPackSackDataVO; +import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; + +@Mapper +public interface TtBoxRecordsMapper extends BaseMapper { + + List selectBoxRecordsList(TtBoxRecordsBody ttBoxRecordsBody); + + List selectBoxRecordsByDate(@Param("boxId") Integer boxId, @Param("formattedDate") String formattedDate); + + List selectBoxRecordsByOrnamentIds(@Param("boxId") Integer boxId, @Param("ornamentIds") List ornamentIds, @Param("beginTime") String beginTime); + + List decomposeLog(DecomposeLogCondition param); + + List myOwnFights(@Param("playerId") Integer playerId); + + List byCondition(@Param("userId") Integer userId, + @Param("limit") Integer limit, + @Param("size") Integer size, + @Param("orderByFie") Integer orderByFie, + @Param("boxRecordId") Long boxRecordId, + @Param("boxId") Integer boxId, + @Param("userType") String userType, + @Param("source") List source, + @Param("status") List status, + @Param("ornamentPriceMin") BigDecimal ornamentPriceMin, + @Param("ornamentPriceMax") BigDecimal ornamentPriceMax, + @Param("ornamentLevelIds") List ornamentLevelIds + ); + + List tenTopQuery( + @Param("limit") Integer limit, + @Param("size") Integer size, + @Param("source") List source, + @Param("status") List status + ); + + List propRankOfDay(@Param("beginT") String beginT, + @Param("endT") String endT, + @Param("sources") Integer[] sources, + @Param("number") Integer number); + + List rollOpenPrize(GetRollOpenPrizeParam param); + + Integer checkDeliveryAble(@Param("packSackIds") List packSackIds); + + Integer checkAllDeliveryAble(@Param("userId") Integer userId); + + /** + * 汰换后的状态更新 + */ + int updateStatusByIds(@Param("list") List list, @Param("status") String status); + + List boxUserIdPrice(@Param("userIds") List userIds, @Param("startDate") String startDate, @Param("endDate") String endDate); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxThirdExplosiveUserMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxThirdExplosiveUserMapper.java new file mode 100644 index 0000000..d754366 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxThirdExplosiveUserMapper.java @@ -0,0 +1,10 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.box.TtBoxThirdExplosiveUser; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtBoxThirdExplosiveUserMapper extends BaseMapper { + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxTypeMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxTypeMapper.java new file mode 100644 index 0000000..8c37408 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtBoxTypeMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtBoxType; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtBoxTypeMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtCompRecordMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtCompRecordMapper.java new file mode 100644 index 0000000..35aa148 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtCompRecordMapper.java @@ -0,0 +1,23 @@ +package com.ruoyi.admin.mapper; + +import com.ruoyi.domain.other.TtCompRecord; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +@Mapper +public interface TtCompRecordMapper { + + public TtCompRecord selectTtCompRecord( + @Param("userId") Integer userId, + @Param("boxId") Integer boxId + ); + + public int insertTtCompRecord(TtCompRecord ttCompRecord); + + public int updateTtCompRecord(TtCompRecord ttCompRecord); + + public int deleteTtCompRecord( + @Param("userId") Integer userId, + @Param("boxId") Integer boxId + ); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtContentMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtContentMapper.java new file mode 100644 index 0000000..041ca44 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtContentMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtContent; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtContentMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtContentTypeMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtContentTypeMapper.java new file mode 100644 index 0000000..c16a343 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtContentTypeMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtContentType; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtContentTypeMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtDeliveryRecordMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtDeliveryRecordMapper.java new file mode 100644 index 0000000..035e76a --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtDeliveryRecordMapper.java @@ -0,0 +1,28 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.dto.userRecord.DeliveryRecordsConfition; +import com.ruoyi.domain.entity.delivery.TtDeliveryRecord; +import com.ruoyi.domain.other.TtDeliveryApplyBody; +import com.ruoyi.domain.vo.DeliveryApplyVO; +import com.ruoyi.domain.other.TtDeliveryRecordBody; +import com.ruoyi.domain.vo.TtDeliveryRecordDataVO; +import com.ruoyi.domain.vo.delivery.DeliveryRecordVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface TtDeliveryRecordMapper extends BaseMapper { + List getDeliveryApplyList(TtDeliveryApplyBody deliveryApplyBody); + + List getDeliveryRecordList(TtDeliveryRecordBody deliveryRecordBody); + + List getDeliveryRecordByUserList(TtDeliveryRecordBody deliveryRecordBody); + + List byCondition(@Param("statusList") List statusList, + @Param("uidList") List uidList, + @Param("limit") Integer limit, + @Param("size") Integer size); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtExponentMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtExponentMapper.java new file mode 100644 index 0000000..a6c6967 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtExponentMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.dto.exponent.ExponentOpenBoxVo; +import com.ruoyi.domain.entity.exponent.TtExponent; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface TtExponentMapper extends BaseMapper { + /** + * 获取开箱记录 + */ + List exponentOpenBox(@Param("userId") Integer userId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtExponentUserBoxMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtExponentUserBoxMapper.java new file mode 100644 index 0000000..231e8df --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtExponentUserBoxMapper.java @@ -0,0 +1,10 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.exponent.TtExponentUserBox; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtExponentUserBoxMapper extends BaseMapper { + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtExponentUserMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtExponentUserMapper.java new file mode 100644 index 0000000..99430ea --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtExponentUserMapper.java @@ -0,0 +1,10 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.exponent.TtExponentUser; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtExponentUserMapper extends BaseMapper { + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFightMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFightMapper.java new file mode 100644 index 0000000..f4a12d2 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFightMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.fight.TtFight; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtFightMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFightRankingRewardMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFightRankingRewardMapper.java new file mode 100644 index 0000000..99cd146 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFightRankingRewardMapper.java @@ -0,0 +1,63 @@ +package com.ruoyi.admin.mapper; + +import java.util.List; +import com.ruoyi.domain.other.TtFightRankingReward; + +/** + * 对战奖励金额Mapper接口 + * + * @author ruoyi + * @date 2024-07-04 + */ +public interface TtFightRankingRewardMapper +{ + /** + * 查询对战奖励金额 + * + * @param id 对战奖励金额主键 + * @return 对战奖励金额 + */ + public TtFightRankingReward selectTtFightRankingRewardById(Integer id); + + /** + * 查询对战奖励金额列表 + * + * @param ttFightRankingReward 对战奖励金额 + * @return 对战奖励金额集合 + */ + public List selectTtFightRankingRewardList(TtFightRankingReward ttFightRankingReward); + + /** + * 新增对战奖励金额 + * + * @param ttFightRankingReward 对战奖励金额 + * @return 结果 + */ + public int insertTtFightRankingReward(TtFightRankingReward ttFightRankingReward); + + /** + * 修改对战奖励金额 + * + * @param ttFightRankingReward 对战奖励金额 + * @return 结果 + */ + public int updateTtFightRankingReward(TtFightRankingReward ttFightRankingReward); + + /** + * 删除对战奖励金额 + * + * @param id 对战奖励金额主键 + * @return 结果 + */ + public int deleteTtFightRankingRewardById(Integer id); + + /** + * 批量删除对战奖励金额 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteTtFightRankingRewardByIds(Integer[] ids); + + public void truncateRankingReward(); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFightResultMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFightResultMapper.java new file mode 100644 index 0000000..3ab724b --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFightResultMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtFightResult; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtFightResultMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFightUserMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFightUserMapper.java new file mode 100644 index 0000000..8786422 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFightUserMapper.java @@ -0,0 +1,13 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtFightUser; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface TtFightUserMapper extends BaseMapper { + List myOwnFights(@Param("playerId") Integer playerId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFirstRechargeMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFirstRechargeMapper.java new file mode 100644 index 0000000..47d75fe --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtFirstRechargeMapper.java @@ -0,0 +1,68 @@ +package com.ruoyi.admin.mapper; + +import com.ruoyi.domain.other.TtFirstRecharge; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 首充赠送Mapper接口 + * + * @author ruoyi + * @date 2024-06-21 + */ +public interface TtFirstRechargeMapper +{ + /** + * 查询首充赠送 + * + * @param id 首充赠送主键 + * @return 首充赠送 + */ + public TtFirstRecharge selectTtFirstRechargeById(Integer id); + + /** + * 查询首充赠送列表 + * + * @param ttFirstRecharge 首充赠送 + * @return 首充赠送集合 + */ + public List selectTtFirstRechargeList(TtFirstRecharge ttFirstRecharge); + + /** + * 新增首充赠送 + * + * @param ttFirstRecharge 首充赠送 + * @return 结果 + */ + public int insertTtFirstRecharge(TtFirstRecharge ttFirstRecharge); + + /** + * 修改首充赠送 + * + * @param ttFirstRecharge 首充赠送 + * @return 结果 + */ + public int updateTtFirstRecharge(TtFirstRecharge ttFirstRecharge); + + /** + * 删除首充赠送 + * + * @param id 首充赠送主键 + * @return 结果 + */ + public int deleteTtFirstRechargeById(Integer id); + + /** + * 批量删除首充赠送 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteTtFirstRechargeByIds(Integer[] ids); + + /** + * 查询赠送比例 + */ + public BigDecimal selectRatioByMinAmount(BigDecimal minAmount); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtMessageMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtMessageMapper.java new file mode 100644 index 0000000..bdd956d --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtMessageMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtMessage; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtMessageMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtMessageSendMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtMessageSendMapper.java new file mode 100644 index 0000000..01f20cd --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtMessageSendMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtMessageSend; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtMessageSendMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOrderMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOrderMapper.java new file mode 100644 index 0000000..e3e0d62 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOrderMapper.java @@ -0,0 +1,32 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.dto.userRecord.OrderCondition; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.vo.order.TtOrderVO; +import com.ruoyi.domain.vo.sys.SimpleTtUserVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.sql.Timestamp; +import java.util.List; + +@Mapper +public interface TtOrderMapper extends BaseMapper { + List byCondition(OrderCondition param); + + List batchRechargeTotal(@Param("userIdList") List userIdList, + @Param("beginTime") String beginTime, + @Param("endTime") String endTime, + @Param("orderByType") Integer orderbyType, + @Param("limit") Integer limit, + @Param("size") Integer size); + + SimpleTtUserVO rechargeTotalOfBoss( + @Param("employeeId") Integer employeeId, + @Param("beginTime") Timestamp beginTime, + @Param("endTime") String endTime, + @Param("orderType") Integer orderType, + @Param("limit") int limit, + @Param("size") Integer size); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOrnamentMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOrnamentMapper.java new file mode 100644 index 0000000..2c2320c --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOrnamentMapper.java @@ -0,0 +1,43 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.domain.dto.queryCondition.OrnamentCondition; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.entity.TtOrnamentsYY; +import com.ruoyi.domain.vo.WebsitePropertyDataVO; +import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO; +import io.swagger.models.auth.In; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +@Mapper +public interface TtOrnamentMapper extends BaseMapper { + + @Select("SELECT * FROM tt_ornament WHERE id = #{id}") + TtOrnament selectOrnamentById(@Param("id") Long id); + + List selectOrnamentsItemIdList(); + + List list(); + + List simpleOrnamentInfo(@Param("idList") List idList); + + List byCondition(OrnamentCondition condition); + + Integer countByCondition(OrnamentCondition condition); + + List selectOrnamentsIdList(); + + List selectOrnamentsMarketHashNameList(); + + int updateWebsiteProperty(WebsitePropertyDataVO websitePropertyDataVO); + + int deleteWebsitePropertyByIds(Integer[] ids); + + //根据id列表批量查询饰品 + List selectByOrnamentIds(@Param("idList") List ornamentIdList); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOrnamentsLevelMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOrnamentsLevelMapper.java new file mode 100644 index 0000000..3e1ea8f --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOrnamentsLevelMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtOrnamentsLevel; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtOrnamentsLevelMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOrnamentsYYMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOrnamentsYYMapper.java new file mode 100644 index 0000000..31179f9 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtOrnamentsYYMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.TtOrnamentsYY; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtOrnamentsYYMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtPromotionLevelMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtPromotionLevelMapper.java new file mode 100644 index 0000000..55abb32 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtPromotionLevelMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.TtPromotionLevel; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtPromotionLevelMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtPromotionRecordMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtPromotionRecordMapper.java new file mode 100644 index 0000000..7d9eb18 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtPromotionRecordMapper.java @@ -0,0 +1,12 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.TtPromotionRecord; +import com.ruoyi.domain.vo.PromotionDataVO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtPromotionRecordMapper extends BaseMapper { + + PromotionDataVO statisticsPromotionData(Integer userId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtPromotionUpdateMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtPromotionUpdateMapper.java new file mode 100644 index 0000000..d8f305a --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtPromotionUpdateMapper.java @@ -0,0 +1,19 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.sys.TtPromotionUpdate; +import com.ruoyi.domain.vo.TeamDetailVO; +import org.apache.ibatis.annotations.MapKey; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; + +@Mapper +public interface TtPromotionUpdateMapper extends BaseMapper { + + // @MapKey("employee_id") + List latelyUpdate(@Param("allEmployeesId") List allEmployeesId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeCardMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeCardMapper.java new file mode 100644 index 0000000..72a34ce --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeCardMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtRechargeCard; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtRechargeCardMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeConfigMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeConfigMapper.java new file mode 100644 index 0000000..640c02a --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeConfigMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.TtRechargeConfig; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtRechargeConfigMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeProdMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeProdMapper.java new file mode 100644 index 0000000..6c2961f --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeProdMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.TtRechargeProd; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtRechargeProdMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeRankingRewardMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeRankingRewardMapper.java new file mode 100644 index 0000000..70bb3d1 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeRankingRewardMapper.java @@ -0,0 +1,66 @@ +package com.ruoyi.admin.mapper; + +import com.ruoyi.domain.other.TtRechargeRankingReward; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 充值奖励金额Mapper接口 + * + * @author ruoyi + * @date 2024-07-04 + */ +@Mapper +public interface TtRechargeRankingRewardMapper +{ + /** + * 查询充值奖励金额 + * + * @param id 充值奖励金额主键 + * @return 充值奖励金额 + */ + public TtRechargeRankingReward selectTtRechargeRankingRewardById(Integer id); + + /** + * 查询充值奖励金额列表 + * + * @param ttRechargeRankingReward 充值奖励金额 + * @return 充值奖励金额集合 + */ + public List selectTtRechargeRankingRewardList(TtRechargeRankingReward ttRechargeRankingReward); + + /** + * 新增充值奖励金额 + * + * @param ttRechargeRankingReward 充值奖励金额 + * @return 结果 + */ + public int insertTtRechargeRankingReward(TtRechargeRankingReward ttRechargeRankingReward); + + /** + * 修改充值奖励金额 + * + * @param ttRechargeRankingReward 充值奖励金额 + * @return 结果 + */ + public int updateTtRechargeRankingReward(TtRechargeRankingReward ttRechargeRankingReward); + + /** + * 删除充值奖励金额 + * + * @param id 充值奖励金额主键 + * @return 结果 + */ + public int deleteTtRechargeRankingRewardById(Integer id); + + /** + * 批量删除充值奖励金额 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteTtRechargeRankingRewardByIds(Integer[] ids); + + public void truncateRankingReward(); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeRecordMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeRecordMapper.java new file mode 100644 index 0000000..e58e804 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRechargeRecordMapper.java @@ -0,0 +1,24 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtRechargeRecord; +import com.ruoyi.domain.other.TtUidTotalRechargeRecord; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; + +@Mapper +public interface TtRechargeRecordMapper extends BaseMapper { + + List getLastHourRechargeUserIds(BigDecimal minRecharge); + + List getLastDayRechargeUserIds(BigDecimal minRecharge); + + List getLastWeekRechargeUserIds(BigDecimal minRecharge); + + List getLastMonthRechargeUserIds(BigDecimal minRecharge); + + List getTotalPriceByUserIds(@Param("userIds") List userIds, @Param("startDate") String startDate, @Param("endDate") String endDate); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRedPackMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRedPackMapper.java new file mode 100644 index 0000000..8b917fe --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRedPackMapper.java @@ -0,0 +1,62 @@ +package com.ruoyi.admin.mapper; + +import com.ruoyi.domain.other.TtRedPack; + +import java.util.List; + +/** + * 【请填写功能名称】Mapper接口 + * + * @author ruoyi + * @date 2023-06-29 + */ +public interface TtRedPackMapper +{ + /** + * 查询【请填写功能名称】 + * + * @param id 【请填写功能名称】主键 + * @return 【请填写功能名称】 + */ + public TtRedPack selectTtRedPackById(Long id); + + /** + * 查询【请填写功能名称】列表 + * + * @param ttRedPack 【请填写功能名称】 + * @return 【请填写功能名称】集合 + */ + public List selectTtRedPackList(TtRedPack ttRedPack); + + /** + * 新增【请填写功能名称】 + * + * @param ttRedPack 【请填写功能名称】 + * @return 结果 + */ + public int insertTtRedPack(TtRedPack ttRedPack); + + /** + * 修改【请填写功能名称】 + * + * @param ttRedPack 【请填写功能名称】 + * @return 结果 + */ + public int updateTtRedPack(TtRedPack ttRedPack); + + /** + * 删除【请填写功能名称】 + * + * @param id 【请填写功能名称】主键 + * @return 结果 + */ + public int deleteTtRedPackById(Long id); + + /** + * 批量删除【请填写功能名称】 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteTtRedPackByIds(Long[] ids); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRedPacketMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRedPacketMapper.java new file mode 100644 index 0000000..75f92bd --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRedPacketMapper.java @@ -0,0 +1,14 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtRedPacket; +import com.ruoyi.domain.other.TtRedPacketBody; +import com.ruoyi.domain.vo.TtRedPacketDataVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface TtRedPacketMapper extends BaseMapper { + List queryList(TtRedPacketBody ttRedPacketBody); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRedPacketRecordMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRedPacketRecordMapper.java new file mode 100644 index 0000000..58cc957 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRedPacketRecordMapper.java @@ -0,0 +1,14 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtRedPacketRecord; +import com.ruoyi.domain.other.TtRedPacketRecordBody; +import com.ruoyi.domain.vo.TtRedPacketRecordDataVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface TtRedPacketRecordMapper extends BaseMapper { + List queryList(TtRedPacketRecordBody ttRedPacketRecordBody); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtReplacementRecordMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtReplacementRecordMapper.java new file mode 100644 index 0000000..37d2f12 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtReplacementRecordMapper.java @@ -0,0 +1,62 @@ +package com.ruoyi.admin.mapper; + +import com.ruoyi.domain.other.TtReplacementRecord; + +import java.util.List; + +/** + * 汰换记录Mapper接口 + * + * @author junhai + * @date 2023-09-10 + */ +public interface TtReplacementRecordMapper +{ + /** + * 查询汰换记录 + * + * @param id 汰换记录主键 + * @return 汰换记录 + */ + public TtReplacementRecord selectTtReplacementRecordById(Long id); + + /** + * 查询汰换记录列表 + * + * @param ttReplacementRecord 汰换记录 + * @return 汰换记录集合 + */ + public List selectTtReplacementRecordList(TtReplacementRecord ttReplacementRecord); + + /** + * 新增汰换记录 + * + * @param ttReplacementRecord 汰换记录 + * @return 结果 + */ + public int insertTtReplacementRecord(TtReplacementRecord ttReplacementRecord); + + /** + * 修改汰换记录 + * + * @param ttReplacementRecord 汰换记录 + * @return 结果 + */ + public int updateTtReplacementRecord(TtReplacementRecord ttReplacementRecord); + + /** + * 删除汰换记录 + * + * @param id 汰换记录主键 + * @return 结果 + */ + public int deleteTtReplacementRecordById(Long id); + + /** + * 批量删除汰换记录 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteTtReplacementRecordByIds(Long[] ids); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRobotFightGroupBoxMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRobotFightGroupBoxMapper.java new file mode 100644 index 0000000..86d3b7b --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRobotFightGroupBoxMapper.java @@ -0,0 +1,70 @@ +package com.ruoyi.admin.mapper; + +import java.util.List; +import com.ruoyi.domain.other.TtRobotFightGroupBox; +import org.apache.ibatis.annotations.Param; + +/** + * 机器人对战分组宝箱Mapper接口 + * + * @author ruoyi + * @date 2024-06-28 + */ +public interface TtRobotFightGroupBoxMapper +{ + /** + * 查询机器人对战分组宝箱 + * + * @param groupId 机器人对战分组宝箱主键 + * @return 机器人对战分组宝箱 + */ + public TtRobotFightGroupBox selectTtRobotFightGroupBoxByBoxId( + @Param("groupId") Integer groupId, + @Param("boxId") Integer boxId + ); + + /** + * 查询机器人对战分组宝箱列表 + * + * @param ttRobotFightGroupBox 机器人对战分组宝箱 + * @return 机器人对战分组宝箱集合 + */ + public List selectTtRobotFightGroupBoxList( + @Param("groupId") Integer groupId, + @Param("ttRobotFightGroupBox") TtRobotFightGroupBox ttRobotFightGroupBox + ); + + public int hasChildByGroupId(Integer groupId); + + /** + * 新增机器人对战分组宝箱 + * + * @param ttRobotFightGroupBox 机器人对战分组宝箱 + * @return 结果 + */ + public int insertTtRobotFightGroupBox(TtRobotFightGroupBox ttRobotFightGroupBox); + + /** + * 修改机器人对战分组宝箱 + * + * @param ttRobotFightGroupBox 机器人对战分组宝箱 + * @return 结果 + */ + public int updateTtRobotFightGroupBox(TtRobotFightGroupBox ttRobotFightGroupBox); + + /** + * 删除机器人对战分组宝箱 + */ + public int deleteTtRobotFightGroupBoxByGroupId( + @Param("groupId") Integer groupId, + @Param("boxId") Integer boxId + ); + + /** + * 批量删除机器人对战分组宝箱 + */ + public int deleteTtRobotFightGroupBoxByGroupIds( + @Param("groupId") Integer groupId, + @Param("boxIds") Integer[] boxIds + ); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRobotFightGroupMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRobotFightGroupMapper.java new file mode 100644 index 0000000..212d3cc --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRobotFightGroupMapper.java @@ -0,0 +1,61 @@ +package com.ruoyi.admin.mapper; + +import java.util.List; +import com.ruoyi.domain.other.TtRobotFightGroup; + +/** + * 机器人对战分组Mapper接口 + * + * @author ruoyi + * @date 2024-06-28 + */ +public interface TtRobotFightGroupMapper +{ + /** + * 查询机器人对战分组 + * + * @param groupId 机器人对战分组主键 + * @return 机器人对战分组 + */ + public TtRobotFightGroup selectTtRobotFightGroupByGroupId(Integer groupId); + + /** + * 查询机器人对战分组列表 + * + * @param ttRobotFightGroup 机器人对战分组 + * @return 机器人对战分组集合 + */ + public List selectTtRobotFightGroupList(TtRobotFightGroup ttRobotFightGroup); + + /** + * 新增机器人对战分组 + * + * @param ttRobotFightGroup 机器人对战分组 + * @return 结果 + */ + public int insertTtRobotFightGroup(TtRobotFightGroup ttRobotFightGroup); + + /** + * 修改机器人对战分组 + * + * @param ttRobotFightGroup 机器人对战分组 + * @return 结果 + */ + public int updateTtRobotFightGroup(TtRobotFightGroup ttRobotFightGroup); + + /** + * 删除机器人对战分组 + * + * @param groupId 机器人对战分组主键 + * @return 结果 + */ + public int deleteTtRobotFightGroupByGroupId(Integer groupId); + + /** + * 批量删除机器人对战分组 + * + * @param groupIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteTtRobotFightGroupByGroupIds(Integer[] groupIds); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollJackpotMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollJackpotMapper.java new file mode 100644 index 0000000..1fc0764 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollJackpotMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.roll.TtRollJackpot; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtRollJackpotMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollJackpotOrnamentsMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollJackpotOrnamentsMapper.java new file mode 100644 index 0000000..31c5782 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollJackpotOrnamentsMapper.java @@ -0,0 +1,30 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.dto.roll.GetRollPrizePool; +import com.ruoyi.domain.entity.roll.TtRollJackpotOrnaments; +import com.ruoyi.domain.entity.roll.TtRollJackpotOrnamentsBody; +import com.ruoyi.domain.vo.TtRollJackpotOrnamentsDataVO; +import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO; +import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsVO; +import com.ruoyi.domain.vo.roll.SimpleRollOrnamentVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface TtRollJackpotOrnamentsMapper extends BaseMapper { + List queryList(TtRollJackpotOrnamentsBody rollJackpotOrnamentsBody); + + List rollShow(@Param("jackpotId") Integer jackpotId); + + List listByRollId(GetRollPrizePool param); + + Integer ornamentsNumberOfRoll(@Param("jackpotId") Integer jackpotId); + + List byOrnamentIds(@Param("rollId") Integer rollId, + @Param("noHOrnamentIds") List noHOrnamentIds); + + Integer totalByCondition(GetRollPrizePool param); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollMapper.java new file mode 100644 index 0000000..2e531d6 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollMapper.java @@ -0,0 +1,21 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.roll.TtRoll; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.vo.TtRollPrizeDataVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface TtRollMapper extends BaseMapper { + + List getRollJackpotOrnamentsList(Integer rollId); + + List getSpecifiedRollJackpotOrnamentsList(Integer rollId); + + List getRollUser(Integer rollId); + + List getRollRobotIds(Integer rollId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollUserMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollUserMapper.java new file mode 100644 index 0000000..3afdae5 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollUserMapper.java @@ -0,0 +1,22 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.roll.GetRollPlayersParam; +import com.ruoyi.domain.entity.roll.TtRollUser; +import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO; +import com.ruoyi.domain.vo.roll.RollUserPrizeVO; +import com.ruoyi.domain.vo.roll.RollUserVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface TtRollUserMapper extends BaseMapper { + R> byRollId(Integer rollId); + + List pageByRollId(GetRollPlayersParam param); + + List rollWinners(@Param("rollId") Integer rollId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollUserPrizeMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollUserPrizeMapper.java new file mode 100644 index 0000000..81c2e8f --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtRollUserPrizeMapper.java @@ -0,0 +1,19 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.roll.TtRollUser; +import com.ruoyi.domain.entity.roll.TtRollUserPrize; +import com.ruoyi.domain.vo.roll.RollUserPrizeVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface TtRollUserPrizeMapper extends BaseMapper { + List byRollUserIds(@Param("rollUserIds") List rollUserIds); + + Integer ownOrnamentNumber(@Param("rollJackpotOrnamentId") Integer rollJackpotOrnamentId,@Param("rollId") Integer rollId); + + List byRollId(@Param("rollId") Integer rollId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtTaskCenterMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtTaskCenterMapper.java new file mode 100644 index 0000000..29cda1e --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtTaskCenterMapper.java @@ -0,0 +1,65 @@ +package com.ruoyi.admin.mapper; + +import java.util.List; +import com.ruoyi.domain.other.TtTaskCenter; +import org.apache.ibatis.annotations.Mapper; + +/** + * 任务中心Mapper接口 + * + * @author ruoyi + * @date 2024-05-25 + */ +@Mapper +public interface TtTaskCenterMapper +{ + /** + * 查询任务中心 + * + * @param taskId 任务中心主键 + * @return 任务中心 + */ + public TtTaskCenter selectTtTaskCenterByTaskId(Integer taskId); + + public TtTaskCenter selectTtTaskCenterByIdentify(String identify); + + /** + * 查询任务中心列表 + * + * @param ttTaskCenter 任务中心 + * @return 任务中心集合 + */ + public List selectTtTaskCenterList(TtTaskCenter ttTaskCenter); + + /** + * 新增任务中心 + * + * @param ttTaskCenter 任务中心 + * @return 结果 + */ + public int insertTtTaskCenter(TtTaskCenter ttTaskCenter); + + /** + * 修改任务中心 + * + * @param ttTaskCenter 任务中心 + * @return 结果 + */ + public int updateTtTaskCenter(TtTaskCenter ttTaskCenter); + + /** + * 删除任务中心 + * + * @param taskId 任务中心主键 + * @return 结果 + */ + public int deleteTtTaskCenterByTaskId(Integer taskId); + + /** + * 批量删除任务中心 + * + * @param taskIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteTtTaskCenterByTaskIds(Integer[] taskIds); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtTimeRollMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtTimeRollMapper.java new file mode 100644 index 0000000..2c060f0 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtTimeRollMapper.java @@ -0,0 +1,14 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.roll.TtTimeRoll; +import com.ruoyi.domain.vo.TtRollPrizeDataVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface TtTimeRollMapper extends BaseMapper { + List getRollJackpotOrnamentsList(Integer id); + List getSpecifiedTimeRollJackpotOrnamentsList(Integer id); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtTimeRollUserMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtTimeRollUserMapper.java new file mode 100644 index 0000000..d1a0eb4 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtTimeRollUserMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.roll.TtTimeRollUser; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtTimeRollUserMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUpgradeFailOrnamentsMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUpgradeFailOrnamentsMapper.java new file mode 100644 index 0000000..58a2c20 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUpgradeFailOrnamentsMapper.java @@ -0,0 +1,27 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.admin.controller.TtUpgradeFailOrnamentsController; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.other.TtUpgradeFailOrnaments; +import com.ruoyi.domain.vo.TtUpgradeFailOrnamentsDataVO; +import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Update; + +import java.math.BigDecimal; +import java.util.List; + +@Mapper +public interface TtUpgradeFailOrnamentsMapper extends BaseMapper { + List queryList(TtUpgradeFailOrnamentsController.listParam param); + + List getFailOrnamentsList(@Param("id") Integer id, @Param("price") BigDecimal price); + + List ornamentInfoByUpgradeId(@Param("upgradeOrnId") Integer upgradeOrnId); + + @Update("TRUNCATE TABLE tt_upgrade_fail_ornaments") + void truncateTable(); + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUpgradeOrnamentsMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUpgradeOrnamentsMapper.java new file mode 100644 index 0000000..e8f65f0 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUpgradeOrnamentsMapper.java @@ -0,0 +1,18 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtUpgradeOrnaments; +import com.ruoyi.domain.other.TtUpgradeOrnamentsBody; +import com.ruoyi.domain.vo.TtUpgradeOrnamentsDataVO; +import org.apache.ibatis.annotations.Mapper; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +@Mapper +public interface TtUpgradeOrnamentsMapper extends BaseMapper { + List queryList(TtUpgradeOrnamentsBody ttUpgradeOrnamentsBody); + + Map getUpgradeProfitStatistics(Integer id); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUpgradeRecordMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUpgradeRecordMapper.java new file mode 100644 index 0000000..30b3ef1 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUpgradeRecordMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.dto.upgrade.UpgradeCondition; +import com.ruoyi.domain.other.TtUpgradeRecord; +import com.ruoyi.domain.other.TtUpgradeRecordBody; +import com.ruoyi.domain.vo.upgrade.UpgradeRecordVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface TtUpgradeRecordMapper extends BaseMapper { + List getUpgradeRecord(TtUpgradeRecordBody ttUpgradeRecordBody); + + List adminGetLog(UpgradeCondition param); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserAmountRecordsMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserAmountRecordsMapper.java new file mode 100644 index 0000000..553c155 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserAmountRecordsMapper.java @@ -0,0 +1,52 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.vo.TtUserAccountRecordsRankVO; +import com.ruoyi.domain.entity.recorde.TtUserAmountRecords; +import com.ruoyi.domain.other.TtUserAmountRecordsBody; +import com.ruoyi.domain.vo.UserBERankVO; +import com.ruoyi.domain.vo.task.SimpleAmountRecordVO; +import com.ruoyi.domain.vo.task.SimpleBlendErcashRecordVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; + +@Mapper +public interface TtUserAmountRecordsMapper extends BaseMapper { + + List queryList(TtUserAmountRecordsBody ttUserAmountRecordsBody); + + List rank(@Param("beginT") String beginT, + @Param("endT") String endT, + @Param("page") Integer page, + @Param("size") Integer size); + + BigDecimal pWHistoryTotal(Integer uid); + + List blendErcashRank(@Param("sourceList") List sourceList, + @Param("beginT") String beginT, + @Param("endT") String endT, + @Param("limit") Integer limit, + @Param("size") Integer size); + + List recordsByTimeAndHasBoss(@Param("beginT") String beginT, + @Param("endT") String endT); + + Integer totalSize(TtUserAmountRecordsBody param); + + List BlendErcashByTimeAndHasBoss(@Param("beginT") String beginT, + @Param("endT") String endT); + + BigDecimal pWelfareByTime(@Param("bossId") Integer bossId, + @Param("beginT") String beginT, + @Param("endT") String endT); + + List bossByHasConsumeEmployee(@Param("beginT") String beginT,@Param("endT") String endT); + + /** + * 获取用户的总消费 + */ + BigDecimal getTotalConsumptionByUserId(Integer userId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserAvatarMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserAvatarMapper.java new file mode 100644 index 0000000..31eabc9 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserAvatarMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtUserAvatar; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtUserAvatarMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserBlendErcashMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserBlendErcashMapper.java new file mode 100644 index 0000000..ce80030 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserBlendErcashMapper.java @@ -0,0 +1,83 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.dto.userRecord.AmountRecordsDetailCondition; +import com.ruoyi.domain.dto.userRecord.BlendErcashCondition; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.vo.ChildDetailByBossVO; +import com.ruoyi.domain.vo.TtUserAmountRecords.PersonBlendErcashVO; +import com.ruoyi.domain.vo.TtUserAmountRecords.TtUserBlendErcashVO; +import com.ruoyi.domain.vo.TtUserAmountRecords.UserAmountDetailVO; +import com.ruoyi.domain.vo.UserBERankVO; +import com.ruoyi.domain.vo.sys.SimpleTtUserVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface TtUserBlendErcashMapper extends BaseMapper { + + List rank(@Param("sourceList") List sourceList, + @Param("beginT") String beginT, + @Param("endT") String endT, + @Param("limit") Integer limit, + @Param("size") Integer size); + + List batchConsumeTotal(@Param("userIdList") List userIdList, + @Param("beginTime") String beginTime, + @Param("endTime") String endTime, + @Param("orderByFie") Integer orderByFie, + @Param("orderByType") Integer orderByType, + @Param("limit") Integer limit, + @Param("size") Integer size); + + /** + * 统计用户在一个时间段内的消费 + */ + PersonBlendErcashVO personTotalConsumeByTime( + @Param("userId") Integer userId, + @Param("beginT") String beginT, + @Param("endT") String endT, + @Param("type") Integer type + ); + + /** + * 批量统计用户在一个时间段内的消费 + */ + List personsTotalConsumeByTime( + @Param("userIds") List userIds, + @Param("beginT") String beginT, + @Param("endT") String endT, + @Param("type") Integer type + + ); + + List userAccountDetail(AmountRecordsDetailCondition param); + + List byCondition( + @Param("userId") Integer userId, + @Param("source") Integer source, + @Param("type") Integer type, + @Param("userName") String userName, + @Param("moneyType") Integer moneyType, + @Param("limit") Integer limit, + @Param("size") Integer size); + + Integer count( + @Param("userId") Integer userId, + @Param("source") Integer source, + @Param("type") Integer type, + @Param("userName") String userName, + @Param("moneyType") Integer moneyType, + @Param("limit") Integer limit, + @Param("size") Integer size + ); + + List collectByUIds(@Param("cIds") List cIds, + @Param("beginTime") String beginTime, + @Param("endTime") String endTime, + @Param("type") Integer type, + @Param("sourceList") List sourceList + ); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserCreditsRecordsMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserCreditsRecordsMapper.java new file mode 100644 index 0000000..fa5b36f --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserCreditsRecordsMapper.java @@ -0,0 +1,23 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.recorde.TtUserCreditsRecords; +import com.ruoyi.domain.other.TtUserCreditsRecordsBody; +import com.ruoyi.domain.vo.TtUserCreditsRecordsRankVO; +import com.ruoyi.domain.vo.task.SimpleCreditsRecordVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface TtUserCreditsRecordsMapper extends BaseMapper { + List queryList(TtUserCreditsRecordsBody ttUserCreditsRecordsBody); + + List rank(@Param("page") Integer page, + @Param("size") Integer size, + @Param("begin") String begin, + @Param("end") String end); + + List recordsByTimeAndHasBoss(@Param("beginT") String beginT,@Param("endT") String endT); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserMapper.java new file mode 100644 index 0000000..e6b3c61 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtUserMapper.java @@ -0,0 +1,70 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtUserPackSackBody; +import com.ruoyi.domain.param.promotion.AnchorDailyTurnoverParam; +import com.ruoyi.domain.vo.TtUserPackSackDataVO; +import com.ruoyi.domain.vo.promotion.AnchorDailyTurnoverVo; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Update; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +@Mapper +public interface TtUserMapper extends BaseMapper { + List getPackSack(TtUserPackSackBody ttUserPackSackBody); + + //@MapKey("userId") + Map getUserProfitStatistics(Integer userId); + + @Delete("DELETE FROM tt_user WHERE user_id = #{userId}") + void removeByUserId(@Param("userId") Integer userId); + + public TtUser selectTtUserById(Long id); + + int updateTtUserCoin(@Param("userId") Long userId, @Param("money") BigDecimal money); + + @Update("update tt_user set account_amount = account_amount + #{money}, account_credits = account_credits + #{money} where user_id = #{userId}") + int addAccountAmount(@Param("userId") Long userId, @Param("money") BigDecimal money); + + @Update("update tt_user set account_amount = account_amount + #{money} where user_id = #{userId}") + int addOnlyAccountAmount(@Param("userId") Long userId, @Param("money") BigDecimal money); + + @Update("update tt_user " + + "set account_amount = account_amount - #{money}, " + + "account_credits = LEAST(account_credits, account_amount) " + + "where user_id = #{userId} " + + " and account_amount >= #{money}") + int subtractAccountAmount(@Param("userId") Long userId, @Param("money") BigDecimal money); + + @Update("update tt_user " + + "set account_credits = account_credits - #{money}, " + + "account_amount = account_amount - #{money} " + + "where user_id = #{userId} " + + " and account_credits >= #{money}") + int subtractCreditsAmount(@Param("userId") Long userId, @Param("money") BigDecimal money); + + List propRankUsers(@Param("sources") Integer[] sources, + @Param("begin") String begin, + @Param("end") String end); + + List maxPricePropByUserIds(@Param("sources") Integer[] sources, + @Param("beginT") String beginT, + @Param("endT") String endT, + @Param("userId") List userIds, + @Param("size") Integer size); + + List allEmployeesByParents(@Param("bossIds") List bossIds); + + List teamUIds(Integer bossId); + + /** + * 查询名下主播每日流水 + */ + List getAnchorDailyTurnover(AnchorDailyTurnoverParam param); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtVipLevelMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtVipLevelMapper.java new file mode 100644 index 0000000..8881ca7 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtVipLevelMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtVipLevel; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtVipLevelMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtWelfareMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtWelfareMapper.java new file mode 100644 index 0000000..e0bb094 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtWelfareMapper.java @@ -0,0 +1,63 @@ +package com.ruoyi.admin.mapper; + +import java.util.List; +import com.ruoyi.domain.other.TtWelfare; +import org.apache.ibatis.annotations.Mapper; + +/** + * 福利列表Mapper接口 + * + * @author ruoyi + * @date 2024-05-11 + */ +@Mapper +public interface TtWelfareMapper +{ + /** + * 查询福利列表 + * + * @param welfareId 福利列表主键 + * @return 福利列表 + */ + public TtWelfare selectTtWelfareByWelfareId(Integer welfareId); + + /** + * 查询福利列表列表 + * + * @param ttWelfare 福利列表 + * @return 福利列表集合 + */ + public List selectTtWelfareList(TtWelfare ttWelfare); + + /** + * 新增福利列表 + * + * @param ttWelfare 福利列表 + * @return 结果 + */ + public int insertTtWelfare(TtWelfare ttWelfare); + + /** + * 修改福利列表 + * + * @param ttWelfare 福利列表 + * @return 结果 + */ + public int updateTtWelfare(TtWelfare ttWelfare); + + /** + * 删除福利列表 + * + * @param welfareId 福利列表主键 + * @return 结果 + */ + public int deleteTtWelfareByWelfareId(Integer welfareId); + + /** + * 批量删除福利列表 + * + * @param welfareIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteTtWelfareByWelfareIds(Integer[] welfareIds); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtWelfareMonthlyRechargesMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtWelfareMonthlyRechargesMapper.java new file mode 100644 index 0000000..c54516c --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtWelfareMonthlyRechargesMapper.java @@ -0,0 +1,61 @@ +package com.ruoyi.admin.mapper; + +import java.util.List; +import com.ruoyi.domain.other.TtWelfareMonthlyRecharges; + +/** + * 每月充值福利Mapper接口 + * + * @author ruoyi + * @date 2024-06-24 + */ +public interface TtWelfareMonthlyRechargesMapper +{ + /** + * 查询每月充值福利 + * + * @param id 每月充值福利主键 + * @return 每月充值福利 + */ + public TtWelfareMonthlyRecharges selectTtWelfareMonthlyRechargesById(Integer id); + + /** + * 查询每月充值福利列表 + * + * @param ttWelfareMonthlyRecharges 每月充值福利 + * @return 每月充值福利集合 + */ + public List selectTtWelfareMonthlyRechargesList(TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges); + + /** + * 新增每月充值福利 + * + * @param ttWelfareMonthlyRecharges 每月充值福利 + * @return 结果 + */ + public int insertTtWelfareMonthlyRecharges(TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges); + + /** + * 修改每月充值福利 + * + * @param ttWelfareMonthlyRecharges 每月充值福利 + * @return 结果 + */ + public int updateTtWelfareMonthlyRecharges(TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges); + + /** + * 删除每月充值福利 + * + * @param id 每月充值福利主键 + * @return 结果 + */ + public int deleteTtWelfareMonthlyRechargesById(Integer id); + + /** + * 批量删除每月充值福利 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteTtWelfareMonthlyRechargesByIds(Integer[] ids); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtYYOrnamentsMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtYYOrnamentsMapper.java new file mode 100644 index 0000000..e291016 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/TtYYOrnamentsMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtYYOrnaments; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TtYYOrnamentsMapper extends BaseMapper { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/WebsiteSetupMapper.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/WebsiteSetupMapper.java new file mode 100644 index 0000000..0ccfd5e --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/mapper/WebsiteSetupMapper.java @@ -0,0 +1,35 @@ +package com.ruoyi.admin.mapper; + +import com.ruoyi.domain.other.ConfigData; +import com.ruoyi.domain.other.OperationalStatistics; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface WebsiteSetupMapper { + + List getOperationalStatistics(); + + List selectParameterSettingList(); + + void truncateSysJobLog(); + + void truncateSysLogininfor(); + + void truncateSysOperLog(); + + void truncateTtBoxRecords(); + + void truncateTtFight(); + + void truncateTtFightResult(); + + void truncateTtFightUser(); + + void truncateTtVipLevel(); + + void truncateTtPromotionLevel(); + + void truncateTtOrnamentsLevel(); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/model/BoxMockCache.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/model/BoxMockCache.java new file mode 100644 index 0000000..368463f --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/model/BoxMockCache.java @@ -0,0 +1,21 @@ +package com.ruoyi.admin.model; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +@Data +public class BoxMockCache implements Serializable { + + private List items; + + @Data + public static class MockItem implements Serializable { + //最低价格(包含) + private BigDecimal lowPrice; + //最高价格(包含) + private BigDecimal highPrice; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/model/UpdateUserAccountBo.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/model/UpdateUserAccountBo.java new file mode 100644 index 0000000..d23dc01 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/model/UpdateUserAccountBo.java @@ -0,0 +1,14 @@ +package com.ruoyi.admin.model; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@AllArgsConstructor +public class UpdateUserAccountBo { + private Long userId; + private BigDecimal accountAmount; + private BigDecimal accountCredits; +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/BoxMockService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/BoxMockService.java new file mode 100644 index 0000000..f648b76 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/BoxMockService.java @@ -0,0 +1,58 @@ +package com.ruoyi.admin.service; + +import com.ruoyi.admin.cahe.BoxMockCacheRepository; +import com.ruoyi.admin.domain.body.BoxMockRequest; +import com.ruoyi.admin.model.BoxMockCache; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +@Service +@Slf4j +public class BoxMockService { + + @Autowired + private BoxMockCacheRepository boxMockCacheRepository; + + //1.根据用户的id获取redis中临时爆率列表 + public List getMockResultList(Integer uid) { + BoxMockCache cache = boxMockCacheRepository.getMockResultCache(uid); + if (cache == null || CollectionUtils.isEmpty(cache.getItems())) { + return new ArrayList<>(); + } + return cache.getItems(); + } + + public List updateMockResultList(BoxMockRequest request) { + //1.根据用户的id获取redis中临时爆率列表 + BoxMockCache cache = boxMockCacheRepository.getMockResultCache(request.getUid()); + if (cache == null) { + cache = new BoxMockCache(); + } + //2.判断lowPrice / highPrice 不能为空,且lowPrice < highPrice,lowPrice不能为负数,将数据填充到items中 + List items = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(request.getItems())) { + for (BoxMockRequest.MockItem req : request.getItems()) { + if (req.getLowPrice() == null || req.getHighPrice() == null) continue; + if (req.getLowPrice().compareTo(BigDecimal.ZERO) < 0) continue; + if (req.getLowPrice().compareTo(req.getHighPrice()) > 0) continue; + + BoxMockCache.MockItem item = new BoxMockCache.MockItem(); + item.setLowPrice(req.getLowPrice()); + item.setHighPrice(req.getHighPrice()); + items.add(item); + } + } + + cache.setItems(items); + //3.将用户的临时爆率存储在redis中,存储七天 + boxMockCacheRepository.setMockResultCache(request.getUid(), cache); + //4.返回临时爆率列表 + return cache.getItems(); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/ConfigService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/ConfigService.java new file mode 100644 index 0000000..1546b89 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/ConfigService.java @@ -0,0 +1,7 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.ConfigData; + +public interface ConfigService extends IService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/ITtWelfareMonthlyRechargesService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/ITtWelfareMonthlyRechargesService.java new file mode 100644 index 0000000..07bcd5a --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/ITtWelfareMonthlyRechargesService.java @@ -0,0 +1,61 @@ +package com.ruoyi.admin.service; + +import java.util.List; +import com.ruoyi.domain.other.TtWelfareMonthlyRecharges; + +/** + * 每月充值福利Service接口 + * + * @author ruoyi + * @date 2024-06-24 + */ +public interface ITtWelfareMonthlyRechargesService +{ + /** + * 查询每月充值福利 + * + * @param id 每月充值福利主键 + * @return 每月充值福利 + */ + public TtWelfareMonthlyRecharges selectTtWelfareMonthlyRechargesById(Integer id); + + /** + * 查询每月充值福利列表 + * + * @param ttWelfareMonthlyRecharges 每月充值福利 + * @return 每月充值福利集合 + */ + public List selectTtWelfareMonthlyRechargesList(TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges); + + /** + * 新增每月充值福利 + * + * @param ttWelfareMonthlyRecharges 每月充值福利 + * @return 结果 + */ + public int insertTtWelfareMonthlyRecharges(TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges); + + /** + * 修改每月充值福利 + * + * @param ttWelfareMonthlyRecharges 每月充值福利 + * @return 结果 + */ + public int updateTtWelfareMonthlyRecharges(TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges); + + /** + * 批量删除每月充值福利 + * + * @param ids 需要删除的每月充值福利主键集合 + * @return 结果 + */ + public int deleteTtWelfareMonthlyRechargesByIds(Integer[] ids); + + /** + * 删除每月充值福利信息 + * + * @param id 每月充值福利主键 + * @return 结果 + */ + public int deleteTtWelfareMonthlyRechargesById(Integer id); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/ShoppingService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/ShoppingService.java new file mode 100644 index 0000000..972e534 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/ShoppingService.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.service; + +import com.ruoyi.domain.other.ShoppingBody; +import com.ruoyi.domain.vo.ShoppingDataVO; +import com.ruoyi.common.core.page.PageDataInfo; + +public interface ShoppingService { + PageDataInfo list(ShoppingBody shoppingBody); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtAdvertisementService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtAdvertisementService.java new file mode 100644 index 0000000..15533a8 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtAdvertisementService.java @@ -0,0 +1,8 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtAdvertisement; + +public interface TtAdvertisementService extends IService { + String updatePicById(TtAdvertisement ttAdvertisement); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtAnnouncementService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtAnnouncementService.java new file mode 100644 index 0000000..d3babe8 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtAnnouncementService.java @@ -0,0 +1,18 @@ +package com.ruoyi.admin.service; + +import com.ruoyi.domain.other.TtAnnouncement; + +import java.util.List; + +public interface TtAnnouncementService { + + List getAnnouncementList(); + + TtAnnouncement getAnnouncementByAnnouncementId(Long userId, Integer announcementId); + + int addAnnouncement(TtAnnouncement ttAnnouncement); + + int editAnnouncement(TtAnnouncement ttAnnouncement); + + int removeAnnouncementByAnnouncementId(Integer announcementId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBannerService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBannerService.java new file mode 100644 index 0000000..8d4ba34 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBannerService.java @@ -0,0 +1,8 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtBanner; + +public interface TtBannerService extends IService { + String updateBannerById(TtBanner ttBanner); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBonusReceiveRecordService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBonusReceiveRecordService.java new file mode 100644 index 0000000..8a88a18 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBonusReceiveRecordService.java @@ -0,0 +1,7 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtBonusReceiveRecord; + +public interface TtBonusReceiveRecordService extends IService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBonusService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBonusService.java new file mode 100644 index 0000000..c016f58 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBonusService.java @@ -0,0 +1,11 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtBonus; + +public interface TtBonusService extends IService { + + String updateBonusById(TtBonus ttBonus); + + void bonus(Integer userId, Integer rechargeRecordId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxOpenChanceService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxOpenChanceService.java new file mode 100644 index 0000000..7331521 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxOpenChanceService.java @@ -0,0 +1,61 @@ +package com.ruoyi.admin.service; + +import java.util.List; +import com.ruoyi.admin.domain.TtBoxOpenChance; + +/** + * 开箱机会Service接口 + * + * @author ruoyi + * @date 2024-07-09 + */ +public interface TtBoxOpenChanceService +{ + /** + * 查询开箱机会 + * + * @param ornamentId 开箱机会主键 + * @return 开箱机会 + */ + public TtBoxOpenChance selectTtBoxOpenChanceByOrnamentId(Integer ornamentId); + + /** + * 查询开箱机会列表 + * + * @param ttBoxOpenChance 开箱机会 + * @return 开箱机会集合 + */ + public List selectTtBoxOpenChanceList(TtBoxOpenChance ttBoxOpenChance); + + /** + * 新增开箱机会 + * + * @param ttBoxOpenChance 开箱机会 + * @return 结果 + */ + public int insertTtBoxOpenChance(TtBoxOpenChance ttBoxOpenChance); + + /** + * 修改开箱机会 + * + * @param ttBoxOpenChance 开箱机会 + * @return 结果 + */ + public int updateTtBoxOpenChance(TtBoxOpenChance ttBoxOpenChance); + + /** + * 批量删除开箱机会 + * + * @param ornamentIds 需要删除的开箱机会主键集合 + * @return 结果 + */ + public int deleteTtBoxOpenChanceByOrnamentIds(Integer[] ornamentIds); + + /** + * 删除开箱机会信息 + * + * @param ornamentId 开箱机会主键 + * @return 结果 + */ + public int deleteTtBoxOpenChanceByOrnamentId(Integer ornamentId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxOrnamentsService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxOrnamentsService.java new file mode 100644 index 0000000..4fb90b0 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxOrnamentsService.java @@ -0,0 +1,26 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.admin.controller.TtBoxOrnamentsController.batchAddParam; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.other.TtBoxOrnaments; +import com.ruoyi.domain.vo.TtBoxOrnamentsDataVO; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO; + +import java.util.List; + +public interface TtBoxOrnamentsService extends IService { + List selectTtBoxOrnamentsList(Integer boxId); + String saveBoxOrnaments(TtBoxOrnaments ttBoxOrnaments); + String updateBoxOrnamentsById(TtBoxOrnamentsDataVO ttBoxOrnamentsDataVO); + String removeBoxOrnamentsByIds(Integer boxId, List list); + AjaxResult getProfitMargin(Integer boxId); + String batchAdd(Integer boxId, List ornamentsIds); + + AjaxResult batchAdd(batchAddParam param); + + List simpleBoxDetail(Integer boxId); + + R globalData(Integer boxId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxRecordsService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxRecordsService.java new file mode 100644 index 0000000..bfb1713 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxRecordsService.java @@ -0,0 +1,15 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.other.TtBoxRecordsBody; +import com.ruoyi.domain.vo.TtBoxRecordsDataVO; + +import java.util.List; + +public interface TtBoxRecordsService extends IService { + + List selectBoxRecordsList(TtBoxRecordsBody ttBoxRecordsBody); + + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxService.java new file mode 100644 index 0000000..6bd0061 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxService.java @@ -0,0 +1,38 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtBoxType; +import com.ruoyi.domain.vo.BoxCacheDataVO; +import com.ruoyi.domain.other.TtBoxBody; +import com.ruoyi.domain.vo.TtBoxDataVO; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.domain.vo.box.TtBoxUserVO; + +import java.util.Date; +import java.util.List; + +public interface TtBoxService extends IService { + + PageDataInfo selectTtBoxList(TtBoxBody ttBoxBody); + + String updateTtBoxById(TtBoxDataVO ttBoxDataVO); + + void isReplenishment(Integer boxId); + + void delCache(Integer boxId); + + BoxCacheDataVO statisticsBoxData(Integer boxId, Date date); + + List getRealList(Integer boxId, int flag); + + String createTtBoxThirdExplosiveUsers(TtBoxUserVO ttBoxUserVO); + + AjaxResult queryTtBoxThirdExplosiveUsers(Integer boxId); + + AjaxResult deleteTtBoxThirdExplosiveUsers(TtBoxUserVO ttBoxUserVO); + + AjaxResult resetBox(Integer boxId); + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxTypeService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxTypeService.java new file mode 100644 index 0000000..fd72cc3 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtBoxTypeService.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtBoxType; + +public interface TtBoxTypeService extends IService { + + String updateBoxTypeById(TtBoxType ttBoxType); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtContentService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtContentService.java new file mode 100644 index 0000000..cc61b7d --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtContentService.java @@ -0,0 +1,11 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtContent; + +import java.util.List; + +public interface TtContentService extends IService { + + List queryList(TtContent ttContent); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtContentTypeService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtContentTypeService.java new file mode 100644 index 0000000..829265d --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtContentTypeService.java @@ -0,0 +1,10 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtContentType; + +import java.util.List; + +public interface TtContentTypeService extends IService { + List queryList(TtContentType ttContentType); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtDeliveryRecordService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtDeliveryRecordService.java new file mode 100644 index 0000000..7cb761a --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtDeliveryRecordService.java @@ -0,0 +1,27 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.dto.userRecord.DeliveryRecordsConfition; +import com.ruoyi.domain.entity.delivery.TtDeliveryRecord; +import com.ruoyi.domain.other.TtDeliveryApplyBody; +import com.ruoyi.domain.vo.DeliveryApplyVO; +import com.ruoyi.domain.other.TtDeliveryRecordBody; +import com.ruoyi.domain.vo.TtDeliveryRecordDataVO; +import com.ruoyi.domain.vo.delivery.DeliveryRecordVO; + +import java.util.List; + +public interface TtDeliveryRecordService extends IService { + + List getDeliveryApplyList(TtDeliveryApplyBody deliveryApplyBody); + + String deliveryFail(Integer deliveryRecordId, String message); + + List getDeliveryRecordList(TtDeliveryRecordBody deliveryRecordBody); + + + List getDeliveryRecordByUserList(TtDeliveryRecordBody deliveryRecordBody); + + List byCondition(DeliveryRecordsConfition param); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtFightRankingRewardService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtFightRankingRewardService.java new file mode 100644 index 0000000..fe968ac --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtFightRankingRewardService.java @@ -0,0 +1,65 @@ +package com.ruoyi.admin.service; + +import java.util.List; +import com.ruoyi.domain.other.TtFightRankingReward; + +/** + * 对战奖励金额Service接口 + * + * @author ruoyi + * @date 2024-07-04 + */ +public interface TtFightRankingRewardService +{ + /** + * 查询对战奖励金额 + * + * @param id 对战奖励金额主键 + * @return 对战奖励金额 + */ + public TtFightRankingReward selectTtFightRankingRewardById(Integer id); + + /** + * 查询对战奖励金额列表 + * + * @param ttFightRankingReward 对战奖励金额 + * @return 对战奖励金额集合 + */ + public List selectTtFightRankingRewardList(TtFightRankingReward ttFightRankingReward); + + // /** + // * 新增对战奖励金额 + // * + // * @param ttFightRankingReward 对战奖励金额 + // * @return 结果 + // */ + // public int insertTtFightRankingReward(TtFightRankingReward ttFightRankingReward); + + public String generateRankingReward(); + + /** + * 修改对战奖励金额 + * + * @param ttFightRankingReward 对战奖励金额 + * @return 结果 + */ + public int updateTtFightRankingReward(TtFightRankingReward ttFightRankingReward); + + /** + * 批量删除对战奖励金额 + * + * @param ids 需要删除的对战奖励金额主键集合 + * @return 结果 + */ + public int deleteTtFightRankingRewardByIds(Integer[] ids); + + // /** + // * 删除对战奖励金额信息 + // * + // * @param id 对战奖励金额主键 + // * @return 结果 + // */ + // public int deleteTtFightRankingRewardById(Integer id); + + public void truncateRankingReward(); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtFightResultService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtFightResultService.java new file mode 100644 index 0000000..ec10017 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtFightResultService.java @@ -0,0 +1,11 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtFightResult; +import com.ruoyi.domain.vo.FightResultDataVO; +import com.ruoyi.domain.vo.fight.FightResultVO; + +public interface TtFightResultService extends IService { + // FightResultDataVO getFightResult(Integer fightId); + FightResultVO getFightResult(Integer fightId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtFightService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtFightService.java new file mode 100644 index 0000000..1d8b2e6 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtFightService.java @@ -0,0 +1,17 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.domain.vo.FightBoxDataVO; +import com.ruoyi.domain.other.TtFightBody; + +import java.util.List; + +public interface TtFightService extends IService { + + List selectFightList(TtFightBody ttFightBody); + + List selectFightBoxList(Integer fightId); + + int endFight(String fightId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtFirstRechargeService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtFirstRechargeService.java new file mode 100644 index 0000000..5fda5a9 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtFirstRechargeService.java @@ -0,0 +1,61 @@ +package com.ruoyi.admin.service; + +import java.util.List; +import com.ruoyi.domain.other.TtFirstRecharge; + +/** + * 首充赠送Service接口 + * + * @author ruoyi + * @date 2024-06-21 + */ +public interface TtFirstRechargeService +{ + /** + * 查询首充赠送 + * + * @param id 首充赠送主键 + * @return 首充赠送 + */ + public TtFirstRecharge selectTtFirstRechargeById(Integer id); + + /** + * 查询首充赠送列表 + * + * @param ttFirstRecharge 首充赠送 + * @return 首充赠送集合 + */ + public List selectTtFirstRechargeList(TtFirstRecharge ttFirstRecharge); + + /** + * 新增首充赠送 + * + * @param ttFirstRecharge 首充赠送 + * @return 结果 + */ + public int insertTtFirstRecharge(TtFirstRecharge ttFirstRecharge); + + /** + * 修改首充赠送 + * + * @param ttFirstRecharge 首充赠送 + * @return 结果 + */ + public int updateTtFirstRecharge(TtFirstRecharge ttFirstRecharge); + + /** + * 批量删除首充赠送 + * + * @param ids 需要删除的首充赠送主键集合 + * @return 结果 + */ + public int deleteTtFirstRechargeByIds(Integer[] ids); + + /** + * 删除首充赠送信息 + * + * @param id 首充赠送主键 + * @return 结果 + */ + public int deleteTtFirstRechargeById(Integer id); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtMessageSendService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtMessageSendService.java new file mode 100644 index 0000000..b8b7e4e --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtMessageSendService.java @@ -0,0 +1,7 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtMessageSend; + +public interface TtMessageSendService extends IService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtMessageService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtMessageService.java new file mode 100644 index 0000000..b53892a --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtMessageService.java @@ -0,0 +1,18 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtMessage; + +import java.util.List; + +public interface TtMessageService extends IService { + + List queryList(TtMessage ttMessage); + String delByIds(List ids); + + String singleMessage(Integer messageId, List userIds); + + String massMessaging(Integer messageId); + + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOrderService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOrderService.java new file mode 100644 index 0000000..2b8cbdc --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOrderService.java @@ -0,0 +1,39 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.sys.OrderQueryCondition; +import com.ruoyi.domain.dto.sys.TeamUsersParam; +import com.ruoyi.domain.dto.userRecord.OrderCondition; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.vo.order.TtOrderVO; +import com.ruoyi.domain.vo.sys.SimpleTtUserVO; + +import java.sql.Timestamp; +import java.util.List; + +public interface TtOrderService extends IService { + + List queryList(TtOrder ttOrder); + + List byCondition(OrderCondition param); + + List batchRechargeTotal(List allEmployeesId, + String beginTime, + String endTime, + Integer orderType, + Integer page, + Integer size); + + R clientList(Integer page, Integer size, int uid); + + R adminList(OrderQueryCondition condition); + + SimpleTtUserVO rechargeTotalOfBoss( + Integer employeeId, + Timestamp beginTime, + String endTime, + Integer orderType, + Integer page, + Integer size); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOrnamentService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOrnamentService.java new file mode 100644 index 0000000..95a3b37 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOrnamentService.java @@ -0,0 +1,34 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.queryCondition.OrnamentCondition; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.entity.TtOrnamentsYY; +import com.ruoyi.domain.other.TtOrnamentsBody; +import com.ruoyi.domain.vo.TtOrnamentVO; +import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO; + +import java.math.BigDecimal; +import java.util.List; + +public interface TtOrnamentService extends IService { + + Page listByParam(TtOrnamentsBody param); + + List selectTtOrnamentsList(TtOrnamentsBody param); + + List selectOrnamentsItemIdList(); + + AjaxResult grantOrnaments(Integer userId, Long ornamentsId, Integer ornamentsLevelId, Integer num); + + List byCondition(OrnamentCondition condition); + + List selectOrnamentsIdList(); + + List selectOrnamentsMarketHashNameList(); + + AjaxResult updateOrnamentPrice(Long ornamentId, BigDecimal price); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOrnamentYYService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOrnamentYYService.java new file mode 100644 index 0000000..67508b2 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOrnamentYYService.java @@ -0,0 +1,8 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.TtOrnamentsYY; + +public interface TtOrnamentYYService extends IService { + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOrnamentsLevelService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOrnamentsLevelService.java new file mode 100644 index 0000000..8549215 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtOrnamentsLevelService.java @@ -0,0 +1,13 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtOrnamentsLevel; + +public interface TtOrnamentsLevelService extends IService { + + String generateOrnamentsLevel(Integer num); + + String updateOrnamentsLevelById(TtOrnamentsLevel ttOrnamentsLevel); + + void truncateOrnamentsLevel(); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtPromoTurnoverService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtPromoTurnoverService.java new file mode 100644 index 0000000..9e551e8 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtPromoTurnoverService.java @@ -0,0 +1,23 @@ +package com.ruoyi.admin.service; + + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.param.promotion.AnchorDailyTurnoverParam; +import com.ruoyi.domain.param.promotion.AnchorRechargeParam; +import com.ruoyi.domain.vo.promotion.AnchorDailyTurnoverVo; + +import java.util.List; + +public interface TtPromoTurnoverService { + + /** + * 获取名下主播每日流水 + */ + List getAnchorDailyTurnover(AnchorDailyTurnoverParam param); + /** + * 主播发放工资 + */ + AjaxResult recharge(AnchorRechargeParam param); + + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtPromotionLevelService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtPromotionLevelService.java new file mode 100644 index 0000000..fd161ee --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtPromotionLevelService.java @@ -0,0 +1,13 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.TtPromotionLevel; + +public interface TtPromotionLevelService extends IService { + + String generateVipLevel(Integer num); + + String updatePromotionLevelById(TtPromotionLevel ttPromotionLevel); + + void truncatePromotionLevel(); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtPromotionRecordService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtPromotionRecordService.java new file mode 100644 index 0000000..8ce3b2d --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtPromotionRecordService.java @@ -0,0 +1,14 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.TtPromotionRecord; +import com.ruoyi.domain.vo.PromotionDataVO; + +import java.util.List; + +public interface TtPromotionRecordService extends IService { + + List getPromotionRecord(TtPromotionRecord ttPromotionRecord); + + PromotionDataVO statisticsPromotionData(Integer userId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeCardService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeCardService.java new file mode 100644 index 0000000..8d6a4af --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeCardService.java @@ -0,0 +1,16 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtRechargeCard; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +public interface TtRechargeCardService extends IService { + + List queryList(TtRechargeCard ttRechargeCard); + + List generateCard(Integer rechargeListId, Integer num); + + void export(HttpServletResponse response, TtRechargeCard ttRechargeCard); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeConfigService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeConfigService.java new file mode 100644 index 0000000..22178af --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeConfigService.java @@ -0,0 +1,7 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.TtRechargeConfig; + +public interface TtRechargeConfigService extends IService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeProdService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeProdService.java new file mode 100644 index 0000000..579c2cb --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeProdService.java @@ -0,0 +1,7 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.TtRechargeProd; + +public interface TtRechargeProdService extends IService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeRankingRewardService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeRankingRewardService.java new file mode 100644 index 0000000..edcfe83 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeRankingRewardService.java @@ -0,0 +1,50 @@ +package com.ruoyi.admin.service; + +import com.ruoyi.domain.other.TtRechargeRankingReward; + +import java.util.List; + +/** + * 充值奖励金额Service接口 + * + * @author ruoyi + * @date 2024-07-04 + */ +public interface TtRechargeRankingRewardService +{ + /** + * 查询充值奖励金额 + * + * @param id 充值奖励金额主键 + * @return 充值奖励金额 + */ + public TtRechargeRankingReward selectTtRechargeRankingRewardById(Integer id); + + /** + * 查询充值奖励金额列表 + * + * @param ttRechargeRankingReward 充值奖励金额 + * @return 充值奖励金额集合 + */ + public List selectTtRechargeRankingRewardList(TtRechargeRankingReward ttRechargeRankingReward); + + public String generateRankingReward(); + + /** + * 修改充值奖励金额 + * + * @param ttRechargeRankingReward 充值奖励金额 + * @return 结果 + */ + public int updateTtRechargeRankingReward(TtRechargeRankingReward ttRechargeRankingReward); + + /** + * 批量删除充值奖励金额 + * + * @param ids 需要删除的充值奖励金额主键集合 + * @return 结果 + */ + public int deleteTtRechargeRankingRewardByIds(Integer[] ids); + + public void truncateRankingReward(); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeRecordService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeRecordService.java new file mode 100644 index 0000000..3a5f779 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRechargeRecordService.java @@ -0,0 +1,12 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtRechargeRecord; +import com.ruoyi.domain.other.TtRechargeRecordBody; + +import java.util.List; + +public interface TtRechargeRecordService extends IService { + + List queryList(TtRechargeRecordBody rechargeRecordBody); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRedPackService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRedPackService.java new file mode 100644 index 0000000..eb56c87 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRedPackService.java @@ -0,0 +1,62 @@ +package com.ruoyi.admin.service; + +import com.ruoyi.domain.other.TtRedPack; + +import java.util.List; + +/** + * 【请填写功能名称】Service接口 + * + * @author ruoyi + * @date 2023-06-29 + */ +public interface TtRedPackService +{ + /** + * 查询【请填写功能名称】 + * + * @param id 【请填写功能名称】主键 + * @return 【请填写功能名称】 + */ + public TtRedPack selectTtRedPackById(Long id); + + /** + * 查询【请填写功能名称】列表 + * + * @param ttRedPack 【请填写功能名称】 + * @return 【请填写功能名称】集合 + */ + public List selectTtRedPackList(TtRedPack ttRedPack); + + /** + * 新增【请填写功能名称】 + * + * @param ttRedPack 【请填写功能名称】 + * @return 结果 + */ + public int insertTtRedPack(TtRedPack ttRedPack); + + /** + * 修改【请填写功能名称】 + * + * @param ttRedPack 【请填写功能名称】 + * @return 结果 + */ + public int updateTtRedPack(TtRedPack ttRedPack); + + /** + * 批量删除【请填写功能名称】 + * + * @param ids 需要删除的【请填写功能名称】主键集合 + * @return 结果 + */ + public int deleteTtRedPackByIds(Long[] ids); + + /** + * 删除【请填写功能名称】信息 + * + * @param id 【请填写功能名称】主键 + * @return 结果 + */ + public int deleteTtRedPackById(Long id); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRedPacketRecordService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRedPacketRecordService.java new file mode 100644 index 0000000..84b8f49 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRedPacketRecordService.java @@ -0,0 +1,13 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtRedPacketRecord; +import com.ruoyi.domain.other.TtRedPacketRecordBody; +import com.ruoyi.domain.vo.TtRedPacketRecordDataVO; + +import java.util.List; + +public interface TtRedPacketRecordService extends IService { + + List queryList(TtRedPacketRecordBody ttRedPacketRecordBody); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRedPacketService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRedPacketService.java new file mode 100644 index 0000000..613a03d --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRedPacketService.java @@ -0,0 +1,17 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.other.TtRedPacket; +import com.ruoyi.domain.other.TtRedPacketBody; +import com.ruoyi.domain.vo.TtRedPacketDataVO; + +import java.util.List; + +public interface TtRedPacketService extends IService { + + List queryList(TtRedPacketBody ttRedPacketBody); + + List insertRedPacket(TtRedPacketDataVO ttRedPacketDataVO); + AjaxResult updateRedPacketById(TtRedPacket redPacket); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtReplacementRecordService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtReplacementRecordService.java new file mode 100644 index 0000000..aae93a4 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtReplacementRecordService.java @@ -0,0 +1,62 @@ +package com.ruoyi.admin.service; + +import com.ruoyi.domain.other.TtReplacementRecord; + +import java.util.List; + +/** + * 汰换记录Service接口 + * + * @author junhai + * @date 2023-09-10 + */ +public interface TtReplacementRecordService +{ + /** + * 查询汰换记录 + * + * @param id 汰换记录主键 + * @return 汰换记录 + */ + public TtReplacementRecord selectTtReplacementRecordById(Long id); + + /** + * 查询汰换记录列表 + * + * @param ttReplacementRecord 汰换记录 + * @return 汰换记录集合 + */ + public List selectTtReplacementRecordList(TtReplacementRecord ttReplacementRecord); + + /** + * 新增汰换记录 + * + * @param ttReplacementRecord 汰换记录 + * @return 结果 + */ + public int insertTtReplacementRecord(TtReplacementRecord ttReplacementRecord); + + /** + * 修改汰换记录 + * + * @param ttReplacementRecord 汰换记录 + * @return 结果 + */ + public int updateTtReplacementRecord(TtReplacementRecord ttReplacementRecord); + + /** + * 批量删除汰换记录 + * + * @param ids 需要删除的汰换记录主键集合 + * @return 结果 + */ + public int deleteTtReplacementRecordByIds(Long[] ids); + + /** + * 删除汰换记录信息 + * + * @param id 汰换记录主键 + * @return 结果 + */ + public int deleteTtReplacementRecordById(Long id); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRobotFightGroupBoxService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRobotFightGroupBoxService.java new file mode 100644 index 0000000..44c7580 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRobotFightGroupBoxService.java @@ -0,0 +1,64 @@ +package com.ruoyi.admin.service; + +import java.util.List; +import com.ruoyi.domain.other.TtRobotFightGroupBox; + +/** + * 机器人对战分组宝箱Service接口 + * + * @author ruoyi + * @date 2024-06-28 + */ +public interface TtRobotFightGroupBoxService +{ + /** + * 查询机器人对战分组宝箱 + * + * @param groupId 机器人对战分组宝箱主键 + * @return 机器人对战分组宝箱 + */ + public TtRobotFightGroupBox selectTtRobotFightGroupBoxByBoxId(Integer groupId, Integer boxId); + + /** + * 查询机器人对战分组宝箱列表 + * + * @param ttRobotFightGroupBox 机器人对战分组宝箱 + * @return 机器人对战分组宝箱集合 + */ + public List selectTtRobotFightGroupBoxList( + Integer groupId, + TtRobotFightGroupBox ttRobotFightGroupBox + ); + + /** + * 新增机器人对战分组宝箱 + * + * @param ttRobotFightGroupBox 机器人对战分组宝箱 + * @return 结果 + */ + public int insertTtRobotFightGroupBox(TtRobotFightGroupBox ttRobotFightGroupBox); + + /** + * 修改机器人对战分组宝箱 + * + * @param ttRobotFightGroupBox 机器人对战分组宝箱 + * @return 结果 + */ + public int updateTtRobotFightGroupBox(TtRobotFightGroupBox ttRobotFightGroupBox); + + /** + * 批量删除机器人对战分组宝箱 + * + * @param groupIds 需要删除的机器人对战分组宝箱主键集合 + * @return 结果 + */ + public int deleteTtRobotFightGroupBoxByGroupIds(Integer groupId, Integer[] boxIds); + + /** + * 删除机器人对战分组宝箱信息 + * + * @param groupId 机器人对战分组宝箱主键 + * @return 结果 + */ + public int deleteTtRobotFightGroupBoxByGroupId(Integer groupId, Integer boxId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRobotFightGroupService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRobotFightGroupService.java new file mode 100644 index 0000000..bdbcf6a --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRobotFightGroupService.java @@ -0,0 +1,61 @@ +package com.ruoyi.admin.service; + +import java.util.List; +import com.ruoyi.domain.other.TtRobotFightGroup; + +/** + * 机器人对战分组Service接口 + * + * @author ruoyi + * @date 2024-06-28 + */ +public interface TtRobotFightGroupService +{ + /** + * 查询机器人对战分组 + * + * @param groupId 机器人对战分组主键 + * @return 机器人对战分组 + */ + public TtRobotFightGroup selectTtRobotFightGroupByGroupId(Integer groupId); + + /** + * 查询机器人对战分组列表 + * + * @param ttRobotFightGroup 机器人对战分组 + * @return 机器人对战分组集合 + */ + public List selectTtRobotFightGroupList(TtRobotFightGroup ttRobotFightGroup); + + /** + * 新增机器人对战分组 + * + * @param ttRobotFightGroup 机器人对战分组 + * @return 结果 + */ + public int insertTtRobotFightGroup(TtRobotFightGroup ttRobotFightGroup); + + /** + * 修改机器人对战分组 + * + * @param ttRobotFightGroup 机器人对战分组 + * @return 结果 + */ + public int updateTtRobotFightGroup(TtRobotFightGroup ttRobotFightGroup); + + /** + * 批量删除机器人对战分组 + * + * @param groupIds 需要删除的机器人对战分组主键集合 + * @return 结果 + */ + public int deleteTtRobotFightGroupByGroupIds(Integer[] groupIds); + + /** + * 删除机器人对战分组信息 + * + * @param groupId 机器人对战分组主键 + * @return 结果 + */ + public int deleteTtRobotFightGroupByGroupId(Integer groupId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollJackpotOrnamentsService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollJackpotOrnamentsService.java new file mode 100644 index 0000000..6c65466 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollJackpotOrnamentsService.java @@ -0,0 +1,20 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.dto.rollJackpotOrnament.RollJOEdit; +import com.ruoyi.domain.entity.roll.TtRollJackpotOrnaments; +import com.ruoyi.domain.entity.roll.TtRollJackpotOrnamentsBody; +import com.ruoyi.domain.vo.TtRollJackpotOrnamentsDataVO; + +import java.util.List; + +public interface TtRollJackpotOrnamentsService extends IService { + + List queryList(TtRollJackpotOrnamentsBody rollJackpotOrnamentsBody); + + String insertRollJackpotOrnaments(TtRollJackpotOrnaments ttRollJackpotOrnaments); + + String updateRollJackpotOrnamentsById(TtRollJackpotOrnaments rollJOEdit); + + String batchAdd(Integer rollJackpotId, List OrnamentsIds); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollJackpotService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollJackpotService.java new file mode 100644 index 0000000..9fb82af --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollJackpotService.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.roll.TtRollJackpot; + +public interface TtRollJackpotService extends IService { + + String removeRollJackpotById(Long jackpotId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollService.java new file mode 100644 index 0000000..150f625 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollService.java @@ -0,0 +1,42 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.admin.controller.TtRollController; +import com.ruoyi.admin.domain.body.AddRobotBody; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.roll.GetRollPrizePool; +import com.ruoyi.domain.dto.roll.InviteRollUser; +import com.ruoyi.domain.entity.roll.TtRoll; +import com.ruoyi.domain.entity.roll.TtRollBody; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.vo.TtRollPrizeDataVO; +import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsByPageVO; +import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsVO; + +import java.util.List; + +public interface TtRollService extends IService { + + List queryList(TtRollBody ttRollBody); + + AjaxResult createRoll(TtRoll ttRoll); + + AjaxResult updateRollById(TtRoll ttRoll); + + List getRollPrizeList(Integer rollId); + + AjaxResult namedWinner(TtRollPrizeDataVO param); + + AjaxResult getRollUsers(Integer rollId); + + R cancelNamedWinner(List rollUserPrizeIds); + + R getRollPrizePool(GetRollPrizePool param); + + R inviteRollUser(InviteRollUser inviteRollUser); + + List getRollUser(Integer rollId); + + AjaxResult addRobot(AddRobotBody addRobotBody); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollUserPrizeService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollUserPrizeService.java new file mode 100644 index 0000000..4a58e2b --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollUserPrizeService.java @@ -0,0 +1,8 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.roll.TtRollUser; +import com.ruoyi.domain.entity.roll.TtRollUserPrize; + +public interface TtRollUserPrizeService extends IService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollUserService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollUserService.java new file mode 100644 index 0000000..f5efaf6 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtRollUserService.java @@ -0,0 +1,10 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.roll.TtRollUser; + +public interface TtRollUserService extends IService { + R rollWinners(Integer rollId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtTaskCenterService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtTaskCenterService.java new file mode 100644 index 0000000..c792a6a --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtTaskCenterService.java @@ -0,0 +1,61 @@ +package com.ruoyi.admin.service; + +import java.util.List; +import com.ruoyi.domain.other.TtTaskCenter; + +/** + * 任务中心Service接口 + * + * @author ruoyi + * @date 2024-05-25 + */ +public interface TtTaskCenterService +{ + /** + * 查询任务中心 + * + * @param taskId 任务中心主键 + * @return 任务中心 + */ + public TtTaskCenter selectTtTaskCenterByTaskId(Integer taskId); + + /** + * 查询任务中心列表 + * + * @param ttTaskCenter 任务中心 + * @return 任务中心集合 + */ + public List selectTtTaskCenterList(TtTaskCenter ttTaskCenter); + + /** + * 新增任务中心 + * + * @param ttTaskCenter 任务中心 + * @return 结果 + */ + public int insertTtTaskCenter(TtTaskCenter ttTaskCenter); + + /** + * 修改任务中心 + * + * @param ttTaskCenter 任务中心 + * @return 结果 + */ + public int updateTtTaskCenter(TtTaskCenter ttTaskCenter); + + /** + * 批量删除任务中心 + * + * @param taskIds 需要删除的任务中心主键集合 + * @return 结果 + */ + public int deleteTtTaskCenterByTaskIds(Integer[] taskIds); + + /** + * 删除任务中心信息 + * + * @param taskId 任务中心主键 + * @return 结果 + */ + public int deleteTtTaskCenterByTaskId(Integer taskId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtTimeRollService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtTimeRollService.java new file mode 100644 index 0000000..893e539 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtTimeRollService.java @@ -0,0 +1,23 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.roll.TtTimeRoll; +import com.ruoyi.domain.vo.TtRollPrizeDataVO; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.exception.job.TaskException; +import org.quartz.SchedulerException; + +import java.util.List; + +public interface TtTimeRollService extends IService { + + String insertTimeRoll(TtTimeRoll ttTimeRoll) throws SchedulerException, TaskException; + + AjaxResult removeTimeRollById(Integer id) throws SchedulerException; + + String changeStatus(TtTimeRoll ttTimeRoll) throws SchedulerException; + + List getTimeRollPrizeList(Integer id); + + String namedWinner(TtRollPrizeDataVO rollPrizeData); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtTimeRollUserService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtTimeRollUserService.java new file mode 100644 index 0000000..2f3b346 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtTimeRollUserService.java @@ -0,0 +1,7 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.roll.TtTimeRollUser; + +public interface TtTimeRollUserService extends IService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUpgradeFailOrnamentsService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUpgradeFailOrnamentsService.java new file mode 100644 index 0000000..7162bc5 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUpgradeFailOrnamentsService.java @@ -0,0 +1,19 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.admin.controller.TtUpgradeFailOrnamentsController; +import com.ruoyi.admin.controller.TtUpgradeFailOrnamentsController.BatchAddParam; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.other.TtUpgradeFailOrnaments; +import com.ruoyi.domain.vo.TtUpgradeFailOrnamentsDataVO; + +import java.util.List; + +public interface TtUpgradeFailOrnamentsService extends IService { + + List queryList(TtUpgradeFailOrnamentsController.listParam param); + + AjaxResult batchAdd(BatchAddParam param); + + String updateUpgradeFailOrnamentsById(TtUpgradeFailOrnamentsDataVO ttUpgradeFailOrnamentsDataVO); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUpgradeOrnamentsService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUpgradeOrnamentsService.java new file mode 100644 index 0000000..9fb937d --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUpgradeOrnamentsService.java @@ -0,0 +1,21 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtUpgradeOrnaments; +import com.ruoyi.domain.other.TtUpgradeOrnamentsBody; +import com.ruoyi.domain.vo.TtUpgradeOrnamentsDataVO; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +public interface TtUpgradeOrnamentsService extends IService { + + List queryList(TtUpgradeOrnamentsBody ttUpgradeOrnamentsBody); + + String batchAdd(List ornamentsIds); + + String updateUpgradeOrnamentsById(TtUpgradeOrnamentsDataVO ttUpgradeOrnamentsDataVO); + + Map getUpgradeProfitStatistics(Integer id); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUpgradeRecordService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUpgradeRecordService.java new file mode 100644 index 0000000..5bda257 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUpgradeRecordService.java @@ -0,0 +1,21 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.domain.dto.upgrade.UpgradeCondition; +import com.ruoyi.domain.other.TtUpgradeRecord; +import com.ruoyi.domain.other.TtUpgradeRecordBody; +import com.ruoyi.domain.vo.upgrade.UpgradeRecordVO; + +import java.util.List; + +public interface TtUpgradeRecordService extends IService { + + List getUpgradeRecord(TtUpgradeRecordBody ttUpgradeRecordBody); + + R> historyDetail(UpgradeCondition param); + + R> adminGetLog(UpgradeCondition param); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserAmountRecordsService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserAmountRecordsService.java new file mode 100644 index 0000000..8bc7a31 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserAmountRecordsService.java @@ -0,0 +1,26 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.userRecord.AmountRecordsDetailCondition; +import com.ruoyi.domain.vo.TtUserAccountRecordsRankVO; +import com.ruoyi.domain.entity.recorde.TtUserAmountRecords; +import com.ruoyi.domain.other.TtUserAmountRecordsBody; +import com.ruoyi.domain.vo.TtUserAmountRecords.UserAmountDetailVO; +import com.ruoyi.domain.vo.UserBERankVO; +import org.apache.ibatis.annotations.Param; + +import java.sql.Timestamp; +import java.util.List; + +public interface TtUserAmountRecordsService extends IService { + + R queryList(TtUserAmountRecordsBody ttUserAmountRecordsBody); + + Page rank(Timestamp begin, Timestamp end, Integer page, Integer size); + + R pWelfareRecords(Integer userId, Integer page, Integer size); + + List userAccountDetail(AmountRecordsDetailCondition param); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserAvatarService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserAvatarService.java new file mode 100644 index 0000000..170bef7 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserAvatarService.java @@ -0,0 +1,9 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtUserAvatar; + +public interface TtUserAvatarService extends IService { + + String updateUserAvatarById(TtUserAvatar ttUserAvatar); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserBlendErcashService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserBlendErcashService.java new file mode 100644 index 0000000..21575da --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserBlendErcashService.java @@ -0,0 +1,16 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.ChildDetailByBoss; +import com.ruoyi.domain.dto.userRecord.BlendErcashCondition; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.vo.ChildDetailByBossVO; + +public interface TtUserBlendErcashService extends IService { + + R byCondition(BlendErcashCondition condition); + + R> childDetailByBoss(ChildDetailByBoss param); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserCreditsRecordsService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserCreditsRecordsService.java new file mode 100644 index 0000000..55b4460 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserCreditsRecordsService.java @@ -0,0 +1,19 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.recorde.TtUserCreditsRecords; +import com.ruoyi.domain.other.TtUserCreditsRecordsBody; +import com.ruoyi.domain.vo.TtUserCreditsRecordsRankVO; + +import java.sql.Timestamp; +import java.util.List; + +public interface TtUserCreditsRecordsService extends IService { + + List queryList(TtUserCreditsRecordsBody ttUserCreditsRecordsBody); + + Page rank(Timestamp begin,Timestamp end, Integer page, Integer size); + + Page pWelfareRecords(Integer uid, Integer type, Integer page, Integer size); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserService.java new file mode 100644 index 0000000..df8afb1 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtUserService.java @@ -0,0 +1,160 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.admin.model.UpdateUserAccountBo; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.sys.TeamUsersParam; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtUserBody; +import com.ruoyi.domain.other.TtUserPackSackBody; +import com.ruoyi.domain.vo.TtUserPackSackDataVO; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.domain.other.ApiUserOnline; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.vo.TtUserVipLevelVo; +import com.ruoyi.domain.vo.sys.SimpleTtUserVO; +import com.ruoyi.domain.vo.user.TtUseChildInfoVo; +import com.ruoyi.domain.vo.user.TtUserInfoVo; +import com.ruoyi.domain.vo.user.TtUserVo; +import org.apache.commons.lang3.tuple.Pair; + +import jakarta.servlet.http.HttpServletResponse; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; + +public interface TtUserService extends IService { + + Pair> queryList(TtUserBody ttUserBody); + + AjaxResult updateUserById(TtUser ttUser); + + AjaxResult updateUserInvitationCodeById(TtUser ttUser); + + void generateAccount(HttpServletResponse response, List accountList); + + List getAccountList(Integer num); + TtUser getAccountList(String phoneNumber, String password); + + List getRobotList(Integer num); + + boolean checkUserNameUnique(TtUser user); + + boolean checkPhoneUnique(TtUser user); + + boolean checkIdNumUnique(TtUser user); + + String getInvitationCode(); + + public TtUser selectTtUserById(Long id); + + ApiUserOnline loginUserToUserOnline(LoginUser user); + + ApiUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user); + + ApiUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user); + + ApiUserOnline selectOnlineByUserName(String userName, LoginUser user); + + @Deprecated + void insertUserAmountRecords(Integer userId, TtAccountRecordType type, TtAccountRecordSource source, BigDecimal amount, BigDecimal finalAmount); + @Deprecated + void insertUserAmountRecords(Integer userId, TtAccountRecordType type, TtAccountRecordSource source, BigDecimal amount, BigDecimal finalAmount, + Integer pwChildId, String pwChildName, BigDecimal childAccount); + + void insertUserAmountRecords(Integer userId, TtAccountRecordType type, TtAccountRecordSource source, BigDecimal amount, BigDecimal finalAmount, + Integer taskId); + + void insertUserCreditsRecords(Integer userId, TtAccountRecordType type, TtAccountRecordSource source, BigDecimal credits, BigDecimal finalCredits); + + void insertUserCreditsRecords(Integer userId, TtAccountRecordType type, TtAccountRecordSource source, BigDecimal credits, BigDecimal finalCredits, + Integer PWChildId, String PWChildName, BigDecimal childAccount); + + void insertUserCreditsRecords(Integer userId, TtAccountRecordType type, TtAccountRecordSource source, BigDecimal credits, BigDecimal finalCredits, + Integer taskId); + + List getPackSack(TtUserPackSackBody ttUserPackSackBody); + + Map getUserProfitStatistics(Integer userId); + + + List propRankOfDay(Timestamp begin,Timestamp end,Integer number); + + R> teamUsers(TeamUsersParam param); + + List teamUIds(Integer bossId); + + void updateUserVIPLevel(Integer userId); + + /** + * 当前vip进度 + */ + TtUserVipLevelVo userVipLeverRate(Integer userId); + /** + * 获取用户信息包含下级信息 + */ + TtUserInfoVo userInfo(Integer userId); + /** + * 获取用户信息包含下级用户信息 + */ + List userChildInfos(Integer userId); + + /** + * 当前用户领取对应VIP等级奖励的钱 + */ + AjaxResult userVipLevelRechargeThreshold(Integer userId, Integer vipLevel); + /** + * 当前用户领取对应VIP等级奖励的钱 + */ + AjaxResult userHasVipLevelRechargeThreshold(Integer userId); + /** + * 当前用户领取充值达到的奖金 + */ + AjaxResult userRechargeBonus(Integer userId, Integer recharge, Double bonus); + + /** + * 当前用户领取充值已达到的奖金 + */ + AjaxResult userHasRechargeBonus(Integer userId); + /** + * 当前用户积分兑换为金币 + */ + AjaxResult userCreditsToAmount(Integer userId, BigDecimal credits); + + /** + * 玩游戏日常金币变更,会变更积分,不是提货变更。 + * 更新用户余额状态。amount>0是增加,amount<0是扣除。 + * 扣除逻辑会先检查余额是否充足,不充足的话,则返回失败。 + * 会异步记录流水表 + * @param userId + * @param amount + * @return + */ + R updateUserAccount(Integer userId, BigDecimal amount, TtAccountRecordSource source); + + /** + * 充值、兑换金额变更。增加时,只增加余额,不动积分。 + * 更新用户余额状态。amount>0是增加,amount<0是扣除。 + * 扣除逻辑会先检查余额是否充足,不充足的话,则返回失败。 + * 会异步记录流水表 + * @param userId + * @param amount + * @return + */ + R updateOnlyUserAccount(Integer userId, BigDecimal amount, TtAccountRecordSource source); + + /** + * 积分变更。比如商城积分换物品。 + * 更新用户余额状态。amount>0是增加,amount<0是扣除。 + * 扣除逻辑会先检查余额是否充足,不充足的话,则返回失败。 + * 会异步记录流水表 + * @param userId + * @param amount + * @return + */ + R updateUserCredits(Integer userId, BigDecimal amount, TtAccountRecordSource source); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtVipLevelService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtVipLevelService.java new file mode 100644 index 0000000..a95a21c --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtVipLevelService.java @@ -0,0 +1,12 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtVipLevel; + +public interface TtVipLevelService extends IService { + + String generateVipLevel(Integer num); + + String updateVipLevelById(TtVipLevel ttVipLevel); + void truncateVipLevel(); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtWelfareService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtWelfareService.java new file mode 100644 index 0000000..6ec7962 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtWelfareService.java @@ -0,0 +1,61 @@ +package com.ruoyi.admin.service; + +import java.util.List; +import com.ruoyi.domain.other.TtWelfare; + +/** + * 福利列表Service接口 + * + * @author ruoyi + * @date 2024-05-11 + */ +public interface TtWelfareService +{ + /** + * 查询福利列表 + * + * @param welfareId 福利列表主键 + * @return 福利列表 + */ + public TtWelfare selectTtWelfareByWelfareId(Integer welfareId); + + /** + * 查询福利列表列表 + * + * @param ttWelfare 福利列表 + * @return 福利列表集合 + */ + public List selectTtWelfareList(TtWelfare ttWelfare); + + /** + * 新增福利列表 + * + * @param ttWelfare 福利列表 + * @return 结果 + */ + public int insertTtWelfare(TtWelfare ttWelfare); + + /** + * 修改福利列表 + * + * @param ttWelfare 福利列表 + * @return 结果 + */ + public int updateTtWelfare(TtWelfare ttWelfare); + + /** + * 批量删除福利列表 + * + * @param welfareIds 需要删除的福利列表主键集合 + * @return 结果 + */ + public int deleteTtWelfareByWelfareIds(Integer[] welfareIds); + + /** + * 删除福利列表信息 + * + * @param welfareId 福利列表主键 + * @return 结果 + */ + public int deleteTtWelfareByWelfareId(Integer welfareId); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtYyOrnamentsService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtYyOrnamentsService.java new file mode 100644 index 0000000..66f5393 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/TtYyOrnamentsService.java @@ -0,0 +1,7 @@ +package com.ruoyi.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtYYOrnaments; + +public interface TtYyOrnamentsService extends IService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/WebsitePropertyService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/WebsitePropertyService.java new file mode 100644 index 0000000..86ec8ae --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/WebsitePropertyService.java @@ -0,0 +1,18 @@ +package com.ruoyi.admin.service; + +import com.ruoyi.domain.vo.WebsitePropertyDataVO; + +import java.util.List; + +public interface WebsitePropertyService { + + List list(); + + WebsitePropertyDataVO getById(Integer id); + + String save(WebsitePropertyDataVO websitePropertyDataVO); + + String updateWebsitePropertyById(WebsitePropertyDataVO websitePropertyDataVO); + + int deleteWebsitePropertyByIds(Integer[] ids); +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/WebsiteSetupService.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/WebsiteSetupService.java new file mode 100644 index 0000000..b79ff04 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/WebsiteSetupService.java @@ -0,0 +1,15 @@ +package com.ruoyi.admin.service; + +import com.ruoyi.domain.other.OperationalStatistics; +import com.ruoyi.domain.other.ParameterSettingBody; + +import java.util.List; + +public interface WebsiteSetupService { + + List getOperationalStatistics(); + ParameterSettingBody getParameterSetting(); + + String updateParameterSetting(ParameterSettingBody parameterSettingBody); + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/ConfigServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/ConfigServiceImpl.java new file mode 100644 index 0000000..7bc0b49 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/ConfigServiceImpl.java @@ -0,0 +1,11 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.ConfigData; +import com.ruoyi.admin.mapper.ConfigMapper; +import com.ruoyi.admin.service.ConfigService; +import org.springframework.stereotype.Service; + +@Service +public class ConfigServiceImpl extends ServiceImpl implements ConfigService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/ShoppingServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/ShoppingServiceImpl.java new file mode 100644 index 0000000..8600cdc --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/ShoppingServiceImpl.java @@ -0,0 +1,70 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.github.pagehelper.PageInfo; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.admin.service.ShoppingService; +import com.ruoyi.domain.other.ShoppingBody; +import com.ruoyi.domain.vo.ShoppingDataVO; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.system.service.ISysConfigService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class ShoppingServiceImpl implements ShoppingService { + + private final ISysConfigService sysConfigService; + private final TtOrnamentMapper ornamentsMapper; + + public ShoppingServiceImpl(ISysConfigService sysConfigService, + TtOrnamentMapper ornamentsMapper) { + this.sysConfigService = sysConfigService; + this.ornamentsMapper = ornamentsMapper; + } + + @Override + public PageDataInfo list(ShoppingBody shoppingBody) { + + LambdaQueryChainWrapper wrapper = new LambdaQueryChainWrapper<>(ornamentsMapper); + if (StringUtils.isNotNull(shoppingBody.getItemName())) + wrapper.like(TtOrnament::getName, shoppingBody.getItemName()); + if (ObjectUtils.isNotEmpty(shoppingBody.getId())) wrapper.eq(TtOrnament::getId, shoppingBody.getId()); + if (StringUtils.isNotNull(shoppingBody.getExterior())) + wrapper.eq(TtOrnament::getExterior, shoppingBody.getExterior()); + if (StringUtils.isNotNull(shoppingBody.getType())) wrapper.eq(TtOrnament::getType, shoppingBody.getType()); + if (StringUtils.isNotNull(shoppingBody.getMaxPrice())) + wrapper.between(TtOrnament::getUsePrice, shoppingBody.getMinPrice(), shoppingBody.getMaxPrice()); + if (StringUtils.isNotNull(shoppingBody.getIsPutaway())) + wrapper.eq(TtOrnament::getIsPutaway, shoppingBody.getIsPutaway()); + // wrapper.eq(TtOrnament::getIsProprietaryProperty, "1"); + wrapper.orderByAsc(TtOrnament::getIsPutaway); + + List list = wrapper.list(); + + String exchangePriceRatio = sysConfigService.selectConfigByKey("exchangePriceRatio"); + List resultList = list.stream().map(ttOrnaments -> { + ShoppingDataVO shoppingDataVO = ShoppingDataVO.builder().build(); + BeanUtils.copyBeanProp(shoppingDataVO, ttOrnaments); + shoppingDataVO.setUseCredits(ObjectUtil.isEmpty(ttOrnaments.getUsePrice()) ? BigDecimal.ZERO : ttOrnaments.getUsePrice().multiply(new BigDecimal(exchangePriceRatio))); + return shoppingDataVO; + }).collect(Collectors.toList()); + PageDataInfo pageDataInfo = new PageDataInfo<>(); + pageDataInfo.setCode(HttpStatus.SUCCESS); + pageDataInfo.setMsg("查询成功"); + pageDataInfo.setRows(resultList); + pageDataInfo.setTotal(new PageInfo<>(list).getTotal()); + return pageDataInfo; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtAdvertisementServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtAdvertisementServiceImpl.java new file mode 100644 index 0000000..bbbde1b --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtAdvertisementServiceImpl.java @@ -0,0 +1,21 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtAdvertisementMapper; +import com.ruoyi.admin.service.TtAdvertisementService; +import com.ruoyi.domain.other.TtAdvertisement; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class TtAdvertisementServiceImpl extends ServiceImpl implements TtAdvertisementService { + + @Override + public String updatePicById(TtAdvertisement ttAdvertisement) { + // String picture = ttBanner.getPicture(); + // ttBanner.setPicture(RuoYiConfig.getDomainName() + picture); + if (this.updateById(ttAdvertisement)) return ""; + return "修改banner数据异常!"; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtAnnouncementServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtAnnouncementServiceImpl.java new file mode 100644 index 0000000..42d1011 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtAnnouncementServiceImpl.java @@ -0,0 +1,43 @@ +package com.ruoyi.admin.service.impl; + +import com.ruoyi.admin.mapper.TtAnnouncementMapper; +import com.ruoyi.admin.service.TtAnnouncementService; +import com.ruoyi.domain.other.TtAnnouncement; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; + +@Service +public class TtAnnouncementServiceImpl implements TtAnnouncementService { + + @Autowired + private TtAnnouncementMapper ttAnnouncementMapper; + + @Override + public List getAnnouncementList() { + return ttAnnouncementMapper.getAnnouncementList(); + } + + @Override + public TtAnnouncement getAnnouncementByAnnouncementId(Long userId, Integer announcementId) { + return ttAnnouncementMapper.getAnnouncementByAnnouncementId(announcementId); + } + + @Override + public int addAnnouncement(TtAnnouncement ttAnnouncement) { + ttAnnouncement.setCreateTime(new Date()); + return ttAnnouncementMapper.addAnnouncement(ttAnnouncement); + } + + @Override + public int editAnnouncement(TtAnnouncement ttAnnouncement) { + return ttAnnouncementMapper.editAnnouncement(ttAnnouncement); + } + + @Override + public int removeAnnouncementByAnnouncementId(Integer announcementId) { + return ttAnnouncementMapper.removeAnnouncementByAnnouncementId(announcementId); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBannerServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBannerServiceImpl.java new file mode 100644 index 0000000..d0ee876 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBannerServiceImpl.java @@ -0,0 +1,23 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtBanner; +import com.ruoyi.admin.mapper.TtBannerMapper; +import com.ruoyi.admin.service.TtBannerService; +import com.ruoyi.common.config.RuoYiConfig; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +@Service +@Slf4j +public class TtBannerServiceImpl extends ServiceImpl implements TtBannerService { + + @Override + public String updateBannerById(TtBanner ttBanner) { + // String picture = ttBanner.getPicture(); + // ttBanner.setPicture(RuoYiConfig.getDomainName() + picture); + if (this.updateById(ttBanner)) return ""; + return "修改banner数据异常!"; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBonusReceiveRecordServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBonusReceiveRecordServiceImpl.java new file mode 100644 index 0000000..b373cd0 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBonusReceiveRecordServiceImpl.java @@ -0,0 +1,11 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtBonusReceiveRecord; +import com.ruoyi.admin.mapper.TtBonusReceiveRecordMapper; +import com.ruoyi.admin.service.TtBonusReceiveRecordService; +import org.springframework.stereotype.Service; + +@Service +public class TtBonusReceiveRecordServiceImpl extends ServiceImpl implements TtBonusReceiveRecordService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBonusServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBonusServiceImpl.java new file mode 100644 index 0000000..e985f65 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBonusServiceImpl.java @@ -0,0 +1,244 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtBonusMapper; +import com.ruoyi.admin.mapper.TtPromotionRecordMapper; +import com.ruoyi.admin.mapper.TtRechargeRecordMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.*; +import com.ruoyi.admin.util.RandomUtils; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtPromotionLevel; +import com.ruoyi.domain.entity.TtPromotionRecord; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.*; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Service +public class TtBonusServiceImpl extends ServiceImpl implements TtBonusService { + + private final TtUserService userService; + private final TtVipLevelService vipLevelService; + private final TtPromotionLevelService promotionLevelService; + private final TtRechargeRecordMapper rechargeRecordMapper; + private final TtPromotionRecordMapper promotionRecordMapper; + private final TtUserBlendErcashMapper ttUserBlendErcashMapper; + private final TtBonusReceiveRecordService bonusReceiveRecordService; + + public TtBonusServiceImpl(TtUserService userService, + TtVipLevelService vipLevelService, + TtPromotionLevelService promotionLevelService, + TtRechargeRecordMapper rechargeRecordMapper, + TtPromotionRecordMapper promotionRecordMapper, + TtUserBlendErcashMapper ttUserBlendErcashMapper, + TtBonusReceiveRecordService bonusReceiveRecordService) { + this.userService = userService; + this.vipLevelService = vipLevelService; + this.promotionLevelService = promotionLevelService; + this.rechargeRecordMapper = rechargeRecordMapper; + this.promotionRecordMapper = promotionRecordMapper; + this.ttUserBlendErcashMapper = ttUserBlendErcashMapper; + this.bonusReceiveRecordService = bonusReceiveRecordService; + } + + @Override + public String updateBonusById(TtBonus ttBonus) { + String coverPicture = ttBonus.getCoverPicture(); + ttBonus.setCoverPicture(RuoYiConfig.getDomainName() + coverPicture); + this.updateById(ttBonus); + return ""; + } + + @Override + public void bonus(Integer userId, Integer rechargeRecordId) { + + List rechargeRecords = new LambdaQueryChainWrapper<>(rechargeRecordMapper) + .eq(TtRechargeRecord::getUserId, userId) + .eq(TtRechargeRecord::getStatus, "0") + .list(); + BigDecimal priceTotal = rechargeRecords.stream() + .map(TtRechargeRecord::getAmountActuallyPaid) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + TtUser ttUser = userService.getById(userId); + TtRechargeRecord ttRechargeRecord = rechargeRecordMapper.selectById(rechargeRecordId); + BigDecimal amountActuallyPaid = ttRechargeRecord.getAmountActuallyPaid(); + if (!Objects.isNull(ttUser.getParentId())) { + TtUser parentUser = userService.getById(ttUser.getParentId()); + if (insertParentUserPromotionRecord(ttUser.getUserId(), parentUser, amountActuallyPaid, rechargeRecordId)) { + updateParentUserPromotionLevel(parentUser); + } + } + if (ttUser.getVipLevel() > 0) { + TtVipLevel ttVipLevel = vipLevelService.getById(ttUser.getVipLevel()); + BigDecimal rebate = ttVipLevel.getCommissions().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) + .multiply(amountActuallyPaid).setScale(2, RoundingMode.HALF_UP); + + userService.updateUserAccount(userId, rebate, TtAccountRecordSource.RECHARGE_AWARD); + } + + List bonusList = new LambdaQueryChainWrapper<>(getBaseMapper()).eq(TtBonus::getStatus, "0").list(); + List bonusReceiveRecordList = new LambdaQueryChainWrapper<>(bonusReceiveRecordService.getBaseMapper()) + .eq(TtBonusReceiveRecord::getUserId, userId) + .eq(TtBonusReceiveRecord::getType, "2") + .list(); + List bonusIds = bonusReceiveRecordList.stream().map(TtBonusReceiveRecord::getBonusId).collect(Collectors.toList()); + List insertList = new ArrayList<>(); + for (TtBonus bonus : bonusList) { + if ("2".equals(bonus.getType())) { + if ("0".equals(bonus.getConditionType())) { + + } else if ("1".equals(bonus.getConditionType())) { + + } else if ("2".equals(bonus.getConditionType())) { + + } else if ("3".equals(bonus.getConditionType())) { + BigDecimal rechargeThreshold = bonus.getRechargeThreshold(); + if (!bonusIds.contains(bonus.getId()) && rechargeThreshold.compareTo(priceTotal) < 0) { + TtBonusReceiveRecord bonusReceiveRecord = TtBonusReceiveRecord.builder().build(); + bonusReceiveRecord.setType("2"); + bonusReceiveRecord.setBonusId(bonus.getId()); + bonusReceiveRecord.setUserId(userId); + bonusReceiveRecord.setAwardType("0"); + bonusReceiveRecord.setAwardPrice(RandomUtils.getRandomPrice(bonus.getAwardSection())); + bonusReceiveRecord.setCreateTime(DateUtils.getNowDate()); + insertList.add(bonusReceiveRecord); + } + } + } else if ("3".equals(bonus.getType())) { + if ("0".equals(bonus.getConditionType())) { + + } else if ("1".equals(bonus.getConditionType())) { + + } else if ("2".equals(bonus.getConditionType())) { + + } else if ("3".equals(bonus.getConditionType())) { + + } + } + } + bonusReceiveRecordService.saveBatch(insertList, 1); + } + + private boolean insertParentUserPromotionRecord(Integer userId, TtUser parentUser, BigDecimal amountActuallyPaid, Integer rechargeRecordId) { + TtPromotionRecord ttPromotionRecord = TtPromotionRecord.builder().build(); + ttPromotionRecord.setUserId(parentUser.getUserId()); + ttPromotionRecord.setSubordinateUserId(userId); + ttPromotionRecord.setRechargePrice(amountActuallyPaid); + ttPromotionRecord.setRechargeRecordId(rechargeRecordId); + ttPromotionRecord.setCreateTime(DateUtils.getNowDate()); + if (parentUser.getPromotionLevel() > 0) { + TtPromotionLevel ttPromotionLevel = promotionLevelService.getById(parentUser.getPromotionLevel()); + BigDecimal rebate = ttPromotionLevel.getCommissions().divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) + .multiply(amountActuallyPaid).setScale(2, RoundingMode.HALF_UP); + ttPromotionRecord.setRebate(rebate); + } + return promotionRecordMapper.insert(ttPromotionRecord) > 0; + } + + private List getLevelIdList(int level, List levelIds) { + List levelList = new ArrayList<>(); + for (int i = 1; i <= level; i++) { + levelList.add(i); + } + levelList.removeAll(levelIds); + return levelList; + } + + private void updateParentUserPromotionLevel(TtUser parentUser) { + List promotionRecordList = new LambdaQueryChainWrapper<>(promotionRecordMapper) + .eq(TtPromotionRecord::getUserId, parentUser.getUserId()) + .list(); + List promotionLevelList = promotionLevelService.list(); + BigDecimal rechargePriceTotal = promotionRecordList.stream().map(TtPromotionRecord::getRechargePrice).reduce(BigDecimal.ZERO, BigDecimal::add); + List promotionLevelPriceList = promotionLevelList.stream().map(TtPromotionLevel::getRechargeThreshold).collect(Collectors.toList()); + int promotionLevel = this.getLevel(rechargePriceTotal, promotionLevelPriceList); + // 如果用户的推广等级发生变化,更新用户的推广等级 + if (parentUser.getPromotionLevel() != promotionLevel) { + parentUser.setPromotionLevel(promotionLevel); + userService.updateById(parentUser); + List bonusReceiveRecordList = new LambdaQueryChainWrapper<>(bonusReceiveRecordService.getBaseMapper()) + .eq(TtBonusReceiveRecord::getUserId, parentUser.getUserId()) + .eq(TtBonusReceiveRecord::getType, "1") + .list(); + List promotionLevelIds = bonusReceiveRecordList.stream().map(TtBonusReceiveRecord::getPromotionLevelId).collect(Collectors.toList()); + List promotionLevelIdList = getLevelIdList(promotionLevel, promotionLevelIds); + if (!promotionLevelIdList.isEmpty()) { + List bonusReceiveRecords = new ArrayList<>(); + for (TtPromotionLevel ttPromotionLevel : promotionLevelService.getBaseMapper().selectBatchIds(promotionLevelIdList)) { + if (BigDecimal.ZERO.compareTo(ttPromotionLevel.getAddedBonus()) < 0) { + TtBonusReceiveRecord bonusReceiveRecord = TtBonusReceiveRecord.builder().build(); + bonusReceiveRecord.setType("1"); + bonusReceiveRecord.setPromotionLevelId(ttPromotionLevel.getId()); + bonusReceiveRecord.setUserId(parentUser.getUserId()); + bonusReceiveRecord.setAwardType("0"); + bonusReceiveRecord.setAwardPrice(ttPromotionLevel.getAddedBonus()); + bonusReceiveRecord.setCreateTime(DateUtils.getNowDate()); + bonusReceiveRecords.add(bonusReceiveRecord); + } + } + bonusReceiveRecordService.saveBatch(bonusReceiveRecords, 1); + } + // TODO 发放等级达标奖励红包 + distributePromotionLevelBonus(parentUser.getPromotionLevel(), parentUser.getUserId()); + } + } + + private int getLevel(BigDecimal price, List levelPriceList) { + levelPriceList.sort(Comparator.naturalOrder()); + for (int i = 0; i < levelPriceList.size(); i++) { + if (price.compareTo(levelPriceList.get(i)) < 0) { + return i; + } + } + return levelPriceList.size(); + } + + /** + * 发放等级达标奖励红包 + */ + private void distributePromotionLevelBonus(Integer promotionLevel, Integer userId) { + // 根据新等级查询红包金额 + TtPromotionLevel ttPromotionLevel = promotionLevelService.getById(promotionLevel); + BigDecimal addedBonus = ttPromotionLevel.getAddedBonus(); + + // 加钱 + TtUser user = userService.getById(userId); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, user.getUserId()) + .setSql("account_credits = account_credits + " + addedBonus); + userService.update(userUpdate); + + // 综合消费日志 + TtUser ttUser = userService.getById(user.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(user.getUserId()) + .credits(addedBonus.compareTo(BigDecimal.ZERO) > 0 ? addedBonus : null) + .finalCredits(addedBonus.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountCredits().add(addedBonus) : null) + .total(addedBonus.add(addedBonus)) // 收支合计 + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_LEVEL_REACH.getCode()) + .remark(TtAccountRecordSource.PROMOTION_LEVEL_REACH.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + ttUserBlendErcashMapper.insert(blendErcash); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxOpenChanceServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxOpenChanceServiceImpl.java new file mode 100644 index 0000000..06748f6 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxOpenChanceServiceImpl.java @@ -0,0 +1,93 @@ +package com.ruoyi.admin.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.admin.mapper.TtBoxOpenChanceMapper; +import com.ruoyi.admin.domain.TtBoxOpenChance; +import com.ruoyi.admin.service.TtBoxOpenChanceService; + +/** + * 开箱机会Service业务层处理 + * + * @author ruoyi + * @date 2024-07-09 + */ +@Service +public class TtBoxOpenChanceServiceImpl implements TtBoxOpenChanceService +{ + @Autowired + private TtBoxOpenChanceMapper ttBoxOpenChanceMapper; + + /** + * 查询开箱机会 + * + * @param ornamentId 开箱机会主键 + * @return 开箱机会 + */ + @Override + public TtBoxOpenChance selectTtBoxOpenChanceByOrnamentId(Integer ornamentId) + { + return ttBoxOpenChanceMapper.selectTtBoxOpenChanceByOrnamentId(ornamentId); + } + + /** + * 查询开箱机会列表 + * + * @param ttBoxOpenChance 开箱机会 + * @return 开箱机会 + */ + @Override + public List selectTtBoxOpenChanceList(TtBoxOpenChance ttBoxOpenChance) + { + return ttBoxOpenChanceMapper.selectTtBoxOpenChanceList(ttBoxOpenChance); + } + + /** + * 新增开箱机会 + * + * @param ttBoxOpenChance 开箱机会 + * @return 结果 + */ + @Override + public int insertTtBoxOpenChance(TtBoxOpenChance ttBoxOpenChance) + { + return ttBoxOpenChanceMapper.insertTtBoxOpenChance(ttBoxOpenChance); + } + + /** + * 修改开箱机会 + * + * @param ttBoxOpenChance 开箱机会 + * @return 结果 + */ + @Override + public int updateTtBoxOpenChance(TtBoxOpenChance ttBoxOpenChance) + { + return ttBoxOpenChanceMapper.updateTtBoxOpenChance(ttBoxOpenChance); + } + + /** + * 批量删除开箱机会 + * + * @param ornamentIds 需要删除的开箱机会主键 + * @return 结果 + */ + @Override + public int deleteTtBoxOpenChanceByOrnamentIds(Integer[] ornamentIds) + { + return ttBoxOpenChanceMapper.deleteTtBoxOpenChanceByOrnamentIds(ornamentIds); + } + + /** + * 删除开箱机会信息 + * + * @param ornamentId 开箱机会主键 + * @return 结果 + */ + @Override + public int deleteTtBoxOpenChanceByOrnamentId(Integer ornamentId) + { + return ttBoxOpenChanceMapper.deleteTtBoxOpenChanceByOrnamentId(ornamentId); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxOrnamentsServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxOrnamentsServiceImpl.java new file mode 100644 index 0000000..95710cc --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxOrnamentsServiceImpl.java @@ -0,0 +1,339 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.controller.TtBoxOrnamentsController.batchAddParam; +import com.ruoyi.admin.mapper.TtBoxMapper; +import com.ruoyi.admin.mapper.TtBoxRecordsMapper; +import com.ruoyi.admin.util.core.fight.LotteryMachine; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.domain.dto.boxRecords.TtBoxRecordsNum; +import com.ruoyi.domain.dto.boxRecords.TtBoxRecordsRemark; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtBoxOrnaments; +import com.ruoyi.admin.mapper.TtBoxOrnamentsMapper; +import com.ruoyi.admin.service.TtBoxOrnamentsService; +import com.ruoyi.admin.service.TtBoxService; +import com.ruoyi.domain.vo.TtBoxOrnamentsDataVO; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.domain.vo.box.BoxGlobalData; +import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Service +public class TtBoxOrnamentsServiceImpl extends ServiceImpl implements TtBoxOrnamentsService { + + @Autowired + private TtBoxService boxService; + + @Autowired + private TtBoxMapper boxMapper; + + @Autowired + private TtBoxRecordsMapper ttBoxRecordsMapper; + + @Autowired + private RedisCache redisCache; + + @Autowired + private LotteryMachine lotteryMachine; + + // 设置日期转换为字符串格式 + private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + static { + sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); + } + + @Override + public List selectTtBoxOrnamentsList(Integer boxId) { + // 宝箱内物品明细 + List boxOrnaments = baseMapper.selectTtBoxOrnamentsList(boxId); + Map ornamentIdToBeginTime = new HashMap<>(); + for (TtBoxOrnamentsDataVO boxOrnament : boxOrnaments) { + if (boxOrnament == null || boxOrnament.getOrnamentId() == null) { + continue; + } + String beginTime = null; + if (StringUtils.isNotEmpty(boxOrnament.getRemark())) { + TtBoxRecordsRemark remark = JSONUtil.toBean(boxOrnament.getRemark(), TtBoxRecordsRemark.class); + if (remark != null) { + beginTime = remark.getRealOddsChange(); + } + } + ornamentIdToBeginTime.put(boxOrnament.getOrnamentId(), beginTime); + } + Map boxRecordsMap = new HashMap<>(); + for (Map.Entry entry : ornamentIdToBeginTime.entrySet()) { + List boxRecordsNums = ttBoxRecordsMapper.selectBoxRecordsByOrnamentIds(boxId, Collections.singletonList(entry.getKey()), entry.getValue()); + if (CollectionUtils.isNotEmpty(boxRecordsNums)) { + for (TtBoxRecordsNum num : boxRecordsNums) { + if (num == null || num.getNum() == null) { + continue; + } + boxRecordsMap.put(num.getOrnamentId(), num.getNum()); + } + } + } + for (TtBoxOrnamentsDataVO boxOrnament : boxOrnaments) { + if (boxOrnament == null || boxOrnament.getOrnamentId() == null) { + continue; + } + if (boxOrnament.getRealOdds() > 0) { + boxOrnament.setRealRemainingOdds(boxOrnament.getRealOdds() - (boxRecordsMap.getOrDefault(boxOrnament.getOrnamentId(), 0) % boxOrnament.getRealOdds())); + } else { + boxOrnament.setRealRemainingOdds(0); + } + + } + return boxOrnaments; + } + + @Override + public String saveBoxOrnaments(TtBoxOrnaments ttBoxOrnaments) { + this.save(ttBoxOrnaments); + boxService.delCache(ttBoxOrnaments.getBoxId()); + boxService.isReplenishment(ttBoxOrnaments.getBoxId()); + return ""; + } + + @Override + public String updateBoxOrnamentsById(TtBoxOrnamentsDataVO ttBoxOrnamentsDataVO) { + + Integer boxId = ttBoxOrnamentsDataVO.getBoxId(), + boxOrnamentsId = ttBoxOrnamentsDataVO.getId(); + + TtBoxOrnaments dbBoxOrnaments = this.getById(boxOrnamentsId); + boolean realOddsChange = false; + if (dbBoxOrnaments != null && !Objects.equals(dbBoxOrnaments.getRealOdds(), ttBoxOrnamentsDataVO.getRealOdds())) { + realOddsChange = true; + } + Long ornamentsId = ttBoxOrnamentsDataVO.getOrnamentId(); + TtBoxOrnaments ttBoxOrnaments = TtBoxOrnaments.builder() + .id(boxOrnamentsId) + .boxId(boxId) + .ornamentId(ornamentsId) + .build(); + BeanUtils.copyBeanProp(ttBoxOrnaments, ttBoxOrnamentsDataVO); + ttBoxOrnaments.setLevel(ttBoxOrnamentsDataVO.getOrnamentsLevelId()); + ttBoxOrnaments.setUpdateBy(SecurityUtils.getUsername()); + ttBoxOrnaments.setUpdateTime(DateUtils.getNowDate()); + if (realOddsChange) { + TtBoxRecordsRemark remark = new TtBoxRecordsRemark(); + remark.setRealOddsChange(sdf.format(new Date())); + ttBoxOrnaments.setRemark(JSONUtil.toJsonStr(remark)); + } + this.updateById(ttBoxOrnaments); + + // todo 旧的开箱机制,全部更新以后可以删除 + // boxService.delCache(boxId); + // boxService.isReplenishment(boxId); + + // 更新redis奖池 + LambdaQueryWrapper ttBoxOrnamentsQuery = new LambdaQueryWrapper<>(); + ttBoxOrnamentsQuery.eq(TtBoxOrnaments::getBoxId, boxId); + List list = this.list(ttBoxOrnamentsQuery); + + String anchorOddsKey = "prize_pool:" + boxId + ":01"; + String realOddsKey = "prize_pool:" + boxId + ":02"; + String compensationOddsKey = "prize_pool:" + boxId + ":03"; // 新增补偿奖池Key + String thirdExplosiveOddsKey = "prize_pool:" + boxId + ":04"; // 新增三级爆率Key + HashMap realOddsSpace = new HashMap<>(); + HashMap anchorOddsSpace = new HashMap<>(); + HashMap compensationOddsSpace = new HashMap<>(); + HashMap thirdExplosiveOddsSpace = new HashMap<>(); + for (TtBoxOrnaments item : list) { + realOddsSpace.put(String.valueOf(item.getOrnamentId()), item.getRealOdds()); + anchorOddsSpace.put(String.valueOf(item.getOrnamentId()), item.getAnchorOdds()); + compensationOddsSpace.put(String.valueOf(item.getOrnamentId()), item.getRealCompOdds()); + thirdExplosiveOddsSpace.put(String.valueOf(item.getOrnamentId()), item.getThirdExplosiveOdds()); + } + redisCache.setCacheMap(realOddsKey, realOddsSpace, 600, TimeUnit.SECONDS); + redisCache.setCacheMap(anchorOddsKey, anchorOddsSpace, 600, TimeUnit.SECONDS); + redisCache.setCacheMap(compensationOddsKey, compensationOddsSpace, 600, TimeUnit.SECONDS); + redisCache.setCacheMap(thirdExplosiveOddsKey, thirdExplosiveOddsSpace, 600, TimeUnit.SECONDS); + + // 清空内存奖池 + lotteryMachine.clearBoxPrizePool(boxId); + + return ""; + } + + @Override + public String removeBoxOrnamentsByIds(Integer boxId, List list) { + this.removeByIds(list); + boxService.delCache(boxId); + boxService.isReplenishment(boxId); + return ""; + } + + @Override + public AjaxResult getProfitMargin(Integer boxId) { + TtBox ttBox = boxService.getById(boxId); + if (Objects.isNull(ttBox)) { + return AjaxResult.error("宝箱不存在"); + } + List list = this.selectTtBoxOrnamentsList(boxId); + list = list.stream().filter(v -> v.getOrnamentId() != null).collect(Collectors.toList()); + int oddsTotalNum = list.stream().map(TtBoxOrnamentsDataVO::getOdds).mapToInt(Integer::intValue).sum(); + BigDecimal oddsAmountConsumed = ttBox.getPrice().multiply(BigDecimal.valueOf(oddsTotalNum)); + BigDecimal oddsAggregateAmount = list.stream().map(ttBoxOrnamentsDataVO -> ttBoxOrnamentsDataVO.getUsePrice() + .multiply(BigDecimal.valueOf(ttBoxOrnamentsDataVO.getOdds()))).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal oddsTotalProfit = oddsAmountConsumed.subtract(oddsAggregateAmount); + BigDecimal oddsTotalProfitMargin = getTotalProfitMargin(oddsTotalProfit, oddsAmountConsumed); + int realOddsTotalNum = list.stream().map(TtBoxOrnamentsDataVO::getRealOdds).mapToInt(Integer::intValue).sum(); + BigDecimal realOddsAmountConsumed = ttBox.getPrice().multiply(BigDecimal.valueOf(realOddsTotalNum)); + BigDecimal realOddsAggregateAmount = list.stream().map(ttBoxOrnamentsDataVO -> ttBoxOrnamentsDataVO.getUsePrice() + .multiply(BigDecimal.valueOf(ttBoxOrnamentsDataVO.getRealOdds()))).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal realOddsTotalProfit = realOddsAmountConsumed.subtract(realOddsAggregateAmount); + BigDecimal realOddsTotalProfitMargin = getTotalProfitMargin(realOddsTotalProfit, realOddsAmountConsumed); + int anchorOddsTotalNum = list.stream().map(TtBoxOrnamentsDataVO::getAnchorOdds).mapToInt(Integer::intValue).sum(); + BigDecimal anchorOddsAmountConsumed = ttBox.getPrice().multiply(BigDecimal.valueOf(anchorOddsTotalNum)); + BigDecimal anchorOddsAggregateAmount = list.stream().map(ttBoxOrnamentsDataVO -> ttBoxOrnamentsDataVO.getUsePrice() + .multiply(BigDecimal.valueOf(ttBoxOrnamentsDataVO.getAnchorOdds()))).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal anchorOddsTotalProfit = anchorOddsAmountConsumed.subtract(anchorOddsAggregateAmount); + BigDecimal anchorOddsTotalProfitMargin = getTotalProfitMargin(anchorOddsTotalProfit, anchorOddsAmountConsumed); + + int robotOddsTotalNum = list.stream().map(TtBoxOrnamentsDataVO::getRobotOdds).mapToInt(Integer::intValue).sum(); + BigDecimal robotOddsAmountConsumed = ttBox.getPrice().multiply(BigDecimal.valueOf(robotOddsTotalNum)); + BigDecimal robotOddsAggregateAmount = list.stream().map(ttBoxOrnamentsDataVO -> ttBoxOrnamentsDataVO.getUsePrice() + .multiply(BigDecimal.valueOf(ttBoxOrnamentsDataVO.getRobotOdds()))).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal robotOddsTotalProfit = robotOddsAmountConsumed.subtract(robotOddsAggregateAmount); + BigDecimal robotOddsTotalProfitMargin = getTotalProfitMargin(robotOddsTotalProfit, robotOddsAmountConsumed); + + + AjaxResult success = AjaxResult.success(); + success.put("oddsTotalNum", oddsTotalNum); + success.put("oddsTotalProfit", oddsTotalProfit); + success.put("oddsTotalProfitMargin", oddsTotalProfitMargin + "%"); + success.put("realOddsTotalNum", realOddsTotalNum); + success.put("realOddsTotalProfit", realOddsTotalProfit); + success.put("realOddsTotalProfitMargin", realOddsTotalProfitMargin + "%"); + success.put("anchorOddsTotalNum", anchorOddsTotalNum); + success.put("anchorOddsTotalProfit", anchorOddsTotalProfit); + success.put("anchorOddsTotalProfitMargin", anchorOddsTotalProfitMargin + "%"); + success.put("robotOddsTotalNum", robotOddsTotalNum); + success.put("robotOddsTotalProfit", robotOddsTotalProfit); + success.put("robotOddsTotalProfitMargin", robotOddsTotalProfitMargin + "%"); + + return success; + } + + @Override + public String batchAdd(Integer boxId, List ornamentsIds) { + List tempList = new ArrayList<>(ornamentsIds); + List ttBoxOrnamentsList = new ArrayList<>(); + for (Long ornamentsId : tempList) { + TtBoxOrnaments ttBoxOrnaments = TtBoxOrnaments.builder().build(); + ttBoxOrnaments.setBoxId(boxId); + ttBoxOrnaments.setOrnamentId(ornamentsId); + ttBoxOrnaments.setCreateBy(SecurityUtils.getUsername()); + ttBoxOrnaments.setCreateTime(DateUtils.getNowDate()); + ttBoxOrnamentsList.add(ttBoxOrnaments); + } + this.saveBatch(ttBoxOrnamentsList, 1); + boxService.delCache(boxId); + boxService.isReplenishment(boxId); + return ""; + } + + @Override + public AjaxResult batchAdd(batchAddParam param) { + + // List tempList = param.getOrnamentsIds(); + List tempList = param.getOrnamentIds(); + List ttBoxOrnamentsList = new ArrayList<>(); + + for (Long ornamentId : tempList) { + TtBoxOrnaments boxOrn = TtBoxOrnaments.builder() + .boxId(param.getBoxId()) + .ornamentId(ornamentId) + // .marketHashName((String) temp.get("market_hash_name")) + // .ornamentsZbtId((String) temp.get("ornaments_zbt_id")) + // .ornamentsYyId((String) temp.get("ornaments_yy_id")) + .createBy(SecurityUtils.getUsername()) + .createTime(DateUtils.getNowDate()) + .build(); + + ttBoxOrnamentsList.add(boxOrn); + } + + this.saveBatch(ttBoxOrnamentsList); + + // TODO: 2024/3/29 更新抽奖机奖品空间 + + return AjaxResult.success("批量填货成功,请手动修改饰品数量!"); + } + + @Override + public List simpleBoxDetail(Integer boxId) { + return baseMapper.simpleBoxDetail(boxId); + } + + @Override + public R globalData(Integer boxId) { + + //检查,空箱子直接返回 + List list = new LambdaQueryChainWrapper<>(baseMapper) + .eq(TtBoxOrnaments::getBoxId, boxId) + .list(); + if (ObjectUtil.isEmpty(list) || list.isEmpty()) return R.ok(new BoxGlobalData()); + + // 宝箱统计 + BoxGlobalData globalData = boxMapper.globalData(boxId); + + // 计算利润率 + // 通用 + BigDecimal commonAmountConsumed = globalData.getCommonAmountConsumed(); + if (commonAmountConsumed.compareTo(BigDecimal.ZERO)==0){ + globalData.setCommonProfit(null); + globalData.setCommonProfitMargin(null); + }else { + BigDecimal commonAggregateAmount = globalData.getCommonAggregateAmount(); + BigDecimal commonProfit = commonAmountConsumed.subtract(commonAggregateAmount); + BigDecimal commonProfitMargin = commonProfit.divide(commonAmountConsumed, 4, RoundingMode.HALF_UP); + + globalData.setCommonProfit(commonProfit); + globalData.setCommonProfitMargin(commonProfitMargin); + } + + // 主播 + BigDecimal anchorAmountConsumed = globalData.getAnchorAmountConsumed(); + if (anchorAmountConsumed.compareTo(BigDecimal.ZERO)==0){ + globalData.setAnchorProfit(null); + globalData.setAnchorProfitMargin(null); + }else { + BigDecimal anchorAggregateAmount = globalData.getAnchorAggregateAmount(); + BigDecimal anchorProfit = anchorAmountConsumed.subtract(anchorAggregateAmount); + BigDecimal anchorProfitMargin = anchorProfit.divide(anchorAmountConsumed, 4, RoundingMode.HALF_UP); + + globalData.setAnchorProfit(anchorProfit); + globalData.setAnchorProfitMargin(anchorProfitMargin); + } + + return R.ok(globalData); + } + + private BigDecimal getTotalProfitMargin(BigDecimal totalProfit, BigDecimal amountConsumed) { + if (BigDecimal.ZERO.compareTo(amountConsumed) == 0) return BigDecimal.ZERO; + return totalProfit.divide(amountConsumed, 4, RoundingMode.HALF_UP) + .multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxRecordsServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxRecordsServiceImpl.java new file mode 100644 index 0000000..d4a2a67 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxRecordsServiceImpl.java @@ -0,0 +1,22 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.admin.mapper.TtBoxRecordsMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.domain.other.TtBoxRecordsBody; +import com.ruoyi.domain.vo.TtBoxRecordsDataVO; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class TtBoxRecordsServiceImpl extends ServiceImpl implements TtBoxRecordsService { + + @Override + public List selectBoxRecordsList(TtBoxRecordsBody ttBoxRecordsBody) { + return baseMapper.selectBoxRecordsList(ttBoxRecordsBody); + } + + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxServiceImpl.java new file mode 100644 index 0000000..3a0bebb --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxServiceImpl.java @@ -0,0 +1,232 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.github.pagehelper.PageInfo; +import com.ruoyi.admin.config.RedisConstants; +import com.ruoyi.admin.enums.BoxPoolType; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.admin.service.TtBoxService; +import com.ruoyi.admin.util.RandomUtils; +import com.ruoyi.admin.util.core.fight.LotteryMachine; +import com.ruoyi.admin.util.core.fight.PrizePool; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.box.TtBoxThirdExplosiveUser; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtBoxBody; +import com.ruoyi.domain.other.TtBoxOrnaments; +import com.ruoyi.domain.vo.BoxCacheDataVO; +import com.ruoyi.domain.vo.TtBoxDataVO; +import com.ruoyi.domain.vo.box.TtBoxUserVO; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class TtBoxServiceImpl extends ServiceImpl implements TtBoxService { + + private final RedisCache redisCache; + private final TtBoxRecordsMapper boxRecordsMapper; + private final TtBoxOrnamentsMapper boxOrnamentsMapper; + + @Autowired + private TtBoxTypeMapper ttBoxTypeMapper; + + @Autowired + private TtBoxThirdExplosiveUserMapper ttBoxThirdExplosiveUserMapper; + + @Autowired + private TtUserMapper ttUserMapper; + + @Autowired + private LotteryMachine lotteryMachine; + + public TtBoxServiceImpl(RedisCache redisCache, + TtBoxRecordsMapper boxRecordsMapper, + TtBoxOrnamentsMapper boxOrnamentsMapper) { + this.redisCache = redisCache; + this.boxRecordsMapper = boxRecordsMapper; + this.boxOrnamentsMapper = boxOrnamentsMapper; + } + + @Override + public PageDataInfo selectTtBoxList(TtBoxBody ttBoxBody) { + + List resultList = baseMapper.selectTtBoxList(ttBoxBody); + List list = resultList.stream().peek(ttBoxDataVO -> { + BigDecimal amountConsumed = ttBoxDataVO.getAmountConsumed(); + BigDecimal aggregateAmount = ttBoxDataVO.getAggregateAmount(); + if (StringUtils.isNotNull(amountConsumed) && StringUtils.isNotNull(aggregateAmount) + && amountConsumed.compareTo(BigDecimal.ZERO) != 0) { + + BigDecimal totalProfit = amountConsumed.subtract(aggregateAmount); + BigDecimal totalProfitMargin = totalProfit + .divide(amountConsumed, 4, RoundingMode.HALF_UP) + .multiply(BigDecimal.valueOf(100)) + .setScale(2, RoundingMode.HALF_UP); + ttBoxDataVO.setProfit(totalProfit); + ttBoxDataVO.setProfitMargin(totalProfitMargin + "%"); + + } + }).collect(Collectors.toList()); + PageDataInfo pageDataInfo = new PageDataInfo<>(); + pageDataInfo.setCode(HttpStatus.SUCCESS); + pageDataInfo.setMsg("查询成功"); + pageDataInfo.setRows(list); + pageDataInfo.setTotal(new PageInfo<>(resultList).getTotal()); + return pageDataInfo; + } + + @Override + public String updateTtBoxById(TtBoxDataVO ttBoxDataVO) { + + String boxImg01 = ttBoxDataVO.getBoxImg01(), boxImg02 = ttBoxDataVO.getBoxImg02(); + TtBox ttBox = TtBox.builder().build(); + BeanUtils.copyBeanProp(ttBox, ttBoxDataVO); + ttBox.setUpdateBy(SecurityUtils.getUsername()); + ttBox.setUpdateTime(DateUtils.getNowDate()); + // ttBox.setBoxImg01(RuoYiConfig.getDomainName() + boxImg01); + // ttBox.setBoxImg02(RuoYiConfig.getDomainName() + boxImg02); + ttBox.setBoxImg01(boxImg01); + ttBox.setBoxImg02(boxImg02); + this.updateById(ttBox); + + //删除该宝箱的奖池 + //BASE_POOL_KEY + item.getBoxId() + ":" + "01" + + return ""; + } + + @Override + public void isReplenishment(Integer boxId) { + TtBox ttBox = this.getById(boxId); + if (ttBox == null) { + return; + } + ttBox.setOpenNum(0L); + this.updateById(ttBox); + + lotteryMachine.resetBoxPrizePool(boxId); + } + + @Override + public void delCache(Integer boxId) { + } + + @Override + public BoxCacheDataVO statisticsBoxData(Integer boxId, Date date) { + BoxCacheDataVO boxCacheDataVO = BoxCacheDataVO.builder().build(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + String formattedDate = StringUtils.isNull(date) ? dateFormat.format(DateUtils.getNowDate()) : dateFormat.format(date); + TtBox ttBox = this.getById(boxId); + + String prizePoolKey = RedisConstants.BASE_POOL_KEY + ttBox.getBoxId() + ":" + BoxPoolType.NORMAL_PLAYER.getId(); + PrizePool realPrizePool = lotteryMachine.prizePools.get(prizePoolKey); + int remainingNum = 0; + if (realPrizePool != null) { + remainingNum = realPrizePool.getGoodsNumber(); + } + + List list = new LambdaQueryChainWrapper<>(boxOrnamentsMapper).eq(TtBoxOrnaments::getBoxId, boxId).list(); + int sum = list.stream().mapToInt(TtBoxOrnaments::getRealOdds).sum(); + List boxRecordsList = boxRecordsMapper.selectBoxRecordsByDate(boxId, formattedDate); + BigDecimal todayArisePriceTotal = boxRecordsList.stream().map(TtBoxRecords::getOrnamentsPrice).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal todayProfit = ttBox.getPrice().multiply(BigDecimal.valueOf(boxRecordsList.size())).subtract(todayArisePriceTotal); + boxCacheDataVO.setRemainingNum(remainingNum + "/" + sum); + boxCacheDataVO.setTodayOpenNum(boxRecordsList.size()); + boxCacheDataVO.setTodayArisePriceTotal(todayArisePriceTotal); + boxCacheDataVO.setTodayProfit(todayProfit); + return boxCacheDataVO; + } + + @Override + public List getRealList(Integer boxId, int flag) { + List boxOrnamentsDataList = new LambdaQueryChainWrapper<>(boxOrnamentsMapper) + .eq(TtBoxOrnaments::getBoxId, boxId) + .list(); + + Map realDataParam = new HashMap<>(); + Map anchorDataParam = new HashMap<>(); + for (TtBoxOrnaments boxOrnaments : boxOrnamentsDataList) { + Long ornamentsId = boxOrnaments.getOrnamentId(); + Integer realOdds = boxOrnaments.getRealOdds(); + realDataParam.put(ornamentsId, realOdds); + Integer anchorOdds = boxOrnaments.getAnchorOdds(); + anchorDataParam.put(ornamentsId, anchorOdds); + } + if (flag == 0) return RandomUtils.toList(realDataParam); + if (flag == 1) return RandomUtils.toList(anchorDataParam); + return null; + } + + @Override + public String createTtBoxThirdExplosiveUsers(TtBoxUserVO vo) { + if (vo == null || vo.getBoxId() == null || CollectionUtils.isEmpty(vo.getUsers())) { + return "请求参数不合法"; + } + for (TtUser ttUser : vo.getUsers()) { + TtBoxThirdExplosiveUser user = new TtBoxThirdExplosiveUser(); + user.setBoxId(vo.getBoxId()); + user.setUserId(ttUser.getUserId()); + user.setCreateTime(new Date()); + ttBoxThirdExplosiveUserMapper.insert(user); + } + return ""; + } + + @Override + public AjaxResult queryTtBoxThirdExplosiveUsers(Integer boxId) { + if (boxId == null) { + return AjaxResult.error("箱子ID为空"); + } + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(TtBoxThirdExplosiveUser::getBoxId, boxId); + List users = ttBoxThirdExplosiveUserMapper.selectList(query); + List userIds = users.stream().filter(Objects::nonNull).map(TtBoxThirdExplosiveUser::getUserId).collect(Collectors.toList()); + TtBoxUserVO vo = new TtBoxUserVO(); + vo.setBoxId(boxId); + if (CollectionUtils.isEmpty(userIds)) { + return AjaxResult.success(vo); + } + List ttUsers = ttUserMapper.selectBatchIds(userIds); + vo.setUsers(ttUsers); + return AjaxResult.success(vo); + } + + @Override + public AjaxResult deleteTtBoxThirdExplosiveUsers(TtBoxUserVO vo) { + if (vo == null || vo.getBoxId() == null || CollectionUtils.isEmpty(vo.getUsers())) { + return AjaxResult.error("请求参数不合法"); + } + List userIds = vo.getUsers().stream().filter(Objects::nonNull).map(TtUser::getUserId).collect(Collectors.toList()); + LambdaUpdateWrapper update = new LambdaUpdateWrapper<>(); + update.eq(TtBoxThirdExplosiveUser::getBoxId, vo.getBoxId()); + update.in(TtBoxThirdExplosiveUser::getUserId, userIds); + ttBoxThirdExplosiveUserMapper.delete(update); + return AjaxResult.success(); + } + + @Override + public AjaxResult resetBox(Integer boxId) { + return null; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxTypeServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxTypeServiceImpl.java new file mode 100644 index 0000000..941182b --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtBoxTypeServiceImpl.java @@ -0,0 +1,22 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtBoxType; +import com.ruoyi.admin.mapper.TtBoxTypeMapper; +import com.ruoyi.admin.service.TtBoxTypeService; +import com.ruoyi.common.config.RuoYiConfig; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class TtBoxTypeServiceImpl extends ServiceImpl implements TtBoxTypeService { + + @Override + public String updateBoxTypeById(TtBoxType ttBoxType) { + String icon = ttBoxType.getIcon(); + ttBoxType.setIcon(RuoYiConfig.getDomainName() + icon); + this.updateById(ttBoxType); + return ""; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtContentServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtContentServiceImpl.java new file mode 100644 index 0000000..9b4caa3 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtContentServiceImpl.java @@ -0,0 +1,28 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtContent; +import com.ruoyi.admin.mapper.TtContentMapper; +import com.ruoyi.admin.service.TtContentService; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class TtContentServiceImpl extends ServiceImpl implements TtContentService { + + @Override + public List queryList(TtContent ttContent) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotNull(ttContent.getId())) wrapper.eq(TtContent::getId, ttContent.getId()); + if (StringUtils.isNotNull(ttContent.getTypeId())) wrapper.eq(TtContent::getTypeId, ttContent.getTypeId()); + if (StringUtils.isNotEmpty(ttContent.getTitle())) wrapper.like(TtContent::getTitle, ttContent.getTitle()); + if (StringUtils.isNotEmpty(ttContent.getContent())) wrapper.like(TtContent::getContent, ttContent.getContent()); + if (StringUtils.isNotEmpty(ttContent.getStatus())) wrapper.eq(TtContent::getStatus, ttContent.getStatus()); + wrapper.orderByDesc(TtContent::getCreateTime); + return this.list(wrapper); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtContentTypeServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtContentTypeServiceImpl.java new file mode 100644 index 0000000..a26888e --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtContentTypeServiceImpl.java @@ -0,0 +1,27 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtContentType; +import com.ruoyi.admin.mapper.TtContentTypeMapper; +import com.ruoyi.admin.service.TtContentTypeService; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class TtContentTypeServiceImpl extends ServiceImpl implements TtContentTypeService { + + @Override + public List queryList(TtContentType ttContentType) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotNull(ttContentType.getId())) wrapper.eq(TtContentType::getId, ttContentType.getId()); + if (StringUtils.isNotNull(ttContentType.getName())) wrapper.like(TtContentType::getName, ttContentType.getName()); + if (StringUtils.isNotEmpty(ttContentType.getAlias())) wrapper.like(TtContentType::getAlias, ttContentType.getAlias()); + if (StringUtils.isNotEmpty(ttContentType.getStatus())) wrapper.eq(TtContentType::getStatus, ttContentType.getStatus()); + wrapper.orderByDesc(TtContentType::getCreateTime); + return this.list(wrapper); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtDeliveryRecordServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtDeliveryRecordServiceImpl.java new file mode 100644 index 0000000..eb8bf01 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtDeliveryRecordServiceImpl.java @@ -0,0 +1,87 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.common.constant.DeliveryOrderStatus; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.dto.userRecord.DeliveryRecordsConfition; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.delivery.TtDeliveryRecord; +import com.ruoyi.admin.mapper.TtBoxRecordsMapper; +import com.ruoyi.admin.mapper.TtDeliveryRecordMapper; +import com.ruoyi.admin.service.TtDeliveryRecordService; +import com.ruoyi.domain.other.TtDeliveryApplyBody; +import com.ruoyi.domain.vo.DeliveryApplyVO; +import com.ruoyi.domain.other.TtDeliveryRecordBody; +import com.ruoyi.domain.vo.TtDeliveryRecordDataVO; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.domain.vo.delivery.DeliveryRecordVO; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class TtDeliveryRecordServiceImpl extends ServiceImpl implements TtDeliveryRecordService { + + private final TtBoxRecordsMapper boxRecordsMapper; + + public TtDeliveryRecordServiceImpl(TtBoxRecordsMapper boxRecordsMapper) { + this.boxRecordsMapper = boxRecordsMapper; + } + + @Override + public List getDeliveryApplyList(TtDeliveryApplyBody deliveryApplyBody) { + return baseMapper.getDeliveryApplyList(deliveryApplyBody); + } + + @Override + public String deliveryFail(Integer deliveryRecordId, String message) { + + TtDeliveryRecord ttDeliveryRecord = this.getById(deliveryRecordId); + ttDeliveryRecord.setStatus(DeliveryOrderStatus.ORDER_CANCEL.getCode()); + ttDeliveryRecord.setMessage(DeliveryOrderStatus.ORDER_CANCEL.getMsg()); + ttDeliveryRecord.setUpdateBy(SecurityUtils.getUsername()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + + this.updateById(ttDeliveryRecord); + + TtBoxRecords ttBoxRecords = new LambdaQueryChainWrapper<>(boxRecordsMapper) + .eq(TtBoxRecords::getId, ttDeliveryRecord.getBoxRecordsId()) + .eq(TtBoxRecords::getHolderUserId, ttDeliveryRecord.getUserId()) + .eq(TtBoxRecords::getOrnamentId, ttDeliveryRecord.getOrnamentId()) + .eq(TtBoxRecords::getStatus, TtboxRecordStatus.APPLY_DELIVERY.getCode()) + .one(); + ttBoxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode()); + ttBoxRecords.setUpdateTime(DateUtils.getNowDate()); + boxRecordsMapper.updateById(ttBoxRecords); + return ""; + } + + @Override + public List getDeliveryRecordList(TtDeliveryRecordBody deliveryRecordBody) { + return baseMapper.getDeliveryRecordList(deliveryRecordBody); + } + + @Override + public List getDeliveryRecordByUserList(TtDeliveryRecordBody deliveryRecordBody) { + return baseMapper.getDeliveryRecordByUserList(deliveryRecordBody); + } + + @Override + public List byCondition(DeliveryRecordsConfition param) { + + param.setLimit((param.getPage()-1)*param.getSize()); + + List list = baseMapper.byCondition( + param.getStatusList(), + param.getUIdList(), + param.getLimit(), + param.getSize()); + + return list; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtFightRankingRewardServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtFightRankingRewardServiceImpl.java new file mode 100644 index 0000000..37ef7db --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtFightRankingRewardServiceImpl.java @@ -0,0 +1,115 @@ +package com.ruoyi.admin.service.impl; + +import java.math.BigDecimal; +import java.util.List; + +import com.ruoyi.admin.service.TtFightRankingRewardService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.admin.mapper.TtFightRankingRewardMapper; +import com.ruoyi.domain.other.TtFightRankingReward; +import org.springframework.transaction.annotation.Transactional; + +/** + * 对战奖励金额Service业务层处理 + * + * @author ruoyi + * @date 2024-07-04 + */ +@Service +public class TtFightRankingRewardServiceImpl implements TtFightRankingRewardService +{ + @Autowired + private TtFightRankingRewardMapper ttFightRankingRewardMapper; + + /** + * 查询对战奖励金额 + * + * @param id 对战奖励金额主键 + * @return 对战奖励金额 + */ + @Override + public TtFightRankingReward selectTtFightRankingRewardById(Integer id) + { + return ttFightRankingRewardMapper.selectTtFightRankingRewardById(id); + } + + /** + * 查询对战奖励金额列表 + * + * @param ttFightRankingReward 对战奖励金额 + * @return 对战奖励金额 + */ + @Override + public List selectTtFightRankingRewardList(TtFightRankingReward ttFightRankingReward) + { + return ttFightRankingRewardMapper.selectTtFightRankingRewardList(ttFightRankingReward); + } + + @Override + @Transactional + public String generateRankingReward() + { + ttFightRankingRewardMapper.truncateRankingReward(); + for (int i = 1; i <= 10; i++) { + TtFightRankingReward ttFightRankingReward = new TtFightRankingReward(); + ttFightRankingReward.setName("TOP" + i); + ttFightRankingReward.setReward(BigDecimal.ZERO); + ttFightRankingRewardMapper.insertTtFightRankingReward(ttFightRankingReward); + } + return ""; + } + + // /** + // * 新增对战奖励金额 + // * + // * @param ttFightRankingReward 对战奖励金额 + // * @return 结果 + // */ + // @Override + // public int insertTtFightRankingReward(TtFightRankingReward ttFightRankingReward) + // { + // return ttFightRankingRewardMapper.insertTtFightRankingReward(ttFightRankingReward); + // } + + /** + * 修改对战奖励金额 + * + * @param ttFightRankingReward 对战奖励金额 + * @return 结果 + */ + @Override + public int updateTtFightRankingReward(TtFightRankingReward ttFightRankingReward) + { + return ttFightRankingRewardMapper.updateTtFightRankingReward(ttFightRankingReward); + } + + /** + * 批量删除对战奖励金额 + * + * @param ids 需要删除的对战奖励金额主键 + * @return 结果 + */ + @Override + public int deleteTtFightRankingRewardByIds(Integer[] ids) + { + return ttFightRankingRewardMapper.deleteTtFightRankingRewardByIds(ids); + } + + @Override + public void truncateRankingReward() { + ttFightRankingRewardMapper.truncateRankingReward(); + } + + // /** + // * 删除对战奖励金额信息 + // * + // * @param id 对战奖励金额主键 + // * @return 结果 + // */ + // @Override + // public int deleteTtFightRankingRewardById(Integer id) + // { + // return ttFightRankingRewardMapper.deleteTtFightRankingRewardById(id); + // } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtFightResultServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtFightResultServiceImpl.java new file mode 100644 index 0000000..19e1b03 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtFightResultServiceImpl.java @@ -0,0 +1,113 @@ +package com.ruoyi.admin.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtBoxMapper; +import com.ruoyi.admin.mapper.TtFightMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtFightService; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtFightResult; +import com.ruoyi.admin.mapper.TtFightResultMapper; +import com.ruoyi.admin.service.TtFightResultService; +import com.ruoyi.domain.vo.FightResultDataVO; +import com.ruoyi.domain.vo.fight.FightBoxVO; +import com.ruoyi.domain.vo.fight.FightResultVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class TtFightResultServiceImpl extends ServiceImpl implements TtFightResultService { + + private final TtFightMapper fightMapper; + + public TtFightResultServiceImpl(TtFightMapper fightMapper) { + this.fightMapper = fightMapper; + } + + @Autowired + private TtFightService ttFightService; + + @Autowired + private TtBoxRecordsService boxRecordsService; + + @Autowired + private TtBoxMapper boxMapper; + + // @Override + // public FightResultDataVO getFightResult(Integer fightId) { + // + // // new LambdaUpdateChainWrapper<>(fightMapper).eq(TtFight::getId, fightId).set(TtFight::getStatus, "2") + // // .set(TtFight::getUpdateTime, new Date()).update(); + // // TtFightResult fightResult = new LambdaQueryChainWrapper<>(baseMapper).eq(TtFightResult::getFightId, fightId).one(); + // + // LambdaUpdateWrapper ttFightUpdate = new LambdaUpdateWrapper<>(); + // ttFightUpdate + // .eq(TtFight::getId, fightId).set(TtFight::getStatus, "2") + // .set(TtFight::getUpdateTime, new Date()); + // ttFightService.update(ttFightUpdate); + // + // LambdaQueryWrapper ttFightResultQuery = new LambdaQueryWrapper<>(); + // ttFightResultQuery.eq(TtFightResult::getFightId, fightId); + // TtFightResult fightResult = getOne(ttFightResultQuery); + // + // log.info("fightId="+fightId); + // String fightResultStr = fightResult.getFightResult(); + // return JSONObject.parseObject(fightResultStr, FightResultDataVO.class); + // } + + @Override + public FightResultVO getFightResult(Integer fightId) { + LambdaQueryWrapper fightQuery = new LambdaQueryWrapper<>(); + fightQuery + .eq(TtFight::getId, fightId); + TtFight fight = ttFightService.getOne(fightQuery); + + LambdaQueryWrapper boxRecordsQuery = new LambdaQueryWrapper<>(); + boxRecordsQuery + .eq(TtBoxRecords::getFightId, fightId); + List allBoxRecords = boxRecordsService.list(boxRecordsQuery); + + Map boxData = fight.getBoxData(); + ArrayList fightBoxVOList = new ArrayList<>(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TtBox::getBoxId, boxData.keySet()); + List boxes = boxMapper.selectList(queryWrapper); + Map boxMap = boxes.stream().filter(Objects::nonNull).collect(Collectors.toMap(TtBox::getBoxId, TtBox::getPrice)); + boxData.keySet().forEach(boxId -> { + Object value =boxData.get(boxId); + if (value instanceof LinkedHashMap) { + Map map = (LinkedHashMap) value; + FightBoxVO vo = new FightBoxVO(map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null, + map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null, + map.get("boxImg01") != null ? map.get("boxImg01").toString() : null, + map.get("boxImg02") != null ? map.get("boxImg02").toString() : null); + vo.setPrice(boxMap.getOrDefault(vo.getBoxId(), BigDecimal.ZERO)); + fightBoxVOList.add(vo); + } else { + FightBoxVO vo = boxData.get(boxId); + vo.setPrice(boxMap.getOrDefault(vo.getBoxId(), BigDecimal.ZERO)); + fightBoxVOList.add(vo); + } + }); + + FightResultVO resultVO = FightResultVO.builder() + .fight(fight) + .winnerIds(fight.getWinnerIds()) + .fightResult(allBoxRecords) + .fightBoxVOList(fightBoxVOList) + .build(); + return resultVO; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtFightServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtFightServiceImpl.java new file mode 100644 index 0000000..9727e59 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtFightServiceImpl.java @@ -0,0 +1,105 @@ +package com.ruoyi.admin.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.admin.mapper.TtBoxMapper; +import com.ruoyi.admin.mapper.TtFightMapper; +import com.ruoyi.admin.service.TtFightService; +import com.ruoyi.domain.vo.FightBoxDataVO; +import com.ruoyi.domain.other.TtFightBody; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.domain.other.BoxDataBodyA; +import com.ruoyi.domain.vo.fight.FightBoxVO; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Service +public class TtFightServiceImpl extends ServiceImpl implements TtFightService { + + private final TtBoxMapper boxMapper; + + private final TtFightMapper ttFightMapper; + + public TtFightServiceImpl(TtBoxMapper boxMapper,TtFightMapper ttFightMapper) { + this.boxMapper = boxMapper; + this.ttFightMapper = ttFightMapper; + } + + @Override + public List selectFightList(TtFightBody ttFightBody) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotNull(ttFightBody.getId())) wrapper.eq(TtFight::getId, ttFightBody.getId()); + if (StringUtils.isNotEmpty(ttFightBody.getStatus())) wrapper.eq(TtFight::getStatus, ttFightBody.getStatus()); + if (StringUtils.isNotNull(ttFightBody.getUserId())) wrapper.eq(TtFight::getUserId, ttFightBody.getUserId()); + return this.list(wrapper); + } + + // @Override + // public List selectFightBoxList(Integer fightId) { + // TtFight ttFight = this.getById(fightId); + // String boxData = JSON.toJSONString(ttFight.getBoxData()); + // List boxDataBodyAList = JSONObject.parseObject(boxData, new TypeReference>() { + // }); + // List resultList = new ArrayList<>(); + // for (BoxDataBodyA boxDataBodyA : boxDataBodyAList) { + // System.err.println(boxDataBodyA); + // // 这里输出的全是BoxDataBodyA(boxId=null, boxNum=null, boxName=null, boxImg01=null, boxImg02=null) + // TtBox ttBox = new LambdaQueryChainWrapper<>(boxMapper).eq(TtBox::getBoxId, boxDataBodyA.getBoxId()).one(); + // for (int i = 0; i < boxDataBodyA.getBoxNum(); i++) { + // FightBoxDataVO fightBoxDataVO = FightBoxDataVO.builder().build(); + // BeanUtils.copyBeanProp(fightBoxDataVO, ttBox); + // resultList.add(fightBoxDataVO); + // } + // } + // return resultList; + // } + + @Override + public List selectFightBoxList(Integer fightId) { + TtFight ttFight = this.getById(fightId); + String boxDataJson = JSON.toJSONString(ttFight.getBoxData()); + + // 解析 JSON 字符串为 Map + Map boxDataMap = JSONObject.parseObject(boxDataJson, new TypeReference>() {}); + + List resultList = new ArrayList<>(); + boxDataMap.forEach((boxId, boxDataBodyA) -> { + + // 根据 boxId 查询对应的 TtBox 对象 + TtBox ttBox = new LambdaQueryChainWrapper<>(boxMapper) + .eq(TtBox::getBoxId, boxDataBodyA.getBoxId()) + .one(); + + if (ttBox != null) { + // 根据 boxNum 创建多个 FightBoxDataVO 对象 + for (int i = 0; i < boxDataBodyA.getNumber(); i++) { + FightBoxDataVO fightBoxDataVO = new FightBoxDataVO(); + BeanUtils.copyProperties(ttBox, fightBoxDataVO); + resultList.add(fightBoxDataVO); + } + } + }); + + return resultList; + } + + @Override + public int endFight(String fightId) { + TtFight ttFight = ttFightMapper.selectOne(new QueryWrapper().eq("fight_id", fightId)); + ttFight.setStatus(2); + return ttFightMapper.updateById(ttFight); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtFirstRechargeServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtFirstRechargeServiceImpl.java new file mode 100644 index 0000000..5caf9f2 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtFirstRechargeServiceImpl.java @@ -0,0 +1,96 @@ +package com.ruoyi.admin.service.impl; + +import java.util.List; +import com.ruoyi.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.admin.mapper.TtFirstRechargeMapper; +import com.ruoyi.domain.other.TtFirstRecharge; +import com.ruoyi.admin.service.TtFirstRechargeService; + +/** + * 首充赠送Service业务层处理 + * + * @author ruoyi + * @date 2024-06-21 + */ +@Service +public class TtFirstRechargeServiceImpl implements TtFirstRechargeService +{ + @Autowired + private TtFirstRechargeMapper ttFirstRechargeMapper; + + /** + * 查询首充赠送 + * + * @param id 首充赠送主键 + * @return 首充赠送 + */ + @Override + public TtFirstRecharge selectTtFirstRechargeById(Integer id) + { + return ttFirstRechargeMapper.selectTtFirstRechargeById(id); + } + + /** + * 查询首充赠送列表 + * + * @param ttFirstRecharge 首充赠送 + * @return 首充赠送 + */ + @Override + public List selectTtFirstRechargeList(TtFirstRecharge ttFirstRecharge) + { + return ttFirstRechargeMapper.selectTtFirstRechargeList(ttFirstRecharge); + } + + /** + * 新增首充赠送 + * + * @param ttFirstRecharge 首充赠送 + * @return 结果 + */ + @Override + public int insertTtFirstRecharge(TtFirstRecharge ttFirstRecharge) + { + ttFirstRecharge.setCreateTime(DateUtils.getNowDate()); + return ttFirstRechargeMapper.insertTtFirstRecharge(ttFirstRecharge); + } + + /** + * 修改首充赠送 + * + * @param ttFirstRecharge 首充赠送 + * @return 结果 + */ + @Override + public int updateTtFirstRecharge(TtFirstRecharge ttFirstRecharge) + { + ttFirstRecharge.setUpdateTime(DateUtils.getNowDate()); + return ttFirstRechargeMapper.updateTtFirstRecharge(ttFirstRecharge); + } + + /** + * 批量删除首充赠送 + * + * @param ids 需要删除的首充赠送主键 + * @return 结果 + */ + @Override + public int deleteTtFirstRechargeByIds(Integer[] ids) + { + return ttFirstRechargeMapper.deleteTtFirstRechargeByIds(ids); + } + + /** + * 删除首充赠送信息 + * + * @param id 首充赠送主键 + * @return 结果 + */ + @Override + public int deleteTtFirstRechargeById(Integer id) + { + return ttFirstRechargeMapper.deleteTtFirstRechargeById(id); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtMessageSendServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtMessageSendServiceImpl.java new file mode 100644 index 0000000..49e101c --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtMessageSendServiceImpl.java @@ -0,0 +1,11 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtMessageSend; +import com.ruoyi.admin.mapper.TtMessageSendMapper; +import com.ruoyi.admin.service.TtMessageSendService; +import org.springframework.stereotype.Service; + +@Service +public class TtMessageSendServiceImpl extends ServiceImpl implements TtMessageSendService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtMessageServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtMessageServiceImpl.java new file mode 100644 index 0000000..99a5320 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtMessageServiceImpl.java @@ -0,0 +1,74 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtMessage; +import com.ruoyi.domain.other.TtMessageSend; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.admin.mapper.TtMessageMapper; +import com.ruoyi.admin.service.TtMessageSendService; +import com.ruoyi.admin.service.TtMessageService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class TtMessageServiceImpl extends ServiceImpl implements TtMessageService { + + private final TtUserService userService; + private final TtMessageSendService messageSendService; + + public TtMessageServiceImpl(TtUserService userService, + TtMessageSendService messageSendService) { + this.userService = userService; + this.messageSendService = messageSendService; + } + + @Override + public List queryList(TtMessage ttMessage) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotNull(ttMessage.getId())) wrapper.eq(TtMessage::getId, ttMessage.getId()); + if (StringUtils.isNotEmpty(ttMessage.getMessage())) wrapper.like(TtMessage::getMessage, ttMessage.getMessage()); + return this.list(wrapper); + } + + @Override + public String delByIds(List ids) { + this.removeByIds(ids); + return ""; + } + + @Override + public String singleMessage(Integer messageId, List userIds) { + List messageSends = new ArrayList<>(); + for (Integer userId : userIds) { + TtMessageSend messageSend = TtMessageSend.builder().build(); + messageSend.setRecId(userId); + messageSend.setMessageId(messageId); + messageSend.setSendTime(DateUtils.getNowDate()); + messageSends.add(messageSend); + } + messageSendService.saveBatch(messageSends, 1); + return ""; + } + + @Override + public String massMessaging(Integer messageId) { + List list = userService.list(); + List messageSends = new ArrayList<>(); + for (TtUser ttUser : list) { + TtMessageSend messageSend = TtMessageSend.builder().build(); + messageSend.setRecId(ttUser.getUserId()); + messageSend.setMessageId(messageId); + messageSend.setSendTime(DateUtils.getNowDate()); + messageSends.add(messageSend); + } + messageSendService.saveBatch(messageSends, 1); + return ""; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOrderServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOrderServiceImpl.java new file mode 100644 index 0000000..da8fe91 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOrderServiceImpl.java @@ -0,0 +1,189 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtOrderMapper; +import com.ruoyi.admin.service.TtOrderService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.PayOrderStatus; +import com.ruoyi.domain.dto.sys.OrderQueryCondition; +import com.ruoyi.domain.dto.sys.TeamUsersParam; +import com.ruoyi.domain.dto.userRecord.OrderCondition; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.vo.order.TtOrderVO; +import com.ruoyi.domain.vo.sys.SimpleTtUserVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.sql.Timestamp; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +@Service +@Slf4j +public class TtOrderServiceImpl extends ServiceImpl implements TtOrderService { + + private final TtOrderMapper ttOrderMapper; + + public TtOrderServiceImpl(TtOrderMapper ttOrderMapper) { + this.ttOrderMapper = ttOrderMapper; + } + + @Override + public List queryList(TtOrder ttOrder) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotNull(ttOrder.getStatus())) + wrapper.eq(TtOrder::getStatus, ttOrder.getStatus()); + if (StringUtils.isNotNull(ttOrder.getUserId())) + wrapper.eq(TtOrder::getUserId, ttOrder.getUserId()); + return this.list(wrapper); + } + + @Override + public List byCondition(OrderCondition param) { + + param.setLimit((param.getPage() - 1) * param.getSize()); + List list = baseMapper.byCondition(param); + + Comparator comparator = null; + if (ObjectUtil.isNull(param.getOrderBy()) || param.getOrderBy().equals(1)) { + comparator = new Comparator() { + @Override + public int compare(TtOrderVO o1, TtOrderVO o2) { + return o1.getCreateTime().compareTo(o2.getCreateTime()); + } + }; + } else if (param.getOrderBy().equals(2)) { + comparator = new Comparator() { + @Override + public int compare(TtOrderVO o1, TtOrderVO o2) { + return o2.getCreateTime().compareTo(o1.getCreateTime()); + } + }; + } + + Collections.sort(list, comparator); + + return list; + + } + + // 批量统计消费 + @Override + public List batchRechargeTotal(List allEmployeesId, + String beginTime, + String endTime, + Integer orderType, + Integer page, + Integer size) { + + int limit = (page - 1) * size; + + if (StringUtils.isBlank(beginTime)) beginTime = null; + if (StringUtils.isBlank(endTime)) endTime = null; + if (ObjectUtil.isNull(orderType)) orderType = 1; + + return baseMapper.batchRechargeTotal( + allEmployeesId, + beginTime, + endTime, + orderType, + limit, + size); + } + + @Override + public R clientList(Integer page, Integer size, int uid) { + + Page pageInfo = new Page<>(page, size); + pageInfo.setOptimizeCountSql(false); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .eq(TtOrder::getUserId,uid) + .eq(TtOrder::getStatus, PayOrderStatus.PAY_COMPLE.getCode()) + .orderByDesc(TtOrder::getCreateTime); + pageInfo = this.page(pageInfo, wrapper); + + return R.ok(pageInfo); + } + + @Override + public R adminList(OrderQueryCondition con) { + + if (ObjectUtil.isNull(con.getPage()) || ObjectUtil.isNull(con.getSize())){ + return R.fail("分页参数不能为空"); + } + + Page pageInfo = new Page<>(con.getPage(), con.getSize()); + pageInfo.setOptimizeCountSql(false); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + wrapper + .in(ObjectUtil.isNotEmpty(con.getUidList()),TtOrder::getUserId,con.getUidList()) + .in(ObjectUtil.isNotEmpty(con.getStatusList()),TtOrder::getStatus,con.getStatusList()); + + if (ObjectUtil.isNotEmpty(con.getBeginTime()) && ObjectUtil.isNotEmpty(con.getEndTime())){ + wrapper.between(TtOrder::getCreateTime,con.getBeginTime(),con.getEndTime()); + } + + if (ObjectUtil.isNull(con.getOrderByFie())) con.setOrderByFie("time"); + if (ObjectUtil.isNull(con.getOrderByType())) con.setOrderByType(1); + + if (con.getOrderByFie().equals("time")){ + + if (con.getOrderByType().equals(1)){ + wrapper.orderByDesc(TtOrder::getCreateTime); + }else if (con.getOrderByType().equals(2)){ + wrapper.orderByAsc(TtOrder::getCreateTime); + }else { + wrapper.orderByDesc(TtOrder::getCreateTime); + } + + }else if (con.getOrderByFie().equals("totalAmount")){ + if (con.getOrderByType().equals(1)){ + wrapper.orderByDesc(TtOrder::getTotalAmount); + }else if (con.getOrderByType().equals(2)){ + wrapper.orderByAsc(TtOrder::getTotalAmount); + }else { + wrapper.orderByDesc(TtOrder::getTotalAmount); + } + }else { + if (con.getOrderByType().equals(1)){ + wrapper.orderByDesc(TtOrder::getCreateTime); + }else if (con.getOrderByType().equals(2)){ + wrapper.orderByAsc(TtOrder::getCreateTime); + }else { + wrapper.orderByDesc(TtOrder::getCreateTime); + } + } + + Page page = this.page(pageInfo, wrapper); + + return R.ok(page); + } + + @Override + public SimpleTtUserVO rechargeTotalOfBoss(Integer employeeId, Timestamp beginTime, String endTime, Integer orderType, Integer page, Integer size) { + + int limit = (page - 1) * size; + + if (StringUtils.isBlank(endTime)) endTime = null; + if (ObjectUtil.isNull(orderType)) orderType = 1; + + return baseMapper.rechargeTotalOfBoss( + employeeId, + beginTime, + endTime, + orderType, + limit, + size); + + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOrnamentServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOrnamentServiceImpl.java new file mode 100644 index 0000000..1927aed --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOrnamentServiceImpl.java @@ -0,0 +1,204 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.admin.service.TtOrnamentYYService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.dto.queryCondition.OrnamentCondition; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.other.TtOrnamentsBody; +import com.ruoyi.domain.vo.TtOrnamentVO; +import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO; +import com.ruoyi.system.mapper.SysDictDataMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.List; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.TtboxRecordSource.SYS_GRANT; + +@Service +public class TtOrnamentServiceImpl extends ServiceImpl implements TtOrnamentService { + + @Autowired + private TtBoxRecordsService boxRecordsService; + + @Autowired + private TtOrnamentYYService ttOrnamentYYService; + + @Autowired + private TtOrnamentMapper ttOrnamentMapper; + + @Autowired + private SysDictDataMapper dictDataMapper; + + @Override + public Page listByParam(TtOrnamentsBody param) { + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // id + wrapper.eq(ObjectUtil.isNotEmpty(param.getId()), TtOrnament::getId, param.getId()); + + // name + wrapper + .like(ObjectUtil.isNotEmpty(param.getName()), TtOrnament::getName, param.getName()); + + // 外观 + wrapper.eq(ObjectUtil.isNotEmpty(param.getExterior()), TtOrnament::getExterior, param.getExterior()); + + // 类型 + wrapper.eq(ObjectUtil.isNotEmpty(param.getType()), TtOrnament::getType, param.getType()); + + // 价格区间 + wrapper + .ge(ObjectUtil.isNotEmpty(param.getMinPrice()), TtOrnament::getUsePrice, param.getMinPrice()) + .le(ObjectUtil.isNotEmpty(param.getMaxPrice()), TtOrnament::getUsePrice, param.getMaxPrice()); + + if (ObjectUtil.isNull(param.getPageNum())) { + param.setPageNum(1); + } + if (ObjectUtil.isNull(param.getPageSize())) { + param.setPageSize(10); + } + Page pageInfo = new Page<>(param.getPageNum(), param.getPageSize()); + pageInfo.setOptimizeCountSql(false); + pageInfo = this.page(pageInfo, wrapper); + + List list = pageInfo.getRecords(); + + List res = list.stream().map(item -> { + TtOrnamentVO vo = new TtOrnamentVO(); + BeanUtil.copyProperties(item, vo); + return vo; + }).collect(Collectors.toList()); + + Page resPage = new Page<>(); + BeanUtil.copyProperties(pageInfo, resPage); + resPage.setRecords(res); + + return resPage; + + } + + @Override + public List selectTtOrnamentsList(TtOrnamentsBody param) { + + + return null; + + // if (param.getPartyType().equals(PartyType.ZBT.getCode())){ + // + // LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + // if (ObjectUtils.isNotEmpty(param.getId())) wrapper.eq(TtOrnament::getId, param.getId()); + // if (StringUtils.isNotNull(param.getName())) wrapper.like(TtOrnament::getName, param.getName()); + // if (StringUtils.isNotNull(param.getTypeId())) wrapper.eq(TtOrnament::getType, param.getTypeId()); + // if (StringUtils.isNotNull(param.getExterior())) wrapper.eq(TtOrnament::getExterior, param.getExterior()); + // if (StringUtils.isNotNull(param.getMaxPrice())) wrapper.between(TtOrnament::getUsePrice, param.getMinPrice(), param.getMaxPrice()); + // wrapper.eq(TtOrnament::getIsProprietaryProperty, "1"); + // wrapper.orderByDesc(TtOrnament::getQuantity); + // List list = this.list(wrapper); + // + // return list.stream().map(item->{ + // CommonOrnamentVO build = CommonOrnamentVO.builder() + // .build(); + // BeanUtil.copyProperties(item,build); + // return build; + // }).collect(Collectors.toList()); + // + // }else if (param.getPartyType().equals(PartyType.YY_YOU_PING.getCode())){ + // + // LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + // if (ObjectUtils.isNotEmpty(param.getId())) wrapper.eq(TtOrnamentsYY::getId, param.getId()); + // if (StringUtils.isNotNull(param.getName())) wrapper.like(TtOrnamentsYY::getName, param.getName()); + // if (StringUtils.isNotNull(param.getTypeId())) wrapper.eq(TtOrnamentsYY::getTypeId, param.getTypeId()); + // if (StringUtils.isNotNull(param.getTypeName())) wrapper.eq(TtOrnamentsYY::getTypeName, param.getTypeName()); + // // if (StringUtils.isNotNull(param.getMaxPrice())) wrapper.between(TtOrnamentsZBT::getUsePrice, param.getMinPrice(), param.getMaxPrice()); + // wrapper.orderByDesc(TtOrnamentsYY::getId); + // List list = ttOrnamentYYService.list(wrapper); + // + // return list.stream().map(item->{ + // CommonOrnamentVO build = CommonOrnamentVO.builder() + // .build(); + // BeanUtil.copyProperties(item,build); + // return build; + // }).collect(Collectors.toList()); + // + // }else { + // log.warn("非法的平台类型参数。"); + // return null; + // } + + } + + @Override + public List selectOrnamentsItemIdList() { + return getBaseMapper().selectOrnamentsItemIdList(); + } + + @Override + public AjaxResult grantOrnaments(Integer userId, Long ornamentId, Integer ornamentsLevelId, Integer num) { + TtOrnament ttOrnament = new LambdaQueryChainWrapper<>(ttOrnamentMapper).eq(TtOrnament::getId, ornamentId).one(); + if (ObjectUtil.isEmpty(ttOrnament)) { + return AjaxResult.error("不存在的饰品"); + } + for (int i = 0; i < num; i++) { + TtBoxRecords boxRecords = TtBoxRecords.builder().build(); + boxRecords.setUserId(userId); + boxRecords.setOrnamentId(ornamentId); + boxRecords.setOrnamentsPrice(ttOrnament.getUsePrice()); + boxRecords.setOrnamentsLevelId(ornamentsLevelId); + boxRecords.setCreateTime(DateUtils.getNowDate()); + boxRecords.setSource(SYS_GRANT.getCode()); + boxRecords.setHolderUserId(userId); + boxRecordsService.save(boxRecords); + } + return AjaxResult.success(); + } + + @Override + public List byCondition(OrnamentCondition condition) { + // condition.setLimit((condition.getPageNum() - 1) * condition.getPageSize()); + return baseMapper.byCondition(condition); + // Integer total = baseMapper.countByCondition(condition); + // + // Page pageInfo = new Page<>(condition.getPageNum(), condition.getPageSize()); + // pageInfo.setRecords(list); + // pageInfo.setTotal(total); + // + // return R.ok(pageInfo); + } + + @Override + public List selectOrnamentsIdList() { + return ttOrnamentMapper.selectOrnamentsIdList(); + } + + @Override + public List selectOrnamentsMarketHashNameList() { + return ttOrnamentMapper.selectOrnamentsMarketHashNameList(); + } + + @Override + public AjaxResult updateOrnamentPrice(Long ornamentId, BigDecimal price) { + TtOrnament ttOrnament = new LambdaQueryChainWrapper<>(ttOrnamentMapper).eq(TtOrnament::getId, ornamentId).one(); + if (ObjectUtil.isEmpty(ttOrnament)) { + return AjaxResult.error("不存在的饰品"); + } + ttOrnament.setUsePrice(price); + ttOrnamentMapper.updateById(ttOrnament); + return AjaxResult.success("价格更新成功"); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOrnamentsLevelServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOrnamentsLevelServiceImpl.java new file mode 100644 index 0000000..4547c8e --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOrnamentsLevelServiceImpl.java @@ -0,0 +1,44 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtOrnamentsLevel; +import com.ruoyi.admin.mapper.TtOrnamentsLevelMapper; +import com.ruoyi.admin.mapper.WebsiteSetupMapper; +import com.ruoyi.admin.service.TtOrnamentsLevelService; +import com.ruoyi.common.config.RuoYiConfig; +import org.springframework.stereotype.Service; + + +@Service +public class TtOrnamentsLevelServiceImpl extends ServiceImpl implements TtOrnamentsLevelService { + + private final WebsiteSetupMapper websiteSetupMapper; + + public TtOrnamentsLevelServiceImpl(WebsiteSetupMapper websiteSetupMapper) { + this.websiteSetupMapper = websiteSetupMapper; + } + + @Override + public String generateOrnamentsLevel(Integer num) { + for (int i = 1; i <= num; i++) { + TtOrnamentsLevel ttOrnamentsLevel = TtOrnamentsLevel.builder().build(); + ttOrnamentsLevel.setLevel(""); + ttOrnamentsLevel.setLevelImg(""); + this.save(ttOrnamentsLevel); + } + return ""; + } + + @Override + public String updateOrnamentsLevelById(TtOrnamentsLevel ttOrnamentsLevel) { + String levelImg = ttOrnamentsLevel.getLevelImg(); + ttOrnamentsLevel.setLevelImg(RuoYiConfig.getDomainName() + levelImg); + this.updateById(ttOrnamentsLevel); + return ""; + } + + @Override + public void truncateOrnamentsLevel() { + websiteSetupMapper.truncateTtOrnamentsLevel(); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOrnamentsYYServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOrnamentsYYServiceImpl.java new file mode 100644 index 0000000..20342e2 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtOrnamentsYYServiceImpl.java @@ -0,0 +1,12 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtOrnamentsYYMapper; +import com.ruoyi.admin.service.TtOrnamentYYService; +import com.ruoyi.domain.entity.TtOrnamentsYY; +import org.springframework.stereotype.Service; + +@Service +public class TtOrnamentsYYServiceImpl extends ServiceImpl implements TtOrnamentYYService { + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtPromoTurnoverServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtPromoTurnoverServiceImpl.java new file mode 100644 index 0000000..bf60183 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtPromoTurnoverServiceImpl.java @@ -0,0 +1,123 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.TtPromoTurnoverService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.param.promotion.AnchorDailyTurnoverParam; +import com.ruoyi.domain.param.promotion.AnchorRechargeParam; +import com.ruoyi.domain.vo.promotion.AnchorDailyTurnoverVo; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; + +@Service +@Slf4j +public class TtPromoTurnoverServiceImpl extends ServiceImpl implements TtPromoTurnoverService { + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + @Override + public List getAnchorDailyTurnover(AnchorDailyTurnoverParam param) { + // 查出来所有主播 + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(param.getParentId() != null, TtUser::getUserId, param.getParentId()); + query.like(param.getParentNickName() != null, TtUser::getNickName, param.getParentNickName()); + query.eq(TtUser::getUserType, "01"); + List users = list(query); + if (CollectionUtils.isEmpty(users)) { + return new ArrayList<>(); + } + List anchorDayTurnoverVos = new ArrayList<>(); + for (TtUser user : users) { + // 递归名下所有玩家 + List allPlayer = recursionAllPlayer(user.getUserId()); + List userIds = new ArrayList<>(); + for (TtUser ttUser : allPlayer) { + userIds.add(ttUser.getUserId()); + } + if (CollectionUtils.isEmpty(userIds)) { + continue; + } + param.setUserIds(userIds); + anchorDayTurnoverVos.addAll(baseMapper.getAnchorDailyTurnover(param)); + } + if (CollectionUtils.isEmpty(anchorDayTurnoverVos)) { + return new ArrayList<>(); + } + anchorDayTurnoverVos.sort((o1, o2) -> o2.getDate().compareTo(o1.getDate())); + return anchorDayTurnoverVos; + } + + @Override + public AjaxResult recharge(AnchorRechargeParam param) { + if (param == null || param.getParentId() == null || param.getRecharge() == null) { + return AjaxResult.error("参数不合法!"); + } + if (param.getRecharge().compareTo(new BigDecimal(0)) <= 0) { + return AjaxResult.error("奖励金额小于0!"); + } + // 查出来所有主播 + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(param.getParentId() != null, TtUser::getUserId, param.getParentId()); + query.eq(TtUser::getUserType, "01"); + List users = list(query); + if (CollectionUtils.isEmpty(users) || users.get(0) == null) { + return AjaxResult.error("主播用户不存在!"); + } + TtUser ttUser = users.get(0); + ttUser.setAccountCredits(ttUser.getAccountCredits() != null ? ttUser.getAccountCredits().add(param.getRecharge()) : param.getRecharge()); + baseMapper.updateById(ttUser); + + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .credits(param.getRecharge().compareTo(BigDecimal.ZERO) > 0 ? param.getRecharge() : null) + .finalCredits(param.getRecharge().compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountCredits().add(param.getRecharge()) : null) + .total(param.getRecharge()) // 收支合计 + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_RECHARGE.getCode()) + .remark(TtAccountRecordSource.PROMOTION_RECHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + ttUserBlendErcashMapper.insert(blendErcash); + + return AjaxResult.success(); + } + + public List recursionAllPlayer(Integer userId) { + List players = new ArrayList<>(); + getAllPlayersByAnchorIdRecursive(userId, players); + return players; + } + + /** + * 递归名下所有玩家 + */ + private void getAllPlayersByAnchorIdRecursive(int parentId, List players) { + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(TtUser::getParentId, parentId); + List users = list(query); + for (TtUser user : users) { + if ("02".equals(user.getUserType())) { // 如果是玩家 + players.add(user); + } else if ("01".equals(user.getUserType())) { // 如果是主播,继续递归 + getAllPlayersByAnchorIdRecursive(user.getUserId(), players); + } + } + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtPromotionLevelServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtPromotionLevelServiceImpl.java new file mode 100644 index 0000000..d3c53b4 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtPromotionLevelServiceImpl.java @@ -0,0 +1,51 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.entity.TtPromotionLevel; +import com.ruoyi.admin.mapper.TtPromotionLevelMapper; +import com.ruoyi.admin.mapper.WebsiteSetupMapper; +import com.ruoyi.admin.service.TtPromotionLevelService; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.DateUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +@Service +public class TtPromotionLevelServiceImpl extends ServiceImpl implements TtPromotionLevelService { + + private final WebsiteSetupMapper websiteSetupMapper; + + public TtPromotionLevelServiceImpl(WebsiteSetupMapper websiteSetupMapper) { + this.websiteSetupMapper = websiteSetupMapper; + } + + @Override + public String generateVipLevel(Integer num) { + for (int i = 1; i <= num; i++) { + TtPromotionLevel ttPromotionLevel = TtPromotionLevel.builder().build(); + ttPromotionLevel.setName("LV" + i); + ttPromotionLevel.setIcon(""); + ttPromotionLevel.setRechargeThreshold(BigDecimal.ZERO); + ttPromotionLevel.setCommissions(BigDecimal.ZERO); + ttPromotionLevel.setAddedBonus(BigDecimal.ZERO); + ttPromotionLevel.setRechargeGiftRatio(BigDecimal.ZERO); + ttPromotionLevel.setCreateTime(DateUtils.getNowDate()); + this.save(ttPromotionLevel); + } + return ""; + } + + @Override + public String updatePromotionLevelById(TtPromotionLevel ttPromotionLevel) { + String icon = ttPromotionLevel.getIcon(); + ttPromotionLevel.setIcon(RuoYiConfig.getDomainName() + icon); + this.updateById(ttPromotionLevel); + return ""; + } + + @Override + public void truncatePromotionLevel() { + websiteSetupMapper.truncateTtPromotionLevel(); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtPromotionRecordServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtPromotionRecordServiceImpl.java new file mode 100644 index 0000000..6bab859 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtPromotionRecordServiceImpl.java @@ -0,0 +1,48 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtUserAmountRecordsMapper; +import com.ruoyi.domain.entity.TtPromotionRecord; +import com.ruoyi.admin.mapper.TtPromotionRecordMapper; +import com.ruoyi.admin.service.TtPromotionRecordService; +import com.ruoyi.domain.vo.PromotionDataVO; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Objects; + +@Service +public class TtPromotionRecordServiceImpl extends ServiceImpl implements TtPromotionRecordService { + + @Autowired + private TtUserAmountRecordsMapper ttUserAmountRecordsMapper; + + @Override + public List getPromotionRecord(TtPromotionRecord ttPromotionRecord) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotNull(ttPromotionRecord.getUserId())) wrapper.eq(TtPromotionRecord::getUserId, ttPromotionRecord.getUserId()); + if (StringUtils.isNotEmpty(ttPromotionRecord.getStatus())) wrapper.eq(TtPromotionRecord::getStatus, ttPromotionRecord.getStatus()); + if (StringUtils.isNotNull(ttPromotionRecord.getRechargeRecordId())) wrapper.eq(TtPromotionRecord::getRechargeRecordId, ttPromotionRecord.getRechargeRecordId()); + wrapper.orderByDesc(TtPromotionRecord::getId); + List ttPromotionRecordList = this.list(wrapper); + for (TtPromotionRecord promotionRecord : ttPromotionRecordList) { + BigDecimal totalConsumption = ttUserAmountRecordsMapper.getTotalConsumptionByUserId(promotionRecord.getSubordinateUserId()); + if (Objects.isNull(totalConsumption)) { + promotionRecord.setTotalConsumption(new BigDecimal(0)); + } else { + promotionRecord.setTotalConsumption(totalConsumption); + } + } + return ttPromotionRecordList; + } + + @Override + public PromotionDataVO statisticsPromotionData(Integer userId) { + return baseMapper.statisticsPromotionData(userId); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeCardServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeCardServiceImpl.java new file mode 100644 index 0000000..e1903c6 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeCardServiceImpl.java @@ -0,0 +1,105 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtRechargeCard; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.admin.mapper.TtRechargeCardMapper; +import com.ruoyi.admin.mapper.TtRechargeProdMapper; +import com.ruoyi.admin.service.TtRechargeCardService; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import org.springframework.stereotype.Service; + +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class TtRechargeCardServiceImpl extends ServiceImpl implements TtRechargeCardService { + + private final TtRechargeProdMapper rechargeListMapper; + + public TtRechargeCardServiceImpl(TtRechargeProdMapper rechargeListMapper) { + this.rechargeListMapper = rechargeListMapper; + } + + @Override + public List queryList(TtRechargeCard ttRechargeCard) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotNull(ttRechargeCard.getId())) wrapper.eq(TtRechargeCard::getId, ttRechargeCard.getId()); + if (StringUtils.isNotNull(ttRechargeCard.getRechargeListId())) wrapper.eq(TtRechargeCard::getRechargeListId, ttRechargeCard.getRechargeListId()); + if (StringUtils.isNotEmpty(ttRechargeCard.getPassword())) wrapper.eq(TtRechargeCard::getPassword, ttRechargeCard.getPassword()); + if (StringUtils.isNotEmpty(ttRechargeCard.getStatus())) wrapper.eq(TtRechargeCard::getStatus, ttRechargeCard.getStatus()); + if (StringUtils.isNotNull(ttRechargeCard.getUseUserId())) wrapper.eq(TtRechargeCard::getUseUserId, ttRechargeCard.getUseUserId()); + wrapper.orderByDesc(TtRechargeCard::getCreateTime); + return this.list(wrapper); + } + + @Override + public List generateCard(Integer rechargeListId, Integer num) { + TtRechargeProd recharge = rechargeListMapper.selectById(rechargeListId); + if (recharge == null) return new ArrayList<>(); + + List cardList = new ArrayList<>(); + for (int i = 0; i < num; i++) { + String password = IdUtils.fastSimpleUUID().toUpperCase(); + cardList.add(password); + TtRechargeCard ttRechargeCard = TtRechargeCard.builder().build(); + ttRechargeCard.setRechargeListId(rechargeListId); + ttRechargeCard.setPrice(recharge.getPrice()); + ttRechargeCard.setPassword(password); + ttRechargeCard.setCreateBy(SecurityUtils.getUsername()); + ttRechargeCard.setCreateTime(DateUtils.getNowDate()); + this.save(ttRechargeCard); + } + return cardList; + } + + @Override + public void export(HttpServletResponse response, TtRechargeCard ttRechargeCard) { + List ttRechargeCards = this.queryList(ttRechargeCard); + List cardList = ttRechargeCards.stream().map(TtRechargeCard::getPassword).collect(Collectors.toList()); + if (cardList.isEmpty()) return; + String folderPath = RuoYiConfig.getDownloadPath() + "/"; + File folder = new File(folderPath); + if (!folder.exists()) { + folder.mkdirs(); + } + ServletOutputStream os = null; + BufferedWriter bw = null; + String uuid = IdUtils.fastSimpleUUID(); + try { + bw = new BufferedWriter(new FileWriter(folderPath + uuid + ".txt")); + for (String card : cardList) { + bw.write(card); + bw.newLine(); + bw.flush(); + } + File uploadFile = new File(folderPath + uuid + ".txt"); + os = response.getOutputStream(); + response.setCharacterEncoding(StandardCharsets.UTF_8.name()); + response.setContentType("application/octet-stream"); + response.addHeader("Content-disposition", "attachment"); + os.write(FileUtil.readBytes(uploadFile)); + IoUtil.flush(os); + } catch (IOException ignored) { + } finally { + if (os != null) IoUtil.close(os); + if (bw != null) IoUtil.close(bw); + } + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeConfigServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeConfigServiceImpl.java new file mode 100644 index 0000000..6f9160a --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeConfigServiceImpl.java @@ -0,0 +1,11 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtRechargeConfigMapper; +import com.ruoyi.admin.service.TtRechargeConfigService; +import com.ruoyi.domain.entity.TtRechargeConfig; +import org.springframework.stereotype.Service; + +@Service +public class TtRechargeConfigServiceImpl extends ServiceImpl implements TtRechargeConfigService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeProdServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeProdServiceImpl.java new file mode 100644 index 0000000..e8f8f56 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeProdServiceImpl.java @@ -0,0 +1,11 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.admin.mapper.TtRechargeProdMapper; +import com.ruoyi.admin.service.TtRechargeProdService; +import org.springframework.stereotype.Service; + +@Service +public class TtRechargeProdServiceImpl extends ServiceImpl implements TtRechargeProdService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeRankingRewardServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeRankingRewardServiceImpl.java new file mode 100644 index 0000000..5a11342 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeRankingRewardServiceImpl.java @@ -0,0 +1,115 @@ +package com.ruoyi.admin.service.impl; + +import com.ruoyi.admin.mapper.TtRechargeRankingRewardMapper; +import com.ruoyi.admin.service.TtRechargeRankingRewardService; +import com.ruoyi.domain.other.TtRechargeRankingReward; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 充值奖励金额Service业务层处理 + * + * @author ruoyi + * @date 2024-07-04 + */ +@Service +public class TtRechargeRankingRewardServiceImpl implements TtRechargeRankingRewardService +{ + @Autowired + private TtRechargeRankingRewardMapper ttRechargeRankingRewardMapper; + + /** + * 查询充值奖励金额 + * + * @param id 充值奖励金额主键 + * @return 充值奖励金额 + */ + @Override + public TtRechargeRankingReward selectTtRechargeRankingRewardById(Integer id) + { + return ttRechargeRankingRewardMapper.selectTtRechargeRankingRewardById(id); + } + + /** + * 查询充值奖励金额列表 + * + * @param ttRechargeRankingReward 充值奖励金额 + * @return 充值奖励金额 + */ + @Override + public List selectTtRechargeRankingRewardList(TtRechargeRankingReward ttRechargeRankingReward) + { + return ttRechargeRankingRewardMapper.selectTtRechargeRankingRewardList(ttRechargeRankingReward); + } + + @Override + @Transactional + public String generateRankingReward() + { + ttRechargeRankingRewardMapper.truncateRankingReward(); + for (int i = 1; i <= 10; i++) { + TtRechargeRankingReward ttRechargeRankingReward = new TtRechargeRankingReward(); + ttRechargeRankingReward.setName("TOP" + i); + ttRechargeRankingReward.setReward(BigDecimal.ZERO); + ttRechargeRankingRewardMapper.insertTtRechargeRankingReward(ttRechargeRankingReward); + } + return ""; + } + + // /** + // * 新增充值奖励金额 + // * + // * @param ttRechargeRankingReward 充值奖励金额 + // * @return 结果 + // */ + // @Override + // public int insertTtRechargeRankingReward(TtRechargeRankingReward ttRechargeRankingReward) + // { + // return ttRechargeRankingRewardMapper.insertTtRechargeRankingReward(ttRechargeRankingReward); + // } + + /** + * 修改充值奖励金额 + * + * @param ttRechargeRankingReward 充值奖励金额 + * @return 结果 + */ + @Override + public int updateTtRechargeRankingReward(TtRechargeRankingReward ttRechargeRankingReward) + { + return ttRechargeRankingRewardMapper.updateTtRechargeRankingReward(ttRechargeRankingReward); + } + + /** + * 批量删除充值奖励金额 + * + * @param ids 需要删除的充值奖励金额主键 + * @return 结果 + */ + @Override + public int deleteTtRechargeRankingRewardByIds(Integer[] ids) + { + return ttRechargeRankingRewardMapper.deleteTtRechargeRankingRewardByIds(ids); + } + + @Override + public void truncateRankingReward() { + ttRechargeRankingRewardMapper.truncateRankingReward(); + } + + // /** + // * 删除充值奖励金额信息 + // * + // * @param id 充值奖励金额主键 + // * @return 结果 + // */ + // @Override + // public int deleteTtRechargeRankingRewardById(Integer id) + // { + // return ttRechargeRankingRewardMapper.deleteTtRechargeRankingRewardById(id); + // } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeRecordServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeRecordServiceImpl.java new file mode 100644 index 0000000..76f42f9 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRechargeRecordServiceImpl.java @@ -0,0 +1,36 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtRechargeRecord; +import com.ruoyi.admin.mapper.TtRechargeRecordMapper; +import com.ruoyi.admin.service.TtRechargeRecordService; +import com.ruoyi.domain.other.TtRechargeRecordBody; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class TtRechargeRecordServiceImpl extends ServiceImpl implements TtRechargeRecordService { + + @Override + public List queryList(TtRechargeRecordBody rechargeRecordBody) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotNull(rechargeRecordBody.getUserId())) + wrapper.eq(TtRechargeRecord::getUserId, rechargeRecordBody.getUserId()); + if (StringUtils.isNotNull(rechargeRecordBody.getParentId())) + wrapper.eq(TtRechargeRecord::getParentId, rechargeRecordBody.getParentId()); + if (StringUtils.isNotEmpty(rechargeRecordBody.getOrderId())) + wrapper.eq(TtRechargeRecord::getOrderId, rechargeRecordBody.getOrderId()); + if (StringUtils.isNotEmpty(rechargeRecordBody.getOutTradeNo())) + wrapper.eq(TtRechargeRecord::getOutTradeNo, rechargeRecordBody.getOutTradeNo()); + if (StringUtils.isNotEmpty(rechargeRecordBody.getChannelType())) + wrapper.eq(TtRechargeRecord::getChannelType, rechargeRecordBody.getChannelType()); + if (StringUtils.isNotNull(rechargeRecordBody.getStartTime()) && StringUtils.isNotNull(rechargeRecordBody.getEndTime())) + wrapper.between(TtRechargeRecord::getCreateTime, rechargeRecordBody.getStartTime(), rechargeRecordBody.getEndTime()); + wrapper.orderByDesc(TtRechargeRecord::getCreateTime); + return this.list(wrapper); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRedPackServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRedPackServiceImpl.java new file mode 100644 index 0000000..21eeb97 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRedPackServiceImpl.java @@ -0,0 +1,97 @@ +package com.ruoyi.admin.service.impl; + +import com.ruoyi.admin.mapper.TtRedPackMapper; +import com.ruoyi.admin.service.TtRedPackService; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.other.TtRedPack; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 【请填写功能名称】Service业务层处理 + * + * @author ruoyi + * @date 2023-06-29 + */ +@Service +public class TtRedPackServiceImpl implements TtRedPackService +{ + @Autowired + private TtRedPackMapper ttRedPackMapper; + + /** + * 查询【请填写功能名称】 + * + * @param id 【请填写功能名称】主键 + * @return 【请填写功能名称】 + */ + @Override + public TtRedPack selectTtRedPackById(Long id) + { + return ttRedPackMapper.selectTtRedPackById(id); + } + + /** + * 查询【请填写功能名称】列表 + * + * @param ttRedPack 【请填写功能名称】 + * @return 【请填写功能名称】 + */ + @Override + public List selectTtRedPackList(TtRedPack ttRedPack) + { + return ttRedPackMapper.selectTtRedPackList(ttRedPack); + } + + /** + * 新增【请填写功能名称】 + * + * @param ttRedPack 【请填写功能名称】 + * @return 结果 + */ + @Override + public int insertTtRedPack(TtRedPack ttRedPack) + { + ttRedPack.setCreateTime(DateUtils.getNowDate()); + return ttRedPackMapper.insertTtRedPack(ttRedPack); + } + + /** + * 修改【请填写功能名称】 + * + * @param ttRedPack 【请填写功能名称】 + * @return 结果 + */ + @Override + public int updateTtRedPack(TtRedPack ttRedPack) + { + ttRedPack.setUpdateTime(DateUtils.getNowDate()); + return ttRedPackMapper.updateTtRedPack(ttRedPack); + } + + /** + * 批量删除【请填写功能名称】 + * + * @param ids 需要删除的【请填写功能名称】主键 + * @return 结果 + */ + @Override + public int deleteTtRedPackByIds(Long[] ids) + { + return ttRedPackMapper.deleteTtRedPackByIds(ids); + } + + /** + * 删除【请填写功能名称】信息 + * + * @param id 【请填写功能名称】主键 + * @return 结果 + */ + @Override + public int deleteTtRedPackById(Long id) + { + return ttRedPackMapper.deleteTtRedPackById(id); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRedPacketRecordServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRedPacketRecordServiceImpl.java new file mode 100644 index 0000000..3f9066c --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRedPacketRecordServiceImpl.java @@ -0,0 +1,20 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtRedPacketRecord; +import com.ruoyi.admin.mapper.TtRedPacketRecordMapper; +import com.ruoyi.admin.service.TtRedPacketRecordService; +import com.ruoyi.domain.other.TtRedPacketRecordBody; +import com.ruoyi.domain.vo.TtRedPacketRecordDataVO; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class TtRedPacketRecordServiceImpl extends ServiceImpl implements TtRedPacketRecordService { + + @Override + public List queryList(TtRedPacketRecordBody ttRedPacketRecordBody) { + return baseMapper.queryList(ttRedPacketRecordBody); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRedPacketServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRedPacketServiceImpl.java new file mode 100644 index 0000000..d016cb4 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRedPacketServiceImpl.java @@ -0,0 +1,91 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.other.TtRedPacket; +import com.ruoyi.admin.mapper.TtRedPacketMapper; +import com.ruoyi.admin.service.TtRedPacketService; +import com.ruoyi.domain.other.TtRedPacketBody; +import com.ruoyi.domain.vo.TtRedPacketDataVO; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Service +public class TtRedPacketServiceImpl extends ServiceImpl implements TtRedPacketService { + + @Override + public List queryList(TtRedPacketBody ttRedPacketBody) { + + List list = baseMapper.queryList(ttRedPacketBody); + + List redPacketList = new LambdaQueryChainWrapper<>(baseMapper) + .eq(TtRedPacket::getStatus, 0) + .list(); + + List expiredRedPack = new ArrayList<>(); + for (TtRedPacket ttRedPacket : redPacketList) { + + Date validity = ttRedPacket.getValidity(); + if (ObjectUtil.isNull(validity) || DateUtil.compare(validity, DateUtils.getNowDate()) > 0) continue; + + expiredRedPack.add(ttRedPacket.getId()); + ttRedPacket.setStatus(1); + } + + if (!expiredRedPack.isEmpty()){ + new LambdaUpdateChainWrapper<>(baseMapper) + .in(TtRedPacket::getId,expiredRedPack) + .set(TtRedPacket::getStatus,1) + .update(); + } + + return list; + } + + @Override + public List insertRedPacket(TtRedPacketDataVO ttRedPacketDataVO) { + + List pwList = new ArrayList<>(); + List rpList = new ArrayList<>(); + + for (int i = 0; i < ttRedPacketDataVO.getCreateNum(); i++) { + + TtRedPacket redPacket = new TtRedPacket(); + + BeanUtil.copyProperties(ttRedPacketDataVO,redPacket); + + redPacket.setPassword(IdUtils.fastSimpleUUID().toUpperCase()); + + pwList.add(redPacket.getPassword()); + redPacket.setCreateBy(SecurityUtils.getUsername()); + redPacket.setCreateTime(DateUtils.getNowDate()); + redPacket.setUseStatus(0); + + rpList.add(redPacket); + + } + + this.saveBatch(rpList,1); + + return pwList; + } + + @Override + public AjaxResult updateRedPacketById(TtRedPacket redPacket) { + if (this.updateById(redPacket)){ + return AjaxResult.success(); + } + return AjaxResult.error(); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtReplacementRecordServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtReplacementRecordServiceImpl.java new file mode 100644 index 0000000..1d10d6e --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtReplacementRecordServiceImpl.java @@ -0,0 +1,97 @@ +package com.ruoyi.admin.service.impl; + +import com.ruoyi.admin.mapper.TtReplacementRecordMapper; +import com.ruoyi.admin.service.TtReplacementRecordService; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.other.TtReplacementRecord; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 汰换记录Service业务层处理 + * + * @author junhai + * @date 2023-09-10 + */ +@Service +public class TtReplacementRecordServiceImpl implements TtReplacementRecordService +{ + @Autowired + private TtReplacementRecordMapper ttReplacementRecordMapper; + + /** + * 查询汰换记录 + * + * @param id 汰换记录主键 + * @return 汰换记录 + */ + @Override + public TtReplacementRecord selectTtReplacementRecordById(Long id) + { + return ttReplacementRecordMapper.selectTtReplacementRecordById(id); + } + + /** + * 查询汰换记录列表 + * + * @param ttReplacementRecord 汰换记录 + * @return 汰换记录 + */ + @Override + public List selectTtReplacementRecordList(TtReplacementRecord ttReplacementRecord) + { + return ttReplacementRecordMapper.selectTtReplacementRecordList(ttReplacementRecord); + } + + /** + * 新增汰换记录 + * + * @param ttReplacementRecord 汰换记录 + * @return 结果 + */ + @Override + public int insertTtReplacementRecord(TtReplacementRecord ttReplacementRecord) + { + ttReplacementRecord.setCreateTime(DateUtils.getNowDate()); + return ttReplacementRecordMapper.insertTtReplacementRecord(ttReplacementRecord); + } + + /** + * 修改汰换记录 + * + * @param ttReplacementRecord 汰换记录 + * @return 结果 + */ + @Override + public int updateTtReplacementRecord(TtReplacementRecord ttReplacementRecord) + { + ttReplacementRecord.setUpdateTime(DateUtils.getNowDate()); + return ttReplacementRecordMapper.updateTtReplacementRecord(ttReplacementRecord); + } + + /** + * 批量删除汰换记录 + * + * @param ids 需要删除的汰换记录主键 + * @return 结果 + */ + @Override + public int deleteTtReplacementRecordByIds(Long[] ids) + { + return ttReplacementRecordMapper.deleteTtReplacementRecordByIds(ids); + } + + /** + * 删除汰换记录信息 + * + * @param id 汰换记录主键 + * @return 结果 + */ + @Override + public int deleteTtReplacementRecordById(Long id) + { + return ttReplacementRecordMapper.deleteTtReplacementRecordById(id); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRobotFightGroupBoxServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRobotFightGroupBoxServiceImpl.java new file mode 100644 index 0000000..7e80a60 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRobotFightGroupBoxServiceImpl.java @@ -0,0 +1,94 @@ +package com.ruoyi.admin.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.admin.mapper.TtRobotFightGroupBoxMapper; +import com.ruoyi.domain.other.TtRobotFightGroupBox; +import com.ruoyi.admin.service.TtRobotFightGroupBoxService; + +/** + * 机器人对战分组宝箱Service业务层处理 + * + * @author ruoyi + * @date 2024-06-28 + */ +@Service +public class TtRobotFightGroupBoxServiceImpl implements TtRobotFightGroupBoxService +{ + @Autowired + private TtRobotFightGroupBoxMapper ttRobotFightGroupBoxMapper; + + /** + * 查询机器人对战分组宝箱 + * + * @param groupId 机器人对战分组宝箱主键 + * @return 机器人对战分组宝箱 + */ + @Override + public TtRobotFightGroupBox selectTtRobotFightGroupBoxByBoxId(Integer groupId, Integer boxId) + { + return ttRobotFightGroupBoxMapper.selectTtRobotFightGroupBoxByBoxId(groupId, boxId); + } + + /** + * 查询机器人对战分组宝箱列表 + * + * @param ttRobotFightGroupBox 机器人对战分组宝箱 + * @return 机器人对战分组宝箱 + */ + @Override + public List selectTtRobotFightGroupBoxList( + Integer groupId, + TtRobotFightGroupBox ttRobotFightGroupBox + ) + { + return ttRobotFightGroupBoxMapper + .selectTtRobotFightGroupBoxList(groupId, ttRobotFightGroupBox); + } + + /** + * 新增机器人对战分组宝箱 + * + * @param ttRobotFightGroupBox 机器人对战分组宝箱 + * @return 结果 + */ + @Override + public int insertTtRobotFightGroupBox(TtRobotFightGroupBox ttRobotFightGroupBox) + { + return ttRobotFightGroupBoxMapper.insertTtRobotFightGroupBox(ttRobotFightGroupBox); + } + + /** + * 修改机器人对战分组宝箱 + * + * @param ttRobotFightGroupBox 机器人对战分组宝箱 + * @return 结果 + */ + @Override + public int updateTtRobotFightGroupBox(TtRobotFightGroupBox ttRobotFightGroupBox) + { + return ttRobotFightGroupBoxMapper.updateTtRobotFightGroupBox(ttRobotFightGroupBox); + } + + /** + * 批量删除机器人对战分组宝箱 + */ + @Override + public int deleteTtRobotFightGroupBoxByGroupIds(Integer groupId, Integer[] boxIds) + { + return ttRobotFightGroupBoxMapper.deleteTtRobotFightGroupBoxByGroupIds(groupId, boxIds); + } + + /** + * 删除机器人对战分组宝箱信息 + * + * @param groupId 机器人对战分组宝箱主键 + * @return 结果 + */ + @Override + public int deleteTtRobotFightGroupBoxByGroupId(Integer groupId, Integer boxId) + { + return ttRobotFightGroupBoxMapper.deleteTtRobotFightGroupBoxByGroupId(groupId, boxId); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRobotFightGroupServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRobotFightGroupServiceImpl.java new file mode 100644 index 0000000..44646e3 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRobotFightGroupServiceImpl.java @@ -0,0 +1,93 @@ +package com.ruoyi.admin.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.admin.mapper.TtRobotFightGroupMapper; +import com.ruoyi.domain.other.TtRobotFightGroup; +import com.ruoyi.admin.service.TtRobotFightGroupService; + +/** + * 机器人对战分组Service业务层处理 + * + * @author ruoyi + * @date 2024-06-28 + */ +@Service +public class TtRobotFightGroupServiceImpl implements TtRobotFightGroupService +{ + @Autowired + private TtRobotFightGroupMapper ttRobotFightGroupMapper; + + /** + * 查询机器人对战分组 + * + * @param groupId 机器人对战分组主键 + * @return 机器人对战分组 + */ + @Override + public TtRobotFightGroup selectTtRobotFightGroupByGroupId(Integer groupId) + { + return ttRobotFightGroupMapper.selectTtRobotFightGroupByGroupId(groupId); + } + + /** + * 查询机器人对战分组列表 + * + * @param ttRobotFightGroup 机器人对战分组 + * @return 机器人对战分组 + */ + @Override + public List selectTtRobotFightGroupList(TtRobotFightGroup ttRobotFightGroup) + { + return ttRobotFightGroupMapper.selectTtRobotFightGroupList(ttRobotFightGroup); + } + + /** + * 新增机器人对战分组 + * + * @param ttRobotFightGroup 机器人对战分组 + * @return 结果 + */ + @Override + public int insertTtRobotFightGroup(TtRobotFightGroup ttRobotFightGroup) + { + return ttRobotFightGroupMapper.insertTtRobotFightGroup(ttRobotFightGroup); + } + + /** + * 修改机器人对战分组 + * + * @param ttRobotFightGroup 机器人对战分组 + * @return 结果 + */ + @Override + public int updateTtRobotFightGroup(TtRobotFightGroup ttRobotFightGroup) + { + return ttRobotFightGroupMapper.updateTtRobotFightGroup(ttRobotFightGroup); + } + + /** + * 批量删除机器人对战分组 + * + * @param groupIds 需要删除的机器人对战分组主键 + * @return 结果 + */ + @Override + public int deleteTtRobotFightGroupByGroupIds(Integer[] groupIds) + { + return ttRobotFightGroupMapper.deleteTtRobotFightGroupByGroupIds(groupIds); + } + + /** + * 删除机器人对战分组信息 + * + * @param groupId 机器人对战分组主键 + * @return 结果 + */ + @Override + public int deleteTtRobotFightGroupByGroupId(Integer groupId) + { + return ttRobotFightGroupMapper.deleteTtRobotFightGroupByGroupId(groupId); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollJackpotOrnamentsServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollJackpotOrnamentsServiceImpl.java new file mode 100644 index 0000000..6a45501 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollJackpotOrnamentsServiceImpl.java @@ -0,0 +1,89 @@ +package com.ruoyi.admin.service.impl; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.admin.mapper.TtRollJackpotMapper; +import com.ruoyi.domain.dto.rollJackpotOrnament.RollJOEdit; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.entity.roll.TtRollJackpot; +import com.ruoyi.domain.entity.roll.TtRollJackpotOrnaments; +import com.ruoyi.admin.mapper.TtRollJackpotOrnamentsMapper; +import com.ruoyi.admin.service.TtRollJackpotOrnamentsService; +import com.ruoyi.domain.entity.roll.TtRollJackpotOrnamentsBody; +import com.ruoyi.domain.vo.TtRollJackpotOrnamentsDataVO; +import com.ruoyi.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Service +public class TtRollJackpotOrnamentsServiceImpl extends ServiceImpl implements TtRollJackpotOrnamentsService { + + + @Autowired + private TtOrnamentMapper ttOrnamentMapper; + + @Autowired + private TtRollJackpotMapper ttRollJackpotMapper; + + @Override + public List queryList(TtRollJackpotOrnamentsBody rollJackpotOrnamentsBody) { + return baseMapper.queryList(rollJackpotOrnamentsBody); + } + + @Override + public String insertRollJackpotOrnaments(TtRollJackpotOrnaments ttRollJackpotOrnaments) { + ttRollJackpotOrnaments.setCreateTime(DateUtils.getNowDate()); + this.save(ttRollJackpotOrnaments); + return ""; + } + + @Override + public String updateRollJackpotOrnamentsById(TtRollJackpotOrnaments rollJOEdit) { + + rollJOEdit.setUpdateTime(DateUtils.getNowDate()); + this.updateById(rollJOEdit); + + // 重新计算奖池总价 + List list = new LambdaQueryChainWrapper<>(baseMapper) + .eq(TtRollJackpotOrnaments::getJackpotId, rollJOEdit.getJackpotId()) + .list(); + BigDecimal total = BigDecimal.ZERO; + for (TtRollJackpotOrnaments item : list){ + total = total.add(item.getPrice().multiply(new BigDecimal(item.getOrnamentsNum()))); + } + new LambdaUpdateChainWrapper<>(ttRollJackpotMapper) + .eq(TtRollJackpot::getJackpotId,rollJOEdit.getJackpotId()) + .set(TtRollJackpot::getTotalPrice,total) + .set(TtRollJackpot::getUpdateTime,new Date()) + .update(); + return ""; + } + + @Override + public String batchAdd(Integer rollJackpotId, List OrnamentsIds) { + + List ornIds = new ArrayList<>(OrnamentsIds); + + List ornamentList = new LambdaQueryChainWrapper<>(ttOrnamentMapper).in(TtOrnament::getId, ornIds).list(); + + for (TtOrnament ornament : ornamentList) { + TtRollJackpotOrnaments ttRollJackpotOrnaments = TtRollJackpotOrnaments.builder().build(); + ttRollJackpotOrnaments.setJackpotId(rollJackpotId); + ttRollJackpotOrnaments.setOrnamentsId(ornament.getId()); + ttRollJackpotOrnaments.setOrnamentsNum(1); + ttRollJackpotOrnaments.setPrice(ObjectUtil.isNotEmpty(ornament.getUsePrice())?ornament.getUsePrice():ornament.getPrice()); + ttRollJackpotOrnaments.setImgUrl(ornament.getImageUrl()); + ttRollJackpotOrnaments.setOrnamentName(ornament.getName()); + ttRollJackpotOrnaments.setCreateTime(DateUtils.getNowDate()); + this.save(ttRollJackpotOrnaments); + } + return ""; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollJackpotServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollJackpotServiceImpl.java new file mode 100644 index 0000000..a247283 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollJackpotServiceImpl.java @@ -0,0 +1,17 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.entity.roll.TtRollJackpot; +import com.ruoyi.admin.mapper.TtRollJackpotMapper; +import com.ruoyi.admin.service.TtRollJackpotService; +import org.springframework.stereotype.Service; + +@Service +public class TtRollJackpotServiceImpl extends ServiceImpl implements TtRollJackpotService { + + @Override + public String removeRollJackpotById(Long jackpotId) { + this.removeById(jackpotId); + return ""; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollServiceImpl.java new file mode 100644 index 0000000..3561f49 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollServiceImpl.java @@ -0,0 +1,700 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.config.RedisConstants; +import com.ruoyi.admin.domain.body.AddRobotBody; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.admin.service.TtRollJackpotService; +import com.ruoyi.admin.service.TtRollUserService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.redis.config.RedisLock; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.common.constant.roll.RollGetPrizeWay; +import com.ruoyi.domain.common.constant.roll.RollType; +import com.ruoyi.domain.dto.roll.GetRollPrizePool; +import com.ruoyi.domain.dto.roll.InviteRollUser; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.entity.roll.*; +import com.ruoyi.admin.service.TtRollService; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtRechargeRecord; +import com.ruoyi.domain.vo.SimpleUserVO; +import com.ruoyi.domain.vo.TtRollPrizeDataVO; +import com.ruoyi.common.rabbitmq.config.DelayedQueueConfig; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsByPageVO; +import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsVO; +import com.ruoyi.domain.vo.roll.RollUserPrizeVO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import jakarta.validation.constraints.NotNull; +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.TtboxRecordSource.ROLL; + +@Service +@Slf4j +public class TtRollServiceImpl extends ServiceImpl implements TtRollService { + + @Autowired + private TtRollUserMapper rollUserMapper; + + @Autowired + private TtRollUserService rollUserService; + + @Autowired + private TtBoxRecordsMapper boxRecordsMapper; + + @Autowired + private RabbitTemplate rabbitTemplate; + + @Autowired + private TtRollMapper ttRollMapper; + + @Autowired + private TtUserMapper ttUserMapper; + + @Autowired + private TtRechargeRecordMapper rechargeRecordMapper; + + // public TtRollServiceImpl(TtRollUserMapper rollUserMapper, + // TtBoxRecordsMapper boxRecordsMapper, + // RabbitTemplate rabbitTemplate) { + // this.rollUserMapper = rollUserMapper; + // this.boxRecordsMapper = boxRecordsMapper; + // this.rabbitTemplate = rabbitTemplate; + // } + + @Autowired + private TtRollJackpotService ttRollJackpotService; + + @Autowired + private TtRollJackpotMapper ttRollJackpotMapper; + + @Autowired + private TtRollJackpotOrnamentsMapper ttRollJackpotOrnamentsMapper; + + @Autowired + private TtRollUserPrizeMapper ttRollUserPrizeMapper; + + // @Autowired + // private TtRollUserMapper ttRollUserMapper; + + @Autowired + private TtOrnamentMapper ttOrnamentMapper; + + @Autowired + private RedisLock redisLock; + + @Autowired + private TtUserMapper userMapper; + + @Autowired + private TtUserService userService; + + @Override + public List queryList(TtRollBody ttRollBody) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotNull(ttRollBody.getId())) wrapper.eq(TtRoll::getId, ttRollBody.getId()); + if (StringUtils.isNotEmpty(ttRollBody.getRollName())) + wrapper.like(TtRoll::getRollName, ttRollBody.getRollName()); + if (StringUtils.isNotEmpty(ttRollBody.getRollStatus())) + wrapper.eq(TtRoll::getRollStatus, ttRollBody.getRollStatus()); + wrapper.orderByAsc(TtRoll::getRollStatus).orderByDesc(TtRoll::getCreateTime); + return this.list(wrapper); + } + + @Override + public AjaxResult createRoll(TtRoll ttRoll) { + + // 创建roll房 + Calendar c = Calendar.getInstance(); + c.add(Calendar.MINUTE, 5); + Timestamp criticalTime = new Timestamp(c.getTimeInMillis()); + + long endTime = ttRoll.getEndTime().getTime(); + + // long nowDate = DateUtils.getNowDate().getTime(); + long difference = endTime - criticalTime.getTime(); + if (difference < 0) return AjaxResult.error("开奖时间在临界保护区【5分钟】内。"); + baseMapper.insert(ttRoll); + + System.out.println("创建roll房:" + ttRoll.getId() + ttRoll.getRollName()); + + // 延时队列实现开奖 + // rabbitTemplate.convertAndSend( + // DelayedQueueConfig.DELAYED_EXCHANGE, + // DelayedQueueConfig.DELAYED_ROUTING_KEY, + // String.valueOf(ttRoll.getId()), + // (msg -> { + // msg.getMessageProperties().setDelay((int) difference); + // return msg; + // })); + + return AjaxResult.success(); + } + + @Override + public AjaxResult updateRollById(TtRoll ttRoll) { + + TtRoll one = new LambdaQueryChainWrapper<>(baseMapper) + .eq(TtRoll::getId, ttRoll.getId()) + .eq(TtRoll::getRollStatus, 0) + .eq(TtRoll::getDelFlag, 0) + .one(); + + Date oldEndTime = one.getEndTime(); + + // 此刻 + Calendar c = Calendar.getInstance(); + + long endTime = ttRoll.getEndTime().getTime(); + long difference = endTime - c.getTimeInMillis(); + if (difference < 10) return AjaxResult.error("开奖时间不能早于现在。"); + + // 临界区检查 + c.add(Calendar.MINUTE, 5); + long criticalTime = c.getTimeInMillis(); + if (criticalTime > oldEndTime.getTime()) { + return AjaxResult.error("该roll房开奖时间已处于临界保护区【5分钟】内,请勿修改开奖时间。"); + } + + baseMapper.updateById(ttRoll); + return AjaxResult.success(); + } + + @Override + public List getRollPrizeList(Integer rollId) { + + TtRoll ttRoll = this.getById(rollId); + + // TtRollJackpot jackpot = new LambdaQueryChainWrapper<>(ttRollJackpotMapper) + // .eq(TtRollJackpot::getJackpotId, ttRoll.getJackpotId()) + // .one(); + + List prizeList = new LambdaQueryChainWrapper<>(ttRollJackpotOrnamentsMapper) + .eq(TtRollJackpotOrnaments::getJackpotId, ttRoll.getJackpotId()) + .list(); + + // 所有系统指定 + List rollUserPrizes = ttRollUserPrizeMapper.byRollId(rollId); + + List res = new ArrayList<>(); + for (TtRollJackpotOrnaments orn : prizeList) { + for (int i = 0; i < orn.getOrnamentsNum(); i++) { + + TtOrnament ornament = new LambdaQueryChainWrapper<>(ttOrnamentMapper) + .eq(TtOrnament::getId, orn.getOrnamentsId()) + .one(); + + TtRollPrizeDataVO vo = TtRollPrizeDataVO.builder() + .rollId(rollId) + .rollUserId(ttRoll.getUserId()) + .rollJackpotOrnamentId(orn.getId()) + .ornamentsId(orn.getOrnamentsId()) + .usePrice(ObjectUtil.isNotEmpty(ornament.getUsePrice()) ? ornament.getUsePrice() : ornament.getPrice()) + .shortName(ornament.getName()) + .itemName(ornament.getName()) + .imageUrl(ornament.getImageUrl()) + .ornamentNum(1) + .build(); + + if (rollUserPrizes.isEmpty()) { + res.add(vo); + continue; + } + + for (RollUserPrizeVO prize : rollUserPrizes) { + if (prize.getRollJackpotOrnamentId().equals(orn.getId())) { + vo.setUserId(prize.getUserId()); + vo.setNickName(prize.getNickName()); + vo.setRollUserPrizeId(prize.getRollUserPrizeId()); + + rollUserPrizes.remove(prize); + break; + } + } + + res.add(vo); + } + } + + if (res.isEmpty()) { + return res; + } + + // 排序 + Collections.sort(res, new Comparator() { + @Override + public int compare(TtRollPrizeDataVO o1, TtRollPrizeDataVO o2) { + return o1.getUsePrice().compareTo(o2.getUsePrice()); + } + }); + + return res; + + // roll房奖池关联的饰品列表 + // List rollJackpotOrnamentsList = baseMapper.getRollJackpotOrnamentsList(rollId); + // + // List list = new ArrayList<>(); + // for (TtRollPrizeDataVO rollPrizeData : rollJackpotOrnamentsList) { + // Integer count = new LambdaQueryChainWrapper<>(rollUserMapper) + // .eq(TtRollUser::getRollId, rollPrizeData.getRollId()) + // .eq(TtRollUser::getJackpotOrnamentsId, rollPrizeData.getJackpotOrnamentsListId()) + // .count(); + // for (int i = 0; i < rollPrizeData.getOrnamentsNum() - count; i++) { + // list.add(rollPrizeData); + // } + // } + // List specifiedRollJackpotOrnamentsList = baseMapper.getSpecifiedRollJackpotOrnamentsList(rollId); + // + // list.addAll(specifiedRollJackpotOrnamentsList); + // + // List collect = list.stream().peek(rollPrizeData -> { + // rollPrizeData.setOrnamentsNum(1); + // }).collect(Collectors.toList()); + // + // return collect; + } + + // roll指定获奖 + @Override + public AjaxResult namedWinner(TtRollPrizeDataVO param) { + + // roll成员 + TtRollUser ttRollUser = rollUserMapper.selectOne( + Wrappers.lambdaQuery(TtRollUser.class) + .eq(TtRollUser::getRollId, param.getRollId()) + .eq(TtRollUser::getUserId, param.getUserId()) + ); + if (ObjectUtil.isEmpty(ttRollUser)) { + return AjaxResult.error("不存在的roll房成员。"); + } + + // roll房 + TtRoll roll = new LambdaQueryChainWrapper<>(baseMapper) + .eq(TtRoll::getId, ttRollUser.getRollId()) + .eq(TtRoll::getRollStatus, 0) + .eq(TtRoll::getDelFlag, 0) + .one(); + + if (ObjectUtil.isEmpty(roll)) return AjaxResult.error("roll房已结束。"); + + // 检查roll是否已结束 + Date endTime = roll.getEndTime(); + // 此刻 + Calendar c = Calendar.getInstance(); + if (c.getTime().compareTo(endTime) >= 0) return AjaxResult.error("该roll房已经结束。"); + // 临界区检查(要不要都无所谓) + c.add(Calendar.MINUTE, 5); + long criticalTime = c.getTimeInMillis(); + if (criticalTime > endTime.getTime()) + return AjaxResult.error("该roll房开奖时间已处于临界保护区【5分钟】内,禁止修改。"); + + + ttRollUser.setGetPrizeWay(RollGetPrizeWay.SYS.getCode()); + ttRollUser.setUpdateTime(new Date()); + + // 检查是否已指定 + List userPrizes = new LambdaQueryChainWrapper<>(ttRollUserPrizeMapper) + .eq(TtRollUserPrize::getRollUserId, ttRollUser.getId()) + .list(); + if (!userPrizes.isEmpty()) + return AjaxResult.error("已为roll房成员【" + ttRollUser.getNickName() + "】已指定奖品,请勿重复操作。"); + + // 奖品信息 + TtRollJackpotOrnaments prizeInfo = new LambdaQueryChainWrapper<>(ttRollJackpotOrnamentsMapper) + .eq(TtRollJackpotOrnaments::getId, param.getRollJackpotOrnamentId()) + .one(); + + // 检查已指定的物品数量 + Integer ownOrnamentNumber = ttRollUserPrizeMapper.ownOrnamentNumber(prizeInfo.getId(), ttRollUser.getRollId()); + ownOrnamentNumber = ObjectUtil.isNotEmpty(ownOrnamentNumber) ? ownOrnamentNumber : 0; + if (prizeInfo.getOrnamentsNum() <= ownOrnamentNumber) { + return AjaxResult.error("该物品已经被指派完,没有多余的物品可以指定。"); + } + + // 指定奖品 + TtRollUserPrize build = TtRollUserPrize.builder() + .rollUserId(param.getRollUserId()) + .rollJackpotId(prizeInfo.getJackpotId()) + .rollJackpotOrnamentId(param.getRollJackpotOrnamentId()) + .ornamentId(prizeInfo.getOrnamentsId()) + .ornamentName(prizeInfo.getOrnamentName()) + .price(prizeInfo.getPrice()) + .imgUrl(prizeInfo.getImgUrl()) + .number(param.getOrnamentNum()) + .build(); + + new LambdaUpdateChainWrapper<>(rollUserMapper) + .eq(TtRollUser::getId, param.getRollUserId()) + .set(TtRollUser::getGetPrizeWay, RollGetPrizeWay.SYS.getCode()) + .set(TtRollUser::getUpdateTime, new Date()) + .set(TtRollUser::getDesignatedBy, "系统指定prize。") + .update(); + ttRollUserPrizeMapper.insert(build); + + return AjaxResult.success(); + + } + + @Override + public AjaxResult getRollUsers(Integer rollId) { + + List list = new LambdaQueryChainWrapper<>(rollUserMapper).eq(TtRollUser::getRollId, rollId).list(); + + List collect = list.stream().map(item -> { + return SimpleUserVO.builder() + .rollUserId(item.getId()) + .userId(item.getUserId()) + .nickName(item.getNickName()) + .userName(item.getUserName()) + .avatar(item.getAvatar()) + .build(); + }).collect(Collectors.toList()); + for (SimpleUserVO simpleUserVO : collect) { + System.err.println(simpleUserVO); + } + + return AjaxResult.success(collect); + } + + @Override + public R cancelNamedWinner(List rollUserPrizeIds) { + + int i = ttRollUserPrizeMapper.deleteBatchIds(rollUserPrizeIds); + if (i >= 0) return R.ok("成功取消" + i + "条奖品指定记录。"); + return R.fail(); + + } + + @Override + public R getRollPrizePool(GetRollPrizePool param) { + + param.setLimit((param.getPage() - 1) * param.getSize()); + + TtRoll one = new LambdaQueryChainWrapper<>(baseMapper) + .eq(TtRoll::getId, param.getRollId()) + .eq(TtRoll::getDelFlag, 0) + .one(); + if (ObjectUtil.isNull(one)) return R.fail("不存在的roll房。"); + + param.setJackpotId(one.getJackpotId()); + List list = ttRollJackpotOrnamentsMapper.listByRollId(param); + + Integer total = ttRollJackpotOrnamentsMapper.totalByCondition(param); + + RollJackpotOrnamentsByPageVO build = RollJackpotOrnamentsByPageVO.builder() + .list(list) + .total(total) + .build(); + + return R.ok(build); + } + + public R inviteRollUserCheck(InviteRollUser param) { + + // roll房 + TtRoll ttRoll = new LambdaQueryChainWrapper<>(baseMapper) + .eq(TtRoll::getId, param.getRollId()) + .eq(TtRoll::getRollStatus, "0") + .gt(TtRoll::getEndTime, DateUtils.getNowDate()) + .one(); + if (StringUtils.isNull(ttRoll)) return R.fail("当前Roll房已结束"); + + // + List rollUserList = new LambdaQueryChainWrapper<>(rollUserMapper) + .eq(TtRollUser::getRollId, param.getRollId()) + .list(); + if (ttRoll.getPeopleNum() <= rollUserList.size()) { + return R.fail("roll房人满。"); + } + + if (ttRoll.getPeopleNum() < rollUserList.size() + param.getUserIds().size()) { + return R.fail("加入人数超过roll房人数上限。"); + } + + // + List rollUsers = new LambdaQueryChainWrapper<>(rollUserMapper) + .in(TtRollUser::getUserId, param.getUserIds()) + .eq(TtRollUser::getRollId, param.getRollId()) + .list(); + if (!rollUsers.isEmpty()) return R.fail("已加入的成员不能重复加入。"); + + return R.ok(ttRoll); + } + + @Override + public R inviteRollUser(InviteRollUser param) { + + // roll房 + // TtRoll ttRoll = new LambdaQueryChainWrapper<>(baseMapper) + // .eq(TtRoll::getId, param.getRollId()) + // .eq(TtRoll::getRollStatus, "0") + // .gt(TtRoll::getEndTime, DateUtils.getNowDate()) + // .one(); + // roll房成员 + // TtRollUser rollUser = new LambdaQueryChainWrapper<>(ttRollUserMapper) + // .eq(TtRollUser::getUserId, player.getUserId()) + // .eq(TtRollUser::getRollId, param.getRollId()) + // .one(); + + // 检查 + // R check = inviteRollUserCheck(param); + // if (!check.getCode().equals(200)) return check; + + // 如果是主播房 + // if (ttRoll.getRollType().equals(RollType.ANCHOR.getCode())) { + // TtUser anchor = new LambdaQueryChainWrapper<>(userMapper) + // .eq(TtUser::getUserId, ttRoll.getUserId()) + // .eq(TtUser::getUserType, UserType.ANCHOR.getCode()) + // .eq(TtUser::getDelFlag, "0") + // .one(); + // // 检查是否粉丝 + // Integer parentId = player.getParentId(); + // if (StringUtils.isNull(parentId) || !anchor.getUserId().equals(parentId)) { + // return R.fail("您不是该主播的粉丝,请先加入主播粉丝团才可加入房间!"); + // } + // } + + // 加入房间 + Boolean lock = false; + for (int i = 0; i < 3; i++) { + + lock = redisLock.tryLock(RedisConstants.JOIN_ROLL_LOCK + param.getRollId(), 2L, 15L, TimeUnit.SECONDS); + + if (lock) break; + + } + + if (lock) { + try { + + R check = inviteRollUserCheck(param); + if (!check.getCode().equals(200)) return check; + + TtRoll roll = check.getData(); + + List userList = new LambdaQueryChainWrapper<>(userMapper) + .eq(TtUser::getDelFlag, "0") + .eq(TtUser::getStatus, "0") + .in(TtUser::getUserId, param.getUserIds()) + .list(); + + List rollUserList = new ArrayList<>(); + for (TtUser player : userList) { + TtRollUser ttRollUser = TtRollUser.builder() + .rollId(param.getRollId()) + .userId(player.getUserId()) + .userName(player.getUserName()) + .nickName(player.getNickName()) + .avatar(player.getAvatar()) + .joinTime(DateUtils.getNowDate()) + .build(); + rollUserList.add(ttRollUser); + } + + if (rollUserService.saveBatch(rollUserList, 1)) return R.ok(); + + return R.fail("Sava db fail"); + + } catch (Exception e) { + e.printStackTrace(); + return R.fail("加入失败,请稍后重试。"); + } finally { + redisLock.unlock(RedisConstants.JOIN_ROLL_LOCK + param.getRollId()); + } + } + + // redisLock.unlock(RedisConstants.JOIN_ROLL_LOCK + param.getRollId()); + return R.ok("服务器繁忙,请稍后重试。"); + + } + + @Override + public List getRollUser(Integer rollId) { + return ttRollMapper.getRollUser(rollId); + } + + @Override + public AjaxResult addRobot(AddRobotBody addRobotBody) { + // 查询所有机器人ID,统一转为 Integer + List robotIdList = ttUserMapper.selectObjs( + new QueryWrapper() + .select("user_id") + .eq("user_type", "03") + ).stream().map(id -> Integer.valueOf(id.toString())).collect(Collectors.toList()); + + // 查询已经在房间中的机器人ID + List rollRobotIds = ttRollMapper.getRollRobotIds(addRobotBody.getRollId()); + + // 过滤掉已经在房间中的机器人ID + List filteredRobotIdList = robotIdList.stream() + .filter(robotId -> !rollRobotIds.contains(robotId)) + .collect(Collectors.toList()); + if (filteredRobotIdList.size() < addRobotBody.getRobotNum()) { + return AjaxResult.error("可用机器人数量不足"); + } + + // 打乱机器人ID顺序 + Collections.shuffle(filteredRobotIdList); + + for (int i = 0; i < addRobotBody.getRobotNum(); i++) { + TtUser player = userService.getById(filteredRobotIdList.get(i)); + // 加入ROLL房 + JoinRollParam joinRollParam = new JoinRollParam(); + joinRollParam.setRollId(addRobotBody.getRollId()); + R r = joinRoll(joinRollParam, player); + log.info("r is {}", r != null ? r.getCode() + ":" + r.getMsg() : null); + } + return AjaxResult.success(); + } + + @AllArgsConstructor + @NoArgsConstructor + @Data + public static class JoinRollParam { + @NotNull(message = "ROLL房ID不能为空。") + private Integer rollId; + private String rollPassword; + } + + private R joinRoll(JoinRollParam param, TtUser player) { + + // roll房 + TtRoll ttRoll = new LambdaQueryChainWrapper<>(ttRollMapper) + .eq(TtRoll::getId, param.getRollId()) + .eq(TtRoll::getRollStatus, "0") + .gt(TtRoll::getEndTime, DateUtils.getNowDate()) + .one(); + + // roll房成员 + TtRollUser rollUser = new LambdaQueryChainWrapper<>(rollUserService.getBaseMapper()) + .eq(TtRollUser::getUserId, player.getUserId()) + .eq(TtRollUser::getRollId, param.getRollId()) + .one(); + + // 检查 + R check = joinRollCheck(ttRoll, rollUser, player, param); + if (!check.getCode().equals(200)) return check; + + // 如果是主播房 + if (ttRoll.getRollType().equals(RollType.ANCHOR.getCode())) { + TtUser anchor = new LambdaQueryChainWrapper<>(userMapper) + .eq(TtUser::getUserId, ttRoll.getUserId()) + .eq(TtUser::getUserType, UserType.ANCHOR.getCode()) + .eq(TtUser::getDelFlag, "0") + .one(); + // 检查是否粉丝 + Integer parentId = player.getParentId(); + if (StringUtils.isNull(parentId) || !anchor.getUserId().equals(parentId)) { + return R.fail("您不是该主播的粉丝,请先加入主播粉丝团才可加入房间!"); + } + } + + // 加入房间 + Boolean lock = false; + for (int i = 0; i < 2; i++) { + + lock = redisLock.tryLock(RedisConstants.JOIN_ROLL_LOCK + param.getRollId(), 2L, 7L, TimeUnit.SECONDS); + + if (!lock) { + try { + TimeUnit.SECONDS.sleep(1L); + } catch (Exception e) { + log.warn("try lock sleep warn"); + } finally { + continue; + } + } else { + break; + } + + } + + if (lock) { + try { + + List rollUserList = new LambdaQueryChainWrapper<>(rollUserService.getBaseMapper()) + .eq(TtRollUser::getRollId, param.getRollId()) + .list(); + + if (ttRoll.getPeopleNum() <= rollUserList.size()) { + return R.fail("roll房人满。"); + } + + TtRollUser ttRollUser = TtRollUser.builder() + .rollId(param.getRollId()) + .userId(player.getUserId()) + .userName(player.getUserName()) + .nickName(player.getNickName()) + .avatar(player.getAvatar()) + .joinTime(DateUtils.getNowDate()) + .build(); + + rollUserService.getBaseMapper().insert(ttRollUser); + + return R.ok(); + + } catch (Exception e) { + e.printStackTrace(); + return R.fail("加入失败,请稍后重试。"); + } finally { + redisLock.unlock(RedisConstants.JOIN_ROLL_LOCK + param.getRollId()); + } + } + + redisLock.unlock(RedisConstants.JOIN_ROLL_LOCK + param.getRollId()); + return R.fail("服务器繁忙。"); + } + + private R joinRollCheck(TtRoll ttRoll, TtRollUser rollUser, TtUser player, JoinRollParam param) { + + if (StringUtils.isNull(ttRoll)) return R.fail("当前Roll房已结束"); + if (StringUtils.isNotNull(rollUser)) return R.fail("您当前已在该房间内,请勿重复加入房间!"); + + String password = ttRoll.getRollPassword(); + if (StringUtils.isNotEmpty(password)) { + if (StringUtils.isEmpty(param.getRollPassword())) return R.fail("请输入密码!"); + if (!password.equals(param.getRollPassword())) return R.fail("密码错误!"); + } + + // 检查加入条件:充值下限 + BigDecimal minRecharge = ttRoll.getMinRecharge(); + if (minRecharge.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal rechargeTotalAmount = getRechargeTotalAmount(player.getUserId(), ttRoll.getRechargeStartTime()); + if (!"03".equals(player.getUserType()) && minRecharge.compareTo(rechargeTotalAmount) > 0) return R.fail("未满足该房间的充值条件!"); + } + + return R.ok(); + } + + private BigDecimal getRechargeTotalAmount(Integer userId, Date rechargeStartTime) { + LambdaQueryChainWrapper wrapper = new LambdaQueryChainWrapper<>(rechargeRecordMapper).eq(TtRechargeRecord::getUserId, userId); + if (StringUtils.isNotNull(rechargeStartTime)) wrapper.gt(TtRechargeRecord::getCreateTime, rechargeStartTime); + return wrapper.list().stream().map(TtRechargeRecord::getAmountActuallyPaid).reduce(BigDecimal.ZERO, BigDecimal::add); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollUserPrizeServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollUserPrizeServiceImpl.java new file mode 100644 index 0000000..19fd567 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollUserPrizeServiceImpl.java @@ -0,0 +1,14 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtRollUserMapper; +import com.ruoyi.admin.mapper.TtRollUserPrizeMapper; +import com.ruoyi.admin.service.TtRollUserPrizeService; +import com.ruoyi.admin.service.TtRollUserService; +import com.ruoyi.domain.entity.roll.TtRollUser; +import com.ruoyi.domain.entity.roll.TtRollUserPrize; +import org.springframework.stereotype.Service; + +@Service +public class TtRollUserPrizeServiceImpl extends ServiceImpl implements TtRollUserPrizeService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollUserServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollUserServiceImpl.java new file mode 100644 index 0000000..d675d26 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtRollUserServiceImpl.java @@ -0,0 +1,23 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.roll.TtRollUser; +import com.ruoyi.admin.mapper.TtRollUserMapper; +import com.ruoyi.admin.service.TtRollUserService; +import com.ruoyi.domain.vo.roll.RollUserPrizeVO; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class TtRollUserServiceImpl extends ServiceImpl implements TtRollUserService { + @Override + public R rollWinners(Integer rollId) { + + List list = baseMapper.rollWinners(rollId); + + return R.ok(list); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtTaskCenterServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtTaskCenterServiceImpl.java new file mode 100644 index 0000000..cc2a500 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtTaskCenterServiceImpl.java @@ -0,0 +1,96 @@ +package com.ruoyi.admin.service.impl; + +import java.util.List; +import com.ruoyi.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.admin.mapper.TtTaskCenterMapper; +import com.ruoyi.domain.other.TtTaskCenter; +import com.ruoyi.admin.service.TtTaskCenterService; + +/** + * 任务中心Service业务层处理 + * + * @author ruoyi + * @date 2024-05-25 + */ +@Service +public class TtTaskCenterServiceImpl implements TtTaskCenterService +{ + @Autowired + private TtTaskCenterMapper ttTaskCenterMapper; + + /** + * 查询任务中心 + * + * @param taskId 任务中心主键 + * @return 任务中心 + */ + @Override + public TtTaskCenter selectTtTaskCenterByTaskId(Integer taskId) + { + return ttTaskCenterMapper.selectTtTaskCenterByTaskId(taskId); + } + + /** + * 查询任务中心列表 + * + * @param ttTaskCenter 任务中心 + * @return 任务中心 + */ + @Override + public List selectTtTaskCenterList(TtTaskCenter ttTaskCenter) + { + return ttTaskCenterMapper.selectTtTaskCenterList(ttTaskCenter); + } + + /** + * 新增任务中心 + * + * @param ttTaskCenter 任务中心 + * @return 结果 + */ + @Override + public int insertTtTaskCenter(TtTaskCenter ttTaskCenter) + { + ttTaskCenter.setCreateTime(DateUtils.getNowDate()); + return ttTaskCenterMapper.insertTtTaskCenter(ttTaskCenter); + } + + /** + * 修改任务中心 + * + * @param ttTaskCenter 任务中心 + * @return 结果 + */ + @Override + public int updateTtTaskCenter(TtTaskCenter ttTaskCenter) + { + ttTaskCenter.setUpdateTime(DateUtils.getNowDate()); + return ttTaskCenterMapper.updateTtTaskCenter(ttTaskCenter); + } + + /** + * 批量删除任务中心 + * + * @param taskIds 需要删除的任务中心主键 + * @return 结果 + */ + @Override + public int deleteTtTaskCenterByTaskIds(Integer[] taskIds) + { + return ttTaskCenterMapper.deleteTtTaskCenterByTaskIds(taskIds); + } + + /** + * 删除任务中心信息 + * + * @param taskId 任务中心主键 + * @return 结果 + */ + @Override + public int deleteTtTaskCenterByTaskId(Integer taskId) + { + return ttTaskCenterMapper.deleteTtTaskCenterByTaskId(taskId); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtTimeRollServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtTimeRollServiceImpl.java new file mode 100644 index 0000000..4f0f34c --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtTimeRollServiceImpl.java @@ -0,0 +1,150 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.roll.TtTimeRoll; +import com.ruoyi.domain.entity.roll.TtTimeRollUser; +import com.ruoyi.admin.mapper.TtBoxRecordsMapper; +import com.ruoyi.admin.mapper.TtTimeRollMapper; +import com.ruoyi.admin.service.TtTimeRollService; +import com.ruoyi.admin.service.TtTimeRollUserService; +import com.ruoyi.domain.vo.TtRollPrizeDataVO; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.quartz.domain.SysJob; +import com.ruoyi.quartz.service.ISysJobService; +import org.quartz.SchedulerException; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.TtboxRecordSource.ROLL; + +@Service +public class TtTimeRollServiceImpl extends ServiceImpl implements TtTimeRollService { + + private final ISysJobService sysJobService; + private final TtTimeRollUserService timeRollUserService; + private final TtBoxRecordsMapper boxRecordsMapper; + + public TtTimeRollServiceImpl(ISysJobService sysJobService, + TtTimeRollUserService timeRollUserService, + TtBoxRecordsMapper boxRecordsMapper) { + this.sysJobService = sysJobService; + this.timeRollUserService = timeRollUserService; + this.boxRecordsMapper = boxRecordsMapper; + } + + @Override + public String insertTimeRoll(TtTimeRoll ttTimeRoll) throws SchedulerException, TaskException { + this.save(ttTimeRoll); + SysJob sysJob = new SysJob(); + sysJob.setJobGroup("TimeRollTask"); + sysJob.setInvokeTarget("TimeRollTask.timeRollSettle(" + ttTimeRoll.getId() + ")"); + sysJob.setCreateBy(SecurityUtils.getUsername()); + sysJob.setCreateTime(DateUtils.getNowDate()); + if ("0".equals(ttTimeRoll.getRechargeCondition())) { + sysJob.setJobName("0"); + sysJob.setCronExpression("0 0 * * * ?"); + } else if ("1".equals(ttTimeRoll.getRechargeCondition())) { + sysJob.setJobName("1"); + sysJob.setCronExpression("0 0 0 * * ?"); + } else if ("2".equals(ttTimeRoll.getRechargeCondition())) { + sysJob.setJobName("2"); + sysJob.setCronExpression("0 0 0 ? * 1"); + } else if ("3".equals(ttTimeRoll.getRechargeCondition())) { + sysJob.setJobName("3"); + sysJob.setCronExpression("0 0 0 1 * ?"); + } + sysJobService.insertJob(sysJob); + ttTimeRoll.setJobId(sysJob.getJobId().intValue()); + this.updateById(ttTimeRoll); + return ""; + } + + @Override + public AjaxResult removeTimeRollById(Integer id) { + this.removeById(id); + return AjaxResult.success(); + } + + @Override + public String changeStatus(TtTimeRoll ttTimeRoll) throws SchedulerException { + TtTimeRoll timeRoll = this.getById(ttTimeRoll.getId()); + timeRoll.setStatus(ttTimeRoll.getStatus()); + this.updateById(timeRoll); + SysJob newJob = sysJobService.selectJobById(timeRoll.getJobId().longValue()); + newJob.setStatus(ttTimeRoll.getStatus()); + sysJobService.changeStatus(newJob); + return ""; + } + + @Override + public List getTimeRollPrizeList(Integer id) { + List rollJackpotOrnamentsList = baseMapper.getRollJackpotOrnamentsList(id); + List list = new ArrayList<>(); + for (TtRollPrizeDataVO rollPrizeData : rollJackpotOrnamentsList) { + Long count = new LambdaQueryChainWrapper<>(timeRollUserService.getBaseMapper()) + .eq(TtTimeRollUser::getTimeRollId, rollPrizeData.getRollId()) + .eq(TtTimeRollUser::getJackpotOrnamentsId, rollPrizeData.getJackpotOrnamentsListId()) + .eq(TtTimeRollUser::getEndStatus, "0") + .count(); + for (int i = 0; i < rollPrizeData.getOrnamentNum() - count; i++) { + list.add(rollPrizeData); + } + } + List specifiedRollJackpotOrnamentsList = baseMapper.getSpecifiedTimeRollJackpotOrnamentsList(id); + list.addAll(specifiedRollJackpotOrnamentsList); + return list.stream().peek(rollPrizeData -> rollPrizeData.setOrnamentNum(1)).collect(Collectors.toList()); + } + + @Override + public String namedWinner(TtRollPrizeDataVO rollPrizeData) { + rollPrizeData.setDesignatedBy(""); + if (StringUtils.isNotNull(rollPrizeData.getRollUserId())) { + TtTimeRollUser ttTimeRollUser = timeRollUserService.getById(rollPrizeData.getRollUserId()); + if (!Objects.equals(rollPrizeData.getUserId(), ttTimeRollUser.getUserId())) { + ttTimeRollUser.setUserId(rollPrizeData.getUserId()); + ttTimeRollUser.setUpdateTime(DateUtils.getNowDate()); + timeRollUserService.updateById(ttTimeRollUser); + TtBoxRecords ttBoxRecords = boxRecordsMapper.selectById(ttTimeRollUser.getBoxRecordId()); + ttBoxRecords.setUserId(rollPrizeData.getUserId()); + ttBoxRecords.setUpdateTime(DateUtils.getNowDate()); + ttBoxRecords.setHolderUserId(rollPrizeData.getUserId()); + boxRecordsMapper.updateById(ttBoxRecords); + } + return ""; + } + TtBoxRecords boxRecords = TtBoxRecords.builder().build(); + boxRecords.setUserId(rollPrizeData.getUserId()); + boxRecords.setOrnamentId(rollPrizeData.getOrnamentsId()); + boxRecords.setOrnamentsPrice(rollPrizeData.getUsePrice()); + boxRecords.setOrnamentsLevelId(rollPrizeData.getOrnamentsLevelId()); + boxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode()); + boxRecords.setCreateTime(DateUtils.getNowDate()); + boxRecords.setSource(ROLL.getCode()); + boxRecords.setRollId(rollPrizeData.getRollId()); + boxRecords.setHolderUserId(rollPrizeData.getUserId()); + if (boxRecordsMapper.insert(boxRecords) > 0) { + TtTimeRollUser timeRollUser = TtTimeRollUser.builder().build(); + timeRollUser.setTimeRollId(rollPrizeData.getRollId()); + timeRollUser.setUserId(rollPrizeData.getUserId()); + timeRollUser.setJackpotOrnamentsId(rollPrizeData.getJackpotOrnamentsListId()); + timeRollUser.setOrnamentsId(rollPrizeData.getOrnamentsId()); + timeRollUser.setBoxRecordId(boxRecords.getId()); + timeRollUser.setStatus("2"); + timeRollUser.setDesignatedBy(SecurityUtils.getUsername()); + timeRollUser.setJoinTime(DateUtils.getNowDate()); + if (timeRollUserService.save(timeRollUser)) return ""; + } + return "error!"; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtTimeRollUserServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtTimeRollUserServiceImpl.java new file mode 100644 index 0000000..aea55c5 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtTimeRollUserServiceImpl.java @@ -0,0 +1,11 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.entity.roll.TtTimeRollUser; +import com.ruoyi.admin.mapper.TtTimeRollUserMapper; +import com.ruoyi.admin.service.TtTimeRollUserService; +import org.springframework.stereotype.Service; + +@Service +public class TtTimeRollUserServiceImpl extends ServiceImpl implements TtTimeRollUserService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUpgradeFailOrnamentsServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUpgradeFailOrnamentsServiceImpl.java new file mode 100644 index 0000000..3993af5 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUpgradeFailOrnamentsServiceImpl.java @@ -0,0 +1,100 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.controller.TtUpgradeFailOrnamentsController; +import com.ruoyi.admin.controller.TtUpgradeFailOrnamentsController.BatchAddParam; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.admin.service.TtUpgradeOrnamentsService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.other.TtUpgradeFailOrnaments; +import com.ruoyi.admin.mapper.TtUpgradeFailOrnamentsMapper; +import com.ruoyi.admin.service.TtUpgradeFailOrnamentsService; +import com.ruoyi.domain.other.TtUpgradeOrnaments; +import com.ruoyi.domain.vo.TtUpgradeFailOrnamentsDataVO; +import com.ruoyi.common.utils.bean.BeanUtils; +import org.apache.ibatis.session.SqlSession; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Service +public class TtUpgradeFailOrnamentsServiceImpl extends ServiceImpl implements TtUpgradeFailOrnamentsService { + + @Autowired + private TtOrnamentMapper ttOrnamentMapper; + + @Autowired + private TtUpgradeOrnamentsService upgradeOrnamentsService; + + + @Override + public List queryList(TtUpgradeFailOrnamentsController.listParam param) { + return baseMapper.queryList(param); + } + + @Override + public AjaxResult batchAdd(BatchAddParam param) { + Map tempList = param.getOrnamentsIds().stream().collect(Collectors.toMap(Function.identity(), Function.identity())); + + List list = new LambdaQueryChainWrapper<>(ttOrnamentMapper) + .in(TtOrnament::getId, tempList.keySet().toArray()) + .list(); + + + //应用到所有 + if (param.getUseAllFlag()){ + //截断表 + baseMapper.truncateTable(); + + //查询所有幸运饰品 + List upgradeOrnamentList = upgradeOrnamentsService.list(Wrappers.lambdaQuery(TtUpgradeOrnaments.class) + .eq(TtUpgradeOrnaments::getStatus, 0)); + //每一个都要去添加 + for (TtUpgradeOrnaments upgradeOrnament : upgradeOrnamentList) { + upgradeOneBatch(upgradeOrnament.getId(), list); + } + return AjaxResult.success(); + + } + + //单个应用 + return upgradeOneBatch(param.getUpgradeId(), list); + } + + private AjaxResult upgradeOneBatch(Integer upgradeId, List list) { + List collect = list.stream().map(ornament -> { + + TtUpgradeFailOrnaments build = TtUpgradeFailOrnaments.builder() + .upgradeId(upgradeId) + .ornamentId(ornament.getId()) + .ornamentName(ornament.getName()) + .ornamentPrice(ornament.getUsePrice()) + .createTime(new Date()) + .updateTime(new Date()) + .ornamentNumber(1) + .build(); + return build; + }).collect(Collectors.toList()); + + this.saveBatch(collect); + + return AjaxResult.success(collect); + } + + @Override + public String updateUpgradeFailOrnamentsById(TtUpgradeFailOrnamentsDataVO ttUpgradeFailOrnamentsDataVO) { + TtUpgradeFailOrnaments ttUpgradeFailOrnaments = TtUpgradeFailOrnaments.builder().build(); + BeanUtils.copyProperties(ttUpgradeFailOrnamentsDataVO, ttUpgradeFailOrnaments); + this.updateById(ttUpgradeFailOrnaments); + return ""; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUpgradeOrnamentsServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUpgradeOrnamentsServiceImpl.java new file mode 100644 index 0000000..b4c7aa4 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUpgradeOrnamentsServiceImpl.java @@ -0,0 +1,103 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.admin.mapper.TtUpgradeOrnamentsMapper; +import com.ruoyi.admin.service.TtUpgradeOrnamentsService; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.other.TtUpgradeOrnaments; +import com.ruoyi.domain.other.TtUpgradeOrnamentsBody; +import com.ruoyi.domain.vo.TtUpgradeOrnamentsDataVO; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Service +public class TtUpgradeOrnamentsServiceImpl extends ServiceImpl implements TtUpgradeOrnamentsService { + + @Value("${mkcsgo.upgrade.defaultRequired}") + private Integer defaultRequired; + + @Value("${mkcsgo.upgrade.anchorDefaultRequired}") + private Integer anchorDefaultRequired; + + private final TtOrnamentMapper ornamentsMapper; + + public TtUpgradeOrnamentsServiceImpl(TtOrnamentMapper ornamentsMapper) { + this.ornamentsMapper = ornamentsMapper; + } + + @Override + public List queryList(TtUpgradeOrnamentsBody ttUpgradeOrnamentsBody) { + return baseMapper.queryList(ttUpgradeOrnamentsBody); + } + + @Override + public String batchAdd(List ornamentsIds) { + + List idList = new ArrayList<>(ornamentsIds); + + // 默认区间 + int[] luckSection = {0, defaultRequired}; + int[] anchorLuckSection = {0, anchorDefaultRequired}; + String luckSectionStr = JSON.toJSONString(luckSection); + String anchorLuckSectionStr = JSON.toJSONString(anchorLuckSection); + + // 饰品列表 + List list = new LambdaQueryChainWrapper<>(ornamentsMapper) + .in(TtOrnament::getId, idList) + .eq(TtOrnament::getIsPutaway, 0) + .list(); + if (list.size() != ornamentsIds.size()) { + log.warn("饰品列表中存在上架状态不是1的饰品"); + } + for (TtOrnament ornament : list) { + + TtUpgradeOrnaments ttUpgradeOrnaments = TtUpgradeOrnaments.builder() + .ornamentsId(ornament.getId()) + .ornamentPrice(ObjectUtil.isNotNull(ornament.getUsePrice()) ? ornament.getUsePrice() : ornament.getPrice()) + + .luckSection(luckSectionStr) + .anchorLuckSection(anchorLuckSectionStr) + + .amountRequired(ornament.getUsePrice() + .multiply(new BigDecimal(luckSection[1])) + .multiply(new BigDecimal("0.01"))) + .amountInvested(BigDecimal.ZERO) + + .anchorAmountRequired(ornament.getUsePrice() + .multiply(new BigDecimal(anchorLuckSection[1])) + .multiply(new BigDecimal("0.01"))) + .anchorAmountInvested(BigDecimal.ZERO) + .createTime(new Date()) + .updateTime(new Date()) + .status("0") + .build(); + + this.save(ttUpgradeOrnaments); + } + return ""; + } + + @Override + public String updateUpgradeOrnamentsById(TtUpgradeOrnamentsDataVO ttUpgradeOrnamentsDataVO) { + TtUpgradeOrnaments ttUpgradeOrnaments = this.getById(ttUpgradeOrnamentsDataVO.getId()); + BeanUtils.copyBeanProp(ttUpgradeOrnaments, ttUpgradeOrnamentsDataVO); + this.updateById(ttUpgradeOrnaments); + return ""; + } + + @Override + public Map getUpgradeProfitStatistics(Integer id) { + return baseMapper.getUpgradeProfitStatistics(id); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUpgradeRecordServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUpgradeRecordServiceImpl.java new file mode 100644 index 0000000..499d2b4 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUpgradeRecordServiceImpl.java @@ -0,0 +1,72 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.domain.dto.upgrade.UpgradeCondition; +import com.ruoyi.domain.other.TtUpgradeRecord; +import com.ruoyi.admin.mapper.TtUpgradeRecordMapper; +import com.ruoyi.admin.service.TtUpgradeRecordService; +import com.ruoyi.domain.other.TtUpgradeRecordBody; +import com.ruoyi.domain.vo.upgrade.UpgradeRecordVO; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class TtUpgradeRecordServiceImpl extends ServiceImpl implements TtUpgradeRecordService { + + @Override + public List getUpgradeRecord(TtUpgradeRecordBody ttUpgradeRecordBody) { + return baseMapper.getUpgradeRecord(ttUpgradeRecordBody); + } + + @Override + public R> historyDetail(UpgradeCondition param) { + + Page pageInfo = new Page<>(param.getPage(), param.getSize()); + pageInfo.setOptimizeCountSql(false); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + wrapper.eq(ObjectUtil.isNotNull(param.getUpgradeRecordId()),TtUpgradeRecord::getId,param.getUpgradeRecordId()); + + wrapper.eq(ObjectUtil.isNotNull(param.getOrnamentId()),TtUpgradeRecord::getTargetOrnamentId,param.getOrnamentId()); + + wrapper.eq(ObjectUtil.isNotNull(param.getUserType()),TtUpgradeRecord::getUserType,param.getUserType()); + + wrapper.eq(ObjectUtil.isNotNull(param.getUserId()),TtUpgradeRecord::getUserId,param.getUserId()); + wrapper.orderByDesc(TtUpgradeRecord::getOpenTime); + + pageInfo = this.page(pageInfo, wrapper); + + return R.ok(pageInfo); + + } + + @Override + public R> adminGetLog(UpgradeCondition param) { + + param.setLimit((param.getPage()-1) * param.getSize()); + + List list = baseMapper.adminGetLog(param); + + Page pageInfo = new Page<>(param.getPage(), param.getSize()); + + if (list.isEmpty()){ + pageInfo.setTotal(0); + pageInfo.setRecords(list); + return R.ok(pageInfo); + } + + + pageInfo.setTotal(list.get(0).getTotal()); + pageInfo.setRecords(list); + + return R.ok(pageInfo); + + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserAmountRecordsServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserAmountRecordsServiceImpl.java new file mode 100644 index 0000000..e90ac88 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserAmountRecordsServiceImpl.java @@ -0,0 +1,239 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.admin.service.TtUserAmountRecordsService; +import com.ruoyi.admin.service.TtUserBlendErcashService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.sys.MoneyType; +import com.ruoyi.domain.dto.userRecord.AmountRecordsDetailCondition; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.recorde.TtUserAmountRecords; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtUserAmountRecordsBody; +import com.ruoyi.domain.vo.TeamDetailVO; +import com.ruoyi.domain.vo.TtUserAccountRecordsRankVO; +import com.ruoyi.domain.vo.TtUserAmountRecords.PWelfareVO; +import com.ruoyi.domain.vo.TtUserAmountRecords.PersonBlendErcashVO; +import com.ruoyi.domain.vo.TtUserAmountRecords.UserAmountDetailVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; + +import static java.math.BigDecimal.ROUND_HALF_UP; + +@Slf4j +@Service +public class TtUserAmountRecordsServiceImpl extends ServiceImpl implements TtUserAmountRecordsService { + + + @Autowired + private TtUserCreditsRecordsMapper ttUserCreditsRecordsMapper; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + @Autowired + private TtUserBlendErcashService ttUserBlendErcashService; + + @Autowired + private TtUserAmountRecordsMapper ttUserAmountRecordsMapper; + + @Autowired + private TtPromotionUpdateMapper ttPromotionUpdateMapper; + + @Autowired + private TtUserMapper userMapper; + + @Override + public R queryList(TtUserAmountRecordsBody param) { + + param.setLimit((param.getPage() - 1) * param.getSize()); + + Page pageInfo = new Page<>(param.getPage(), param.getSize()); + + List list = baseMapper.queryList(param); + Integer totalSize = baseMapper.totalSize(param); + + pageInfo.setRecords(list); + pageInfo.setTotal(totalSize); + + return R.ok(pageInfo); + } + + @Override + public Page rank(Timestamp begin, Timestamp end, Integer page, Integer size) { + + Page pageInfo = new Page<>(page, size); + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String beginT = dateFormat.format(begin); + String endT = dateFormat.format(end); + + List rank = baseMapper.rank(beginT, endT, page - 1, size); + pageInfo.setRecords(rank); + pageInfo.setTotal(rank.size()); + return pageInfo; + + } + + // TODO 梳理充值网推广奖励明细 + @Override + public R pWelfareRecords(Integer uid, Integer page, Integer size) { + + // 测试时间段(今天00:00:00 - 现在)------------- + Calendar c = Calendar.getInstance(); + Timestamp end = new Timestamp(c.getTimeInMillis()); + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + Timestamp begin = new Timestamp(c.getTimeInMillis()); + // 测试时间段------------------------------------ + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String beginT = dateFormat.format(begin); + String endT = dateFormat.format(end); + + if (StringUtils.isBlank(beginT) || StringUtils.isBlank(endT)) { + log.warn("推广福利统计失败!"); + return R.fail("时间 !!!"); + } + + Page pageInfo = new Page<>(page, size); + pageInfo.setOptimizeCountSql(false); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .eq(TtUserBlendErcash::getUserId, uid) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.P_WELFARE.getCode()) + .orderByDesc(TtUserBlendErcash::getCreateTime); + + // 历史收益明细 + pageInfo = ttUserBlendErcashService.page(pageInfo, wrapper); + PWelfareVO pWelfareVO = PWelfareVO.builder() + .timeTotal(BigDecimal.ZERO) + .details(pageInfo) + .build(); + + // 今日预计收益 + BigDecimal pWelfareByTime = pWelfarePrizeToBoss(uid, beginT, endT); + pWelfareVO.setTodayPredict(pWelfareByTime); + + // BigDecimal pWelfareByTime = ttUserAmountRecordsMapper.pWelfareByTime(uid,beginT, endT); + // if (ObjectUtil.isEmpty(pWelfareByTime)) pWelfareByTime = BigDecimal.ZERO; + // pWelfareByTime = pWelfareByTime.abs().setScale(2,BigDecimal.ROUND_HALF_UP); + // pWelfareVO.setTodayPredict(pWelfareByTime); + + // 历史总收益 + BigDecimal pWHistoryTotal = baseMapper.pWHistoryTotal(uid); + pWelfareVO.setHistoryTotal(pWHistoryTotal); + + return R.ok(pWelfareVO); + } + + public BigDecimal pWelfarePrizeToBoss(Integer bossId,String beginTimeStr,String endTimeStr){ + + // 解析时间 + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date beginTime = null; + try { + beginTime = dateFormat.parse(beginTimeStr); + } catch (ParseException e) { + log.warn("日期解析异常。"); + return BigDecimal.ZERO; + } + + // 查询所有下级id + List allEmployeesId = userMapper.allEmployeesByParents(Arrays.asList(bossId)); + + // 查询下级的最近一个绑定时间 + if (allEmployeesId.isEmpty()) return BigDecimal.ZERO; + List mit = ttPromotionUpdateMapper.latelyUpdate(allEmployeesId); + if (mit.size() < allEmployeesId.size()) log.warn("下级已绑定上级,但未写入更新日志,请及时检查!!!"); + // 如果最近绑定时间大于本次查询的起始时间,以最近绑定时间为准 + List empIds1 = new ArrayList<>(); + List empIds2 = new ArrayList<>(); + for (TeamDetailVO item : mit) { + Timestamp latelyTime = item.getBeginTime(); + if (latelyTime.compareTo(new Timestamp(beginTime.getTime())) < 0) { + empIds1.add(item.getEmployeeId()); + } else { + empIds2.add(item); + } + } + + List personBEList1 = new ArrayList<>(); + if (!empIds1.isEmpty()){ + // 当日没有 更换绑定的下级消费统计 + personBEList1 = ttUserBlendErcashMapper.personsTotalConsumeByTime( + empIds1, + beginTimeStr, + endTimeStr, + 0 + ); + } + + // 当日有 更换绑定的下级消费统计 + List personBEList2 = new ArrayList<>(); + if (!empIds2.isEmpty()){ + for (TeamDetailVO vo : empIds2){ + String beginT = dateFormat.format(vo.getBeginTime()); + PersonBlendErcashVO personBE = ttUserBlendErcashMapper.personTotalConsumeByTime( + vo.getEmployeeId(), + beginT, + endTimeStr, + 0 + ); + if (ObjectUtil.isEmpty(personBE)) continue; + personBEList2.add(personBE); + } + } + + personBEList1.addAll(personBEList2); + + // 发奖 + BigDecimal totalConsume = BigDecimal.ZERO; + for (PersonBlendErcashVO vo : personBEList1){ + totalConsume = totalConsume.add(vo.getTotal().abs()); + } + + TtUser ttUser = userMapper.selectTtUserById(bossId.longValue()); + if ("01".equals(ttUser.getUserType())) { + return totalConsume.multiply(new BigDecimal("0.045")).setScale(2,ROUND_HALF_UP); + } else { + return totalConsume.multiply(new BigDecimal("0.01")).setScale(2,ROUND_HALF_UP); + } + } + + @Override + public List userAccountDetail(AmountRecordsDetailCondition param) { + + param.setLimit((param.getPage() - 1) * param.getSize()); + + List res = ttUserBlendErcashMapper.userAccountDetail(param); + + if (param.getMoneyType().equals(MoneyType.GOLD.getCode())) { + + } else if (param.getMoneyType().equals(MoneyType.CREDITS.getCode())) { + + res.stream().forEach(item->{ + item.setAmount(item.getCredits()); + }); + + } + + return res; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserAvatarServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserAvatarServiceImpl.java new file mode 100644 index 0000000..7ec28c4 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserAvatarServiceImpl.java @@ -0,0 +1,20 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtUserAvatar; +import com.ruoyi.admin.mapper.TtUserAvatarMapper; +import com.ruoyi.admin.service.TtUserAvatarService; +import com.ruoyi.common.config.RuoYiConfig; +import org.springframework.stereotype.Service; + +@Service +public class TtUserAvatarServiceImpl extends ServiceImpl implements TtUserAvatarService { + + @Override + public String updateUserAvatarById(TtUserAvatar ttUserAvatar) { + String avatar = ttUserAvatar.getAvatar(); + ttUserAvatar.setAvatar(avatar); + this.updateById(ttUserAvatar); + return ""; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserBlendErcashServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserBlendErcashServiceImpl.java new file mode 100644 index 0000000..ec4fb53 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserBlendErcashServiceImpl.java @@ -0,0 +1,208 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtPromotionUpdateMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.TtUserBlendErcashService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.dto.ChildDetailByBoss; +import com.ruoyi.domain.dto.userRecord.BlendErcashCondition; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.vo.ChildDetailByBossVO; +import com.ruoyi.domain.vo.TeamDetailVO; +import com.ruoyi.domain.vo.TtUserAmountRecords.TtUserBlendErcashVO; +import com.ruoyi.system.service.ISysConfigService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class TtUserBlendErcashServiceImpl extends ServiceImpl implements TtUserBlendErcashService { + + @Autowired + private ISysConfigService configService; + + @Autowired + private TtPromotionUpdateMapper promotionUpdateMapper; + + @Autowired + private TtUserService userService; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + @Override + public R byCondition(BlendErcashCondition condition) { + + condition.setLimit((condition.getPage() - 1) * condition.getSize()); + if (StringUtils.isBlank(condition.getUserName())) condition.setUserName(null); + + Page pageInfo = new Page<>(condition.getPage(), condition.getSize()); + List list = baseMapper.byCondition( + condition.getUserId(), + condition.getSource(), + condition.getType(), + condition.getUserName(), + condition.getMoneyType(), + condition.getLimit(), + condition.getSize() + ); + + Integer total = baseMapper.count( + condition.getUserId(), + condition.getSource(), + condition.getType(), + condition.getUserName(), + condition.getMoneyType(), + condition.getLimit(), + condition.getSize()); + pageInfo.setTotal(total); + pageInfo.setRecords(list); + + return R.ok(pageInfo); + } + + @Override + public R> childDetailByBoss(ChildDetailByBoss param) { + + // 解析开始时间 + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Timestamp beginTS = null; + if (!StringUtils.isBlank(param.getBeginTime())) { + Date parse; + try { + parse = dateFormat.parse(param.getBeginTime()); + if (!StringUtils.isBlank(param.getEndTime())) dateFormat.parse(param.getEndTime()); + } catch (ParseException e) { + return R.fail("解析时间异常。"); + } + beginTS = new Timestamp(parse.getTime()); + } + + // boss所有粉丝 + List cIds = userService.teamUIds(param.getBossId()); + if (cIds.isEmpty()) { + return R.ok(new Page()); + } + + // 查询最新的绑定更新记录 + List teamDetailVOS = promotionUpdateMapper.latelyUpdate(cIds.stream().map(Integer::valueOf).collect(Collectors.toList())); + if (cIds.size() > teamDetailVOS.size()) { + log.warn("用户{}的下级绑定记录不完整,请及时检查。", param.getBossId()); + } + + // 如果最新绑定记录时间大于查询起始时间,以最新绑定记录时间为准 + for (TeamDetailVO vo : teamDetailVOS) { + + if (ObjectUtil.isNotEmpty(beginTS) && vo.getBeginTime().compareTo(beginTS) <= 0) { + vo.setBeginTime(null); + } else { + String uid = String.valueOf(vo.getEmployeeId()); + cIds.remove(uid); + } + } + + // 消费途径 + Integer[] sources = new Integer[]{ + TtAccountRecordSource.GAME_TYPE_01.getCode(), + TtAccountRecordSource.GAME_TYPE_02.getCode(), + TtAccountRecordSource.GAME_TYPE_04.getCode() + }; + // 统计查询时间区间内每一个下级的消费 + List data = new ArrayList<>(); + if (!cIds.isEmpty()) { + data = baseMapper.collectByUIds( + cIds, + param.getBeginTime(), + param.getEndTime(), + TtAccountRecordType.OUTPUT.getCode(), + Arrays.asList(sources) + ); + } + + // 统计查询时间区间内有绑定更新的每一个下级的消费 + for (TeamDetailVO vo : teamDetailVOS) { + if (vo.getBeginTime() == null) { + continue; + } + List d = baseMapper.collectByUIds( + Arrays.asList(String.valueOf(vo.getEmployeeId())), + dateFormat.format(vo.getBeginTime()), + param.getEndTime(), + TtAccountRecordType.OUTPUT.getCode(), + Arrays.asList(sources) + ); + // if (d.isEmpty()) + // d.add(ChildDetailByBossVO.builder().nickName(vo.getEmployeeNickName()).total(BigDecimal.ZERO).build()); + data.addAll(d); + } + + // 补充没有消费记录的人员信息 + if (data.size() < teamDetailVOS.size()) { + for (TeamDetailVO tdVo : teamDetailVOS) { + boolean flag = false; + for (ChildDetailByBossVO vo : data) { + if (tdVo.getEmployeeId().equals(vo.getUserId())) { + flag = true; + break; + } + } + if (!flag) { + data.add(ChildDetailByBossVO.builder().nickName(tdVo.getEmployeeNickName()).total(BigDecimal.ZERO).build()); + } + } + } + + // 推广收益比例 + String pWelfarePrizeRatioStr = configService.selectConfigByKey("pWelfarePrize"); + if (StringUtils.isBlank(pWelfarePrizeRatioStr)) { + pWelfarePrizeRatioStr = "0.045"; + } + + // 补充收益信息 + for (ChildDetailByBossVO vo : data) { + vo.setEarnings(vo.getTotal().multiply(new BigDecimal(pWelfarePrizeRatioStr)) + .setScale(2, RoundingMode.HALF_UP).abs()); + } + + // 分页 + List collect = data.stream() + .sorted(new Comparator() { + @Override + public int compare(ChildDetailByBossVO o1, ChildDetailByBossVO o2) { + if (param.getOrderType().equals(1)) return o1.getTotal().compareTo(o2.getTotal()); + if (param.getOrderType().equals(2)) return o2.getTotal().compareTo(o1.getTotal()); + return o1.getTotal().compareTo(o2.getTotal()); + } + }) + .skip((long) (param.getPage() - 1) * param.getSize()) + .limit(param.getSize()) + .collect(Collectors.toList()); + + Page pageInfo = new Page<>(param.getPage(), param.getSize()); + pageInfo.setTotal(data.size()); + pageInfo.setRecords(collect); + + return R.ok(pageInfo); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserCreditsRecordsServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserCreditsRecordsServiceImpl.java new file mode 100644 index 0000000..78a86be --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserCreditsRecordsServiceImpl.java @@ -0,0 +1,88 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.entity.recorde.TtUserCreditsRecords; +import com.ruoyi.admin.mapper.TtUserCreditsRecordsMapper; +import com.ruoyi.admin.service.TtUserCreditsRecordsService; +import com.ruoyi.domain.other.TtUserCreditsRecordsBody; +import com.ruoyi.domain.vo.TtUserCreditsRecordsRankVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.List; + +@Service +public class TtUserCreditsRecordsServiceImpl extends ServiceImpl implements TtUserCreditsRecordsService { + + + @Autowired + private TtUserMapper userMapper; + + @Override + public List queryList(TtUserCreditsRecordsBody ttUserCreditsRecordsBody) { + return baseMapper.queryList(ttUserCreditsRecordsBody); + } + + /** + * + * @param page + * @param size + * @return + */ + @Override + public Page rank(Timestamp begin,Timestamp end,Integer page, Integer size) { + + Page pageInfo = new Page<>(page,size); + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String beginT = dateFormat.format(begin); + String endT = dateFormat.format(end); + + List rank = baseMapper.rank(page - 1, size,beginT ,endT); + pageInfo.setRecords(rank); + pageInfo.setTotal(rank.size()); + return pageInfo; + } + + @Override + public Page pWelfareRecords(Integer uid, Integer type, Integer page, Integer size) { + + Calendar c = Calendar.getInstance(); + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + Timestamp today = new Timestamp(c.getTimeInMillis()); + + Page pageInfo = new Page<>(page, size); + pageInfo.setOptimizeCountSql(false); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .eq(TtUserCreditsRecords::getUserId,uid) + .eq(TtUserCreditsRecords::getSource, TtAccountRecordSource.P_WELFARE.getCode()); + if (type.equals(1)){ + wrapper.ge(TtUserCreditsRecords::getCreateTime,today); + }else if (type.equals(2)){ + c.add(Calendar.DAY_OF_MONTH,-1); + Timestamp yesterday = new Timestamp(c.getTimeInMillis()); + wrapper + .ge(TtUserCreditsRecords::getCreateTime,yesterday) + .le(TtUserCreditsRecords::getCreateTime,today); + }else if (type.equals(3)){ + c.add(Calendar.DAY_OF_MONTH,-6); + Timestamp week = new Timestamp(c.getTimeInMillis()); + wrapper + .ge(TtUserCreditsRecords::getCreateTime,week); + } + + return page(pageInfo,wrapper); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserServiceImpl.java new file mode 100644 index 0000000..a9df035 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtUserServiceImpl.java @@ -0,0 +1,1339 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.RandomUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtBoxRecordsMapper; +import com.ruoyi.admin.mapper.TtPromotionUpdateMapper; +import com.ruoyi.admin.mapper.TtRechargeRecordMapper; +import com.ruoyi.admin.mapper.TtUserAmountRecordsMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.mapper.TtUserCreditsRecordsMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.model.UpdateUserAccountBo; +import com.ruoyi.admin.service.TtOrderService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.service.TtVipLevelService; +import com.ruoyi.admin.util.PhoneNumberUtil; +import com.ruoyi.admin.util.RandomUtils; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.common.utils.uuid.UUID; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.common.constant.TtboxRecordSource; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.dto.sys.TeamUsersParam; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.recorde.TtUserAmountRecords; +import com.ruoyi.domain.entity.recorde.TtUserCreditsRecords; +import com.ruoyi.domain.entity.sys.TtPromotionUpdate; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.ApiUserOnline; +import com.ruoyi.domain.other.TtBoxUserIdPrice; +import com.ruoyi.domain.other.TtRechargeRecord; +import com.ruoyi.domain.other.TtUidTotalRechargeRecord; +import com.ruoyi.domain.other.TtUserBody; +import com.ruoyi.domain.other.TtUserPackSackBody; +import com.ruoyi.domain.other.TtVipLevel; +import com.ruoyi.domain.vo.TtUserPackSackDataVO; +import com.ruoyi.domain.vo.TtUserVipLevelVo; +import com.ruoyi.domain.vo.sys.SimpleTtUserVO; +import com.ruoyi.domain.vo.user.TtUseChildInfoVo; +import com.ruoyi.domain.vo.user.TtUserInfoVo; +import com.ruoyi.domain.vo.user.TtUserVo; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; + +@Service +public class TtUserServiceImpl extends ServiceImpl implements TtUserService { + + private final TtUserAmountRecordsMapper userAmountRecordsMapper; + private final TtUserCreditsRecordsMapper userCreditsRecordsMapper; + + public TtUserServiceImpl(TtUserAmountRecordsMapper userAmountRecordsMapper, + TtUserCreditsRecordsMapper userCreditsRecordsMapper) { + this.userAmountRecordsMapper = userAmountRecordsMapper; + this.userCreditsRecordsMapper = userCreditsRecordsMapper; + } + + @Autowired + private TtOrderService orderService; + + @Autowired + private TtUserMapper userMapper; + + @Autowired + private TtBoxRecordsMapper boxRecordsMapper; + + @Autowired + private TtPromotionUpdateMapper ttPromotionUpdateMapper; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + @Autowired + private TtVipLevelService ttVipLevelService; + + @Autowired + private TtRechargeRecordMapper ttRechargeRecordMapper; + + @Autowired + Executor threadPoolTaskExecutor; + + @Override + public Pair> queryList(TtUserBody ttUserBody) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotNull(ttUserBody.getUserId())) wrapper.eq(TtUser::getUserId, ttUserBody.getUserId()); + if (StringUtils.isNotNull(ttUserBody.getParentId())) wrapper.eq(TtUser::getParentId, ttUserBody.getParentId()); + if (StringUtils.isNotEmpty(ttUserBody.getUserName())) + wrapper.like(TtUser::getUserName, ttUserBody.getUserName()); + if (StringUtils.isNotNull(ttUserBody.getNickName())) + wrapper.like(TtUser::getNickName, ttUserBody.getNickName()); + if (StringUtils.isNotNull(ttUserBody.getUserType())) wrapper.eq(TtUser::getUserType, ttUserBody.getUserType()); + if (StringUtils.isNotEmpty(ttUserBody.getPhoneNumber())) + wrapper.likeRight(TtUser::getPhoneNumber, ttUserBody.getPhoneNumber()); + if (StringUtils.isNotEmpty(ttUserBody.getStatus())) wrapper.eq(TtUser::getStatus, ttUserBody.getStatus()); + if (StringUtils.isNotEmpty(ttUserBody.getInvitationCode())) + wrapper.eq(TtUser::getInvitationCode, ttUserBody.getInvitationCode()); + List users = this.list(wrapper); + if (CollectionUtils.isEmpty(users)) { + return Pair.of(0, new ArrayList<>()); + } + int total = (int) this.count(wrapper); + List vos = new ArrayList<>(); + List userIds = users.stream().filter(Objects::nonNull).map(TtUser::getUserId).collect(Collectors.toList()); + String startDate = StringUtils.isBlank(ttUserBody.getStartDate()) ? null : ttUserBody.getStartDate(); + String endDate = StringUtils.isBlank(ttUserBody.getEndDate()) ? null : ttUserBody.getEndDate(); + List records = ttRechargeRecordMapper.getTotalPriceByUserIds(userIds, startDate, endDate); + Map userIdToTotalPrice = records.stream().collect(Collectors.toMap(TtUidTotalRechargeRecord::getUserId, TtUidTotalRechargeRecord::getTotalPrice)); + List prices = boxRecordsMapper.boxUserIdPrice(userIds, startDate, endDate); + Map userIdToBoxPrice = prices.stream().collect(Collectors.toMap(TtBoxUserIdPrice::getUserId, TtBoxUserIdPrice::getTotalPrice)); + for (TtUser user : users) { + TtUserVo vo = new TtUserVo(); + BeanUtils.copyProperties(user, vo); + vo.setTotalPrice(userIdToTotalPrice.getOrDefault(user.getUserId(), BigDecimal.ZERO)); + BigDecimal boxPrice = userIdToBoxPrice.getOrDefault(user.getUserId(), BigDecimal.ZERO); + BigDecimal totalLoss = boxPrice.subtract(vo.getTotalPrice()); + vo.setTotalLoss(totalLoss); + vos.add(vo); + } + return Pair.of(total, vos); + } + + public boolean checkParentRule(TtUser user) { + if (user == null) { + return false; + } + if (StringUtils.isEmpty(user.getUserType())) { + return true; + } + if (Objects.equals(user.getUserId(), user.getParentId())) { + return false; + } + + // 主播号必须有招商号上级 + if (UserType.ANCHOR.getCode().equals(user.getUserType())) { + if (user.getParentId() == null) { + return false; + } + TtUser parent = this.getById(user.getParentId()); + if (parent == null) { + return false; + } + return UserType.BUSINESS_USER.getCode().equals(parent.getUserType()); + } + if (UserType.COMMON_USER.getCode().equals(user.getUserType())) { + // 如果有上级,上级必须是主播号 + if (user.getParentId() != null) { + TtUser parent = this.getById(user.getParentId()); + if (parent == null) { + return false; + } + return UserType.ANCHOR.getCode().equals(parent.getUserType()); + } + } + // 招商号不能有上级 + if (UserType.BUSINESS_USER.getCode().equals(user.getUserType())) { + return user.getParentId() == null; + } + return true; + } + + public boolean checkInvitationCode(TtUser user) { + if (user == null) { + return false; + } + if (StringUtils.isEmpty(user.getInvitationCode()) + && (Objects.equals(UserType.ANCHOR.getCode(), user.getUserType()) + || Objects.equals(UserType.BUSINESS_USER.getCode(), user.getUserType()))) { + return false; + } + if (StringUtils.isEmpty(user.getInvitationCode())) { + return true; + } + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(TtUser::getInvitationCode, user.getInvitationCode()); + TtUser oldUser = userMapper.selectOne(wrapper); + return oldUser == null || Objects.equals(oldUser.getUserId(), user.getUserId()); + } + + public String userInfoCheck(TtUser ttUser) { + + String nickName = ttUser.getNickName(); + if (ObjectUtil.isNotEmpty(nickName)) { + if (nickName.length() < 2 || nickName.length() > 12) { + return "昵称长度2-12。"; + } + } + return ""; + } + + @Override + @Transactional + public AjaxResult updateUserById(TtUser ttUser) { + if (!checkParentRule(ttUser)) { + return AjaxResult.error("上级设置不符合要求"); + } + if (!checkInvitationCode(ttUser)) { + return AjaxResult.error("邀请码重复或者为空"); + } + + String check = userInfoCheck(ttUser); + if (!StringUtils.isBlank(check)) return AjaxResult.error(check); + + String password = ttUser.getPassword(); + // String avatar = ttUser.getAvatar(); + + TtUser oldUser = this.getById(ttUser.getUserId()); + + // ttUser.setAvatar(RuoYiConfig.getDomainName() + avatar); + + if (StringUtils.isNotEmpty(password)) { + ttUser.setPassword(SecurityUtils.encryptPassword(password)); + ttUser.setRemark("明文密码:" + password); + } else { + ttUser.setPassword(oldUser.getPassword()); + ttUser.setRemark(oldUser.getRemark()); + } + + this.updateById(ttUser); + + if (!Objects.equals(oldUser.getParentId(), ttUser.getParentId())) { + if (ObjectUtil.isNotEmpty(oldUser.getParentId()) && ObjectUtil.isNotEmpty(ttUser.getParentId()) && oldUser.getParentId().equals(ttUser.getParentId())) { + return AjaxResult.success(); + } + // 上下级绑定关系表 + TtPromotionUpdate build = TtPromotionUpdate.builder() + .employeeId(ttUser.getUserId()) + .bossId(ttUser.getParentId()) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + ttPromotionUpdateMapper.insert(build); + return AjaxResult.success(); + } + return AjaxResult.success(); + + } + + @Override + public AjaxResult updateUserInvitationCodeById(TtUser ttUser) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(TtUser::getUserId, ttUser.getUserId()); + updateWrapper.set(TtUser::getInvitationCode, ttUser.getInvitationCode()); + updateWrapper.set(TtUser::getUpdateBy, ttUser.getUpdateBy()); + updateWrapper.set(TtUser::getUpdateTime, ttUser.getUpdateTime()); + update(updateWrapper); + return AjaxResult.success(); + } + + @Override + public void generateAccount(HttpServletResponse response, List accountList) { + String folderPath = RuoYiConfig.getDownloadPath() + "/"; + File folder = new File(folderPath); + if (!folder.exists()) { + folder.mkdirs(); + } + ServletOutputStream os = null; + BufferedWriter bw = null; + String uuid = IdUtils.fastSimpleUUID(); + try { + bw = new BufferedWriter(new FileWriter(folderPath + uuid + ".txt")); + for (int i = 0; i < accountList.size(); i++) { + bw.write("账号[" + (i + 1) + "]: " + accountList.get(i).getUserName() + " 密码: " + accountList.get(i).getRemark().substring(5)); + bw.newLine(); + bw.flush(); + } + File uploadFile = new File(folderPath + uuid + ".txt"); + os = response.getOutputStream(); + response.setCharacterEncoding(StandardCharsets.UTF_8.name()); + response.setContentType("application/octet-stream"); + response.addHeader("Content-disposition", "attachment"); + os.write(FileUtil.readBytes(uploadFile)); + IoUtil.flush(os); + } catch (IOException ignored) { + } finally { + if (os != null) IoUtil.close(os); + if (bw != null) IoUtil.close(bw); + } + } + + @Override + public List getAccountList(Integer num) { + if (num <= 0 || num > 10) return null; + List userList = new ArrayList<>(); + for (int i = 0; i < num; i++) { + String phoneNumber = getRandomPhoneNumber(); + String nickName = RandomUtils.getRandomName(new Random().nextInt(2)); + String password = RandomUtil.randomString(10); + String invitationCode = getInvitationCode(); + TtUser ttUser = TtUser.builder().build(); + ttUser.setUserName(phoneNumber); + ttUser.setNickName(nickName); + ttUser.setUserType(UserType.ANCHOR.getCode()); + ttUser.setPhoneNumber(phoneNumber); + ttUser.setAvatar(""); + ttUser.setPassword(SecurityUtils.encryptPassword(password)); + ttUser.setInvitationCode(invitationCode.toLowerCase()); + ttUser.setIsRealCheck("1"); + ttUser.setRemark(password); + ttUser.setCreateBy(SecurityUtils.getUsername()); + ttUser.setCreateTime(DateUtils.getNowDate()); + ttUser.setAccountAmount(BigDecimal.ZERO); + ttUser.setAccountCredits(BigDecimal.ZERO); + this.save(ttUser); + userList.add(ttUser); + } + return userList; + } + + @Override + public TtUser getAccountList(String phoneNumber, String password) { + if (StringUtils.isBlank(phoneNumber) || StringUtils.isBlank(password)) return null; + + String nickName = RandomUtils.getRandomName(new Random().nextInt(2)); + + String invitationCode = getInvitationCode(); + TtUser ttUser = TtUser.builder().build(); + ttUser.setUserName(phoneNumber); + ttUser.setNickName(nickName); + ttUser.setUserType("01"); + ttUser.setPhoneNumber(phoneNumber); + ttUser.setAvatar(""); + ttUser.setPassword(SecurityUtils.encryptPassword(password)); + ttUser.setInvitationCode(invitationCode.toLowerCase()); + ttUser.setIsRealCheck("1"); + ttUser.setRemark(password); + ttUser.setCreateBy("admin"); + ttUser.setCreateTime(DateUtils.getNowDate()); + ttUser.setAccountAmount(BigDecimal.ZERO); + ttUser.setAccountCredits(BigDecimal.ZERO); + this.save(ttUser); + return ttUser; + } + + @Override + public List getRobotList(Integer num) { + if (num <= 0 || num > 10) return null; + List userList = new ArrayList<>(); + for (int i = 0; i < num; i++) { + String phoneNumber = getRandomPhoneNumber(); + String nickName = RandomUtils.getRandomName(new Random().nextInt(2)); + String password = RandomUtil.randomString(10); + String invitationCode = getInvitationCode(); + TtUser ttUser = TtUser.builder().build(); + ttUser.setUserName(phoneNumber); + ttUser.setNickName(nickName); + ttUser.setUserType("03"); + ttUser.setPhoneNumber(phoneNumber); + ttUser.setAvatar(""); + ttUser.setPassword(SecurityUtils.encryptPassword(password)); + ttUser.setAccountAmount(new BigDecimal(99999999)); + ttUser.setAccountCredits(BigDecimal.ZERO); + ttUser.setInvitationCode(invitationCode.toLowerCase()); + ttUser.setIsRealCheck("1"); + ttUser.setRemark(password); + ttUser.setCreateBy(SecurityUtils.getUsername()); + ttUser.setCreateTime(DateUtils.getNowDate()); + this.save(ttUser); + userList.add(ttUser); + } + return userList; + } + + @Override + public boolean checkUserNameUnique(TtUser user) { + int userId = StringUtils.isNull(user.getUserId()) ? -1 : user.getUserId(); + TtUser info = new LambdaQueryChainWrapper<>(baseMapper) + .eq(TtUser::getUserName, user.getUserName()) + .eq(TtUser::getDelFlag, "0") + .one(); + if (StringUtils.isNotNull(info) && info.getUserId() != userId) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + @Override + public boolean checkPhoneUnique(TtUser user) { + int userId = StringUtils.isNull(user.getUserId()) ? -1 : user.getUserId(); + TtUser info = new LambdaQueryChainWrapper<>(baseMapper) + .eq(TtUser::getPhoneNumber, user.getPhoneNumber()) + .eq(TtUser::getDelFlag, "0") + .one(); + if (StringUtils.isNotNull(info) && info.getUserId() != userId) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + @Override + public boolean checkIdNumUnique(TtUser user) { + int userId = StringUtils.isNull(user.getUserId()) ? -1 : user.getUserId(); + TtUser info = new LambdaQueryChainWrapper<>(baseMapper) + .eq(TtUser::getIdNum, user.getIdNum()) + .eq(TtUser::getIsRealCheck, "1") + .eq(TtUser::getDelFlag, "0") + .one(); + if (StringUtils.isNotNull(info) && info.getUserId() != userId) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + @Override + public String getInvitationCode() { + while (true) { + try { + String randomInvitationCode = UUID.randomUUID().toString().replace("-", "").substring(0, 6).toUpperCase(); + TtUser ttUser = new LambdaQueryChainWrapper<>(baseMapper) + .eq(TtUser::getInvitationCode, randomInvitationCode) + .eq(TtUser::getDelFlag, "0") + .one(); + if (StringUtils.isNull(ttUser)) { + return randomInvitationCode; + } + } catch (Exception e) { + return null; + } + } + } + + @Override + public TtUser selectTtUserById(Long id) { + return baseMapper.selectTtUserById(id); + } + + @Override + public ApiUserOnline loginUserToUserOnline(LoginUser user) { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserData())) { + return null; + } + ApiUserOnline userOnline = new ApiUserOnline(); + BeanUtils.copyBeanProp(userOnline, user); + userOnline.setNickName(user.getUserData().getNickName()); + userOnline.setUserName(user.getUsername()); + return userOnline; + } + + @Override + public ApiUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user) { + if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername())) { + return loginUserToUserOnline(user); + } + return null; + } + + @Override + public ApiUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user) { + if (StringUtils.equals(ipaddr, user.getIpaddr())) { + return loginUserToUserOnline(user); + } + return null; + } + + @Override + public ApiUserOnline selectOnlineByUserName(String userName, LoginUser user) { + if (StringUtils.equals(userName, user.getUsername())) { + return loginUserToUserOnline(user); + } + return null; + } + + @Override + public void insertUserAmountRecords(Integer userId, TtAccountRecordType type, TtAccountRecordSource source, BigDecimal amount, BigDecimal finalAmount) { + insertUserAmountRecords(userId, type, source, amount, finalAmount, null, null, null); + } + + @Override + public void insertUserAmountRecords(Integer userId, TtAccountRecordType type, TtAccountRecordSource source, BigDecimal amount, BigDecimal finalAmount, + Integer pwChildId, String pwChildName, BigDecimal childAccount) { + + TtUserAmountRecords userAmountRecords = TtUserAmountRecords.builder().build(); + userAmountRecords.setUserId(userId); + userAmountRecords.setType(type.getCode()); + userAmountRecords.setSource(source.getCode()); + userAmountRecords.setAmount(amount); + userAmountRecords.setFinalAmount(finalAmount); + userAmountRecords.setCreateTime(DateUtils.getNowDate()); + userAmountRecords.setRemark(source.getMsg()); + if (source.equals(TtAccountRecordSource.P_WELFARE)) { + userAmountRecords.setPwChildId(pwChildId); + userAmountRecords.setPwChildName(pwChildName); + userAmountRecords.setPwChildAccount(childAccount); + } + // switch (source) { + // case "0": + // userAmountRecords.setRemark("充值"); + // break; + // case "1": + // userAmountRecords.setRemark("开箱消费"); + // break; + // case "2": + // userAmountRecords.setRemark("创建对战消费"); + // break; + // case "3": + // userAmountRecords.setRemark("加入对战消费"); + // break; + // case "4": + // userAmountRecords.setRemark("对战结果收入"); + // break; + // case "5": + // userAmountRecords.setRemark("分解收入"); + // break; + // case "6": + // userAmountRecords.setRemark("注册红包收入"); + // break; + // case "7": + // userAmountRecords.setRemark("口令红包收入"); + // break; + // case "8": + // userAmountRecords.setRemark("商城积分转换收入"); + // break; + // case "9": + // userAmountRecords.setRemark("VIP等级返佣收入"); + // break; + // case "10": + // userAmountRecords.setRemark("后台操作余额变动"); + // break; + // case "11": + // userAmountRecords.setRemark("幸运升级消费"); + // break; + // case "12": + // userAmountRecords.setRemark("商城购物消费"); + // break; + // case "13": + // userAmountRecords.setRemark("推广福利收入"); + // userAmountRecords.setPwChildId(pwChildId); + // userAmountRecords.setPwChildName(pwChildName); + // userAmountRecords.setPwChildAccount(account); + // break; + // } + userAmountRecordsMapper.insert(userAmountRecords); + } + + @Override + public void insertUserAmountRecords(Integer userId, TtAccountRecordType type, TtAccountRecordSource source, BigDecimal amount, BigDecimal finalAmount, Integer taskId) { + + TtUserAmountRecords userAmountRecords = TtUserAmountRecords.builder().build(); + userAmountRecords.setUserId(userId); + userAmountRecords.setType(type.getCode()); + userAmountRecords.setSource(source.getCode()); + userAmountRecords.setAmount(amount); + userAmountRecords.setFinalAmount(finalAmount); + userAmountRecords.setCreateTime(DateUtils.getNowDate()); + userAmountRecords.setRemark(source.getMsg()); + if (source.equals(TtAccountRecordSource.TASK)) { + userAmountRecords.setTaskId(taskId); + } + userAmountRecordsMapper.insert(userAmountRecords); + } + + @Override + public void insertUserCreditsRecords(Integer userId, TtAccountRecordType type, TtAccountRecordSource source, BigDecimal credits, BigDecimal finalCredits) { + insertUserCreditsRecords(userId, type, source, credits, finalCredits, null, null, null); + } + + /** + * @param userId + * @param type + * @param source + * @param credits 变动金额 + * @param finalCredits 账户最终金额 + * @param pwChildId 推广福利 上级id + * @param pwChildName + * @param childAccount 推广福利 上级流水 + */ + @Override + public void insertUserCreditsRecords(Integer userId, TtAccountRecordType type, TtAccountRecordSource source, BigDecimal credits, BigDecimal finalCredits, + Integer pwChildId, String pwChildName, BigDecimal childAccount) { + TtUserCreditsRecords userCreditsRecords = TtUserCreditsRecords.builder().build(); + userCreditsRecords.setUserId(userId); + userCreditsRecords.setType(type.getCode()); + userCreditsRecords.setSource(source.getCode()); + userCreditsRecords.setCredits(credits); + userCreditsRecords.setFinalCredits(finalCredits); + userCreditsRecords.setCreateTime(DateUtils.getNowDate()); + userCreditsRecords.setRemark(source.getMsg()); + if (source.equals(TtAccountRecordSource.P_WELFARE)) { + userCreditsRecords.setPwChildId(pwChildId); + userCreditsRecords.setPwChildName(pwChildName); + userCreditsRecords.setPwChildAccount(childAccount); + } + // switch (source) { + // case "0": + // userCreditsRecords.setRemark("积分商城兑换消费"); + // break; + // case "1": + // userCreditsRecords.setRemark("开箱收入"); + // break; + // case "2": + // userCreditsRecords.setRemark("创建对战收入"); + // break; + // case "3": + // userCreditsRecords.setRemark("加入对战收入"); + // break; + // case "4": + // userCreditsRecords.setRemark("商城积分转换消费"); + // break; + // case "5": + // userCreditsRecords.setRemark("幸运升级收入"); + // break; + // case "10": + // userCreditsRecords.setRemark("后台操作积分变动"); + // break; + // case "11": + // // String remark = ""; + // // if (remarks.length>0){ + // // for (int i = 0;i < remarks.length;i++){ + // // remark = remark + remarks[i]; + // // } + // // } + // userCreditsRecords.setRemark("推广福利积分收入"); + // userCreditsRecords.setPwChildId(pwChildId); + // userCreditsRecords.setPwChildName(pwChildName); + // userCreditsRecords.setPwChildAccount(account); + // break; + // case "task_1": + // userCreditsRecords.setRemark("{首次下载奖励}任务奖励"); + // break; + // case "task_2": + // userCreditsRecords.setRemark("{每日流水奖励}任务奖励"); + // break; + // } + userCreditsRecordsMapper.insert(userCreditsRecords); + } + + @Override + public void insertUserCreditsRecords(Integer userId, TtAccountRecordType type, TtAccountRecordSource source, BigDecimal credits, BigDecimal finalCredits, Integer taskId) { + + TtUserCreditsRecords userCreditsRecords = TtUserCreditsRecords.builder().build(); + userCreditsRecords.setUserId(userId); + userCreditsRecords.setType(type.getCode()); + userCreditsRecords.setSource(source.getCode()); + userCreditsRecords.setCredits(credits); + userCreditsRecords.setFinalCredits(finalCredits); + userCreditsRecords.setCreateTime(DateUtils.getNowDate()); + userCreditsRecords.setRemark(source.getMsg()); + if (source.equals(TtAccountRecordSource.TASK)) { + userCreditsRecords.setTaskId(taskId); + } + + userCreditsRecordsMapper.insert(userCreditsRecords); + + } + + @Override + public List getPackSack(TtUserPackSackBody ttUserPackSackBody) { + return baseMapper.getPackSack(ttUserPackSackBody); + } + + @Override + public Map getUserProfitStatistics(Integer userId) { + return baseMapper.getUserProfitStatistics(userId); + } + + @Override + public List propRankOfDay(Timestamp beginTime, Timestamp endTime, Integer number) { + + // String[] sources = new String[]{"0","1","2","6"}; + Integer[] sources = new Integer[]{ + TtboxRecordSource.BLIND_BOX.getCode(), + TtboxRecordSource.FIGHT.getCode(), + // TtboxRecordSource.ROLL.getCode(), + TtboxRecordSource.UPGRADE.getCode() + }; + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String beginT = dateFormat.format(beginTime); + String endT = dateFormat.format(endTime); + + // System.out.println("出货排行榜时间区间:"+beginT+","+endT); + + List rank = boxRecordsMapper.propRankOfDay(beginT, endT, sources, number); + + return rank; + } + + @Override + public R> teamUsers(TeamUsersParam param) { + + Page pageInfo = new Page<>(param.getPage(), param.getSize()); + pageInfo.setOptimizeCountSql(false); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .like(ObjectUtil.isNotEmpty(param.getUserName()), TtUser::getNickName, param.getUserName()) + .or() + .like(ObjectUtil.isNotEmpty(param.getUserName()), TtUser::getUserName, param.getUserName()) + .in(ObjectUtil.isNotEmpty(param.getEmployeeIds()), TtUser::getParentId, param.getBossIds()) + .in(ObjectUtil.isNotEmpty(param.getEmployeeIds()), TtUser::getUserId, param.getEmployeeIds()); + + Page page = page(pageInfo, wrapper); + + List voList = page.getRecords().stream().map(item -> { + SimpleTtUserVO vo = new SimpleTtUserVO(); + BeanUtil.copyProperties(item, vo); + return vo; + }).collect(Collectors.toList()); + + Page p = new Page<>(); + BeanUtil.copyProperties(pageInfo, p, "records"); + p.setRecords(voList); + return R.ok(p); + } + + @Override + public List teamUIds(Integer bossId) { + return baseMapper.teamUIds(bossId); + } + + @Override + public void updateUserVIPLevel(Integer userId) { + // 查询用户累计总充值量 + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(TtUserBlendErcash::getUserId, userId) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode()) + .select(TtUserBlendErcash::getTotal); + BigDecimal totalRechargeAmount = ttUserBlendErcashMapper.selectObjs(queryWrapper) + .stream() + .map(obj -> (BigDecimal) obj) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + // 查询VIP等级列表 + TtUser ttUser = getById(userId); + List ttVipLevels = ttVipLevelService.list(); + for (TtVipLevel ttVipLevel : ttVipLevels) { + if (totalRechargeAmount.compareTo(ttVipLevel.getRechargeThreshold()) >= 0 && + ttUser.getVipLevel() < ttVipLevel.getId()) { + ttUser.setVipLevel(ttVipLevel.getId()); + this.updateById(ttUser); + } + } + } + + private String getRandomPhoneNumber() { + while (true) { + try { + String phoneNumber = PhoneNumberUtil.createPhoneNumber(new Random().nextInt(3)); + TtUser ttUser = new LambdaQueryChainWrapper<>(baseMapper).eq(TtUser::getPhoneNumber, phoneNumber) + .eq(TtUser::getDelFlag, "0").one(); + if (StringUtils.isNull(ttUser)) { + return phoneNumber; + } + } catch (Exception e) { + return null; + } + } + } + + @Override + public TtUserVipLevelVo userVipLeverRate(Integer userId) { + // 查询用户累计总充值量 + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(TtUserBlendErcash::getUserId, userId) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode()) + .select(TtUserBlendErcash::getTotal); + BigDecimal totalRechargeAmount = ttUserBlendErcashMapper.selectObjs(queryWrapper) + .stream() + .map(obj -> (BigDecimal) obj) + .reduce(BigDecimal.ZERO, BigDecimal::add); + // 查询VIP等级列表 + TtUser ttUser = getById(userId); + List ttVipLevels = ttVipLevelService.list(); + Optional optional = ttVipLevels.stream().map(TtVipLevel::getRechargeThreshold).max(BigDecimal::compareTo); + BigDecimal maxRate = optional.orElse(BigDecimal.ZERO); + return TtUserVipLevelVo.builder() + .userId(userId) + .vipLevel(ttUser.getVipLevel()) + .rate(totalRechargeAmount.compareTo(maxRate) > 0 ? maxRate : totalRechargeAmount) + .maxRate(maxRate) + .build(); + } + + @Override + public TtUserInfoVo userInfo(Integer userId) { + TtUserInfoVo vo = new TtUserInfoVo(); + TtUser ttUser = getById(userId); + vo.setUserId(ttUser.getUserId()); + if (ttUser.getParentId() != null) { + TtUser parent = getById(ttUser.getParentId()); + if (parent != null) { + vo.setParentId(parent.getParentId()); + vo.setParentNickName(parent.getNickName()); + } + } + // 查询所有下级id + List allEmployeesId = userMapper.allEmployeesByParents(Collections.singletonList(ttUser.getUserId())); + if (CollectionUtils.isNotEmpty(allEmployeesId)) { + vo.setChildNum(allEmployeesId.size()); + // 查询用户累计总充值量 + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.in(TtUserBlendErcash::getUserId, allEmployeesId) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode()) + .select(TtUserBlendErcash::getTotal); + BigDecimal totalRechargeAmount = ttUserBlendErcashMapper.selectObjs(queryWrapper) + .stream() + .map(obj -> (BigDecimal) obj) + .reduce(BigDecimal.ZERO, BigDecimal::add); + vo.setRechargePrice(totalRechargeAmount); + } + vo.setCommissionsRate(new BigDecimal(3)); + vo.setTurnoverRate(new BigDecimal(3)); + return vo; + } + + @Override + public List userChildInfos(Integer userId) { + // 查询结果 + LambdaQueryWrapper ttUserQuery = new LambdaQueryWrapper<>(); + ttUserQuery.eq(TtUser::getParentId, userId); + List ttUsers = userMapper.selectList(ttUserQuery); + if (CollectionUtils.isEmpty(ttUsers)) { + return Collections.emptyList(); + } + List vos = new ArrayList<>(); + for (TtUser ttUser : ttUsers) { + TtUseChildInfoVo vo = new TtUseChildInfoVo(); + BeanUtils.copyProperties(ttUser, vo); + vos.add(vo); + } + return vos; + } + + @Override + public AjaxResult userVipLevelRechargeThreshold(Integer userId, Integer vipLevel) { + TtUser ttUser = getById(userId); + if (ttUser == null) { + return AjaxResult.error("当前用户不存在"); + } + if (ttUser.getVipLevel() == null || vipLevel == null || ttUser.getVipLevel() < vipLevel) { + return AjaxResult.error("VIP等级参数不合法"); + } + TtVipLevel level = ttVipLevelService.getById(vipLevel); + if (level == null) { + return AjaxResult.error("VIP等级不合法"); + } + if (level.getAddedBonus() == null) { + return AjaxResult.success(); + } + String ramark = TtAccountRecordSource.VIP_LEVEL_REACH.getMsg() + level.getId(); + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(TtUserBlendErcash::getUserId, userId) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.VIP_LEVEL_REACH.getCode()) + .eq(TtUserBlendErcash::getRemark, ramark); + List dbBlendErcash = ttUserBlendErcashMapper.selectList(query); + if (CollectionUtils.isNotEmpty(dbBlendErcash)) { + return AjaxResult.error("该VIP等级金额已领取"); + } + // 加钱 + TtUser user = this.getById(userId); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, user.getUserId()) + .setSql("account_amount = account_amount + " + level.getAddedBonus()); + this.update(userUpdate); + + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(userId) + .credits(level.getAddedBonus().compareTo(BigDecimal.ZERO) > 0 ? level.getAddedBonus() : null) + .finalCredits(level.getAddedBonus().compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountCredits().add(level.getAddedBonus()) : null) + .total(level.getAddedBonus()) // 收支合计 + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.VIP_LEVEL_REACH.getCode()) + .remark(ramark) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + ttUserBlendErcashMapper.insert(blendErcash); + + return AjaxResult.success(); + } + + @Override + public AjaxResult userHasVipLevelRechargeThreshold(Integer userId) { + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(TtUserBlendErcash::getUserId, userId) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.VIP_LEVEL_REACH.getCode()) + ; + List dbBlendErcash = ttUserBlendErcashMapper.selectList(query); + if (CollectionUtils.isEmpty(dbBlendErcash)) { + return AjaxResult.success(new ArrayList<>()); + } + List vips = new ArrayList<>(); + for (TtUserBlendErcash ercash : dbBlendErcash) { + if (ercash == null || StringUtils.isBlank(ercash.getRemark())) { + continue; + } + String vip = ercash.getRemark().replace(TtAccountRecordSource.VIP_LEVEL_REACH.getMsg(), ""); + vips.add(vip); + } + return AjaxResult.success(vips); + } + + @Override + public AjaxResult userRechargeBonus(Integer userId, Integer recharge, Double bonus) { + TtUser ttUser = getById(userId); + if (ttUser == null) { + return AjaxResult.error("当前用户不存在"); + } + if (recharge == null) { + return AjaxResult.error("充值金额门槛为空"); + } + if (bonus == null) { + return AjaxResult.error("奖励金额为空"); + } + // 创建日期格式化对象,指定日期格式 + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + // 获取Calendar实例 + Calendar calendar = Calendar.getInstance(); + // 设置时间为今天凌晨 + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + // 获取今天凌晨时间 + Date todayMidnight = calendar.getTime(); + String formattedTodayMidnight = formatter.format(todayMidnight); + // 获取明天凌晨时间 + calendar.add(Calendar.DAY_OF_MONTH, 1); + Date tomorrowMidnight = calendar.getTime(); + String formattedTomorrowMidnight = formatter.format(tomorrowMidnight); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(TtRechargeRecord::getUserId, userId); + wrapper.between(TtRechargeRecord::getCreateTime, formattedTodayMidnight, formattedTomorrowMidnight); + List records = ttRechargeRecordMapper.selectList(wrapper); + if (CollectionUtils.isEmpty(records)) { + return AjaxResult.error("今天没有充值记录"); + } + BigDecimal total = records.stream().filter(Objects::nonNull).map(TtRechargeRecord::getAmountActuallyPaid).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal rechargeB = new BigDecimal(recharge); + if (rechargeB.compareTo(total) > 0) { + return AjaxResult.error("今天充值未达到门槛"); + } + String ramark = TtAccountRecordSource.RECHARGE_RESEARCH_BONUS.getMsg() + recharge; + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(TtUserBlendErcash::getUserId, userId) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE_RESEARCH_BONUS.getCode()) + .eq(TtUserBlendErcash::getRemark, ramark) + .between(TtUserBlendErcash::getCreateTime, formattedTodayMidnight, formattedTomorrowMidnight) + ; + List dbBlendErcash = ttUserBlendErcashMapper.selectList(query); + if (CollectionUtils.isNotEmpty(dbBlendErcash)) { + return AjaxResult.error("今天充值门槛达标奖励已领取"); + } + // 加钱 + TtUser user = this.getById(userId); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, user.getUserId()) + .setSql("account_amount = account_amount + " + bonus); + this.update(userUpdate); + BigDecimal bonusB = new BigDecimal(bonus); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(userId) + .credits(bonusB.compareTo(BigDecimal.ZERO) > 0 ? bonusB : null) + .finalCredits(bonusB.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountCredits().add(bonusB) : null) + .total(bonusB) // 收支合计 + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.RECHARGE_RESEARCH_BONUS.getCode()) + .remark(ramark) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + ttUserBlendErcashMapper.insert(blendErcash); + + return AjaxResult.success(); + } + + @Override + public AjaxResult userHasRechargeBonus(Integer userId) { + // 创建日期格式化对象,指定日期格式 + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + // 获取Calendar实例 + Calendar calendar = Calendar.getInstance(); + // 设置时间为今天凌晨 + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + // 获取今天凌晨时间 + Date todayMidnight = calendar.getTime(); + String formattedTodayMidnight = formatter.format(todayMidnight); + // 获取明天凌晨时间 + calendar.add(Calendar.DAY_OF_MONTH, 1); + Date tomorrowMidnight = calendar.getTime(); + String formattedTomorrowMidnight = formatter.format(tomorrowMidnight); + + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(TtUserBlendErcash::getUserId, userId) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE_RESEARCH_BONUS.getCode()) + .between(TtUserBlendErcash::getCreateTime, formattedTodayMidnight, formattedTomorrowMidnight) + ; + List dbBlendErcash = ttUserBlendErcashMapper.selectList(query); + if (CollectionUtils.isEmpty(dbBlendErcash)) { + return AjaxResult.success(new ArrayList<>()); + } + List recharges = new ArrayList<>(); + for (TtUserBlendErcash ercash : dbBlendErcash) { + if (ercash == null || StringUtils.isBlank(ercash.getRemark())) { + continue; + } + String recharge = ercash.getRemark().replace(TtAccountRecordSource.RECHARGE_RESEARCH_BONUS.getMsg(), ""); + recharges.add(recharge); + } + return AjaxResult.success(recharges); + } + + @Override + public AjaxResult userCreditsToAmount(Integer userId, BigDecimal credits) { + TtUser ttUser = getById(userId); + if (ttUser == null) { + return AjaxResult.error("当前用户不存在"); + } + if (credits == null || credits.compareTo(BigDecimal.ZERO) <= 0) { + return AjaxResult.error("积分数据不合法"); + } + // 检查积分小数点是否超过两位 + if (credits.scale() > 2) { + return AjaxResult.error("积分小数点不能超过两位"); + } + if (credits.compareTo(ttUser.getAccountCredits()) > 0) { + return AjaxResult.error("兑换积分超过账户当前积分"); + } + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_credits = account_credits - " + credits) + .setSql("account_amount = account_amount + " + credits) + ; + this.update(userUpdate); + return AjaxResult.success(); + } + + /** + * 日常金币变更,不是提货变更。 + * 根据金币变更规则,附带积分变更。 + * + * @param userId + * @param amount + * @param source + * @return + */ + @Override + public R updateUserAccount(Integer userId, BigDecimal amount, TtAccountRecordSource source) { + if (amount == null || amount.compareTo(BigDecimal.ZERO) == 0) { + return R.fail("金额为零不需要更新"); + } + + TtUser user = this.getById(userId); + if (user == null) { + return R.fail("用户不存在"); + } + + if (amount.compareTo(BigDecimal.ZERO) > 0) { + int count = userMapper.addAccountAmount(Long.valueOf(userId), amount); + if (count <= 0) { + return R.fail("更新账户金额失败"); + } + + BigDecimal newAccountAmount = user.getAccountAmount().add(amount); + BigDecimal newCredits = user.getAccountCredits().add(amount); + CompletableFuture.runAsync(() -> { + + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(userId) + + .amount(amount) + .finalAmount(newAccountAmount) + + .credits(amount) + .finalCredits(newCredits) + + .total(amount) // 收支合计 + + .type(TtAccountRecordType.INPUT.getCode()) + .source(source.getCode()) + .remark(source.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + ttUserBlendErcashMapper.insert(blendErcash); + }, threadPoolTaskExecutor); + return R.ok(new UpdateUserAccountBo(userId.longValue(), newAccountAmount, newCredits)); + } else { + int count = userMapper.subtractAccountAmount(Long.valueOf(userId), amount.abs()); + if (count <= 0) { + return R.fail("账户余额不足"); + } + BigDecimal newAccountAmount = user.getAccountAmount().subtract(amount.abs()); + CompletableFuture.runAsync(() -> { + + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(userId) + + .amount(amount) + .finalAmount(newAccountAmount) + + .total(amount) // 收支合计 + + .type(TtAccountRecordType.OUTPUT.getCode()) + .source(source.getCode()) + .remark(source.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + if (newAccountAmount.compareTo(user.getAccountCredits()) < 0) { + BigDecimal diff = user.getAccountCredits().subtract(newAccountAmount); + blendErcash.setCredits(diff.negate()); + blendErcash.setFinalCredits(newAccountAmount); + } + + ttUserBlendErcashMapper.insert(blendErcash); + + }, threadPoolTaskExecutor); + double newCredits = Math.min(user.getAccountCredits().doubleValue(), newAccountAmount.doubleValue()); + return R.ok(new UpdateUserAccountBo(userId.longValue(), newAccountAmount, BigDecimal.valueOf(newCredits))); + } + } + + @Override + public R updateOnlyUserAccount(Integer userId, BigDecimal amount, TtAccountRecordSource source) { + if (amount == null || amount.compareTo(BigDecimal.ZERO) == 0) { + return R.fail("金额为零不需要更新"); + } + + TtUser user = this.getById(userId); + if (user == null) { + return R.fail("用户不存在"); + } + + if (amount.compareTo(BigDecimal.ZERO) > 0) { + int count = userMapper.addOnlyAccountAmount(Long.valueOf(userId), amount); + if (count <= 0) { + return R.fail("更新账户金额失败"); + } + + BigDecimal newAccountAmount = user.getAccountAmount().add(amount); + CompletableFuture.runAsync(() -> { + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(userId) + + .amount(amount) + .finalAmount(newAccountAmount) + + .total(amount) // 收支合计 + + .type(TtAccountRecordType.INPUT.getCode()) + .source(source.getCode()) + .remark(source.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + ttUserBlendErcashMapper.insert(blendErcash); + }, threadPoolTaskExecutor); + return R.ok(new UpdateUserAccountBo(userId.longValue(), newAccountAmount, user.getAccountCredits())); + } else { + int count = userMapper.subtractAccountAmount(Long.valueOf(userId), amount.abs()); + if (count <= 0) { + return R.fail("账户余额不足"); + } + BigDecimal newAccountAmount = user.getAccountAmount().subtract(amount.abs()); + CompletableFuture.runAsync(() -> { + + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(userId) + + .amount(amount) + .finalAmount(newAccountAmount) + + .total(amount) // 收支合计 + + .type(TtAccountRecordType.OUTPUT.getCode()) + .source(source.getCode()) + .remark(source.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + if (newAccountAmount.compareTo(user.getAccountCredits()) < 0) { + BigDecimal diff = user.getAccountCredits().subtract(newAccountAmount); + blendErcash.setCredits(diff.negate()); + blendErcash.setFinalCredits(newAccountAmount); + } + + ttUserBlendErcashMapper.insert(blendErcash); + + }, threadPoolTaskExecutor); + double newCredits = Math.min(user.getAccountCredits().doubleValue(), newAccountAmount.doubleValue()); + return R.ok(new UpdateUserAccountBo(userId.longValue(), newAccountAmount, BigDecimal.valueOf(newCredits))); + } + } + + /** + * 日常金币变更,不是提货变更。 + * 根据金币变更规则,附带积分变更。 + * + * @param userId + * @param amount + * @param source + * @return + */ + @Override + public R updateUserCredits(Integer userId, BigDecimal amount, TtAccountRecordSource source) { + if (amount == null || amount.compareTo(BigDecimal.ZERO) == 0) { + return R.fail("金额为零不需要更新"); + } + + TtUser user = this.getById(userId); + if (user == null) { + return R.fail("用户不存在"); + } + + if (amount.compareTo(BigDecimal.ZERO) > 0) { + int count = userMapper.addAccountAmount(Long.valueOf(userId), amount); + if (count <= 0) { + return R.fail("更新账户积分失败"); + } + + BigDecimal newAccountAmount = user.getAccountAmount().add(amount); + BigDecimal newCredits = user.getAccountCredits().add(amount); + CompletableFuture.runAsync(() -> { + + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(userId) + + .amount(amount) + .finalAmount(newAccountAmount) + + .credits(amount) + .finalCredits(newCredits) + + .total(amount) // 收支合计 + + .type(TtAccountRecordType.INPUT.getCode()) + .source(source.getCode()) + .remark(source.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + ttUserBlendErcashMapper.insert(blendErcash); + }, threadPoolTaskExecutor); + return R.ok(new UpdateUserAccountBo(userId.longValue(), newAccountAmount, newCredits)); + } else { + int count = userMapper.subtractCreditsAmount(Long.valueOf(userId), amount.abs()); + if (count <= 0) { + return R.fail("账户积分不足"); + } + BigDecimal newAccountCredits = user.getAccountCredits().subtract(amount.abs()); + BigDecimal newAccountAmount = user.getAccountAmount().subtract(amount.abs()); + CompletableFuture.runAsync(() -> { + + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(userId) + + .amount(amount) + .finalAmount(newAccountAmount) + + .credits(amount) + .finalCredits(newAccountCredits) + + .total(amount) // 收支合计 + + .type(TtAccountRecordType.OUTPUT.getCode()) + .source(source.getCode()) + .remark(source.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + ttUserBlendErcashMapper.insert(blendErcash); + + }, threadPoolTaskExecutor); + double newCredits = Math.min(user.getAccountCredits().doubleValue(), newAccountAmount.doubleValue()); + return R.ok(new UpdateUserAccountBo(userId.longValue(), newAccountAmount, BigDecimal.valueOf(newCredits))); + } + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtVipLevelServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtVipLevelServiceImpl.java new file mode 100644 index 0000000..7d18754 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtVipLevelServiceImpl.java @@ -0,0 +1,49 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtVipLevel; +import com.ruoyi.admin.mapper.TtVipLevelMapper; +import com.ruoyi.admin.mapper.WebsiteSetupMapper; +import com.ruoyi.admin.service.TtVipLevelService; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.DateUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +@Service +public class TtVipLevelServiceImpl extends ServiceImpl implements TtVipLevelService { + + private final WebsiteSetupMapper websiteSetupMapper; + + public TtVipLevelServiceImpl(WebsiteSetupMapper websiteSetupMapper) { + this.websiteSetupMapper = websiteSetupMapper; + } + + @Override + public String generateVipLevel(Integer num) { + for (int i = 1; i <= num; i++) { + TtVipLevel ttVipLevel = TtVipLevel.builder().build(); + ttVipLevel.setName("VIP" + i); + ttVipLevel.setIcon(""); + ttVipLevel.setRechargeThreshold(BigDecimal.ZERO); + ttVipLevel.setCommissions(BigDecimal.ZERO); + ttVipLevel.setAddedBonus(BigDecimal.ZERO); + ttVipLevel.setCreateTime(DateUtils.getNowDate()); + this.save(ttVipLevel); + } + return ""; + } + + @Override + public String updateVipLevelById(TtVipLevel ttVipLevel) { + ttVipLevel.setIcon(RuoYiConfig.getDomainName() + ttVipLevel.getIcon()); + this.updateById(ttVipLevel); + return ""; + } + + @Override + public void truncateVipLevel() { + websiteSetupMapper.truncateTtVipLevel(); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtWelfareMonthlyRechargesServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtWelfareMonthlyRechargesServiceImpl.java new file mode 100644 index 0000000..ed3c2dd --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtWelfareMonthlyRechargesServiceImpl.java @@ -0,0 +1,93 @@ +package com.ruoyi.admin.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.admin.mapper.TtWelfareMonthlyRechargesMapper; +import com.ruoyi.domain.other.TtWelfareMonthlyRecharges; +import com.ruoyi.admin.service.ITtWelfareMonthlyRechargesService; + +/** + * 每月充值福利Service业务层处理 + * + * @author ruoyi + * @date 2024-06-24 + */ +@Service +public class TtWelfareMonthlyRechargesServiceImpl implements ITtWelfareMonthlyRechargesService +{ + @Autowired + private TtWelfareMonthlyRechargesMapper ttWelfareMonthlyRechargesMapper; + + /** + * 查询每月充值福利 + * + * @param id 每月充值福利主键 + * @return 每月充值福利 + */ + @Override + public TtWelfareMonthlyRecharges selectTtWelfareMonthlyRechargesById(Integer id) + { + return ttWelfareMonthlyRechargesMapper.selectTtWelfareMonthlyRechargesById(id); + } + + /** + * 查询每月充值福利列表 + * + * @param ttWelfareMonthlyRecharges 每月充值福利 + * @return 每月充值福利 + */ + @Override + public List selectTtWelfareMonthlyRechargesList(TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges) + { + return ttWelfareMonthlyRechargesMapper.selectTtWelfareMonthlyRechargesList(ttWelfareMonthlyRecharges); + } + + /** + * 新增每月充值福利 + * + * @param ttWelfareMonthlyRecharges 每月充值福利 + * @return 结果 + */ + @Override + public int insertTtWelfareMonthlyRecharges(TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges) + { + return ttWelfareMonthlyRechargesMapper.insertTtWelfareMonthlyRecharges(ttWelfareMonthlyRecharges); + } + + /** + * 修改每月充值福利 + * + * @param ttWelfareMonthlyRecharges 每月充值福利 + * @return 结果 + */ + @Override + public int updateTtWelfareMonthlyRecharges(TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges) + { + return ttWelfareMonthlyRechargesMapper.updateTtWelfareMonthlyRecharges(ttWelfareMonthlyRecharges); + } + + /** + * 批量删除每月充值福利 + * + * @param ids 需要删除的每月充值福利主键 + * @return 结果 + */ + @Override + public int deleteTtWelfareMonthlyRechargesByIds(Integer[] ids) + { + return ttWelfareMonthlyRechargesMapper.deleteTtWelfareMonthlyRechargesByIds(ids); + } + + /** + * 删除每月充值福利信息 + * + * @param id 每月充值福利主键 + * @return 结果 + */ + @Override + public int deleteTtWelfareMonthlyRechargesById(Integer id) + { + return ttWelfareMonthlyRechargesMapper.deleteTtWelfareMonthlyRechargesById(id); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtWelfareServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtWelfareServiceImpl.java new file mode 100644 index 0000000..fc61571 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtWelfareServiceImpl.java @@ -0,0 +1,96 @@ +package com.ruoyi.admin.service.impl; + +import java.util.List; +import com.ruoyi.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.admin.mapper.TtWelfareMapper; +import com.ruoyi.domain.other.TtWelfare; +import com.ruoyi.admin.service.TtWelfareService; + +/** + * 福利列表Service业务层处理 + * + * @author ruoyi + * @date 2024-05-11 + */ +@Service +public class TtWelfareServiceImpl implements TtWelfareService +{ + @Autowired + private TtWelfareMapper ttWelfareMapper; + + /** + * 查询福利列表 + * + * @param welfareId 福利列表主键 + * @return 福利列表 + */ + @Override + public TtWelfare selectTtWelfareByWelfareId(Integer welfareId) + { + return ttWelfareMapper.selectTtWelfareByWelfareId(welfareId); + } + + /** + * 查询福利列表列表 + * + * @param ttWelfare 福利列表 + * @return 福利列表 + */ + @Override + public List selectTtWelfareList(TtWelfare ttWelfare) + { + return ttWelfareMapper.selectTtWelfareList(ttWelfare); + } + + /** + * 新增福利列表 + * + * @param ttWelfare 福利列表 + * @return 结果 + */ + @Override + public int insertTtWelfare(TtWelfare ttWelfare) + { + ttWelfare.setCreateTime(DateUtils.getNowDate()); + return ttWelfareMapper.insertTtWelfare(ttWelfare); + } + + /** + * 修改福利列表 + * + * @param ttWelfare 福利列表 + * @return 结果 + */ + @Override + public int updateTtWelfare(TtWelfare ttWelfare) + { + ttWelfare.setUpdateTime(DateUtils.getNowDate()); + return ttWelfareMapper.updateTtWelfare(ttWelfare); + } + + /** + * 批量删除福利列表 + * + * @param welfareIds 需要删除的福利列表主键 + * @return 结果 + */ + @Override + public int deleteTtWelfareByWelfareIds(Integer[] welfareIds) + { + return ttWelfareMapper.deleteTtWelfareByWelfareIds(welfareIds); + } + + /** + * 删除福利列表信息 + * + * @param welfareId 福利列表主键 + * @return 结果 + */ + @Override + public int deleteTtWelfareByWelfareId(Integer welfareId) + { + return ttWelfareMapper.deleteTtWelfareByWelfareId(welfareId); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtYyOrnamentsServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtYyOrnamentsServiceImpl.java new file mode 100644 index 0000000..37299cd --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/TtYyOrnamentsServiceImpl.java @@ -0,0 +1,11 @@ +package com.ruoyi.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtYYOrnamentsMapper; +import com.ruoyi.admin.service.TtYyOrnamentsService; +import com.ruoyi.domain.other.TtYYOrnaments; +import org.springframework.stereotype.Service; + +@Service +public class TtYyOrnamentsServiceImpl extends ServiceImpl implements TtYyOrnamentsService { +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/WebsitePropertyServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/WebsitePropertyServiceImpl.java new file mode 100644 index 0000000..1422393 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/WebsitePropertyServiceImpl.java @@ -0,0 +1,76 @@ +package com.ruoyi.admin.service.impl; + +import cn.hutool.core.util.IdUtil; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.admin.service.WebsitePropertyService; +import com.ruoyi.domain.vo.WebsitePropertyDataVO; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@Service +public class WebsitePropertyServiceImpl implements WebsitePropertyService { + + private final TtOrnamentMapper ornamentsMapper; + + public WebsitePropertyServiceImpl(TtOrnamentMapper ornamentsMapper) { + this.ornamentsMapper = ornamentsMapper; + } + + @Override + public List list() { + return ornamentsMapper.list(); + } + + @Override + public WebsitePropertyDataVO getById(Integer id) { + TtOrnament ornaments = new LambdaQueryChainWrapper<>(ornamentsMapper) + .eq(TtOrnament::getId, id) + // .eq(TtOrnament::getIsProprietaryProperty, "0") + .one(); + WebsitePropertyDataVO websitePropertyDataVO = WebsitePropertyDataVO.builder().build(); + BeanUtils.copyBeanProp(websitePropertyDataVO, ornaments); + return websitePropertyDataVO; + } + + @Override + public String save(WebsitePropertyDataVO websitePropertyDataVO) { + if (StringUtils.isEmpty(websitePropertyDataVO.getImageUrl())) websitePropertyDataVO.setImageUrl(""); + else websitePropertyDataVO.setImageUrl(RuoYiConfig.getDomainName() + websitePropertyDataVO.getImageUrl()); + websitePropertyDataVO.setCreateTime(DateUtils.getNowDate()); + websitePropertyDataVO.setIsPutaway("1"); + websitePropertyDataVO.setIsProprietaryProperty("0"); + TtOrnament ttOrnament = TtOrnament.builder().build(); + BeanUtils.copyBeanProp(ttOrnament, websitePropertyDataVO); + ttOrnament.setMarketHashName(UUID.randomUUID().toString()); + ttOrnament.setId(IdUtil.getSnowflake().nextId() % 1000000000L); + ttOrnament.setShortName(websitePropertyDataVO.getName()); + ornamentsMapper.insert(ttOrnament); + return ""; + } + + @Override + public String updateWebsitePropertyById(WebsitePropertyDataVO websitePropertyDataVO) { + websitePropertyDataVO.setUpdateTime(DateUtils.getNowDate()); + String imageUrl = websitePropertyDataVO.getImageUrl(); + websitePropertyDataVO.setImageUrl(RuoYiConfig.getDomainName() + imageUrl); + // TtOrnament ttOrnament = TtOrnament.builder().build(); + // BeanUtils.copyBeanProp(ttOrnament, websitePropertyDataVO); + // ornamentsMapper.updateById(ttOrnament); + ornamentsMapper.updateWebsiteProperty(websitePropertyDataVO); + return ""; + } + + @Override + public int deleteWebsitePropertyByIds(Integer[] ids) { + return ornamentsMapper.deleteWebsitePropertyByIds(ids); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/WebsiteSetupServiceImpl.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/WebsiteSetupServiceImpl.java new file mode 100644 index 0000000..3c20d7f --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/service/impl/WebsiteSetupServiceImpl.java @@ -0,0 +1,137 @@ +package com.ruoyi.admin.service.impl; + +import com.ruoyi.domain.other.ConfigData; +import com.ruoyi.admin.mapper.WebsiteSetupMapper; +import com.ruoyi.admin.service.ConfigService; +import com.ruoyi.admin.service.WebsiteSetupService; +import com.ruoyi.domain.other.OperationalStatistics; +import com.ruoyi.domain.other.ParameterSettingBody; +import com.ruoyi.system.service.ISysConfigService; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +public class WebsiteSetupServiceImpl implements WebsiteSetupService { + + private final ConfigService configService; + private final ISysConfigService iSysConfigService; + private final WebsiteSetupMapper websiteSetupMapper; + + public WebsiteSetupServiceImpl(ConfigService configService, + ISysConfigService iSysConfigService, + WebsiteSetupMapper websiteSetupMapper) { + this.configService = configService; + this.iSysConfigService = iSysConfigService; + this.websiteSetupMapper = websiteSetupMapper; + } + + @Override + public List getOperationalStatistics() { + return websiteSetupMapper.getOperationalStatistics(); + } + + @Override + public ParameterSettingBody getParameterSetting() { + List parameterSettingList = websiteSetupMapper.selectParameterSettingList(); + Map> parameterSettingMap = parameterSettingList.stream() + .collect(Collectors.groupingBy(ConfigData::getConfigKey)); + ParameterSettingBody parameterSetting = ParameterSettingBody.builder().build(); + parameterSetting.setUsePricePremiumRate(parameterSettingMap.get("usePricePremiumRate").get(0).getConfigValue()); + parameterSetting.setExchangePriceRatio(parameterSettingMap.get("exchangePriceRatio").get(0).getConfigValue()); + parameterSetting.setRegisterRedPacket(parameterSettingMap.get("registerRedPacket").get(0).getConfigValue()); + parameterSetting.setMaxCompoundNum(parameterSettingMap.get("maxCompoundNum").get(0).getConfigValue()); + parameterSetting.setCompoundMinPrice(parameterSettingMap.get("compoundMinPrice").get(0).getConfigValue()); + parameterSetting.setCompoundMinPremiumRate(parameterSettingMap.get("compoundMinPremiumRate").get(0).getConfigValue()); + parameterSetting.setCompoundMaxPremiumRate(parameterSettingMap.get("compoundMaxPremiumRate").get(0).getConfigValue()); + parameterSetting.setZBTParities(parameterSettingMap.get("ZBTParities").get(0).getConfigValue()); + parameterSetting.setAutoDeliveryMinPrice(parameterSettingMap.get("autoDeliveryMinPrice").get(0).getConfigValue()); + parameterSetting.setBuyPricePremiumRate(parameterSettingMap.get("buyPricePremiumRate").get(0).getConfigValue()); + parameterSetting.setWebsiteMaintenance(parameterSettingMap.get("websiteMaintenance").get(0).getConfigValue()); + parameterSetting.setShoppingMaintenance(parameterSettingMap.get("shoppingMaintenance").get(0).getConfigValue()); + parameterSetting.setFightMaintenance(parameterSettingMap.get("fightMaintenance").get(0).getConfigValue()); + parameterSetting.setBindBoxMaintenance(parameterSettingMap.get("bindBoxMaintenance").get(0).getConfigValue()); + parameterSetting.setRollMaintenance(parameterSettingMap.get("rollMaintenance").get(0).getConfigValue()); + parameterSetting.setCompoundMaintenance(parameterSettingMap.get("compoundMaintenance").get(0).getConfigValue()); + return parameterSetting; + } + + @Override + public String updateParameterSetting(ParameterSettingBody parameterSettingBody) { + String usePricePremiumRate = parameterSettingBody.getUsePricePremiumRate(), + exchangePriceRatio = parameterSettingBody.getExchangePriceRatio(), + registerRedPacket = parameterSettingBody.getRegisterRedPacket(), + maxCompoundNum = parameterSettingBody.getMaxCompoundNum(), + compoundMinPrice = parameterSettingBody.getCompoundMinPrice(), + compoundMinPremiumRate = parameterSettingBody.getCompoundMinPremiumRate(), + compoundMaxPremiumRate = parameterSettingBody.getCompoundMaxPremiumRate(), + zbtParities = parameterSettingBody.getZBTParities(), + autoDeliveryMinPrice = parameterSettingBody.getAutoDeliveryMinPrice(), + buyPricePremiumRate = parameterSettingBody.getBuyPricePremiumRate(), + websiteMaintenance = parameterSettingBody.getWebsiteMaintenance(), + shoppingMaintenance = parameterSettingBody.getShoppingMaintenance(), + fightMaintenance = parameterSettingBody.getFightMaintenance(), + bindBoxMaintenance = parameterSettingBody.getBindBoxMaintenance(), + rollMaintenance = parameterSettingBody.getRollMaintenance(), + compoundMaintenance = parameterSettingBody.getCompoundMaintenance(); + List parameterSettingList = websiteSetupMapper.selectParameterSettingList(); + Map> parameterSettingMap = parameterSettingList.stream().collect(Collectors.groupingBy(ConfigData::getConfigKey)); + List updateList = new ArrayList<>(); + ConfigData usePricePremiumRateSysConfig = parameterSettingMap.get("usePricePremiumRate").get(0); + usePricePremiumRateSysConfig.setConfigValue(usePricePremiumRate); + updateList.add(usePricePremiumRateSysConfig); + ConfigData exchangePriceRatioSysConfig = parameterSettingMap.get("exchangePriceRatio").get(0); + exchangePriceRatioSysConfig.setConfigValue(exchangePriceRatio); + updateList.add(exchangePriceRatioSysConfig); + ConfigData registerRedPacketSysConfig = parameterSettingMap.get("registerRedPacket").get(0); + registerRedPacketSysConfig.setConfigValue(registerRedPacket); + updateList.add(registerRedPacketSysConfig); + ConfigData maxCompoundNumSysConfig = parameterSettingMap.get("maxCompoundNum").get(0); + maxCompoundNumSysConfig.setConfigValue(maxCompoundNum); + updateList.add(maxCompoundNumSysConfig); + ConfigData compoundMinPriceSysConfig = parameterSettingMap.get("compoundMinPrice").get(0); + compoundMinPriceSysConfig.setConfigValue(compoundMinPrice); + updateList.add(compoundMinPriceSysConfig); + ConfigData compoundMinPremiumRateSysConfig = parameterSettingMap.get("compoundMinPremiumRate").get(0); + compoundMinPremiumRateSysConfig.setConfigValue(compoundMinPremiumRate); + updateList.add(compoundMinPremiumRateSysConfig); + ConfigData compoundMaxPremiumRateSysConfig = parameterSettingMap.get("compoundMaxPremiumRate").get(0); + compoundMaxPremiumRateSysConfig.setConfigValue(compoundMaxPremiumRate); + updateList.add(compoundMaxPremiumRateSysConfig); + ConfigData zbtParitiesSysConfig = parameterSettingMap.get("ZBTParities").get(0); + zbtParitiesSysConfig.setConfigValue(zbtParities); + updateList.add(zbtParitiesSysConfig); + ConfigData buyPricePremiumRateSysConfig = parameterSettingMap.get("buyPricePremiumRate").get(0); + buyPricePremiumRateSysConfig.setConfigValue(buyPricePremiumRate); + updateList.add(buyPricePremiumRateSysConfig); + ConfigData autoDeliveryMinPriceSysConfig = parameterSettingMap.get("autoDeliveryMinPrice").get(0); + autoDeliveryMinPriceSysConfig.setConfigValue(autoDeliveryMinPrice); + updateList.add(autoDeliveryMinPriceSysConfig); + ConfigData websiteMaintenanceSysConfig = parameterSettingMap.get("websiteMaintenance").get(0); + websiteMaintenanceSysConfig.setConfigValue(websiteMaintenance); + updateList.add(websiteMaintenanceSysConfig); + ConfigData shoppingMaintenanceSysConfig = parameterSettingMap.get("shoppingMaintenance").get(0); + shoppingMaintenanceSysConfig.setConfigValue(shoppingMaintenance); + updateList.add(shoppingMaintenanceSysConfig); + ConfigData fightMaintenanceSysConfig = parameterSettingMap.get("fightMaintenance").get(0); + fightMaintenanceSysConfig.setConfigValue(fightMaintenance); + updateList.add(fightMaintenanceSysConfig); + ConfigData bindBoxMaintenanceSysConfig = parameterSettingMap.get("bindBoxMaintenance").get(0); + bindBoxMaintenanceSysConfig.setConfigValue(bindBoxMaintenance); + updateList.add(bindBoxMaintenanceSysConfig); + ConfigData rollMaintenanceSysConfig = parameterSettingMap.get("rollMaintenance").get(0); + rollMaintenanceSysConfig.setConfigValue(rollMaintenance); + updateList.add(rollMaintenanceSysConfig); + ConfigData compoundMaintenanceSysConfig = parameterSettingMap.get("compoundMaintenance").get(0); + compoundMaintenanceSysConfig.setConfigValue(compoundMaintenance); + updateList.add(compoundMaintenanceSysConfig); + for (ConfigData configData : updateList) { + configService.updateById(configData); + } + iSysConfigService.resetConfigCache(); + return ""; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/task/TimeRollTask.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/task/TimeRollTask.java new file mode 100644 index 0000000..3c91bff --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/task/TimeRollTask.java @@ -0,0 +1,181 @@ +package com.ruoyi.admin.task; + +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.admin.mapper.TtRechargeRecordMapper; +import com.ruoyi.admin.mapper.TtRollJackpotOrnamentsMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtTimeRollService; +import com.ruoyi.admin.service.TtTimeRollUserService; +import com.ruoyi.admin.util.RandomUtils; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.entity.roll.TtRollJackpotOrnaments; +import com.ruoyi.domain.entity.roll.TtTimeRoll; +import com.ruoyi.domain.entity.roll.TtTimeRollUser; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.TtboxRecordSource.ROLL; + +@Slf4j +@Component("TimeRollTask") +public class TimeRollTask { + + private final TtOrnamentMapper ornamentsMapper; + private final TtBoxRecordsService boxRecordsService; + private final TtTimeRollService timeRollService; + private final TtTimeRollUserService timeRollUserService; + private final TtRechargeRecordMapper rechargeRecordMapper; + private final TtRollJackpotOrnamentsMapper rollJackpotOrnamentsMapper; + + public TimeRollTask(TtOrnamentMapper ornamentsMapper, + TtBoxRecordsService boxRecordsService, + TtTimeRollService timeRollService, + TtTimeRollUserService timeRollUserService, + TtRechargeRecordMapper rechargeRecordMapper, + TtRollJackpotOrnamentsMapper rollJackpotOrnamentsMapper) { + this.ornamentsMapper = ornamentsMapper; + this.boxRecordsService = boxRecordsService; + this.timeRollService = timeRollService; + this.timeRollUserService = timeRollUserService; + this.rechargeRecordMapper = rechargeRecordMapper; + this.rollJackpotOrnamentsMapper = rollJackpotOrnamentsMapper; + } + + public void timeRollSettle(Integer id){ + TtTimeRoll timeRoll = timeRollService.getById(id); + String rechargeCondition = timeRoll.getRechargeCondition(); + BigDecimal minRecharge = timeRoll.getMinRecharge(); + List userIds = null; + if ("0".equals(rechargeCondition)) { + userIds = rechargeRecordMapper.getLastHourRechargeUserIds(minRecharge); + } else if ("1".equals(rechargeCondition)) { + userIds = rechargeRecordMapper.getLastDayRechargeUserIds(minRecharge); + } else if ("2".equals(rechargeCondition)) { + userIds = rechargeRecordMapper.getLastWeekRechargeUserIds(minRecharge); + } else if ("3".equals(rechargeCondition)) { + userIds = rechargeRecordMapper.getLastMonthRechargeUserIds(minRecharge); + } + if (StringUtils.isNull(userIds) || userIds.isEmpty()) return; + List allocatedTimeRollUsers = new LambdaQueryChainWrapper<>(timeRollUserService.getBaseMapper()) + .eq(TtTimeRollUser::getTimeRollId, id).eq(TtTimeRollUser::getStatus, "2").eq(TtTimeRollUser::getEndStatus, "0").list(); + List assignedUserIds = allocatedTimeRollUsers.stream().map(TtTimeRollUser::getUserId) + .collect(Collectors.toList()); + userIds.removeAll(assignedUserIds); + if (userIds.isEmpty()) return; + List timeRollUserList = new ArrayList<>(); + for (Integer userId : userIds) { + TtTimeRollUser timeRollUser = TtTimeRollUser.builder().build(); + timeRollUser.setTimeRollId(id); + timeRollUser.setUserId(userId); + timeRollUser.setJoinTime(DateUtils.getNowDate()); + timeRollUserList.add(timeRollUser); + } + if (timeRollUserService.saveBatch(timeRollUserList, 1)) { + List surplusOrnamentsList = getSurplusOrnamentsList(id, timeRoll.getJackpotId(), allocatedTimeRollUsers); + if (!surplusOrnamentsList.isEmpty()) { + List timeRollUsers = new LambdaQueryChainWrapper<>(timeRollUserService.getBaseMapper()) + .eq(TtTimeRollUser::getTimeRollId, id).eq(TtTimeRollUser::getStatus, "0").eq(TtTimeRollUser::getEndStatus, "0").list(); + Collections.shuffle(timeRollUsers); + int index = 0; + Map map = new HashMap<>(); + if (surplusOrnamentsList.size() > timeRollUsers.size()) { + for (TtTimeRollUser ttTimeRollUser : timeRollUsers) { + Long ornamentsId = surplusOrnamentsList.get(index); + TtRollJackpotOrnaments rollJackpotOrnaments = getRollJackpotOrnaments(ornamentsId, map, timeRoll.getJackpotId()); + TtBoxRecords boxRecords = addBoxRecordsData(id, ornamentsId, ttTimeRollUser.getUserId(), rollJackpotOrnaments); + boxRecordsService.save(boxRecords); + ttTimeRollUser.setJackpotOrnamentsId(rollJackpotOrnaments.getId()); + ttTimeRollUser.setOrnamentsId(ornamentsId); + ttTimeRollUser.setBoxRecordId(boxRecords.getId()); + ttTimeRollUser.setStatus("1"); + ttTimeRollUser.setDesignatedBy("系统随机分配"); + ttTimeRollUser.setUpdateTime(DateUtils.getNowDate()); + ttTimeRollUser.setEndStatus("1"); + index++; + } + } else { + for (Long ornamentsId : surplusOrnamentsList) { + TtRollJackpotOrnaments rollJackpotOrnaments = getRollJackpotOrnaments(ornamentsId, map, timeRoll.getJackpotId()); + TtBoxRecords boxRecords = addBoxRecordsData(id, ornamentsId, timeRollUsers.get(index).getUserId(), rollJackpotOrnaments); + boxRecordsService.save(boxRecords); + timeRollUsers.get(index).setJackpotOrnamentsId(rollJackpotOrnaments.getId()); + timeRollUsers.get(index).setOrnamentsId(ornamentsId); + timeRollUsers.get(index).setBoxRecordId(boxRecords.getId()); + timeRollUsers.get(index).setStatus("1"); + timeRollUsers.get(index).setDesignatedBy("系统随机分配"); + timeRollUsers.get(index).setUpdateTime(DateUtils.getNowDate()); + timeRollUsers.get(index).setEndStatus("1"); + index++; + } + } + timeRollUserService.updateBatchById(timeRollUsers, 1); + } + List boxRecordsList = new LambdaQueryChainWrapper<>(boxRecordsService.getBaseMapper()) + .eq(TtBoxRecords::getRollId, id) + .eq(TtBoxRecords::getStatus, "7") + .eq(TtBoxRecords::getSource, "7") + .list(); + boxRecordsList = boxRecordsList.stream().peek(ttBoxRecords -> { + ttBoxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode()); + ttBoxRecords.setUpdateTime(DateUtils.getNowDate()); + }).collect(Collectors.toList()); + boxRecordsService.updateBatchById(boxRecordsList, 1); + } + } + + private TtBoxRecords addBoxRecordsData(Integer timeRollId, Long ornamentsId, Integer userId, TtRollJackpotOrnaments rollJackpotOrnaments) { + TtOrnament ttOrnament = new LambdaQueryChainWrapper<>(ornamentsMapper).eq(TtOrnament::getId, ornamentsId).one(); + TtBoxRecords boxRecords = TtBoxRecords.builder().build(); + boxRecords.setUserId(userId); + boxRecords.setOrnamentId(ornamentsId); + boxRecords.setOrnamentsPrice(ttOrnament.getUsePrice()); + boxRecords.setOrnamentsLevelId(rollJackpotOrnaments.getOrnamentLevelId()); + boxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode()); + boxRecords.setCreateTime(DateUtils.getNowDate()); + boxRecords.setSource(ROLL.getCode()); + boxRecords.setRollId(timeRollId); + boxRecords.setHolderUserId(userId); + return boxRecords; + } + + private TtRollJackpotOrnaments getRollJackpotOrnaments(Long ornamentsId, Map map, Integer jackpotId) { + TtRollJackpotOrnaments ttRollJackpotOrnaments = map.get(ornamentsId); + if (ttRollJackpotOrnaments == null) { + ttRollJackpotOrnaments = new LambdaQueryChainWrapper<>(rollJackpotOrnamentsMapper).eq(TtRollJackpotOrnaments::getJackpotId, jackpotId) + .eq(TtRollJackpotOrnaments::getOrnamentsId, ornamentsId).one(); + map.put(ornamentsId, ttRollJackpotOrnaments); + } + return ttRollJackpotOrnaments; + } + + private List getSurplusOrnamentsList(Integer id, Integer jackpotId, List allocatedTimeRollUsers) { + List ornamentsList = + new LambdaQueryChainWrapper<>(rollJackpotOrnamentsMapper).eq(TtRollJackpotOrnaments::getJackpotId, jackpotId).list(); + Map data = new HashMap<>(); + for (TtRollJackpotOrnaments ornaments : ornamentsList) { + data.put(ornaments.getOrnamentsId(), ornaments.getOrnamentsNum()); + } + List surplusOrnamentsList = RandomUtils.toList(data); + List allocated = allocatedTimeRollUsers.stream().map(TtTimeRollUser::getOrnamentsId).collect(Collectors.toList()); + for (Long i : allocated) { + Iterator iterator = surplusOrnamentsList.iterator(); + while (iterator.hasNext()) { + Long next = iterator.next(); + if (Objects.equals(next, i)) { + iterator.remove(); + break; + } + } + } + return surplusOrnamentsList; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/task/TtExponentGenerateTask.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/task/TtExponentGenerateTask.java new file mode 100644 index 0000000..f3d33e5 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/task/TtExponentGenerateTask.java @@ -0,0 +1,99 @@ +package com.ruoyi.admin.task; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.mapper.TtExponentMapper; +import com.ruoyi.domain.entity.exponent.TtExponent; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Random; + +@Slf4j +@Component("TtExponentGenerateTask") +public class TtExponentGenerateTask { + + private final static int EXPONENT_BASE = 30050000; + + @Autowired + private TtExponentMapper ttExponentMapper; + + @Scheduled(cron = "*/30 * * * * ?") + public void generateExponent() { + // 创建日期格式化对象,指定日期格式 + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + // 获取Calendar实例 + Calendar calendar = Calendar.getInstance(); + // 设置时间为今天凌晨 + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + // 获取今天凌晨时间 + Date todayMidnight = calendar.getTime(); + String formattedTodayMidnight = formatter.format(todayMidnight); + // 获取明天凌晨时间 + calendar.add(Calendar.DAY_OF_MONTH, 1); + Date tomorrowMidnight = calendar.getTime(); + String formattedTomorrowMidnight = formatter.format(tomorrowMidnight); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.between(TtExponent::getCreateTime, formattedTodayMidnight, formattedTomorrowMidnight); + wrapper.orderByDesc(TtExponent::getId); + wrapper.last("limit 1"); + TtExponent db = ttExponentMapper.selectOne(wrapper); + TtExponent exponent = new TtExponent(); + if (db == null) { + int value = 0; + StringBuilder points = new StringBuilder(); + points.append(EXPONENT_BASE).append(","); + int exponentValue = 0; + for (int i = 0; i < 10; i++) { + value = generate(); + exponentValue = EXPONENT_BASE + value; + points.append(exponentValue).append(","); + } + exponent.setExponent(exponentValue); + exponent.setPoints(points.substring(0, points.length() - 1)); + exponent.setForward(value > 0 ? 1 : 2); + exponent.setNumber(1); + exponent.setCreateTime(new Date()); + } else { + StringBuilder points = new StringBuilder(); + points.append(db.getExponent()).append(","); + int value = 0; + int exponentValue = 0; + for (int i = 0; i < 10; i++) { + value = generate(); + exponentValue = db.getExponent() + value; + points.append(exponentValue).append(","); + } + exponent.setExponent(exponentValue); + exponent.setPoints(points.substring(0, points.length() - 1)); + exponent.setForward(value > 0 ? 1 : 2); + exponent.setNumber(db.getNumber() + 1); + exponent.setCreateTime(new Date()); + } + ttExponentMapper.insert(exponent); +// log.info("更新数据完成"); + } + + + /** + * 随机生成数据 + */ + private int generate() { + Random random = new Random(); + // 生成100到1000之间的随机数 + int randomNumber = 100 + random.nextInt(901); // nextInt(901)生成0到900之间的数,加上100后就是100到1000 + // 随机决定是正数还是负数 + if (random.nextBoolean()) { // 有50%的概率是true,50%的概率是false + randomNumber = -randomNumber; + } + return randomNumber; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/OperatorEnum.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/OperatorEnum.java new file mode 100644 index 0000000..007713e --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/OperatorEnum.java @@ -0,0 +1,27 @@ +package com.ruoyi.admin.util; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum OperatorEnum { + + CHINA_MOBILE(0, "中国移动"), + CHINA_UNICOM(1, "中国联通"), + CHINA_TELECOM(2, "中国电信"); + + private final Integer code; + + private final String name; + + public static String getOperateNameByCode(Integer code) { + for (OperatorEnum value : OperatorEnum.values()) { + if (value.code.equals(code)) { + return value.name; + } + } + return null; + } +} + diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/PhoneNumberUtil.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/PhoneNumberUtil.java new file mode 100644 index 0000000..2814a18 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/PhoneNumberUtil.java @@ -0,0 +1,43 @@ +package com.ruoyi.admin.util; + +import java.util.Random; + +public class PhoneNumberUtil { + + private static final String[] CHINA_MOBILE = { + "134", "135", "136", "137", "138", "139", "150", "151", "152", "157", "158", "159", + "182", "183", "184", "187", "188", "178", "147", "172", "198" + }; + + private static final String[] CHINA_UNICOM = {"130", "131", "132", "145", "155", "156", "166", "171", "175", "176", "185", "186", "166"}; + + private static final String[] CHINA_TELECOM = {"133", "149", "153", "173", "177", "180", "181", "189", "199"}; + + public static String createPhoneNumber(int operator) { + Random random = new Random(); + StringBuilder builder = new StringBuilder(); + String mobilePrefix = null; + switch (operator) { + case 0: + mobilePrefix = CHINA_MOBILE[random.nextInt(CHINA_MOBILE.length)]; + break; + case 1: + mobilePrefix = CHINA_UNICOM[random.nextInt(CHINA_UNICOM.length)]; + break; + case 2: + mobilePrefix = CHINA_TELECOM[random.nextInt(CHINA_TELECOM.length)]; + break; + default: + mobilePrefix = "运营商错误"; + break; + } + + builder.append(mobilePrefix); + int temp; + for (int i = 0; i < 8; i++) { + temp = random.nextInt(10); + builder.append(temp); + } + return builder.toString(); + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/RandomUtils.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/RandomUtils.java new file mode 100644 index 0000000..a39fb92 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/RandomUtils.java @@ -0,0 +1,128 @@ +package com.ruoyi.admin.util; + +import cn.hutool.core.util.RandomUtil; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Random; + +public class RandomUtils { + + private static final Logger log = LoggerFactory.getLogger(RandomUtils.class); + + private static final int SURNAME_PROBABILITY = 5; + private static final String FAMILY_ONE_NAME = "赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻水云苏潘葛奚范彭郎鲁韦昌马苗凤花方俞任" + + "袁柳鲍史唐费岑薛雷贺倪汤滕殷罗毕郝邬安常乐于时傅卞齐康伍余元卜顾孟平黄和穆萧尹姚邵湛汪祁毛禹狄米贝明臧计成戴宋茅庞熊纪舒屈项祝董粱杜阮" + + "席季麻强贾路娄危江童颜郭梅盛林刁钟徐邱骆高夏蔡田胡凌霍万柯卢莫房缪干解应宗丁宣邓郁单杭洪包诸左石崔吉龚程邢滑裴陆荣翁荀羊甄家封芮储靳邴" + + "松井富乌焦巴弓牧隗山谷车侯伊宁仇祖武符刘景詹束龙叶幸司韶黎乔苍双闻莘劳逄姬冉宰桂牛寿通边燕冀尚农温庄晏瞿茹习鱼容向古戈终居衡步都耿满弘" + + "国文东殴沃曾关红游盖益桓公晋楚闫"; + + private static final String FAMILY_TWO_NAME = "欧阳太史端木上官司马东方独孤南宫万俟闻人夏侯诸葛尉迟公羊赫连澹台皇甫宗政濮阳公冶太叔申屠公孙慕容仲孙钟离长孙宇" + + "文司徒鲜于司空闾丘子车亓官司寇巫马公西颛孙壤驷公良漆雕乐正宰父谷梁拓跋夹谷轩辕令狐段干百里呼延东郭南门羊舌微生公户公玉公仪梁丘公仲公上" + + "公门公山公坚左丘公伯西门公祖第五公乘贯丘公皙南荣东里东宫仲长子书子桑即墨达奚褚师吴铭"; + + public static String getRandomName(int sex) { + String girlName = "秀娟英华慧巧美娜静淑惠珠翠雅芝玉萍红娥玲芬芳燕彩春菊兰凤洁梅琳素云莲真环雪荣爱妹霞香月莺媛艳瑞凡佳嘉琼勤珍贞莉桂娣叶璧" + + "璐娅琦晶妍茜秋珊莎锦黛青倩婷姣婉娴瑾颖露瑶怡婵雁蓓纨仪荷丹蓉眉君琴蕊薇菁梦岚苑婕馨瑗琰韵融园艺咏卿聪澜纯毓悦昭冰爽琬茗羽希宁欣飘育滢馥" + + "筠柔竹霭凝晓欢霄枫芸菲寒伊亚宜可姬舒影荔枝思丽"; + String boyName = "伟刚勇毅俊峰强军平保东文辉力明永健世广志义兴良海山仁波宁贵福生龙元全国胜学祥才发武新利清飞彬富顺信子杰涛昌成康星光天达" + + "安岩中茂进林有坚和彪博诚先敬震振壮会思群豪心邦承乐绍功松善厚庆磊民友裕河哲江超浩亮政谦亨奇固之轮翰朗伯宏言若鸣朋斌梁栋维启克伦翔旭鹏泽" + + "晨辰士以建家致树炎德行时泰盛雄琛钧冠策腾楠榕风航弘"; + return sex == 1 ? getRandomName(boyName) : getRandomName(girlName); + } + + private static String getRandomName(String name) { + int bodNameIndexOne = randomInt(name.length()); + int bodNameIndexTwo = randomInt(name.length()); + if (randomInt(100) > SURNAME_PROBABILITY) { + int familyOneNameIndex = randomInt(FAMILY_ONE_NAME.length()); + return FAMILY_ONE_NAME.charAt(familyOneNameIndex) + + name.substring(bodNameIndexOne, bodNameIndexOne + 1) + + name.charAt(bodNameIndexTwo); + } else { + int familyTwoNameIndex = randomInt(FAMILY_TWO_NAME.length()); + familyTwoNameIndex = familyTwoNameIndex % 2 == 0 ? familyTwoNameIndex : familyTwoNameIndex - 1; + return FAMILY_TWO_NAME.substring(familyTwoNameIndex, familyTwoNameIndex + 2) + + name.charAt(bodNameIndexOne) + + name.charAt(bodNameIndexTwo); + } + } + + public static int randomInt(int maxNum) { + Random random = new Random(); + return random.nextInt(maxNum); + } + + public static List toList(Map data) { + List resultList = new ArrayList<>(); + try { + for (Map.Entry entry : data.entrySet()) { + Long key = entry.getKey(); + Integer value = entry.getValue(); + for (int i = 0; i < value; i++) { + resultList.add(key); + } + } + Collections.shuffle(resultList); + } catch (Exception e) { + log.error("调用RandomUtils.toList()方法时出现异常,请检查代码!"); + } + return resultList; + } + + public static int getRandomIndex(int maxValue) { + Random random = new Random(); + return random.nextInt(maxValue); + } + + public static BigDecimal getRandomPrice(String priceSectionJSONStr) { + List priceSection = JSONObject.parseObject(priceSectionJSONStr, new TypeReference>() { + }); + if (priceSection.get(0).compareTo(priceSection.get(1)) == 0) { + return priceSection.get(0); + } else if (priceSection.get(0).compareTo(priceSection.get(1)) > 0) { + return RandomUtil.randomBigDecimal(priceSection.get(1), priceSection.get(0)); + } else { + return RandomUtil.randomBigDecimal(priceSection.get(0), priceSection.get(1)); + } + } + + /** + * 获取两个数之间的随机数 + * + * @param beginB + * @param endB + * @return + */ + public static BigDecimal getRandomaBigdecimal(BigDecimal beginB, BigDecimal endB) { + double begin = beginB.doubleValue(); + double end = endB.doubleValue(); + Double rtn = begin + (Double) (Math.random() * (end - begin)); + if (rtn.equals(begin) || rtn.equals(end)) { + return new BigDecimal(getRandomaDouble(begin, end)).setScale(2, BigDecimal.ROUND_HALF_DOWN); + } + return new BigDecimal(rtn).setScale(2, BigDecimal.ROUND_HALF_DOWN); + } + + /** + * 获取两个数之间的随机数 + * + * @param begin + * @param end + * @return + */ + public static double getRandomaDouble(Double begin, Double end) { + Double rtn = begin + (Double) (Math.random() * (end - begin)); + if (rtn.equals(begin) || rtn.equals(end)) { + return getRandomaDouble(begin, end); + } + return rtn; + } +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/core/fight/LotteryMachine.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/core/fight/LotteryMachine.java new file mode 100644 index 0000000..90a5555 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/core/fight/LotteryMachine.java @@ -0,0 +1,614 @@ +package com.ruoyi.admin.util.core.fight; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.cahe.BoxMockCacheRepository; +import com.ruoyi.admin.enums.BoxPoolType; +import com.ruoyi.admin.mapper.TtBoxOrnamentsMapper; +import com.ruoyi.admin.mapper.TtBoxThirdExplosiveUserMapper; +import com.ruoyi.admin.mapper.TtCompRecordMapper; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.admin.model.BoxMockCache; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.redis.config.RedisLock; +import com.ruoyi.common.utils.RandomUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.entity.box.TtBoxThirdExplosiveUser; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtBoxOrnaments; +import com.ruoyi.domain.other.TtCompRecord; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; + +import java.math.BigDecimal; +import java.util.*; +import java.util.concurrent.TimeUnit; + +import static com.ruoyi.admin.config.RedisConstants.BASE_POOL_KEY; + +// 抽奖机 +@Slf4j +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Data +public class LotteryMachine { + + + // 抽奖锁 + private static final String LOTTERY_LOCK = "lotteryLock_"; + + // 从mysql加载抽奖空间锁 + private static final String PRIZE_POOL_LOAD_LOCK = "prize_pool_load_lock"; + + /** + * 奖池信息预热key + */ + private static final String All_PRIZE_POOL_KEY = "all_prize_pool:"; + + private RedisLock redisLock; + + private RedisCache redisCache; + + private TtBoxOrnamentsMapper boxOrnamentsMapper; + + // 奖池(实时的) 空的时候加锁同步更新奖池 + // 奖池key:PrizePool + public Map prizePools; + + @Autowired + private TtCompRecordMapper ttCompRecordMapper; + + @Autowired + private TtBoxThirdExplosiveUserMapper ttBoxThirdExplosiveUserMapper; + + @Autowired + private BoxMockCacheRepository boxMockCacheRepository; + + @Autowired + private TtOrnamentMapper ttOrnamentMapper; + + // 预热抽奖机,初始化所有奖池 + // TODO: 2024/3/22 缓存预热的数据要在数据更新时自动更新 + public void preheat() { + + try { + LambdaQueryWrapper boxOrnamentsQuery = new LambdaQueryWrapper<>(); + boxOrnamentsQuery.orderByDesc(TtBoxOrnaments::getBoxId); + List all = boxOrnamentsMapper.selectList(boxOrnamentsQuery); + + Integer boxId = -1; + PrizePool anchorPrizePool = null; + PrizePool realPrizePool = null; + PrizePool robotPrizePool = null; // 新增机器人用户奖池 + PrizePool compensationPrizePool = null; // 新增补偿奖池 + PrizePool thirdExplosivePrizePool = null; // 新增三级爆率奖池 + for (TtBoxOrnaments item : all) { + + if (!item.getBoxId().equals(boxId)) { + + // 新箱子 + boxId = item.getBoxId(); + + // 把上个箱子的数据保存 + if (ObjectUtil.isNotEmpty(anchorPrizePool) + && ObjectUtil.isNotEmpty(realPrizePool) + && ObjectUtil.isNotEmpty(compensationPrizePool) + && ObjectUtil.isNotEmpty(thirdExplosivePrizePool) + && ObjectUtil.isNotEmpty(robotPrizePool)) { + // 奖池写入内存 + String anchorKey = anchorPrizePool.getKey(); + String realKey = realPrizePool.getKey(); + String robotKey = robotPrizePool.getKey(); // 新增机器人用户奖池Key + String compensationKey = compensationPrizePool.getKey(); // 新增补偿奖池Key + String thirdExplosiveKey = thirdExplosivePrizePool.getKey(); // 新增三级爆率 + this.prizePools.put(anchorKey, anchorPrizePool); + this.prizePools.put(robotKey, robotPrizePool); // 写入机器人用户奖池 + this.prizePools.put(realKey, realPrizePool); + this.prizePools.put(compensationKey, compensationPrizePool); // 写入补偿奖池 + this.prizePools.put(thirdExplosiveKey, thirdExplosivePrizePool); + + // 奖池写入redis(先删再存) + redisCache.deleteObject(anchorKey); + redisCache.deleteObject(robotKey); // 删除机器人用户奖池Key + redisCache.deleteObject(realKey); + redisCache.deleteObject(compensationKey); // 删除补偿奖池Key + redisCache.deleteObject(thirdExplosiveKey); // 删除三级爆率Key + + redisCache.setCacheMap(anchorKey, anchorPrizePool.getBoxSpace(), 600, TimeUnit.SECONDS); + redisCache.setCacheMap(robotKey, robotPrizePool.getBoxSpace(), 600, TimeUnit.SECONDS); // 缓存机器人用户奖池 + redisCache.setCacheMap(realKey, realPrizePool.getBoxSpace(), 600, TimeUnit.SECONDS); + redisCache.setCacheMap(compensationKey, compensationPrizePool.getBoxSpace(), 600, TimeUnit.SECONDS); // 缓存补偿奖池 + redisCache.setCacheMap(thirdExplosiveKey, thirdExplosivePrizePool.getBoxSpace(), 600, TimeUnit.SECONDS); // 缓存三级爆率奖池 + } + + // 01 + HashMap anchorBoxSpace = new HashMap<>(); + anchorBoxSpace.put(String.valueOf(item.getOrnamentId()), item.getAnchorOdds()); + + anchorPrizePool = PrizePool.builder() + .key(BASE_POOL_KEY + item.getBoxId() + ":" + BoxPoolType.ANCHOR_PLAYER.getId()) + .boxId(String.valueOf(item.getBoxId())) + .poolType(BoxPoolType.ANCHOR_PLAYER.getId()) + .goodsNumber(item.getAnchorOdds()) + .boxSpace(anchorBoxSpace) + .build(); + + // 02 + HashMap realBoxSpace = new HashMap<>(); + realBoxSpace.put(String.valueOf(item.getOrnamentId()), item.getRealOdds()); + + realPrizePool = PrizePool.builder() + .key(BASE_POOL_KEY + item.getBoxId() + ":" + BoxPoolType.NORMAL_PLAYER.getId()) + .boxId(String.valueOf(item.getBoxId())) + .poolType(BoxPoolType.NORMAL_PLAYER.getId()) + .goodsNumber(item.getRealOdds()) + .boxSpace(realBoxSpace) + .build(); + + // 05 机器人用户奖池 + HashMap robotBoxSpace = new HashMap<>(); + robotBoxSpace.put(String.valueOf(item.getOrnamentId()), item.getRobotOdds()); + robotPrizePool = PrizePool.builder() + .key(BASE_POOL_KEY + item.getBoxId() + ":" + BoxPoolType.ROBOT_PLAYER.getId()) + .boxId(String.valueOf(item.getBoxId())) + .poolType(BoxPoolType.ROBOT_PLAYER.getId()) + .goodsNumber(item.getRobotOdds()) // 机器人用户奖池数量 + .goodsNumber(item.getRobotOdds()) + .boxSpace(robotBoxSpace) + .build(); + + // 03 补偿奖池 + HashMap compensationBoxSpace = new HashMap<>(); + compensationBoxSpace.put(String.valueOf(item.getOrnamentId()), item.getRealCompOdds()); + compensationPrizePool = PrizePool.builder() + .key(BASE_POOL_KEY + item.getBoxId() + ":" + BoxPoolType.COMPENSATION.getId()) + .boxId(String.valueOf(item.getBoxId())) + .poolType(BoxPoolType.COMPENSATION.getId()) + .goodsNumber(item.getRealCompOdds()) + .boxSpace(compensationBoxSpace) + .build(); + + // 04 三级爆率奖池 + HashMap thirdExplosiveBoxSpace = new HashMap<>(); + thirdExplosiveBoxSpace.put(String.valueOf(item.getOrnamentId()), item.getRealCompOdds()); + thirdExplosivePrizePool = PrizePool.builder() + .key(BASE_POOL_KEY + item.getBoxId() + ":" + BoxPoolType.THIRD_EXPLOSIVE.getId()) + .boxId(String.valueOf(item.getBoxId())) + .poolType(BoxPoolType.THIRD_EXPLOSIVE.getId()) + .goodsNumber(item.getThirdExplosiveOdds()) + .boxSpace(thirdExplosiveBoxSpace) + .build(); + } else { + // 累加 + anchorPrizePool.setGoodsNumber(anchorPrizePool.getGoodsNumber() + item.getAnchorOdds()); + Integer i1 = anchorPrizePool.getBoxSpace().get(String.valueOf(item.getOrnamentId())); + i1 = ObjectUtil.isEmpty(i1) ? 0 : i1; + anchorPrizePool.getBoxSpace().put(String.valueOf(item.getOrnamentId()), i1 + item.getAnchorOdds()); + + realPrizePool.setGoodsNumber(realPrizePool.getGoodsNumber() + item.getRealOdds()); + Integer i2 = realPrizePool.getBoxSpace().get(String.valueOf(item.getOrnamentId())); + i2 = ObjectUtil.isEmpty(i2) ? 0 : i2; + realPrizePool.getBoxSpace().put(String.valueOf(item.getOrnamentId()), i2 + item.getRealOdds()); + + // 05 机器人用户奖池 + robotPrizePool.setGoodsNumber(robotPrizePool.getGoodsNumber() + item.getRobotOdds()); + Integer i5 = robotPrizePool.getBoxSpace().get(String.valueOf(item.getOrnamentId())); + i5 = ObjectUtil.isEmpty(i5) ? 0 : i5; + robotPrizePool.getBoxSpace().put(String.valueOf(item.getOrnamentId()), i5 + item.getRobotOdds()); + + compensationPrizePool.setGoodsNumber(compensationPrizePool.getGoodsNumber() + item.getRealCompOdds()); + Integer i3 = compensationPrizePool.getBoxSpace().get(String.valueOf(item.getOrnamentId())); + i3 = ObjectUtil.isEmpty(i3) ? 0 : i3; + compensationPrizePool.getBoxSpace().put(String.valueOf(item.getOrnamentId()), i3 + item.getRealCompOdds()); + + thirdExplosivePrizePool.setGoodsNumber(thirdExplosivePrizePool.getGoodsNumber() + item.getThirdExplosiveOdds()); + Integer i4 = thirdExplosivePrizePool.getBoxSpace().get(String.valueOf(item.getOrnamentId())); + i4 = ObjectUtil.isEmpty(i4) ? 0 : i4; + thirdExplosivePrizePool.getBoxSpace().put(String.valueOf(item.getOrnamentId()), i4 + item.getThirdExplosiveOdds()); + } + + } + + // 保存最后一个箱子的奖池 + // 奖池写入内存 + if (ObjectUtil.isEmpty(anchorPrizePool) || ObjectUtil.isEmpty(realPrizePool)) { + log.info("没有宝箱绑定饰品信息,奖池预热结束。"); + return; + } + ; + String anchorKey = anchorPrizePool.getKey(); + String realKey = realPrizePool.getKey(); + String robotKey = robotPrizePool.getKey(); // 新增机器人用户奖池Key + String compensationKey = compensationPrizePool.getKey(); + String thirdExplosiveKey = thirdExplosivePrizePool.getKey(); // 新增三级爆率 + this.prizePools.put(anchorKey, anchorPrizePool); + this.prizePools.put(realKey, realPrizePool); + this.prizePools.put(robotKey, robotPrizePool); // 新增机器人用户奖池 + this.prizePools.put(compensationKey, compensationPrizePool); + this.prizePools.put(thirdExplosiveKey, thirdExplosivePrizePool); + // 奖池写入redis(先删再存) + redisCache.deleteObject(anchorKey); + redisCache.deleteObject(realKey); + redisCache.deleteObject(robotKey); // 新增机器人用户奖池 + redisCache.deleteObject(compensationKey); + redisCache.deleteObject(thirdExplosiveKey); + + redisCache.setCacheMap(anchorKey, anchorPrizePool.getBoxSpace(), 600, TimeUnit.SECONDS); + redisCache.setCacheMap(realKey, realPrizePool.getBoxSpace(), 600, TimeUnit.SECONDS); + redisCache.setCacheMap(robotKey, robotPrizePool.getBoxSpace(), 600, TimeUnit.SECONDS); // 新增机器人用户奖池 + redisCache.setCacheMap(compensationKey, compensationPrizePool.getBoxSpace(), 600, TimeUnit.SECONDS); + redisCache.setCacheMap(thirdExplosiveKey, thirdExplosivePrizePool.getBoxSpace(), 600, TimeUnit.SECONDS); // 缓存三级爆率奖池 + + log.info("抽奖机预热成功"); + } catch (Exception e) { + log.error("抽奖机预热失败!", e); + } + } + + // 单次抽奖(要加锁) + public String singleLottery(TtUser player, TtBox box) { + + // 1 构建抽奖key + UserType userType = UserType.fromCode(player.getUserType()); + if (userType == null) { + return null; + } + + String playerPoolType = switch (userType) { + case COMMON_USER -> BoxPoolType.NORMAL_PLAYER.getId(); + case ROBOT_USER -> BoxPoolType.ROBOT_PLAYER.getId(); // 新增机器人用户奖池 + default -> BoxPoolType.ANCHOR_PLAYER.getId(); + }; + + String prizePoolKey = BASE_POOL_KEY + box.getBoxId() + ":" + playerPoolType; + String compensationPrizePoolKey = BASE_POOL_KEY + box.getBoxId() + ":" + BoxPoolType.COMPENSATION.getId(); // 补偿奖池key + String thirdExplosivePrizePoolKey = BASE_POOL_KEY + box.getBoxId() + ":" + BoxPoolType.THIRD_EXPLOSIVE.getId(); // 三级爆率奖池key + + String lotteryLock = LOTTERY_LOCK + box.getBoxId() + "_" + player.getUserType(); + + Boolean lock = false; + int doTry = 0; + while (doTry < 3) { + lock = redisLock.tryLock(lotteryLock, 3L, 10L, TimeUnit.SECONDS); + if (!lock) { + doTry++; + continue; + } + break; + } + + if (!lock) return null; + + // 判断是否存在三级爆率的存储 + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(TtBoxThirdExplosiveUser::getBoxId, box.getBoxId()); + query.eq(TtBoxThirdExplosiveUser::getUserId, player.getUserId()); + List users = ttBoxThirdExplosiveUserMapper.selectList(query); + boolean thirdExplosive = CollectionUtils.isNotEmpty(users); + + try { + + // 2 选中奖池 + PrizePool prizePool = this.prizePools.get(prizePoolKey); + PrizePool compensationPrizePool = this.prizePools.get(compensationPrizePoolKey); + PrizePool thirdExplosivePrizePool = this.prizePools.get(thirdExplosivePrizePoolKey); + + // 如果为空,则初始化 + if (ObjectUtil.isNull(prizePool)) { + prizePool = PrizePool.builder() + .key(BASE_POOL_KEY + box.getBoxId() + ":" + playerPoolType) + .boxId(String.valueOf(box.getBoxId())) + .poolType(playerPoolType) + .boxSpace(new HashMap()) + .goodsNumber(0) + .build(); + } + + // 如果为空,则初始化补偿奖池 + if (ObjectUtil.isNull(compensationPrizePool)) { + compensationPrizePool = PrizePool.builder() + .key(BASE_POOL_KEY + box.getBoxId() + ":" + BoxPoolType.COMPENSATION.getId()) + .boxId(String.valueOf(box.getBoxId())) + .poolType(BoxPoolType.COMPENSATION.getId()) + .boxSpace(new HashMap()) + .goodsNumber(0) + .build(); + } + + // 如果为空,则初始化三级爆率 + if (ObjectUtil.isNull(thirdExplosivePrizePool)) { + thirdExplosivePrizePool = PrizePool.builder() + .key(BASE_POOL_KEY + box.getBoxId() + ":" + BoxPoolType.THIRD_EXPLOSIVE.getId()) + .boxId(String.valueOf(box.getBoxId())) + .poolType(BoxPoolType.THIRD_EXPLOSIVE.getId()) + .boxSpace(new HashMap()) + .goodsNumber(0) + .build(); + } + + // 3 检查奖池 + PrizePool finalPrizePool = checkPool(thirdExplosive ? thirdExplosivePrizePool : prizePool); + if (ObjectUtil.isEmpty(finalPrizePool)) return null; + + // 如果满足补偿条件,使用补偿奖池 + TtCompRecord ttCompRecord = ttCompRecordMapper.selectTtCompRecord(player.getUserId(), box.getBoxId()); + if ("3".equals(box.getBoxType()) && Objects.isNull(ttCompRecord)) { + TtCompRecord insertTtCompRecord = new TtCompRecord(); + insertTtCompRecord.setUserId(player.getUserId()); + insertTtCompRecord.setBoxId(box.getBoxId()); + insertTtCompRecord.setCompAmount(0); + ttCompRecordMapper.insertTtCompRecord(insertTtCompRecord); + } + if (!Objects.isNull(ttCompRecord)) { + if ("3".equals(box.getBoxType()) && ttCompRecord.getCompAmount() >= box.getCompAmount()) { + finalPrizePool = checkPool(compensationPrizePool); + // 补偿值清零 + ttCompRecordMapper.deleteTtCompRecord(ttCompRecord.getUserId(), box.getBoxId()); + } + } + // ===== 临时爆率逻辑开始 ===== + //根据用户的id,从redis中取出用户的临时爆率 + BoxMockCache boxMockCache = boxMockCacheRepository.getMockResultCache(player.getUserId()); + //判空 + if (boxMockCache != null && boxMockCache.getItems() != null && !boxMockCache.getItems().isEmpty()) { + //顺序取第一条,获取最低和最高的价格 + BoxMockCache.MockItem mockItem = boxMockCache.getItems().get(0); + BigDecimal lowPrice = mockItem.getLowPrice(); + BigDecimal highPrice = mockItem.getHighPrice(); + + //批量查询当前奖池内所有库存>0的饰品价格 + List ornamentIdList = new ArrayList<>(); + for (Map.Entry entry : finalPrizePool.getBoxSpace().entrySet()) { + if (entry.getValue() > 0) { + try { + ornamentIdList.add(Long.parseLong(entry.getKey())); + } catch (NumberFormatException e) { + log.warn("临时爆率: ornamentId解析异常 ornamentId={}", entry.getKey()); + } + } + } + + // 从当前奖池中找出 usePrice in [lowPrice, highPrice] 且库存 > 0 的饰品 + List eligibleIds = new ArrayList<>(); + if (!ornamentIdList.isEmpty()) { + //根据id列表批量查询饰品 + List ornaments = ttOrnamentMapper.selectByOrnamentIds(ornamentIdList); + for (TtOrnament o : ornaments) { + if (o.getUsePrice() == null) continue; + if (o.getUsePrice().compareTo(lowPrice) >= 0 + && o.getUsePrice().compareTo(highPrice) <= 0) { + eligibleIds.add(String.valueOf(o.getId())); + } + } + } + + if (!eligibleIds.isEmpty()) { + // 箱子内存在满足价格区间的饰品 → 消费本条 + boxMockCache.getItems().remove(0); + boxMockCacheRepository.setMockResultCache(player.getUserId(), boxMockCache); + + // 从满足条件的饰品中随机返回一个 + String forcedId = eligibleIds.get((int) (Math.random() * eligibleIds.size())); + return forcedId; + + } else { + // 箱子内无满足条件的饰品 → 本条【不消费】,走正常抽奖,下次开箱继续检查 + log.info("用户{}临时爆率不生效: boxId={} 内无[{},{}]区间饰品,本条保留,走正常开奖", + player.getUserId(), box.getBoxId(), lowPrice, highPrice); + } + } + // ===== 临时爆率逻辑结束 ===== + // 4 抽奖 + return doLottery(finalPrizePool); + + } catch (Exception e) { + e.printStackTrace(); + log.warn("用户{}抽奖异常", player.getUserId()); + return null; + } finally { + redisLock.unlock(lotteryLock); + } + + } + + // 抽奖算法 + private String doLottery(PrizePool prizePool) { + + Map boxSpace = prizePool.getBoxSpace(); + + log.info("抽奖空间:{}", boxSpace); + + // 2 计算随机数,开始抽奖(5次尝试) + String targetOrnamentId = null; + for (int c = 0; c < 5; c++) { + + int r = 0; + for (int i = 0; i < 5; i++) { + r = RandomUtil.rand(prizePool.getGoodsNumber()); + } + + log.info("抽奖随机数:{}", r); + + int count = 0; // 宝箱的第几个饰品 + boolean flag = false; + for (String ornamentId : boxSpace.keySet()) { + if (flag) break; + for (int i = 0; i < boxSpace.get(ornamentId); i++) { + if (count == r) { + targetOrnamentId = ornamentId; + flag = true; + break; + } + count++; + } + } + + if (!StringUtils.isBlank(targetOrnamentId)) { + break; + } + } + + // 多次尝试依然没抽到东西,按序给一个 + if (ObjectUtil.isEmpty(targetOrnamentId)) { + for (String ornamentId : boxSpace.keySet()) { + if (boxSpace.get(ornamentId) > 0) { + targetOrnamentId = ornamentId; + break; + } + } + } + + // 3 库存减一,更新奖池 + PrizePool newPool = prizePool.sub(targetOrnamentId); + this.prizePools.put(newPool.getKey(), newPool); + + log.info("抽奖奖品:{}", targetOrnamentId); + log.info("最终抽奖空间:{}", newPool.getBoxSpace()); + + return targetOrnamentId; + + } + + // 检查奖池,如果没有自动补充 + private PrizePool checkPool(PrizePool prizePool) { + + boolean isEmpty = true; + Map boxSpace = prizePool.getBoxSpace(); + if (ObjectUtil.isNotNull(boxSpace)) { + Set keySet = boxSpace.keySet(); + for (String ornamentId : keySet) { + if (prizePool.getBoxSpace().get(ornamentId) > 0) { + isEmpty = false; + break; + } + } + } + + // 有库存 + if (prizePool.getGoodsNumber() > 0 && !isEmpty) { + return prizePool; + } + + // 没库存,从redis补充 + Map prizeSpace = redisCache.getCacheMap(prizePool.getKey()); + if (ObjectUtil.isNotEmpty(prizeSpace)) { + int number = 0; + for (Integer p : prizeSpace.values()) { + number = number + p; + } + prizePool.setGoodsNumber(number); + prizePool.setBoxSpace(prizeSpace); + return prizePool; + } + + // redis也没有,从mysql加载 + Boolean tryLoad = false; + for (int i = 0; i < 2; i++) { + + tryLoad = redisLock.tryLock(PRIZE_POOL_LOAD_LOCK, 2L, 7L, TimeUnit.SECONDS); + + if (tryLoad) { + break; + } + } + + if (!tryLoad) return null; + + try { + List list = new LambdaQueryChainWrapper<>(boxOrnamentsMapper) + .eq(TtBoxOrnaments::getBoxId, prizePool.getBoxId()) + .list(); + + HashMap space = new HashMap<>(); + Integer goodsNumber = 0; + String poolType = prizePool.getPoolType(); + var pt = BoxPoolType.fromId(poolType); + if (pt == null) { + pt = BoxPoolType.ANCHOR_PLAYER; + } + for (TtBoxOrnaments item : list) { + switch (pt) { + case BoxPoolType.ANCHOR_PLAYER -> { + space.put(String.valueOf(item.getOrnamentId()), item.getAnchorOdds()); + goodsNumber += item.getAnchorOdds(); + } + case BoxPoolType.NORMAL_PLAYER -> { + space.put(String.valueOf(item.getOrnamentId()), item.getRealOdds()); + goodsNumber += item.getRealOdds(); + } + case BoxPoolType.ROBOT_PLAYER -> { + space.put(String.valueOf(item.getOrnamentId()), item.getRobotOdds()); + goodsNumber += item.getRobotOdds(); + } + case BoxPoolType.COMPENSATION -> { + space.put(String.valueOf(item.getOrnamentId()), item.getRealCompOdds()); + goodsNumber += item.getRealCompOdds(); + } + case BoxPoolType.THIRD_EXPLOSIVE -> { + space.put(String.valueOf(item.getOrnamentId()), item.getThirdExplosiveOdds()); + goodsNumber += item.getThirdExplosiveOdds(); + } + } + } + + prizePool.setGoodsNumber(goodsNumber); + prizePool.setBoxSpace(space); + redisCache.setCacheMap(prizePool.getKey(), space, 600, TimeUnit.SECONDS); + return prizePool; + } catch (Exception e) { + log.warn("prize load warn"); + return null; + } finally { + redisLock.unlock(PRIZE_POOL_LOAD_LOCK); + } + + } + + // 删除内存奖池 + public boolean removeMemoryPrizePool(String key) { + this.prizePools.remove(key); + return true; + } + + // 删除redis奖池 + public boolean removeRedisPrizePool(String key) { + return redisCache.deleteObject(key); + } + + // 清空box奖池 + public boolean clearBoxPrizePool(Integer boxId) { + for (BoxPoolType poolType : BoxPoolType.allTypes()) { + String key = BASE_POOL_KEY + boxId + ":" + poolType.getId(); + this.removeMemoryPrizePool(key); + this.removeRedisPrizePool(key); + } + return true; + } + + public void resetBoxPrizePool(Integer boxId) { + for (BoxPoolType poolType : BoxPoolType.allTypes()) { + String key = BASE_POOL_KEY + boxId + ":" + poolType.getId(); + this.removeMemoryPrizePool(key); + var prizePool = PrizePool.builder() + .key(key) + .boxId(String.valueOf(boxId)) + .poolType(poolType.getId()) + .boxSpace(new HashMap<>()) + .goodsNumber(0) + .build(); + this.checkPool(prizePool); + } + + } + +} diff --git a/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/core/fight/PrizePool.java b/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/core/fight/PrizePool.java new file mode 100644 index 0000000..6fce082 --- /dev/null +++ b/skins-service/service-admin/src/main/java/com/ruoyi/admin/util/core/fight/PrizePool.java @@ -0,0 +1,35 @@ +package com.ruoyi.admin.util.core.fight; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PrizePool { + + private String key; + + private String boxId; + + private String poolType; + + // 物品件数 + private Integer goodsNumber; + + // ornamentId : number + private Map boxSpace; + + // 减库存 + public PrizePool sub(String ornamentId) { + this.goodsNumber--; + this.boxSpace.put(ornamentId, this.boxSpace.get(ornamentId) - 1); + return this; + } + +} diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtAnnouncementMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtAnnouncementMapper.xml new file mode 100644 index 0000000..372a991 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtAnnouncementMapper.xml @@ -0,0 +1,41 @@ + + + + + + + + + + insert into tt_announcement + + title, + content, + create_time, + + + #{title}, + #{content}, + #{createTime}, + + + + + update tt_announcement + + title = #{title}, + content = #{content}, + + where announcement_id = #{announcementId} + + + + delete from tt_announcement where announcement_id = #{announcementId} + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxMapper.xml new file mode 100644 index 0000000..b07a04c --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxMapper.xml @@ -0,0 +1,93 @@ + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxOpenChanceMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxOpenChanceMapper.xml new file mode 100644 index 0000000..595f608 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxOpenChanceMapper.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + select ornament_id, box_id, chance_num from tt_box_open_chance + + + + + + + + insert into tt_box_open_chance + + ornament_id, + box_id, + chance_num, + + + #{ornamentId}, + #{boxId}, + #{chanceNum}, + + + + + update tt_box_open_chance + + box_id = #{boxId}, + chance_num = #{chanceNum}, + + where ornament_id = #{ornamentId} + + + + delete from tt_box_open_chance where ornament_id = #{ornamentId} + + + + delete from tt_box_open_chance where ornament_id in + + #{ornamentId} + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxOrnamentsMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxOrnamentsMapper.xml new file mode 100644 index 0000000..adc004e --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxOrnamentsMapper.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxRecordsMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxRecordsMapper.xml new file mode 100644 index 0000000..75065e1 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxRecordsMapper.xml @@ -0,0 +1,378 @@ + + + + + + + + + + + + + + + + + + + + + + + + + select + ttu.nick_name as holder_user_nick_name, + ttu.avatar as avatar, + tbr.holder_user_id as holder_user_id, + + tbr.ornament_id, + tto.name as ornament_name, + tto.image_url, + tbr.ornaments_price, + + tol.level_img as ornament_level_img, + + tbr.`status`, + tbr.create_time, + tbr.update_time + from tt_box_records tbr + left join tt_user ttu on ttu.user_id = tbr.holder_user_id + left join tt_roll_jackpot_ornaments trjo on tbr.ornament_id = trjo.ornaments_id + left join tt_ornament tto on tto.id = tbr.ornament_id + left join tt_ornaments_level tol on tol.id = trjo.ornament_level_id + + AND tbr.roll_id = #{rollId} + + order by tbr.ornaments_price desc + limit #{limit},#{size} + + + + + + + + + update tt_box_records + set status = #{status} + where id IN + + #{id} + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxThirdExplosiveUserMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxThirdExplosiveUserMapper.xml new file mode 100644 index 0000000..d401354 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtBoxThirdExplosiveUserMapper.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtCompRecordMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtCompRecordMapper.xml new file mode 100644 index 0000000..4e17e4e --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtCompRecordMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + INSERT INTO tt_comp_record(user_id, box_id, comp_amount) + VALUES (#{userId}, #{boxId}, #{compAmount}) + + + + update tt_comp_record + + comp_amount = #{compAmount}, + + where user_id = #{userId} and box_id = #{boxId} + + + + delete from tt_comp_record where user_id = #{userId} and box_id = #{boxId} + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtDeliveryRecordMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtDeliveryRecordMapper.xml new file mode 100644 index 0000000..02342e5 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtDeliveryRecordMapper.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtExponentMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtExponentMapper.xml new file mode 100644 index 0000000..b557113 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtExponentMapper.xml @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtFightMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtFightMapper.xml new file mode 100644 index 0000000..4e6a923 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtFightMapper.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtFightRankingRewardMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtFightRankingRewardMapper.xml new file mode 100644 index 0000000..d5b3323 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtFightRankingRewardMapper.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + select id, name, reward from tt_fight_ranking_reward + + + + + + + + insert into tt_fight_ranking_reward + + id, + name, + reward, + + + #{id}, + #{name}, + #{reward}, + + + + + update tt_fight_ranking_reward + + name = #{name}, + reward = #{reward}, + + where id = #{id} + + + + delete from tt_fight_ranking_reward where id = #{id} + + + + delete from tt_fight_ranking_reward where id in + + #{id} + + + + + TRUNCATE TABLE tt_fight_ranking_reward; + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtFightUserMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtFightUserMapper.xml new file mode 100644 index 0000000..5d771c8 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtFightUserMapper.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtFirstRechargeMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtFirstRechargeMapper.xml new file mode 100644 index 0000000..0307b90 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtFirstRechargeMapper.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + select id, min_amount, ratio, description, create_by, create_time, update_by, update_time from tt_first_recharge + + + + + + + + insert into tt_first_recharge + + id, + min_amount, + ratio, + description, + create_by, + create_time, + update_by, + update_time, + + + #{id}, + #{minAmount}, + #{ratio}, + #{description}, + #{createBy}, + #{createTime}, + #{updateBy}, + #{updateTime}, + + + + + update tt_first_recharge + + min_amount = #{minAmount}, + ratio = #{ratio}, + description = #{description}, + create_by = #{createBy}, + create_time = #{createTime}, + update_by = #{updateBy}, + update_time = #{updateTime}, + + where id = #{id} + + + + delete from tt_first_recharge where id = #{id} + + + + delete from tt_first_recharge where id in + + #{id} + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtOrderMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtOrderMapper.xml new file mode 100644 index 0000000..eb0cbd2 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtOrderMapper.xml @@ -0,0 +1,221 @@ + + + + + + + + + + + + + select + user_id, + nick_name, + avatar, + user_type + from tt_user ttu + + + ttu.user_id in + + #{uid} + + + + + ttu.user_id = -1 + + + + + + + select + any_value(tto.user_id) as user_id, + sum(tto.total_amount) as recharge + from tt_order tto + left join tt_user ttu on tto.user_id = ttu.user_id + + + tto.user_id in + + #{uid} + + + + + and tto.create_time between #{beginTime} and #{endTime} + + and tto.status = 4 + + group by user_id + + + + select + any_value(tube.user_id) as user_id, + sum(tube.total) as total, + sum(tube.credits) as credits, + sum(tube.amount) as amount + from tt_user_blend_ercash tube + left join tt_user ttu on ttu.user_id = tube.user_id + + + tube.user_id in + + #{uid} + + + + and tube.create_time between #{beginTime} and #{endTime} + + and type = 0 + + group by user_id + + + + + + + select + t2.user_id as user_id, + t2.nick_name as nick_name, + t2.avatar as avatar, + t2.user_type as user_type, + t2.recharge as recharge + from ( + + select + any_value(t1.user_id) as user_id, + any_value(t1.nick_name) as nick_name, + any_value(t1.user_type) as user_type, + any_value(t1.avatar) as avatar, + sum(t1.total_amount) as recharge + from ( + select + tto.user_id as user_id, + ttu.nick_name as nick_name, + ttu.user_type as user_type, + ttu.avatar as avatar, + tto.total_amount as total_amount + from tt_order tto + left join tt_user ttu on tto.user_id = ttu.user_id + + + tto.user_id in + + #{uid} + + + + + and tto.create_time between #{beginTime} and #{endTime} + + and tto.status in (1,4) + + ) t1 + group by t1.user_id + + ) t2 + order by t2.recharge + asc + desc + limit #{limit},#{size} + + + + + + select + any_value(t1.user_id) as user_id, + any_value(t1.nick_name) as nick_name, + any_value(t1.avatar) as avatar, + any_value(t1.user_type) as user_type, + sum(t1.total) as be_consume, + sum(t1.credits) as credits_consume, + sum(t1.amount) as amount_consume + from ( + select + tube.user_id as user_id, + tube.total as total, + tube.credits as credits, + tube.amount as amount, + ttu.nick_name as nick_name, + ttu.avatar as avatar, + ttu.user_type as user_type + from tt_user_blend_ercash tube + left join tt_user ttu on ttu.user_id = tube.user_id + + + tube.user_id in + + #{uid} + + + + and tube.create_time between #{beginTime} and #{endTime} + + + ) t1 + group by t1.user_id + +-- select +-- user_id, +-- be_consume, +-- credits_consume, +-- amount_consume, +-- nick_name, +-- avatar, +-- user_type +-- from ( +-- +-- ) t2 + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtOrnamentMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtOrnamentMapper.xml new file mode 100644 index 0000000..5ca0625 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtOrnamentMapper.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + update tt_ornament + + name = #{name}, + short_name = #{name}, + use_price = #{usePrice}, + image_url = #{imageUrl}, + quantity = #{quantity}, + update_time = sysdate() + + where id = #{id} + + + + DELETE FROM tt_ornament WHERE id IN + + #{id} + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtOrnamentsZBTMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtOrnamentsZBTMapper.xml new file mode 100644 index 0000000..aba1ca8 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtOrnamentsZBTMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtPromotionRecordMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtPromotionRecordMapper.xml new file mode 100644 index 0000000..367cd44 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtPromotionRecordMapper.xml @@ -0,0 +1,25 @@ + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtPromotionUpdateMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtPromotionUpdateMapper.xml new file mode 100644 index 0000000..c661132 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtPromotionUpdateMapper.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRechargeRankingRewardMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRechargeRankingRewardMapper.xml new file mode 100644 index 0000000..ba919fe --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRechargeRankingRewardMapper.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + select id, name, reward from tt_recharge_ranking_reward + + + + + + + + insert into tt_recharge_ranking_reward + + id, + name, + reward, + + + #{id}, + #{name}, + #{reward}, + + + + + update tt_recharge_ranking_reward + + name = #{name}, + reward = #{reward}, + + where id = #{id} + + + + delete from tt_recharge_ranking_reward where id = #{id} + + + + delete from tt_recharge_ranking_reward where id in + + #{id} + + + + + TRUNCATE TABLE tt_recharge_ranking_reward; + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRechargeRecordMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRechargeRecordMapper.xml new file mode 100644 index 0000000..3dc9662 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRechargeRecordMapper.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRedPackMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRedPackMapper.xml new file mode 100644 index 0000000..f0a0e62 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRedPackMapper.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + select id, title, pack_type, recharge_limit, pack_count, valid_count, value_min, value_max, begin_time, end_time, status, create_time, update_time from tt_red_pack + + + + + + + + insert into tt_red_pack + + title, + pack_type, + recharge_limit, + pack_count, + valid_count, + value_min, + value_max, + begin_time, + end_time, + status, + create_time, + update_time, + + + #{title}, + #{packType}, + #{rechargeLimit}, + #{packCount}, + #{validCount}, + #{valueMin}, + #{valueMax}, + #{beginTime}, + #{endTime}, + #{status}, + #{createTime}, + #{updateTime}, + + + + + update tt_red_pack + + title = #{title}, + pack_type = #{packType}, + recharge_limit = #{rechargeLimit}, + pack_count = #{packCount}, + valid_count = #{validCount}, + value_min = #{valueMin}, + value_max = #{valueMax}, + begin_time = #{beginTime}, + end_time = #{endTime}, + status = #{status}, + create_time = #{createTime}, + update_time = #{updateTime}, + + where id = #{id} + + + + delete from tt_red_pack where id = #{id} + + + + delete from tt_red_pack where id in + + #{id} + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRedPacketMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRedPacketMapper.xml new file mode 100644 index 0000000..fe72200 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRedPacketMapper.xml @@ -0,0 +1,52 @@ + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRedPacketRecordMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRedPacketRecordMapper.xml new file mode 100644 index 0000000..9ce6255 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRedPacketRecordMapper.xml @@ -0,0 +1,27 @@ + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtReplacementRecordMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtReplacementRecordMapper.xml new file mode 100644 index 0000000..b880a10 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtReplacementRecordMapper.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + select id, uid, uname, oids, award_oid, award_oname, award_oprice,award_oimg, time, create_time, update_time from tt_replacement_record + + + + + + + + insert into tt_replacement_record + + uid, + uname, + oids, + award_oid, + award_oname, + award_oprice, + award_oimg, + time, + create_time, + update_time, + + + #{uid}, + #{uname}, + #{oids}, + #{awardOid}, + #{awardOname}, + #{awardOprice}, + #{awardOimg}, + #{time}, + #{createTime}, + #{updateTime}, + + + + + update tt_replacement_record + + uid = #{uid}, + uname = #{uname}, + oids = #{oids}, + award_oid = #{awardOid}, + award_oname = #{awardOname}, + award_oprice = #{awardOprice}, + time = #{time}, + create_time = #{createTime}, + update_time = #{updateTime}, + + where id = #{id} + + + + delete from tt_replacement_record where id = #{id} + + + + delete from tt_replacement_record where id in + + #{id} + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRobotFightGroupBoxMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRobotFightGroupBoxMapper.xml new file mode 100644 index 0000000..ba0f1bf --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRobotFightGroupBoxMapper.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + select group_id, box_id, box_num from tt_robot_fight_group_box + + + + + + + + + + insert into tt_robot_fight_group_box + + group_id, + box_id, + box_num, + + + #{groupId}, + #{boxId}, + #{boxNum}, + + + + + update tt_robot_fight_group_box + + group_id = #{groupId}, + box_id = #{boxId}, + box_num = #{boxNum}, + + where group_id = #{groupId} and box_id = #{boxId} + + + + delete from tt_robot_fight_group_box where group_id = #{groupId} and box_id = #{boxId} + + + + delete from tt_robot_fight_group_box where group_id = #{groupId} and box_id in + + #{boxId} + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRobotFightGroupMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRobotFightGroupMapper.xml new file mode 100644 index 0000000..a1d6e34 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRobotFightGroupMapper.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + select group_id, group_name, seat_num, model from tt_robot_fight_group + + + + + + + + insert into tt_robot_fight_group + + group_id, + group_name, + seat_num, + model, + + + #{groupId}, + #{groupName}, + #{seatNum}, + #{model}, + + + + + update tt_robot_fight_group + + group_name = #{groupName}, + seat_num = #{seatNum}, + model = #{model}, + + where group_id = #{groupId} + + + + delete from tt_robot_fight_group where group_id = #{groupId} + + + + delete from tt_robot_fight_group where group_id in + + #{groupId} + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRollJackpotOrnamentsMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRollJackpotOrnamentsMapper.xml new file mode 100644 index 0000000..a46f4f8 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRollJackpotOrnamentsMapper.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + select + jackpot_id + from + tt_roll + where id = #{rollId} + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRollMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRollMapper.xml new file mode 100644 index 0000000..be526ba --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRollMapper.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRollUserMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRollUserMapper.xml new file mode 100644 index 0000000..79148e2 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRollUserMapper.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRollUserPrizeMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRollUserPrizeMapper.xml new file mode 100644 index 0000000..cffee60 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtRollUserPrizeMapper.xml @@ -0,0 +1,67 @@ + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtTaskCenterMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtTaskCenterMapper.xml new file mode 100644 index 0000000..ed71e87 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtTaskCenterMapper.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + select task_id, task_name, description, identify, status, create_by, create_time, update_by, update_time from tt_task_center + + + + + + + + + + insert into tt_task_center + + task_name, + description, + identify, + status, + create_by, + create_time, + update_by, + update_time, + + + #{taskName}, + #{description}, + #{identify}, + #{status}, + #{createBy}, + #{createTime}, + #{updateBy}, + #{updateTime}, + + + + + update tt_task_center + + task_name = #{taskName}, + description = #{description}, + identify = #{identify}, + status = #{status}, + create_by = #{createBy}, + create_time = #{createTime}, + update_by = #{updateBy}, + update_time = #{updateTime}, + + where task_id = #{taskId} + + + + delete from tt_task_center where task_id = #{taskId} + + + + delete from tt_task_center where task_id in + + #{taskId} + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtTimeRollMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtTimeRollMapper.xml new file mode 100644 index 0000000..9037997 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtTimeRollMapper.xml @@ -0,0 +1,42 @@ + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUpgradeFailOrnamentsMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUpgradeFailOrnamentsMapper.xml new file mode 100644 index 0000000..f98f741 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUpgradeFailOrnamentsMapper.xml @@ -0,0 +1,59 @@ + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUpgradeOrnamentsMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUpgradeOrnamentsMapper.xml new file mode 100644 index 0000000..dc0f609 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUpgradeOrnamentsMapper.xml @@ -0,0 +1,46 @@ + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUpgradeRecordMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUpgradeRecordMapper.xml new file mode 100644 index 0000000..4cfba92 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUpgradeRecordMapper.xml @@ -0,0 +1,69 @@ + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUserAmountRecordsMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUserAmountRecordsMapper.xml new file mode 100644 index 0000000..60e08e4 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUserAmountRecordsMapper.xml @@ -0,0 +1,247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUserBlendErcashMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUserBlendErcashMapper.xml new file mode 100644 index 0000000..88c9ad7 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUserBlendErcashMapper.xml @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + select + user_id, + nick_name, + avatar, + user_type + from tt_user ttu + + + ttu.user_id in + + #{uid} + + + + + ttu.user_id = -1 + + + + + + + + select + any_value(tube.user_id) as user_id, + sum(tube.total) as total, + sum(tube.credits) as credits, + sum(tube.amount) as amount + from tt_user_blend_ercash tube + left join tt_user ttu on ttu.user_id = tube.user_id + + + tube.user_id in + + #{uid} + + + + and tube.create_time between #{beginTime} and #{endTime} + + and type = 0 + + group by user_id + + + + + select + any_value(tto.user_id) as user_id, + sum(tto.total_amount) as recharge + from tt_order tto + left join tt_user ttu on tto.user_id = ttu.user_id + + + tto.user_id in + + #{uid} + + + + + and tto.create_time between #{beginTime} and #{endTime} + + and tto.status = 4 + + group by user_id + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUserCreditsRecordsMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUserCreditsRecordsMapper.xml new file mode 100644 index 0000000..e7a2432 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUserCreditsRecordsMapper.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUserMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUserMapper.xml new file mode 100644 index 0000000..9971f7d --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtUserMapper.xml @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select user_id, + user_name, + nick_name, + user_type, + email, + phone_number, + avatar, + password, + account_amount, + account_credits, + invitation_code, + parent_id, + vip_level, + promotion_level, + status, + delivery_status, + steam_id, + transaction_link, + real_name, + id_num, + certify_id, + is_real_check, + commission_rate, + login_ip, + login_date, + create_by, + create_time, + update_by, + update_time, + remark, + del_flag + from tt_user + + + + + + + + + + update tt_user + set bean = (bean + #{money}) + where user_id = #{userId} + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtWelfareMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtWelfareMapper.xml new file mode 100644 index 0000000..65b388e --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtWelfareMapper.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + select welfare_id, welfare_name, type, vip_level, box_id, create_time, update_time from tt_welfare + + + + + + + + insert into tt_welfare + + welfare_name, + type, + vip_level, + box_id, + create_time, + update_time, + + + #{welfareName}, + #{type}, + #{vipLevel}, + #{boxId}, + #{createTime}, + #{updateTime}, + + + + + update tt_welfare + + welfare_name = #{welfareName}, + type = #{type}, + vip_level = #{vipLevel}, + box_id = #{boxId}, + create_time = #{createTime}, + update_time = #{updateTime}, + + where welfare_id = #{welfareId} + + + + delete from tt_welfare where welfare_id = #{welfareId} + + + + delete from tt_welfare where welfare_id in + + #{welfareId} + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtWelfareMonthlyRechargesMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtWelfareMonthlyRechargesMapper.xml new file mode 100644 index 0000000..181ec05 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/TtWelfareMonthlyRechargesMapper.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + select welfare_id, welfare_name, type, vip_level, amount, box_id from tt_welfare_monthly_recharges + + + + + + + + insert into tt_welfare_monthly_recharges + + welfare_id, + welfare_name, + type, + vip_level, + amount, + box_id, + + + #{welfareId}, + #{welfareName}, + #{type}, + #{vipLevel}, + #{amount}, + #{boxId}, + + + + + update tt_welfare_monthly_recharges + + welfare_name = #{welfareName}, + type = #{type}, + vip_level = #{vipLevel}, + amount = #{amount}, + box_id = #{boxId}, + + where welfare_id = #{welfareId} + + + + delete from tt_welfare_monthly_recharges where welfare_id = #{welfareId} + + + + delete from tt_welfare_monthly_recharges where welfare_id in + + #{welfareId} + + + \ No newline at end of file diff --git a/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/WebsiteSetupMapper.xml b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/WebsiteSetupMapper.xml new file mode 100644 index 0000000..8c76509 --- /dev/null +++ b/skins-service/service-admin/src/main/resources/com/ruoyi/admin/mapper/WebsiteSetupMapper.xml @@ -0,0 +1,80 @@ + + + + + + + + + + TRUNCATE TABLE sys_job_log; + + + TRUNCATE TABLE sys_logininfor; + + + TRUNCATE TABLE sys_oper_log; + + + TRUNCATE TABLE tt_box_records; + + + TRUNCATE TABLE tt_fight; + + + TRUNCATE TABLE tt_fight_result; + + + TRUNCATE TABLE tt_fight_user; + + + TRUNCATE TABLE tt_vip_level; + + + TRUNCATE TABLE tt_promotion_level; + + + TRUNCATE TABLE tt_ornaments_level; + + + \ No newline at end of file diff --git a/skins-service/service-playingmethod/pom.xml b/skins-service/service-playingmethod/pom.xml new file mode 100644 index 0000000..5775f5b --- /dev/null +++ b/skins-service/service-playingmethod/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + com.ruoyi + skins-service + 4.8.2 + + + service-playingmethod + + + 8 + 8 + UTF-8 + + + + + + com.ruoyi + ruoyi-framework + + + + com.ruoyi + service-admin + 4.8.2 + + + + com.ruoyi + service-user + 4.8.2 + + + + \ No newline at end of file diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/config/FightRabbitConsumer.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/config/FightRabbitConsumer.java new file mode 100644 index 0000000..6d3886c --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/config/FightRabbitConsumer.java @@ -0,0 +1,42 @@ +package com.ruoyi.playingmethod.config; + +import com.rabbitmq.client.Channel; +import com.ruoyi.admin.service.TtFightService; +import com.ruoyi.common.rabbitmq.config.DelayedQueueConfig; +import com.ruoyi.common.rabbitmq.config.FightQueueConfig; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.playingmethod.service.ApiRollService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Date; + +@Component +@Slf4j +public class FightRabbitConsumer { + + // private final TtFightService ttFightService; + // + // public FightRabbitConsumer(TtFightService ttFightService) { + // this.ttFightService = ttFightService; + // } + // + // @RabbitListener(queues = FightQueueConfig.FIGHT_QUEUE) + // public void rollReceiveDelayed(String fightId,Message message, Channel channel) throws IOException { + // log.info("===============接收对战队列消息====================" + fightId); + // + // // 通知 MQ 消息已被接收,可以ACK(从队列中删除)了 + // channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); + // try { + // ttFightService.endFight(fightId); + // } catch (Exception e) { + // log.error("============消费失败,尝试消息补发再次消费!=============="); + // log.error(e.getMessage()); + // // 消息补发 true=消息退回到queue(有可能被其它的consumer(集群)接收到) false=只补发给当前的consumer + // channel.basicRecover(false); + // } + // } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/config/LotteryMachineConfig.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/config/LotteryMachineConfig.java new file mode 100644 index 0000000..efcdf5a --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/config/LotteryMachineConfig.java @@ -0,0 +1,39 @@ +package com.ruoyi.playingmethod.config; + +import com.ruoyi.admin.mapper.TtBoxOrnamentsMapper; +import com.ruoyi.admin.util.core.fight.LotteryMachine; +import com.ruoyi.admin.util.core.fight.PrizePool; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.redis.config.RedisLock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.HashMap; + +@Configuration +public class LotteryMachineConfig { + + @Autowired + RedisLock redisLock; + @Autowired + RedisCache redisCache; + @Autowired + TtBoxOrnamentsMapper boxOrnamentsMapper; + + @Bean + public LotteryMachine lotteryMachine(){ + + LotteryMachine machine = LotteryMachine.builder() + .boxOrnamentsMapper(boxOrnamentsMapper) + .redisLock(redisLock) + .redisCache(redisCache) + .prizePools(new HashMap()) + .build(); + + machine.preheat(); + + return machine; + } + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/config/RollRabbitConsumer.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/config/RollRabbitConsumer.java new file mode 100644 index 0000000..a889e51 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/config/RollRabbitConsumer.java @@ -0,0 +1,62 @@ +package com.ruoyi.playingmethod.config; + +import cn.hutool.core.util.ObjectUtil; +import com.rabbitmq.client.Channel; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.rabbitmq.config.DelayedQueueConfig; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.playingmethod.service.ApiRollService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.core.ExchangeTypes; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.*; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +@Slf4j +public class RollRabbitConsumer { + + private final ApiRollService apiRollService; + + public RollRabbitConsumer(ApiRollService apiRollService) { + this.apiRollService = apiRollService; + } + + @RabbitListener(queues = DelayedQueueConfig.DLK_QUEUE) + public void rollReceiveDelayed(Message message, Channel channel) throws IOException { + + long deliveryTag = message.getMessageProperties().getDeliveryTag(); + + Integer rollId = Integer.valueOf(new String(message.getBody())); + log.info("【死信队列消费】 roll房开奖,接受到的Roll房ID:{}", rollId); + + try { + // Roll房分配结果逻辑 + R r = apiRollService.endROLL(rollId); + if (!r.getCode().equals(200)) { + log.error("roll开奖 消费失败,尝试消息补发再次消费!" + r.getMsg()); + channel.basicNack(deliveryTag, true, true); + } + if (ObjectUtil.isNotEmpty(r.getMsg())) log.info("{}", r.getMsg()); + + // 通知 MQ 消息已被接收,可以ACK(从队列中删除)了 + channel.basicAck(deliveryTag, true); + } catch (Exception e) { + log.error("roll开奖 消费失败,尝试消息补发再次消费!"); + log.error(e.getMessage()); + channel.basicNack(deliveryTag, true, true); + } + } + + //@RabbitListener(queues = DelayedQueueConfig.DLK_QUEUE) + public void t1(Message message, Channel channel) throws IOException { + + // long deliveryTag = message.getMessageProperties().getDeliveryTag(); + + log.info("mq 测试"); + + System.out.println(new String(message.getBody())); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiBattleRoyaleController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiBattleRoyaleController.java new file mode 100644 index 0000000..25075bd --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiBattleRoyaleController.java @@ -0,0 +1,9 @@ +package com.ruoyi.playingmethod.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/battleRoyale") +public class ApiBattleRoyaleController { +} \ No newline at end of file diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiBindBoxController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiBindBoxController.java new file mode 100644 index 0000000..d4e4ea9 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiBindBoxController.java @@ -0,0 +1,217 @@ +package com.ruoyi.playingmethod.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.TtUserAmountRecordsService; +import com.ruoyi.admin.service.TtBoxService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.common.constant.TtboxRecordSource; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.dto.boxRecords.TenTopQuery; +import com.ruoyi.domain.dto.boxRecords.queryCondition; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.recorde.TtUserAmountRecords; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.*; +import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO; +import com.ruoyi.playingmethod.service.ApiBindBoxService; +import com.ruoyi.playingmethod.service.ApiBoxRecordsService; +import com.ruoyi.system.service.ISysConfigService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.*; + +@Api(tags = "玩转盲盒模式") +@RestController +@RequestMapping("/api/bindbox") +public class ApiBindBoxController extends BaseController { + + private final ISysConfigService sysConfigService; + private final ApiBindBoxService bindBoxService; + private final TtUserService userService; + private final TtBoxService boxService; + private final TtUserAmountRecordsService userAmountRecordsService; + + public ApiBindBoxController(ISysConfigService sysConfigService, + ApiBindBoxService bindBoxService, + TtUserAmountRecordsService userAmountRecordsService, + TtUserService userService, + TtBoxService boxService) { + this.sysConfigService = sysConfigService; + this.bindBoxService = bindBoxService; + this.userService = userService; + this.userAmountRecordsService = userAmountRecordsService; + this.boxService = boxService; + } + + @Autowired + private ApiBoxRecordsService apiBoxRecordsService; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + @ApiOperation("获取宝箱信息") + @Anonymous + @GetMapping("/{boxId}") + public R getBoxData(@PathVariable(value = "boxId") Integer boxId) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + String bindBoxMaintenance = sysConfigService.selectConfigByKey("bindBoxMaintenance"); + if ("1".equals(bindBoxMaintenance)) { + return R.fail("盲盒开箱功能正在维护中......"); + } + TtBoxA boxData = bindBoxService.getBoxData(boxId); + return R.ok(boxData); + } + + @ApiOperation("获取补偿箱信息") + @GetMapping("/getCompBoxData/{boxId}") + public AjaxResult getCompBoxData(@PathVariable("boxId") Integer boxId) { + List compBoxData = bindBoxService.getCompBoxData(boxId); + return success(compBoxData); + } + + @ApiOperation("获取宝箱列表") + @ApiImplicitParams({ + @ApiImplicitParam(name = "boxType", value = "宝箱类型(1普通箱 2对战箱 3补偿箱 4九宫格)", dataType = "String", dataTypeClass = String.class) + }) + @Anonymous + @GetMapping("/getBoxList") + public R> getBoxList( + @RequestParam(value = "boxTypeId", required = false) Integer boxTypeId, + @RequestParam(value = "homeFlag", required = false) String homeFlag, + @RequestParam(value = "boxType", required = false) String boxType + ) { + List boxData = bindBoxService.getBoxList(boxTypeId, homeFlag, boxType); + if (boxData.isEmpty()) { + return R.ok(null, "没有匹配的数据。"); + } + return R.ok(boxData); + } + + /** + * 盲盒开箱模式开箱 + */ + @ApiOperation("盲盒开箱") + @UpdateUserCache + @PostMapping("/openBox") + public R openBox(@RequestParam("boxId") Integer boxId, @RequestParam("num") Integer num) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + String bindBoxMaintenance = sysConfigService.selectConfigByKey("bindBoxMaintenance"); + if ("1".equals(bindBoxMaintenance)) { + return R.fail("盲盒开箱功能正在维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + TtBox ttBox = boxService.getById(boxId); + /*if ("0".equals(ttUser.getIsRealCheck())) { + return R.fail("您的账户未进行实名认证,请先进行实名认证!"); + }*/ + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(TtUserBlendErcash::getUserId, ttUser.getUserId()) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode()) + .select(TtUserBlendErcash::getTotal); + List ercash = ttUserBlendErcashMapper.selectList(queryWrapper); + if (CollectionUtils.isEmpty(ercash)) { + return R.fail("您的账户未进行充过值!"); + } + + // BigDecimal openBoxBeanTotal = ttBox.getPrice().multiply(new BigDecimal(num)); + // if (ttUser.getAccountAmount().compareTo(openBoxBeanTotal) < 0) return R.fail("您的账户游戏币不足!"); + return bindBoxService.blindBox(ttUser, ttBox, num); + } + + @ApiOperation("获取历史开箱信息") + @Anonymous + @PostMapping("/getBindBoxHistory") + public R getBindBoxHistory(@RequestBody @Validated queryCondition param) { + if (param.getStatus().equals(1) || param.getStatus().equals(10)) { + return R.fail("非法的状态参数。"); + } + PageUtils.clearPage(); + List ttBoxRecordsVOS = apiBoxRecordsService.byCondition(param); + return R.ok(ttBoxRecordsVOS); + } + + // @ApiOperation("获取历史个人开箱信息") + // @Anonymous + // @GetMapping("/selectBoxRecordsUserLogList") + // public R selectBoxRecordsUserLogList(TtUserAmountRecordsBody ttUserAmountRecordsBody) { + // System.out.println("==================个人开箱记录===================="); + // ttUserAmountRecordsBody.setUserId(getUserId().intValue()); + // return userAmountRecordsService.queryList(ttUserAmountRecordsBody); + // //return R.ok(list); + // } + + @ApiOperation("获取当前补偿值") + @GetMapping("/getCompValue/{boxId}") + public AjaxResult getCompValue(@PathVariable("boxId") Integer boxId) { + Long userId = getUserId(); + TtUser ttUser = userService.getById(userId); + TtBox ttBox = boxService.getById(boxId); + if (Objects.isNull(ttBox)) + return error("宝箱不存在"); + if (!"3".equals(ttBox.getBoxType())) + return error("该宝箱不是补偿宝箱"); + return success(bindBoxService.getCompValue(ttUser, ttBox)); + } + + @ApiOperation("盲盒top") + @GetMapping("/top") + public R> topBlinkBox() { + Long userId = getUserId(); + if (ObjectUtil.isNull(userId)) { + return R.fail("登录过期。"); + } + queryCondition param = new queryCondition(); + param.setUserId(userId.intValue()); + param.setOrderByFie(2); + param.setSource(Collections.singletonList(TtboxRecordSource.BLIND_BOX.getCode())); + param.setStatus(Arrays.asList(TtboxRecordStatus.IN_PACKSACK_ON.getCode(), TtboxRecordStatus.APPLY_DELIVERY.getCode(), TtboxRecordStatus.DELIVERY_YET.getCode())); + param.setPage(1); + param.setSize(1); + PageUtils.clearPage(); + List ttBoxRecordsVOS = new ArrayList<>(apiBoxRecordsService.byCondition(param)); + param.setSource(Collections.singletonList(TtboxRecordSource.FIGHT.getCode())); + ttBoxRecordsVOS.addAll(apiBoxRecordsService.byCondition(param)); + param.setSource(Collections.singletonList(TtboxRecordSource.UPGRADE.getCode())); + ttBoxRecordsVOS.addAll(apiBoxRecordsService.byCondition(param)); + return R.ok(ttBoxRecordsVOS); + } + + @ApiOperation("十佳掉落") + @GetMapping("/tenTop") + public R> tenTop() { + TenTopQuery param = new TenTopQuery(); + param.setSource(Collections.singletonList(TtboxRecordSource.BLIND_BOX.getCode())); + param.setStatus(Arrays.asList(TtboxRecordStatus.IN_PACKSACK_ON.getCode(), TtboxRecordStatus.APPLY_DELIVERY.getCode(), TtboxRecordStatus.DELIVERY_YET.getCode())); + param.setPage(1); + param.setSize(10); + PageUtils.clearPage(); + List ttBoxRecordsVOS = apiBoxRecordsService.tenTopQuery(param); + return R.ok(ttBoxRecordsVOS); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiBoxRecordsController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiBoxRecordsController.java new file mode 100644 index 0000000..6993e15 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiBoxRecordsController.java @@ -0,0 +1,38 @@ +package com.ruoyi.playingmethod.controller; + +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.domain.dto.boxRecords.queryCondition; +import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO; +import com.ruoyi.playingmethod.service.ApiBoxRecordsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Api(tags = "出货记录") +@RestController +@RequestMapping("/api/boxRecords") +public class ApiBoxRecordsController { + + @Autowired + private ApiBoxRecordsService apiBoxRecordsService; + + @ApiOperation("获取历史出货记录") + @PostMapping("/historyByCondition") + public R getBindBoxHistory(@RequestBody @Validated queryCondition param) { + if (param.getStatus().contains(1) || param.getStatus().contains(10)) { + return R.fail("非法的状态参数。"); + } + PageUtils.clearPage(); + List ttBoxRecordsVOS = apiBoxRecordsService.byCondition(param); + return R.ok(ttBoxRecordsVOS); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiCompoundController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiCompoundController.java new file mode 100644 index 0000000..744cd64 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiCompoundController.java @@ -0,0 +1,56 @@ +package com.ruoyi.playingmethod.controller; + +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.playingmethod.service.ApiCompoundService; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.domain.vo.UserPackSackDataVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Api(tags = "合成") +@RestController +@RequestMapping("/api/compound") +public class ApiCompoundController extends BaseController { + + private final ISysConfigService sysConfigService; + private final TtUserService userService; + private final ApiCompoundService compoundService; + + public ApiCompoundController(ISysConfigService sysConfigService, + TtUserService userService, + ApiCompoundService compoundService) { + this.sysConfigService = sysConfigService; + this.userService = userService; + this.compoundService = compoundService; + } + + @ApiOperation("合成") + @PostMapping("/compound") + public R compound(@RequestBody List packSackIds) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + String compoundMaintenance = sysConfigService.selectConfigByKey("compoundMaintenance"); + if ("1".equals(compoundMaintenance)) { + return R.fail("汰换合成功能正在维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + return compoundService.compound(packSackIds, ttUser); + } + + @ApiOperation("获取用户合成记录") + @GetMapping("/getUserCompoundRecord") + public PageDataInfo getUserCompoundRecord() { + startPage(); + List userCompoundRecord = compoundService.getUserCompoundRecord(getUserId().intValue()); + return getPageData(userCompoundRecord); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiExponentController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiExponentController.java new file mode 100644 index 0000000..beb8dc4 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiExponentController.java @@ -0,0 +1,125 @@ +package com.ruoyi.playingmethod.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.TtBoxService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.dto.exponent.ExponentJoinParam; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.playingmethod.mapper.ApiBindBoxMapper; +import com.ruoyi.playingmethod.service.ApiExponentService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +@Api(tags = "指数盲盒") +@RestController +@RequestMapping("/api/exponent") +public class ApiExponentController extends BaseController { + + @Autowired + private ApiExponentService apiExponentService; + + @Autowired + private TtUserService ttUserService; + + @Autowired + private ApiBindBoxMapper bindBoxMapper; + + @Autowired + private TtBoxService boxService; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + @ApiOperation("加入指数盲盒") + @Anonymous + @UpdateUserCache + @PostMapping("/join") + public R join(@RequestBody @Validated ExponentJoinParam param) { + TtUser ttUser = ttUserService.getById(getUserId()); + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(TtUserBlendErcash::getUserId, ttUser.getUserId()) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode()) + .select(TtUserBlendErcash::getTotal); + List ercash = ttUserBlendErcashMapper.selectList(queryWrapper); + if (CollectionUtils.isEmpty(ercash)) { + return R.fail("您的账户未进行充过值!"); + } + + return apiExponentService.join(ttUser, param); + } + + @ApiOperation("获取宝箱列表") + @ApiImplicitParams({ + @ApiImplicitParam(name = "boxType", value = "宝箱类型(1普通箱 2对战箱 3补偿箱 4九宫格 5指数盲盒)", dataType = "String", dataTypeClass = String.class) + }) + @Anonymous + @GetMapping("/box") + public R box(@RequestParam("id") Integer id) { + TtUser ttUser = ttUserService.getById(getUserId()); + return apiExponentService.box(ttUser, id); + } + + @ApiOperation("最近十期结果") + @Anonymous + @GetMapping("/recenttenperiod") + public R recenttenperiod() { + PageUtils.clearPage(); + return apiExponentService.recenttenperiod(); + } + + @ApiOperation("结果信息") + @Anonymous + @GetMapping("/info") + public R info() { + PageUtils.clearPage(); + TtUser ttUser = ttUserService.getById(getUserId()); + return apiExponentService.info(ttUser); + } + + @ApiOperation("指数记录") + @Anonymous + @GetMapping("/records") + public R records() { + startPage(); + TtUser ttUser = ttUserService.getById(getUserId()); + return apiExponentService.records(ttUser); + } + + @ApiOperation("我的箱子") + @Anonymous + @GetMapping("/mybox") + public R mybox() { + TtUser ttUser = ttUserService.getById(getUserId()); + return apiExponentService.mybox(ttUser); + } + @ApiOperation("盲盒开箱") + @UpdateUserCache + @GetMapping("/openBox") + public R openBox(@RequestParam("boxId") Integer boxId, @RequestParam("num") Integer num) { + TtUser ttUser = ttUserService.getById(getUserId()); + TtBox ttBox = boxService.getById(boxId); + return apiExponentService.openbox(ttUser, ttBox, num); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiFightController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiFightController.java new file mode 100644 index 0000000..495f8c9 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiFightController.java @@ -0,0 +1,492 @@ +package com.ruoyi.playingmethod.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.*; +import com.ruoyi.admin.util.core.fight.LotteryMachine; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.RandomUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.dto.fight.FightDetailParam; +import com.ruoyi.domain.dto.fight.FightOnMyOwnParam; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateFightBody; +import com.ruoyi.domain.other.FightBoutData; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtBoxVO; +import com.ruoyi.domain.vo.ApiFightListDataVO; +import com.ruoyi.domain.vo.FightResultDataVO; +import com.ruoyi.domain.vo.fight.FightResultVO; +import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO; +import com.ruoyi.playingmethod.model.vo.ApiFightRankingVO; +import com.ruoyi.playingmethod.service.ApiFightService; +import com.ruoyi.system.service.ISysConfigService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.TimeUnit; + +@Api(tags = "对战模式") +@Slf4j +@RestController +@RequestMapping("/api/fight") +public class ApiFightController extends BaseController { + + private final TtFightResultService fightResultService; + private final ISysConfigService sysConfigService; + private final TtUserService userService; + private final ApiFightService apiFightService; + private final RedisCache redisCache; + + @Autowired + private TtBoxOrnamentsService boxOrnamentsService; + + @Autowired + private TtOrnamentService ttOrnamentService; + + @Autowired + private LotteryMachine lotteryMachine; + + @Autowired + private TtBoxService boxService; + + @Autowired + private TtUserMapper ttUserMapper; + + public ApiFightController(TtFightResultService fightResultService, + ISysConfigService sysConfigService, + TtUserService userService, + ApiFightService apiFightService, + RedisCache redisCache) { + this.fightResultService = fightResultService; + this.sysConfigService = sysConfigService; + this.userService = userService; + this.apiFightService = apiFightService; + this.redisCache = redisCache; + } + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + public R checkLogin() { + Long userId; + try { + userId = getUserId(); + if (ObjectUtil.isEmpty(userId)) return R.fail(401, "登录过期,请重新登录。"); + return R.ok(userId); + } catch (Exception e) { + return R.fail("登录过期,请重新登录。"); + } + } + + /** + * Step1 创建对战房间 + */ + @ApiOperation("创建对战房间") + @UpdateUserCache + @PostMapping("/createFight") + public R createFight(@Validated @RequestBody CreateFightBody createFightParam) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance"); + if ("1".equals(fightMaintenance)) { + return R.fail("开箱对战功能正在维护中......"); + } + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) { + return checkLogin; + } + Integer userId = ((Long) checkLogin.getData()).intValue(); + TtUser player = userService.getById(userId); + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(TtUserBlendErcash::getUserId, userId) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode()) + .select(TtUserBlendErcash::getTotal); + List ercash = ttUserBlendErcashMapper.selectList(queryWrapper); + if (CollectionUtils.isEmpty(ercash)) { + return R.fail("您的账户未进行充过值!"); + } + + return apiFightService.createFight(createFightParam, player); + } + + /** + * Step2 加入房间(占座),先行HTTP + */ + @ApiOperation("加入对战房间") + @PostMapping("/joinFightRoom") + public R joinFightRoom(@RequestParam("fightId") Integer fightId) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance"); + if ("1".equals(fightMaintenance)) { + return R.fail("开箱对战功能正在维护中......"); + } + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) return checkLogin; + Integer userId = ((Long) checkLogin.getData()).intValue(); + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(TtUserBlendErcash::getUserId, userId) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode()) + .select(TtUserBlendErcash::getTotal); + List ercash = ttUserBlendErcashMapper.selectList(queryWrapper); + if (CollectionUtils.isEmpty(ercash)) { + return R.fail("您的账户未进行充过值!"); + } + + TtUser player = userService.getById(userId); + + return apiFightService.joinFight(fightId, player); + } + + /** + * 退出房间 + */ + @ApiOperation("退出房间") + @PostMapping("/fightRoomExit") + public R fightRoomExit(@RequestParam("fightId") Integer fightId) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) return R.fail("网站维护中......"); + String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance"); + if ("1".equals(fightMaintenance)) return R.fail("开箱对战功能正在维护中......"); + + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) return checkLogin; + Integer userId = ((Long) checkLogin.getData()).intValue(); + + TtUser player = userService.getById(userId); + + return apiFightService.fightRoomExit(fightId, player); + } + + /** + * Step3 玩家准备游戏 + */ + @ApiOperation("玩家准备游戏") + @UpdateUserCache + @GetMapping("/seatrReady") + public R seatrReady(@RequestParam("fightId") Integer fightId) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance"); + if ("1".equals(fightMaintenance)) { + return R.fail("开箱对战功能正在维护中......"); + } + + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) return checkLogin; + Integer userId = ((Long) checkLogin.getData()).intValue(); + + TtUser player = userService.getById(userId); + + return apiFightService.seatrReady(fightId, player); + } + + /** + * Step4 开始游戏(前端满人自动开始) + */ + @ApiOperation("开始游戏") + @GetMapping("/fightBegin") + public R fightBegin(@RequestParam("fightId") Integer fightId) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance"); + if ("1".equals(fightMaintenance)) { + return R.fail("开箱对战功能正在维护中......"); + } + + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) return checkLogin; + Integer userId = ((Long) checkLogin.getData()).intValue(); + + TtUser player = userService.getById(userId); + + return apiFightService.fightBegin(fightId, player); + } + + /** + * 观战 + */ + @ApiOperation("观战") + @GetMapping("/audience") + public R audience(@RequestParam("fightId") Integer fightId) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance"); + if ("1".equals(fightMaintenance)) { + return R.fail("开箱对战功能正在维护中......"); + } + + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) return checkLogin; + + return apiFightService.audience(fightId); + } + + /** + * 游戏结束标记 + */ + @ApiOperation("游戏结束标记") + @GetMapping("/fightEnd") + public R fightEnd(@RequestParam("fightId") Integer fightId) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) return R.fail("网站维护中......"); + String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance"); + if ("1".equals(fightMaintenance)) return R.fail("开箱对战功能正在维护中......"); + + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) return checkLogin; + + return apiFightService.fightEnd(fightId); + } + + /** + * 抽奖机测试 + */ + // @ApiOperation("抽奖机测试") + // @GetMapping("LotteryMachineTest") + public R LotteryMachineTest() { + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) return checkLogin; + Integer userId = ((Long) checkLogin.getData()).intValue(); + TtUser player = userService.getById(userId); + + TtBox box = boxService.getById(2L); + String ornamentId = lotteryMachine.singleLottery(player, box); + + return R.ok(ornamentId); + } + + @ApiOperation("我参与的历史对战记录") + @PostMapping("/fightOnMyOwn") + public R fightOnMyOwn(@RequestBody FightOnMyOwnParam param) { + Long userId = getUserId(); + if (ObjectUtil.isNull(userId)) return R.fail(401, "登录过期。"); + + param.setPlayerId(userId.intValue()); + + List list = apiFightService.getFightList(param); + return R.ok(list); + } + + @ApiOperation("全服历史对战记录") + @PostMapping("/fightOnAll") + public R fightOnAll(@RequestBody FightOnMyOwnParam param) { + List list = apiFightService.getFightList(param); + return R.ok(list); + } + + @ApiOperation("历史对战详情") + @PostMapping("/fightDetail") + public R fightDetail(@RequestBody FightDetailParam param) { + return apiFightService.fightDetail(param); + } + + @ApiOperation("获取指定记录更早的历史对战") + @PostMapping("/earlierHistory") + public R earlierHistory(@RequestBody FightDetailParam param) { + return apiFightService.earlierHistory(param); + } + + @ApiOperation("获取对战宝箱详情") + @GetMapping("/simpleBoxDetail") + public PageDataInfo simpleBoxDetail(Integer boxId) { + startPage(); + List list = boxOrnamentsService.simpleBoxDetail(boxId); + return getPageData(list); + } + + @ApiOperation("获取对战宝箱") + @GetMapping("/getFightBoxList") + @Anonymous + public R> getFightBoxList(@RequestParam(value = "boxTypeId", required = false) Integer boxTypeId) { + List boxData = apiFightService.getFightBoxList(boxTypeId); + if (ObjectUtil.isEmpty(boxData) || boxData.isEmpty()) return R.ok(null, "没有匹配的数据。"); + return R.ok(boxData); + } + + @ApiOperation("获取对战列表") + @Anonymous + @GetMapping("/getFightList") + public PageDataInfo getFightList(@RequestParam(required = false) String model, + @RequestParam(required = false) String status, + @RequestParam(required = false) Integer userId) { + startPage(); + List list = apiFightService.getFightList(model, status, userId, null); + return getPageData(list); + } + + @ApiOperation("获取对战数据") + @GetMapping("/getFightData/{fightId}") + public R getFightData(@PathVariable("fightId") Integer fightId) { + ApiFightListDataVO apiFightListDataVO = apiFightService.getFightList(null, null, null, fightId).get(0); + return R.ok(apiFightListDataVO); + } + + // @ApiOperation("获取对战结果") + // @GetMapping("/getFightResult/{fightId}") + // public R getFightResult(@PathVariable("fightId") Integer fightId) { + // FightResultDataVO fightResultDataVO = fightResultService.getFightResult(fightId); + // return R.ok(fightResultDataVO); + // } + + @ApiOperation("获取对战结果") + @GetMapping("/getFightResult/{fightId}") + public R getFightResult(@PathVariable("fightId") Integer fightId) { + FightResultVO fightResultVO = fightResultService.getFightResult(fightId); + return R.ok(fightResultVO); + } + + @ApiOperation("存储对战回合数据") + @PostMapping("/saveFightBoutData") + public R saveFightBoutData(@RequestBody FightBoutData fightBoutData) { + String key = "fight_bout_data:fight_" + fightBoutData.getFightId(); + redisCache.setCacheObject(key, fightBoutData, fightBoutData.getExpirationTime(), TimeUnit.MILLISECONDS); + return R.ok(); + } + + @ApiOperation("获取对战回合数") + @PostMapping("/getFightBoutNum/{fightId}") + public R getFightBoutNum(@PathVariable Integer fightId) { + String key = "fight_bout_data:fight_" + fightId; + FightBoutData fightBoutData = redisCache.getCacheObject(key); + if (StringUtils.isNotNull(fightBoutData)) { + return R.ok(fightBoutData.getBoutNum()); + } else { + // return R.fail("对战回合不存在"); + return R.ok(); + } + } + + /** + * 获取当前回合开箱结果 + */ + // @GetMapping("/getFightRecord") + public R getFightRecord(@RequestParam(value = "fightId", required = false) Integer fightId, + @RequestParam(value = "round", required = false) Integer round, + @RequestParam(value = "rounds", required = false) Integer rounds) { + // System.out.println("=========================获取当前回合开箱结果============================="); + // System.out.println("fightId:" + fightId); + // System.out.println("round:" + round); + // System.out.println("rounds:" + rounds); + + log.info("获取当前回合开箱结果 api /{} 参数fightId{} round{} rounds{}", "getFightRecord", fightId, round, rounds); + + // 检查是否已经开始游戏 + TtFight fight = apiFightService.getById(fightId); + if (ObjectUtil.isEmpty(fight)) return R.fail("不存在的对局"); + if (fight.getStatus().equals(0)) return R.fail("对局尚未开始,请稍后重试。"); + + FightResultDataVO fightResultDataVO = apiFightService.getFightRecord(fightId, round, rounds); + if (ObjectUtil.isEmpty(fightResultDataVO)) { + return R.fail("对局fightId 没有产生结果"); + } + return R.ok(fightResultDataVO); + } + + @ApiOperation("获取对战排行榜") + @Anonymous + @GetMapping("/fightRanking") + public AjaxResult getFightRanking() { + // 创建日期格式化对象,指定日期格式 + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + + // 获取今天日期 + Date today = new Date(); + String formattedTodayDate = formatter.format(today); + List todayFightRanking = apiFightService.getFightRankingByDate(formattedTodayDate); + + // 获取昨天日期 + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DATE, -1); + Date yesterday = cal.getTime(); + String formattedYesterdayDate = formatter.format(yesterday); + List yesterdayFightRanking = apiFightService.getFightRankingByDate(formattedYesterdayDate); + + Map> map = new HashMap<>(); + map.put("todayFightRanking", todayFightRanking); + map.put("yesterdayFightRanking", yesterdayFightRanking); + return AjaxResult.success(map); + } + + @ApiOperation("十佳对战, 1:最大金额,2:最大增长,3:最大倍数") + @Anonymous + @GetMapping("/tenTopFight") + public AjaxResult tenTopFight(@RequestParam("maxType") Integer maxType) { + return AjaxResult.success(apiFightService.tenTopFight(maxType)); + } + + @ApiOperation("邀请机器人加入对战房间") + @PostMapping({"/joinFightRoomForRobot"}) + public R joinFightRoomForRobot(@RequestParam("fightId") Integer fightId) { + String websiteMaintenance = this.sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } else { + String fightMaintenance = this.sysConfigService.selectConfigByKey("fightMaintenance"); + if ("1".equals(fightMaintenance)) { + return R.fail("开箱对战功能正在维护中......"); + } else { + List robotIdList = new LambdaQueryChainWrapper(this.ttUserMapper) + .eq(TtUser::getUserType, UserType.ROBOT_USER.getCode()).list(); + if (CollectionUtils.isEmpty(robotIdList)) { + return R.ok(); + } else { + TtUser player = RandomUtil.choice(robotIdList); + R objectR = this.apiFightService.joinFight(fightId, player); + if (objectR.getCode() == 500) { + return objectR; + } else { + log.info("机器人{}加入对战{}", player.getUserId(), fightId); + R fight = this.apiFightService.seatrReady(fightId, player); + log.info("fight robot task fight is {}", fight == null ? "null" : fight.toString()); + FightBoutData fightBoutData = new FightBoutData(); + fightBoutData.setFightId(fightId); + fightBoutData.setBoutNum(1); + fightBoutData.setExpirationTime(16000); + String key = "fight_bout_data:fight_" + fightBoutData.getFightId(); + this.redisCache.setCacheObject(key, fightBoutData, fightBoutData.getExpirationTime(), TimeUnit.MILLISECONDS); + this.apiFightService.fightBegin(fightId, player); + return R.ok(); + } + } + } + } + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiLuckyUpgradeController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiLuckyUpgradeController.java new file mode 100644 index 0000000..13139ff --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiLuckyUpgradeController.java @@ -0,0 +1,128 @@ +package com.ruoyi.playingmethod.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.dto.upgrade.UpgradeCondition; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.other.ApiLuckyUpgradeBody; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.UpgradeBodyA; +import com.ruoyi.domain.vo.ApiLuckyOrnamentsDataVO; +import com.ruoyi.playingmethod.service.ApiLuckyUpgradeService; +import com.ruoyi.playingmethod.service.ApiUpgradeRecordService; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.user.service.ApiUserPackSackService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.hibernate.validator.constraints.Range; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.List; + +@Api(tags = "幸运升级模式") +@RestController +@RequestMapping("/api/luckyUpgrade") +public class ApiLuckyUpgradeController extends BaseController { + + @Autowired + private ApiUserPackSackService userPackSackService; + + @Autowired + private ApiUpgradeRecordService upgradeRecordService; + + private final TtUserService userService; + private final ApiLuckyUpgradeService apiLuckyUpgradeService; + private final ISysConfigService sysConfigService; + + public ApiLuckyUpgradeController(TtUserService userService, + ApiLuckyUpgradeService apiLuckyUpgradeService, + ISysConfigService sysConfigService) { + this.userService = userService; + this.apiLuckyUpgradeService = apiLuckyUpgradeService; + this.sysConfigService = sysConfigService; + } + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + public R checkLogin(){ + Long userId; + try { + userId = getUserId(); + if (ObjectUtil.isEmpty(userId)) AjaxResult.error(401,"登录过期,请重新登录。"); + return R.ok(userId); + }catch (Exception e){ + return R.fail(401,"登录过期,请重新登录。"); + } + } + + @ApiOperation("获取可升级的饰品列表") + @Anonymous + @GetMapping("/getOrnamentsList") + public R> getOrnamentsList(ApiLuckyUpgradeBody apiLuckyUpgradeBody){ + List list = apiLuckyUpgradeService.getOrnamentsList(apiLuckyUpgradeBody); + return R.ok(list); + } + + @ApiOperation("幸运升级") + @UpdateUserCache + @PostMapping("/upgrade") + public R upgrade(@RequestBody @Validated UpgradeBodyA upgradeParam){ + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) return R.fail("网站维护中......"); + + R check = checkLogin(); + if (!check.getCode().equals(200)) return R.fail(401,"登录过期,请重新登录。"); + Integer userId = ((Long)check.getData()).intValue(); + + TtUser ttUser = userService.getById(userId); + BigDecimal add = ttUser.getAccountAmount(); + + if (add.compareTo(upgradeParam.getPrice()) <= 0) { + return R.fail("余额不足!"); + } + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(TtUserBlendErcash::getUserId, userId) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode()) + .select(TtUserBlendErcash::getTotal); + List ercash = ttUserBlendErcashMapper.selectList(queryWrapper); + if (CollectionUtils.isEmpty(ercash)) { + return R.fail("您的账户未进行充过值!"); + } + + return apiLuckyUpgradeService.upgrade(ttUser, upgradeParam); + } + + @ApiOperation("获取幸运升级历史记录") + @PostMapping("/getUpgradeRecord") + public R getUpgradeRecord(@RequestBody @Validated UpgradeCondition param){ + return upgradeRecordService.historyDetail(param); + } + + @ApiOperation("获取幸运升级十佳记录") + @GetMapping("/tenTopUpgradeRecord") + public R tenTopUpgradeRecord(@RequestParam(required = false) Boolean isVictory){ + return upgradeRecordService.tenTopDetail(isVictory); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiRedPackContoller.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiRedPackContoller.java new file mode 100644 index 0000000..c36d2be --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiRedPackContoller.java @@ -0,0 +1,98 @@ +package com.ruoyi.playingmethod.controller; + +import com.ruoyi.common.annotation.NoRepeatSubmit; +import com.ruoyi.common.annotation.UserPermission; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.domain.other.TtRedPack; +import com.ruoyi.domain.other.TtRedPacketRecord; +import com.ruoyi.playingmethod.service.ApiRedPackService; +import com.ruoyi.thirdparty.wechat.domain.PtLoginUser; +import com.ruoyi.thirdparty.wechat.service.ApiTokenService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 不用这个api领红包了 + * + * @see com.ruoyi.user.controller.ApiBonusController + */ +@Deprecated +@RestController +@RequestMapping("/api/redPackData") +@Api(tags = "红包") +public class ApiRedPackContoller extends BaseController { + + @Autowired + private ApiTokenService tokenService; + + @Autowired + private ApiRedPackService redPackService; + + @PostMapping("/getRedPackList") + @UserPermission + @NoRepeatSubmit + @ApiOperation(value = "获取红包列表") + public TableDataInfo getRedPackList(HttpServletRequest request, Integer type) { + PtLoginUser user = tokenService.getLoginUser(request); + + startPage(); + List redPacks = redPackService.getRedPackList(user, type); + + return getDataTable(redPacks); + } + + @Deprecated + @PostMapping("/getRedPack") + @UserPermission + @NoRepeatSubmit + @ApiOperation(value = "领取红包") + public AjaxResult getRedPack(@RequestParam("redPackId") Long redPackId) { + Long userId = getUserId(); + return redPackService.getRedPack(userId, redPackId); + } + + @PostMapping("/getRedPackByKey") + @UserPermission + @NoRepeatSubmit + @ApiOperation(value = "根据红包口令兑换红包") + public AjaxResult getRedPackByKey(@RequestParam("redPackKey") String redPackKey) { + LoginUser loginUser = getLoginUser(); + if (redPackKey == null || "".equals(redPackKey)) { + return AjaxResult.error("没有填写红包口令"); + } + return redPackService.getRedPackByKey(loginUser, redPackKey); + } + + @PostMapping("/getActivityDataHis") + @UserPermission + @NoRepeatSubmit + @ApiOperation(value = "查询参与的红包活动记录") + public TableDataInfo getBoxDataHis(HttpServletRequest request) { + PtLoginUser user = tokenService.getLoginUser(request); + startPage(); + List boxRecords = redPackService.getActivityDataHis(user); + + return getDataTable(boxRecords); + } + + @PostMapping("/getRedPackDataDetail") + @UserPermission + @NoRepeatSubmit + @ApiOperation(value = "查询历史记录") + public AjaxResult getRedPackDataDetail(HttpServletRequest request, @RequestParam("recordId") Long recordId) { + PtLoginUser user = tokenService.getLoginUser(request); + return redPackService.getRedPackDataDetail(user, recordId); + } + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiReplacementRecordController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiReplacementRecordController.java new file mode 100644 index 0000000..22a60ee --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiReplacementRecordController.java @@ -0,0 +1,42 @@ +package com.ruoyi.playingmethod.controller; + +import com.ruoyi.common.annotation.UserPermission; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.playingmethod.model.body.SynthesizeRequest; +import com.ruoyi.playingmethod.service.IApiReplacementRecordService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.servlet.http.HttpServletRequest; + + +/** + * 汰换记录Controller + * + * @author junhai + * @date 2023-09-10 + */ +@RestController +@RequestMapping("/api/replacementRecord") +@Api(tags = "汰换记录") +public class ApiReplacementRecordController extends BaseController { + + @Autowired + private IApiReplacementRecordService ttReplacementRecordService; + + @PostMapping("/synthesizeItems") + @UserPermission + @ApiOperation("汰换饰品") + public AjaxResult synthesizeItems(@RequestBody SynthesizeRequest synthesizeRequest) { + LoginUser loginUser = getLoginUser(); + return ttReplacementRecordService.synthesizeItems(loginUser, synthesizeRequest.getBoIds()); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiRollController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiRollController.java new file mode 100644 index 0000000..2c0e333 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiRollController.java @@ -0,0 +1,176 @@ +package com.ruoyi.playingmethod.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.rabbitmq.config.DelayedQueueConfig; +import com.ruoyi.domain.dto.roll.GetRollOpenPrizeParam; +import com.ruoyi.domain.dto.roll.GetRollPlayersParam; +import com.ruoyi.domain.dto.roll.GetRollPrizePool; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO; +import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsVO; +import com.ruoyi.domain.vo.roll.RollUserVO; +import com.ruoyi.playingmethod.service.ApiRollService; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.domain.vo.RollDetailsDataVO; +import com.ruoyi.domain.vo.RollListDataVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageProperties; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@Api(tags = "ROLL房模式") +@RestController +@RequestMapping("/api/roll") +public class ApiRollController extends BaseController { + + private final ISysConfigService sysConfigService; + private final TtUserService userService; + private final ApiRollService apiRollService; + + public ApiRollController(ISysConfigService sysConfigService, + TtUserService userService, + ApiRollService apiRollService) { + this.sysConfigService = sysConfigService; + this.userService = userService; + this.apiRollService = apiRollService; + } + + + @AllArgsConstructor + @NoArgsConstructor + @Data + public static class JoinRollParam { + @NotNull(message = "roll房id不能为空。") + private Integer rollId; + private String rollPassword; + } + + @ApiOperation("加入ROLL房") + @RepeatSubmit(message = "操作过于频繁,请稍后重试!") + @PostMapping("/joinRoll") + public R joinRoll(@RequestBody @Validated JoinRollParam param) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) return R.fail("网站维护中......"); + String rollMaintenance = sysConfigService.selectConfigByKey("rollMaintenance"); + if ("1".equals(rollMaintenance)) return R.fail("Roll房功能正在维护中......"); + + Long userId = getUserId(); + if (ObjectUtil.isEmpty(userId)) return R.fail(401, "登录过期,请重新登录。"); + TtUser player = userService.getById(userId); + if (ObjectUtil.isEmpty(player)) return R.fail("异常的用户状态。"); + + return apiRollService.joinRoll(param, player); + // return StringUtils.isEmpty(msg) ? R.ok("成功加入Roll房!") : R.fail(msg, "加入失败!"); + } + + @AllArgsConstructor + @NoArgsConstructor + @Data + public static class GetRollListParam { + + private String rollName; + + // 0官方 1主播 + @ApiModelProperty("0官方 1主播") + private Integer rollType; + + // 0未开奖 1已开奖 + @ApiModelProperty("0未开奖 1已开奖") + private Integer rollStatus; + + private Integer userId; + } + + @ApiOperation("获取ROLL房列表") + @GetMapping("/getRollList") + public TableDataInfo getRollList(GetRollListParam param) { + startPage(); + List list = apiRollService.getRollList(param); + return getDataTable(list); + } + + /** + * 我参与的ROLL房列表 + */ + // @GetMapping("/getMyPartRollList") + public PageDataInfo getMyPartRollList(@RequestBody GetRollListParam param) { + startPage(); + List list = apiRollService.getRollList(param); + return getPageData(list); + } + + @ApiOperation("获取ROLL房详情") + @GetMapping("/getRollDetails/{rollId}") + public R getRollDetails(@PathVariable("rollId") Integer rollId) { + return apiRollService.getRollDetails(rollId); + } + + @ApiOperation("获取ROLL房参与人员详情") + @PostMapping("/getRollPlayers") + public R> getRollPlayers(@RequestBody @Validated GetRollPlayersParam param) { + return apiRollService.getRollPlayers(param); + } + + @ApiOperation("获取ROLL房奖池详情") + @PostMapping("/getRollPrizePool") + public R> getRollPrizePool(@RequestBody @Validated GetRollPrizePool param) { + return apiRollService.getRollPrizePool(param); + } + + @ApiOperation("获取ROLL开奖详情") + @PostMapping("/getRollOpenPrize") + public R> getRollOpenPrize(@RequestBody @Validated GetRollOpenPrizeParam param) { + return apiRollService.getRollOpenPrize(param); + } + + @ApiOperation("开奖测试,正式环境删除") + @GetMapping("/endRoll/{rollId}") + public R endRoll(@PathVariable("rollId") Integer rollId){ + return apiRollService.endROLL(rollId); + } + + // @Autowired + private RabbitTemplate rabbitTemplate; + + // @GetMapping("/endRoll/{rollId}") + public String mqTest(@PathVariable("rollId") Integer rollId) { + + // Calendar c = Calendar.getInstance(); + // long timeInMillis = c.getTimeInMillis(); + // c.add(Calendar.SECOND, 3); + // long timeInMillis1 = c.getTimeInMillis(); + // long l = timeInMillis1 - timeInMillis; + + MessageProperties messageProperties = new MessageProperties(); + messageProperties.setExpiration("3000"); // 消息的过期属性,单位 ms + Message message = new Message("这条消息 4 秒后过期".getBytes(), messageProperties); + + rabbitTemplate.convertAndSend( + DelayedQueueConfig.OPEN_ROLL_EXCHANGE, + DelayedQueueConfig.OPEN_ROLL_KEY, + message + ); + + return "1"; + } + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiTtAttendanceRecordController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiTtAttendanceRecordController.java new file mode 100644 index 0000000..3ee98af --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiTtAttendanceRecordController.java @@ -0,0 +1,52 @@ +package com.ruoyi.playingmethod.controller; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.playingmethod.entity.TtAttendanceRecord; +import com.ruoyi.playingmethod.service.IApiAttendanceRecordService; +import io.swagger.annotations.Api; +import org.apache.poi.ss.formula.functions.T; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Api(tags = "签到记录") +@RestController +@RequestMapping("/api/attendanceRecord") +public class ApiTtAttendanceRecordController extends BaseController { + private final IApiAttendanceRecordService iApiAttendanceRecordService; + + public ApiTtAttendanceRecordController(IApiAttendanceRecordService iApiAttendanceRecordService) { + this.iApiAttendanceRecordService = iApiAttendanceRecordService; + } + + /** + * 签到 + */ + // @GetMapping("/attendance") + public R attendance() { + System.out.println("==================签到================="); + TtAttendanceRecord ttAttendanceRecord = iApiAttendanceRecordService.selectByUid(getUserId().intValue()); + if (ttAttendanceRecord == null){ + // 增加签到记录 + iApiAttendanceRecordService.insert(getUserId().intValue()); + return R.ok("签到成功"); + } + // List userCompoundRecord = iApiAttendanceRecordService.getUserCompoundRecord(getUserId().intValue()); + return R.fail("签到失败,一天只能签到一次"); + } + + /** + * 七天签到记录 + */ + // @GetMapping("/sevenAttendance") + public R> sevenAttendance() { + System.out.println("==================七天签到================="); + // TtAttendanceRecord ttAttendanceRecord = iApiAttendanceRecordService.selectByUid(getUserId().intValue()); + List ttAttendanceRecordList = iApiAttendanceRecordService.selectSevenAttendance(getUserId().intValue()); + return R.ok(ttAttendanceRecordList); + } + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiUserAvatarController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiUserAvatarController.java new file mode 100644 index 0000000..39f4123 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiUserAvatarController.java @@ -0,0 +1,30 @@ +package com.ruoyi.playingmethod.controller; + +import com.ruoyi.admin.service.TtUserAvatarService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.other.TtUserAvatar; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Api(tags = "用户头像") +@RestController +@RequestMapping("/api/userAvatar") +public class ApiUserAvatarController extends BaseController { + + private final TtUserAvatarService userAvatarService; + + public ApiUserAvatarController(TtUserAvatarService userAvatarService) { + this.userAvatarService = userAvatarService; + } + + @ApiOperation("用户头像列表") + @GetMapping("/list") + public R> list() { + List list = userAvatarService.list(); + return R.ok(list); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiWelfareController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiWelfareController.java new file mode 100644 index 0000000..3608218 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiWelfareController.java @@ -0,0 +1,92 @@ +package com.ruoyi.playingmethod.controller; + +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.service.TtWelfareService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtWelfare; +import com.ruoyi.domain.other.TtWelfareMonthlyRecharges; +import com.ruoyi.domain.vo.OpenBoxVO; +import com.ruoyi.playingmethod.model.ApiWelfare; +import com.ruoyi.playingmethod.model.ApiWelfareRecord; +import com.ruoyi.playingmethod.service.ApiWelfareService; +import com.ruoyi.system.service.ISysConfigService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageProperties; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.*; + +@Api(tags = "福利") +@RestController +@RequestMapping("/api/welfare") +public class ApiWelfareController extends BaseController { + + @Autowired + private ApiWelfareService apiWelfareService; + + @Autowired + private TtWelfareService ttWelfareService; + + @Autowired + private ISysConfigService sysConfigService; + + @Autowired + private TtUserService userService; + + @ApiOperation("获取福利列表") + @ApiResponse(code = 200, message = "eligible【是否具备领取条件(0否 1是)】" + + "
claimStatus【领取状态(0未领取 1已领取)】") + @GetMapping("/getWelfareList") + public TableDataInfo getWelfareList() { + startPage(); + Long userId = SecurityUtils.getUserId(); + List list = apiWelfareService.getWelfareList(userId); + return getDataTable(list); + } + + @ApiOperation("领取福利") + @GetMapping("/claimWelfare/{welfareId}") + public AjaxResult claimWelfare(@PathVariable Integer welfareId) { + Long userId = SecurityUtils.getUserId(); + // 检查福利是否存在 + TtWelfare ttWelfare = ttWelfareService.selectTtWelfareByWelfareId(welfareId); + if (Objects.isNull(ttWelfare)) { + return error("福利不存在"); + } + // 检查是否尚未具备领取条件 + if (apiWelfareService.checkNotEligible(welfareId, userId)) { + return AjaxResult.error("尚未达到领取条件"); + } + // 检查是否已领取 + if (apiWelfareService.checkClaimed(welfareId, userId)) { + return AjaxResult.error("该福利已被领取"); + } + // 进入开箱流程 + String bindBoxMaintenance = sysConfigService.selectConfigByKey("bindBoxMaintenance"); + if ("1".equals(bindBoxMaintenance)) { + return AjaxResult.error("盲盒开箱功能正在维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + if ("0".equals(ttUser.getIsRealCheck())) { + return AjaxResult.error("您的账户未进行实名认证,请先进行实名认证!"); + } + OpenBoxVO openBoxVO = apiWelfareService.claimWelfare(welfareId, userId); + return AjaxResult.success(openBoxVO); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiWelfareMonthlyRechargesController.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiWelfareMonthlyRechargesController.java new file mode 100644 index 0000000..fb9d4fa --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/controller/ApiWelfareMonthlyRechargesController.java @@ -0,0 +1,85 @@ +package com.ruoyi.playingmethod.controller; + +import com.ruoyi.admin.service.ITtWelfareMonthlyRechargesService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtWelfareMonthlyRecharges; +import com.ruoyi.domain.vo.OpenBoxVO; +import com.ruoyi.playingmethod.model.ApiWelfare; +import com.ruoyi.playingmethod.model.vo.ApiWelfareMonthlyRechargesVO; +import com.ruoyi.playingmethod.service.ApiWelfareMonthlyRechargesService; +import com.ruoyi.system.service.ISysConfigService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Objects; + +@Api(tags = "每月充值福利") +@RestController +@RequestMapping("/api/welfare/monthlyRecharges") +public class ApiWelfareMonthlyRechargesController extends BaseController { + + @Autowired + private ApiWelfareMonthlyRechargesService apiWelfareMonthlyRechargesService; + + @Autowired + private ITtWelfareMonthlyRechargesService ttWelfareMonthlyRechargesService; + + @Autowired + private ISysConfigService sysConfigService; + + @Autowired + private TtUserService userService; + + @ApiOperation("获取每月充值福利列表") + @ApiResponse(code = 200, message = "eligible【是否具备领取条件(0否 1是)】" + + "
claimStatus【领取状态(0未领取 1已领取)】") + @GetMapping("/list") + public TableDataInfo getWelfareList() { + startPage(); + Long userId = SecurityUtils.getUserId(); + List list = apiWelfareMonthlyRechargesService.getWelfareList(userId); + return getDataTable(list); + } + + @ApiOperation("领取福利") + @GetMapping("/claimWelfare/{welfareId}") + public AjaxResult claimWelfare(@PathVariable Integer welfareId) { + Long userId = SecurityUtils.getUserId(); + // 检查福利是否存在 + TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges = ttWelfareMonthlyRechargesService.selectTtWelfareMonthlyRechargesById(welfareId); + if (Objects.isNull(ttWelfareMonthlyRecharges)) { + return error("福利不存在"); + } + // 检查是否尚未具备领取条件 + if (!apiWelfareMonthlyRechargesService.checkEligible(welfareId, userId)) { + return AjaxResult.error("尚未达到领取条件"); + } + // 检查是否已领取 + if (apiWelfareMonthlyRechargesService.checkClaimed(welfareId, userId)) { + return AjaxResult.error("该福利已被领取"); + } + // 进入开箱流程 + String bindBoxMaintenance = sysConfigService.selectConfigByKey("bindBoxMaintenance"); + if ("1".equals(bindBoxMaintenance)) { + return AjaxResult.error("盲盒开箱功能正在维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + if ("0".equals(ttUser.getIsRealCheck())) { + return AjaxResult.error("您的账户未进行实名认证,请先进行实名认证!"); + } + OpenBoxVO openBoxVO = apiWelfareMonthlyRechargesService.claimWelfare(welfareId, userId); + return AjaxResult.success(openBoxVO); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/customException/CreateFightException.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/customException/CreateFightException.java new file mode 100644 index 0000000..2e4ea32 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/customException/CreateFightException.java @@ -0,0 +1,7 @@ +package com.ruoyi.playingmethod.customException; + +public class CreateFightException extends RuntimeException{ + public CreateFightException(String msg){ + super(msg); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/customException/WsRepetitiontException.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/customException/WsRepetitiontException.java new file mode 100644 index 0000000..b379e41 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/customException/WsRepetitiontException.java @@ -0,0 +1,8 @@ +package com.ruoyi.playingmethod.customException; + +// websocket用户重复连接异常 +public class WsRepetitiontException extends RuntimeException{ + public WsRepetitiontException(String msg){ + super(msg); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/domain/vo/ApiRobotFightGroupVO.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/domain/vo/ApiRobotFightGroupVO.java new file mode 100644 index 0000000..c2139ef --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/domain/vo/ApiRobotFightGroupVO.java @@ -0,0 +1,20 @@ +package com.ruoyi.playingmethod.domain.vo; + +import com.ruoyi.domain.other.TtRobotFightGroupBox; +import lombok.Data; + +import java.util.List; + +@Data +public class ApiRobotFightGroupVO { + + private Integer groupId; + + private String groupName; + + private Integer seatNum; + + private String model; + + private List ttRobotFightGroupBoxList; +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/domain/vo/CompValueVO.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/domain/vo/CompValueVO.java new file mode 100644 index 0000000..caefbeb --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/domain/vo/CompValueVO.java @@ -0,0 +1,13 @@ +package com.ruoyi.playingmethod.domain.vo; + +import lombok.Data; + +@Data +public class CompValueVO { + + /** 当前补偿值 */ + private Integer currentCompValue; + + /** 设定补偿值 */ + private Integer desiredCompValue; +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/entity/TtAttendanceRecord.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/entity/TtAttendanceRecord.java new file mode 100644 index 0000000..ec54c22 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/entity/TtAttendanceRecord.java @@ -0,0 +1,49 @@ +package com.ruoyi.playingmethod.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +@TableName(value = "tt_attendance_record") +public class TtAttendanceRecord { + + private static final long serialVersionUID = 1L; + + /** */ + private Integer id; + + /** 用户ID */ + @Excel(name = "用户ID") + private Integer userId; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 获取金币数 */ + @Excel(name = "获取金币数") + private Integer coin; + + @Excel(name = "是否删除") + private String delFlag; + + + private String isStatus = "0"; + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiAttendanceRecordMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiAttendanceRecordMapper.java new file mode 100644 index 0000000..d6752a7 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiAttendanceRecordMapper.java @@ -0,0 +1,19 @@ +package com.ruoyi.playingmethod.mapper; + +import com.ruoyi.playingmethod.entity.TtAttendanceRecord; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +@Mapper +public interface ApiAttendanceRecordMapper { + TtAttendanceRecord selectByUid(@Param("uid") Integer uid); + + int insertAttendanceRecord(TtAttendanceRecord ttAttendanceRecord); + + + TtAttendanceRecord selectSevenAttendance(@Param("uid") Integer uid,@Param("createTime") String createTime); + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiBattleRoyaleMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiBattleRoyaleMapper.java new file mode 100644 index 0000000..466f2f9 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiBattleRoyaleMapper.java @@ -0,0 +1,7 @@ +package com.ruoyi.playingmethod.mapper; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ApiBattleRoyaleMapper { +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiBindBoxMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiBindBoxMapper.java new file mode 100644 index 0000000..e8ad2da --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiBindBoxMapper.java @@ -0,0 +1,34 @@ +package com.ruoyi.playingmethod.mapper; + +import com.ruoyi.domain.other.TtBoxUser; +import com.ruoyi.domain.other.TtBoxA; +import com.ruoyi.domain.other.TtOrnamentsA; +import com.ruoyi.domain.other.TtBoxLevelA; +import com.ruoyi.playingmethod.controller.ApiBindBoxController; +import com.ruoyi.playingmethod.domain.vo.CompValueVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface ApiBindBoxMapper { + + TtBoxA getBoxData(Integer boxId); + + List getBoxOrnamentsList(Integer boxId); + + List getBoxList( + @Param("boxTypeId") Integer boxTypeId, + @Param("homeFlag") String homeFlag, + @Param("boxType") String boxType + ); + + TtOrnamentsA getOrnamentsData(@Param("boxId") Integer boxId, @Param("ornamentsId") Integer ornamentsId, @Param("id") Long id); + + // List getBindBoxHistory(ApiBindBoxController.boxHistoryParam param); + + List getProbabilityDistribution(Integer boxId); + + TtOrnamentsA ornamentsInfo(@Param("boxId") Integer boxId,@Param("ornamentId") String ornamentId); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiBoxOrnamentsMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiBoxOrnamentsMapper.java new file mode 100644 index 0000000..a46242e --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiBoxOrnamentsMapper.java @@ -0,0 +1,10 @@ +package com.ruoyi.playingmethod.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtBoxOrnaments; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ApiBoxOrnamentsMapper extends BaseMapper { + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiCompoundMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiCompoundMapper.java new file mode 100644 index 0000000..c4fb1ce --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiCompoundMapper.java @@ -0,0 +1,13 @@ +package com.ruoyi.playingmethod.mapper; + +import com.ruoyi.domain.vo.UserPackSackDataVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface ApiCompoundMapper { + UserPackSackDataVO selectCompoundDataById(Long packSackId); + + List selectCompoundRecordByUserId(Integer userId); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiFightMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiFightMapper.java new file mode 100644 index 0000000..06c7fd5 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiFightMapper.java @@ -0,0 +1,39 @@ +package com.ruoyi.playingmethod.mapper; + +import com.ruoyi.domain.other.TtBoxA; +import com.ruoyi.domain.vo.ApiFightListDataVO; +import com.ruoyi.domain.vo.JoinFightUserDataVO; +import com.ruoyi.domain.vo.UserPackSackDataVO; +import com.ruoyi.playingmethod.model.vo.ApiFightRankingVO; +import com.ruoyi.playingmethod.model.vo.ApiFightTenTopVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +@Mapper +public interface ApiFightMapper { + + JoinFightUserDataVO selectJoinFightUserData(Integer fightUserId); + + List selectOrnamentsDataListByUserIdAndFightId(@Param("userId") Integer userId, @Param("fightId") Integer fightId); + + List getFightList(@Param("model") String model, @Param("status") String status, @Param("userId") Integer userId, @Param("fightId") Integer fightId); + + List getFightBoxList(Integer boxTypeId); + + /** + * 获取指定日期的对战排行榜 + */ + List getFightRankingByDate(String date); + + /** + * 盲盒对战十佳对战 + */ + List tenTopFight(@Param("startDate") String startDate, @Param("endDate") String endDate, @Param("maxType") Integer maxType); + /** + * 查看对战开箱出来的总金额 + */ + List fightOrnamentsPrice(@Param("fightIds") List fightIds); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiLuckyUpgradeMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiLuckyUpgradeMapper.java new file mode 100644 index 0000000..c115a87 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiLuckyUpgradeMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.playingmethod.mapper; + +import com.ruoyi.domain.dto.upgrade.UpgradeCondition; +import com.ruoyi.domain.vo.ApiLuckyOrnamentsDataVO; +import com.ruoyi.domain.other.ApiLuckyUpgradeBody; +import com.ruoyi.domain.vo.ApiLuckyUpgradeRecordDataVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface ApiLuckyUpgradeMapper { + List getOrnamentsList(ApiLuckyUpgradeBody apiLuckyUpgradeBody); + + List getUpgradeRecord(UpgradeCondition param); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiReplacementRecordMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiReplacementRecordMapper.java new file mode 100644 index 0000000..8ca0292 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiReplacementRecordMapper.java @@ -0,0 +1,80 @@ +package com.ruoyi.playingmethod.mapper; + +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.other.TtReplacementRecord; +import com.ruoyi.domain.vo.UserPackSackDataVO; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * 汰换记录Mapper接口 + * + * @author junhai + * @date 2023-09-10 + */ +public interface ApiReplacementRecordMapper { + /** + * 查询汰换记录 + * + * @param id 汰换记录主键 + * @return 汰换记录 + */ + public TtReplacementRecord selectTtReplacementRecordById(Long id); + + /** + * 查询汰换记录列表 + * + * @param ttReplacementRecord 汰换记录 + * @return 汰换记录集合 + */ + public List selectTtReplacementRecordList(TtReplacementRecord ttReplacementRecord); + + /** + * 新增汰换记录 + * + * @param ttReplacementRecord 汰换记录 + * @return 结果 + */ + public int insertTtReplacementRecord(TtReplacementRecord ttReplacementRecord); + + /** + * 修改汰换记录 + * + * @param ttReplacementRecord 汰换记录 + * @return 结果 + */ + public int updateTtReplacementRecord(TtReplacementRecord ttReplacementRecord); + + /** + * 删除汰换记录 + * + * @param id 汰换记录主键 + * @return 结果 + */ + public int deleteTtReplacementRecordById(Long id); + + /** + * 批量删除汰换记录 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteTtReplacementRecordByIds(Long[] ids); + + /** + * 根据以下参数进行饰品的查询 + * + * @param map + * @return + */ + List findByPriceRange(Map map); + + /** + * 查询背包饰品 + */ + List selectUserPackSack(@Param("userId") Integer userId, + @Param("itemIds") List itemIds); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiRobotFightGroupMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiRobotFightGroupMapper.java new file mode 100644 index 0000000..805f5df --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiRobotFightGroupMapper.java @@ -0,0 +1,12 @@ +package com.ruoyi.playingmethod.mapper; + +import com.ruoyi.playingmethod.domain.vo.ApiRobotFightGroupVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface ApiRobotFightGroupMapper { + + public List selectApiRobotFightGroupBoxVOList(); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiRollMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiRollMapper.java new file mode 100644 index 0000000..460ab51 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiRollMapper.java @@ -0,0 +1,19 @@ +package com.ruoyi.playingmethod.mapper; + +import com.ruoyi.domain.other.JackpotData; +import com.ruoyi.domain.vo.RollDetailsDataVO; +import com.ruoyi.domain.vo.RollListDataVO; +import com.ruoyi.playingmethod.controller.ApiRollController.GetRollListParam; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface ApiRollMapper { + + List getRollList(GetRollListParam param); + + RollDetailsDataVO getRollDetails(Integer rollId); + + List getJackpotData(Integer jackpotId); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiTtUserBlendErcashMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiTtUserBlendErcashMapper.java new file mode 100644 index 0000000..c4ac9bd --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiTtUserBlendErcashMapper.java @@ -0,0 +1,10 @@ +package com.ruoyi.playingmethod.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.other.TtUpgradeRecord; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ApiTtUserBlendErcashMapper extends BaseMapper { +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiUpgradeRecordMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiUpgradeRecordMapper.java new file mode 100644 index 0000000..6f3bd20 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiUpgradeRecordMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.playingmethod.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.other.TtUpgradeRecord; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ApiUpgradeRecordMapper extends BaseMapper { +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiWelfareMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiWelfareMapper.java new file mode 100644 index 0000000..9609f3e --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiWelfareMapper.java @@ -0,0 +1,28 @@ +package com.ruoyi.playingmethod.mapper; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.playingmethod.model.ApiWelfare; +import com.ruoyi.playingmethod.model.ApiWelfareRecord; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; + +@Mapper +public interface ApiWelfareMapper { + + List getWelfareList(Long userId); + + /** + * 检查是否已领取 + */ + boolean checkClaimed( + @Param("welfareId") Integer welfareId, + @Param("userId") Long userId, + @Param("type") String type); + + Integer getBoxIdByWelfareId(Integer welfareId); + + int saveClaimWelfareRecord(ApiWelfareRecord apiWelfareRecord); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiWelfareMonthlyRechargesMapper.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiWelfareMonthlyRechargesMapper.java new file mode 100644 index 0000000..06546b6 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/mapper/ApiWelfareMonthlyRechargesMapper.java @@ -0,0 +1,33 @@ +package com.ruoyi.playingmethod.mapper; + +import com.ruoyi.playingmethod.model.ApiWelfare; +import com.ruoyi.playingmethod.model.ApiWelfareRecord; +import com.ruoyi.playingmethod.model.vo.ApiWelfareMonthlyRechargesVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; + +@Mapper +public interface ApiWelfareMonthlyRechargesMapper { + + List getWelfareList(Long userId); + + /** + * 查询当月充值量 + */ + BigDecimal selectRechargeRecordsOfMonth(Long userId); + + /** + * 检查是否已领取 + */ + boolean checkClaimed( + @Param("welfareId") Integer welfareId, + @Param("userId") Long userId, + @Param("type") String type); + + Integer getBoxIdByWelfareId(Integer welfareId); + + int saveClaimWelfareRecord(ApiWelfareRecord apiWelfareRecord); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/ApiWelfare.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/ApiWelfare.java new file mode 100644 index 0000000..3c8717e --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/ApiWelfare.java @@ -0,0 +1,51 @@ +package com.ruoyi.playingmethod.model; + +import com.ruoyi.domain.other.TtBox; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ApiWelfare { + + /** + * 福利ID + */ + private Integer welfareId; + + /** + * 福利名称 + */ + private String welfareName; + + /** + * 福利类型 + */ + private String type; + + /** + * VIP等级 + */ + private Integer vipLevel; + + /** + * 是否具备领取条件(0否 1是) + */ + private String eligible; + + /** + * 领取状态(0未领取 1已领取) + */ + private String claimStatus; + + /** + * 对应等级达标金额 + */ + private BigDecimal rechargeThreshold; + + /** + * 宝箱信息 + */ + private TtBox ttBox; +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/ApiWelfareRecord.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/ApiWelfareRecord.java new file mode 100644 index 0000000..4e22b84 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/ApiWelfareRecord.java @@ -0,0 +1,17 @@ +package com.ruoyi.playingmethod.model; + +import lombok.Data; + +import java.util.Date; + +@Data +public class ApiWelfareRecord { + + private Integer welfareId; + + private Long userId; + + private String type; + + private Date createTime; +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/body/SynthesizeRequest.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/body/SynthesizeRequest.java new file mode 100644 index 0000000..b7dcdb1 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/body/SynthesizeRequest.java @@ -0,0 +1,12 @@ +package com.ruoyi.playingmethod.model.body; + +import lombok.Data; + +import java.util.List; + +@Data +public class SynthesizeRequest { + + private List boIds; +// private List yoIds; +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/vo/ApiFightRankingVO.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/vo/ApiFightRankingVO.java new file mode 100644 index 0000000..1c7a3a8 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/vo/ApiFightRankingVO.java @@ -0,0 +1,17 @@ +package com.ruoyi.playingmethod.model.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ApiFightRankingVO { + + private Long userId; + + private String nickName; + + private String avatar; + + private BigDecimal totalBoxPrice; +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/vo/ApiFightTenTopVo.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/vo/ApiFightTenTopVo.java new file mode 100644 index 0000000..0f9156c --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/vo/ApiFightTenTopVo.java @@ -0,0 +1,21 @@ +package com.ruoyi.playingmethod.model.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ApiFightTenTopVo { + /** + * 对战ID + */ + private Integer id; + /** + * 宝箱开出来饰品总价值 + */ + private BigDecimal totalOrnamentsPrice; + /** + * 宝箱开出来饰品减去宝箱总金额 * 玩家个数 + */ + private BigDecimal increaseOrnamentsPrice; +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/vo/ApiWelfareMonthlyRechargesVO.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/vo/ApiWelfareMonthlyRechargesVO.java new file mode 100644 index 0000000..44680b5 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/vo/ApiWelfareMonthlyRechargesVO.java @@ -0,0 +1,28 @@ +package com.ruoyi.playingmethod.model.vo; + +import com.ruoyi.domain.other.TtBox; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ApiWelfareMonthlyRechargesVO { + + /** 福利ID */ + private Integer welfareId; + + /** 福利名称 */ + private String welfareName; + + /** 充值金额 */ + private BigDecimal amount; + + /** 是否具备领取条件(0否 1是) */ + private String eligible; + + /** 领取状态(0未领取 1已领取) */ + private String claimStatus; + + /** 宝箱信息 */ + private TtBox ttBox; +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/vo/ApiWelfareVO.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/vo/ApiWelfareVO.java new file mode 100644 index 0000000..fa3254e --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/model/vo/ApiWelfareVO.java @@ -0,0 +1,26 @@ +package com.ruoyi.playingmethod.model.vo; + +import com.ruoyi.domain.other.TtBox; +import lombok.Data; + +@Data +public class ApiWelfareVO { + + /** 福利ID */ + private Integer welfareId; + + /** 福利名称 */ + private String welfareName; + + /** 福利类型 */ + private String type; + + /** 是否具备领取条件(0否 1是) */ + private String eligible; + + /** 领取状态(0未领取 1已领取) */ + private String claimStatus; + + /** 宝箱信息 */ + private TtBox ttBox; +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/FightRankingRewardTask.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/FightRankingRewardTask.java new file mode 100644 index 0000000..a74facd --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/FightRankingRewardTask.java @@ -0,0 +1,78 @@ +package com.ruoyi.playingmethod.scheduled; + +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.TtFightRankingRewardService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtFightRankingReward; +import com.ruoyi.playingmethod.model.vo.ApiFightRankingVO; +import com.ruoyi.playingmethod.service.ApiFightService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +@Slf4j +@Component("FightRankingRewardTask") +public class FightRankingRewardTask { + + @Autowired + private ApiFightService apiFightService; + + @Autowired + private TtFightRankingRewardService ttFightRankingRewardService; + + @Autowired + private TtUserService userService; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + /** 发放对战排行榜奖励 */ + public void distributeFightRankingReward() { + log.info("开始发放对战排行榜奖励"); + // 查询昨日排行榜 + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DATE, -1); + Date yesterday = cal.getTime(); + String formattedYesterdayDate = formatter.format(yesterday); + List yesterdayFightRanking = apiFightService.getFightRankingByDate(formattedYesterdayDate); + for (int i = 0; i < yesterdayFightRanking.size(); i++) { + // 查询排行榜对应奖励 + TtFightRankingReward ttFightRankingReward = ttFightRankingRewardService.selectTtFightRankingRewardById(i + 1); + Long userId = yesterdayFightRanking.get(i).getUserId(); + BigDecimal reward = ttFightRankingReward.getReward(); + // 加钱 + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, userId) + .setSql("account_amount = account_amount + " + reward); + userService.update(userUpdate); + // 综合消费日志 + TtUser ttUser = userService.getById(userId); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(userId.intValue()) + .amount(reward.compareTo(BigDecimal.ZERO) > 0 ? reward : null) + .finalAmount(reward.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(reward) : null) + .total(reward.add(reward)) // 收支合计 + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.FIGHT_RANKING_REWARD.getCode()) + .remark(TtAccountRecordSource.FIGHT_RANKING_REWARD.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + ttUserBlendErcashMapper.insert(blendErcash); + } + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/FightRobotTask.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/FightRobotTask.java new file mode 100644 index 0000000..b61cb37 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/FightRobotTask.java @@ -0,0 +1,325 @@ +package com.ruoyi.playingmethod.scheduled; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.mapper.TtBoxMapper; +import com.ruoyi.admin.mapper.TtFightMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.DateTimeUtils; +import com.ruoyi.common.utils.RandomUtil; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.entity.fight.FightSeat; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateFightBody; +import com.ruoyi.domain.other.FightBoutData; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtRobotFightGroupBox; +import com.ruoyi.domain.vo.ApiFightListDataVO; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.playingmethod.domain.vo.ApiRobotFightGroupVO; +import com.ruoyi.playingmethod.mapper.ApiFightMapper; +import com.ruoyi.playingmethod.mapper.ApiRobotFightGroupMapper; +import com.ruoyi.playingmethod.service.ApiFightService; +import com.ruoyi.system.service.ISysConfigService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Slf4j +@Component("FightRobotTask") +public class FightRobotTask { + + @Value("${mkcsgo.fight.roundTime}") + private final Integer fightRoundTimeMs = 4000; + + @Autowired + private TtFightMapper ttFightMapper; + + @Autowired + private TtUserMapper ttUserMapper; + + @Autowired + private ISysConfigService sysConfigService; + + @Autowired + private ApiFightService apiFightService; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ApiRobotFightGroupMapper robotFightGroupMapper; + + @Autowired + private ApiFightMapper apiFightMapper; + + @Autowired + private TtBoxMapper boxMapper; + + private static final List roomExistTime = new ArrayList<>() {{ + add(new Integer[]{200, 30}); + add(new Integer[]{800, 60}); + add(new Integer[]{1500, 240}); + }}; + + /** + * 机器人加入对战 + */ + public void robotJoinFight() { + log.info("机器人加入对战"); + + // 查询所有处于等待中的对战房间ID + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("status", 0); + queryWrapper.eq("model", 0); + List fightList = ttFightMapper.selectList(queryWrapper); + if (CollectionUtils.isEmpty(fightList)) { + log.info("没有等待中的对战房间"); + return; + } + + // 查询所有机器人ID + List robotIdList = ttUserMapper.selectObjs( + new QueryWrapper() + .select("user_id") + .eq("user_type", UserType.ROBOT_USER.getCode()) + ); + if (robotIdList.isEmpty()) { + log.info("没有机器人"); + return; + } + + // 打乱对战ID列表顺序,取前面指定数量的对战ID + Collections.shuffle(fightList); + + List userIdList = fightList.stream().map(TtFight::getUserId).filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (userIdList.isEmpty()) { + return; + } + + Map userMap = ttUserMapper.selectByIds(userIdList).stream() + .collect(Collectors.toMap(TtUser::getUserId, v -> v)); + + for (TtFight ttFight : fightList) { + TtUser user = userMap.get(ttFight.getUserId()); + if (user == null || user.getUserType().equals(UserType.ROBOT_USER.getCode())) { + continue; + } + + Optional config = roomExistTime.stream() + .filter(arr -> ttFight.getBoxPriceTotal().doubleValue() <= arr[0]).findFirst(); + if (config.isEmpty()) { + continue; + } + + var time = config.get()[1]; + if (ttFight.getCreateTime().getTime() + time * 1000 >= DateTimeUtils.now() * 1000) { + continue; + } + + List availableRobotIds = new ArrayList<>(robotIdList); + List seats = ttFight.getSeats(); + + var inRoomPlayerIds = seats.stream() + .map(FightSeat::getPlayerId) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + // 只有一个空位才加入机器人 + if (inRoomPlayerIds.size() != seats.size() - 1) { + continue; + } + + availableRobotIds.removeAll(inRoomPlayerIds); + if (availableRobotIds.isEmpty()) { + log.warn("No available robots for fight: {}", ttFight.getId()); + return; + } + + for (FightSeat seat : seats) { + // 随机机器人进入空座位 + if (Objects.isNull(seat.getPlayerId())) { + Integer robotId = availableRobotIds.get(RandomUtil.rand(availableRobotIds.size())); + TtUser player = ttUserMapper.selectById(robotId); + R objectR = apiFightService.joinFight(ttFight.getId(), player); + if (objectR.getCode() == 500) { + return; + } + log.info("机器人{}加入对战{}", robotId, ttFight.getId()); + // 座位就绪 + R fight = apiFightService.seatrReady(ttFight.getId(), player); + FightBoutData fightBoutData = new FightBoutData(); + fightBoutData.setFightId(ttFight.getId()); + fightBoutData.setBoutNum(1); + fightBoutData.setExpirationTime(16000); + String key = "fight_bout_data:fight_" + fightBoutData.getFightId(); + redisCache.setCacheObject(key, fightBoutData, fightBoutData.getExpirationTime(), TimeUnit.MILLISECONDS); + // 调用开始对战方法 + apiFightService.fightBegin(ttFight.getId(), player); + + int gameTime = ttFight.getRoundNumber() * fightRoundTimeMs; + AsyncManager.me().timeout(new TimerTask() { + @Override + public void run() { + apiFightService.fightEnd(ttFight.getId()); + } + }, gameTime, TimeUnit.MILLISECONDS); + } + } + } + } + + /** + * 机器人创建对战 + */ + public void robotCreateFight() { + log.info("机器人创建对战"); + + // 查询所有机器人ID + List robotIdList = ttUserMapper.selectObjs( + new QueryWrapper() + .select("user_id") + .eq("user_type", UserType.ROBOT_USER.getCode()) + ); + if (robotIdList.isEmpty()) { + log.warn("无可用机器人,停止创建对战"); + return; + } + + // 定义价格档位配置 + BigDecimal[] priceLimits = {BigDecimal.valueOf(200), BigDecimal.valueOf(800), BigDecimal.valueOf(1500)}; + int[] targetCounts = {6, 4, 1}; + + List vos = apiFightMapper.getFightList(null, "0", null, null); + if (CollectionUtils.isEmpty(vos)) { + vos = new ArrayList<>(); + } + vos.forEach(v -> { + if (v.getBoxPriceTotal() == null) { + v.setBoxPriceTotal(BigDecimal.ZERO); + } + }); + + // 统计当前各价位对战的存量 + int[] currentCounts = new int[3]; // 对应 200, 800, 1500 + for (ApiFightListDataVO vo : vos) { + if (vo.getBoxPriceTotal().compareTo(BigDecimal.valueOf(200)) <= 0) { + currentCounts[0]++; + } else if (vo.getBoxPriceTotal().compareTo(BigDecimal.valueOf(800)) <= 0) { + currentCounts[1]++; + } else if (vo.getBoxPriceTotal().compareTo(BigDecimal.valueOf(1500)) <= 0) { + currentCounts[2]++; + } + } + + // 计算需要创建的数量 + int[] needCreateCounts = new int[3]; + for (int i = 0; i < 3; i++) { + needCreateCounts[i] = Math.max(targetCounts[i] - currentCounts[i], 0); + } + + // 查询机器人宝箱分组 + List apiRobotFightGroupVOList = robotFightGroupMapper.selectApiRobotFightGroupBoxVOList(); + if (CollectionUtils.isEmpty(apiRobotFightGroupVOList)) { + return; + } + Set boxIds = apiRobotFightGroupVOList.stream() + .flatMap(v -> v.getTtRobotFightGroupBoxList().stream()) + .map(TtRobotFightGroupBox::getBoxId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + List allBoxes = Collections.emptyList(); + if (!boxIds.isEmpty()) { + allBoxes = new LambdaQueryChainWrapper<>(boxMapper) + .in(TtBox::getBoxId, boxIds) + .eq(TtBox::getIsFight, "0") + .eq(TtBox::getStatus, "0") + .list(); + } + // 构建 BoxId -> Box 的 Map,方便快速查找 + Map boxMap = allBoxes.stream() + .collect(Collectors.toMap(TtBox::getBoxId, v -> v, (v1, v2) -> v1)); + + + var groupMap = apiRobotFightGroupVOList.stream().collect(Collectors.toMap(ApiRobotFightGroupVO::getGroupId, v -> v)); + + Map groupPriceMap = new HashMap<>(); + for (ApiRobotFightGroupVO groupVO : apiRobotFightGroupVOList) { + BigDecimal boxTotalPrice = BigDecimal.ZERO; + if (CollectionUtils.isNotEmpty(groupVO.getTtRobotFightGroupBoxList())) { + for (TtRobotFightGroupBox groupBox : groupVO.getTtRobotFightGroupBoxList()) { + TtBox box = boxMap.get(groupBox.getBoxId()); + if (box != null) { + BigDecimal multiply = BigDecimal.valueOf(groupBox.getBoxNum()).multiply(box.getPrice()); + boxTotalPrice = boxTotalPrice.add(multiply); + } + } + } + groupPriceMap.put(groupVO.getGroupId(), boxTotalPrice); + } + + // 6. 循环处理不同价位的创建任务 + for (int i = 0; i < needCreateCounts.length; i++) { + int needCount = needCreateCounts[i]; + if (needCount <= 0) { + continue; + } + + BigDecimal limitPrice = priceLimits[i]; + BigDecimal lowPrice; + if (i > 0) { + lowPrice = priceLimits[i - 1]; + } else { + lowPrice = BigDecimal.ZERO; + } + + // 筛选出符合当前价位的分组 + List matchedGroups = groupPriceMap.entrySet().stream() + .filter(entry -> entry.getValue().compareTo(lowPrice) > 0 && entry.getValue().compareTo(limitPrice) <= 0) + .map(entry -> groupMap.get(entry.getKey())) + .collect(Collectors.toList()); + + if (CollectionUtils.isNotEmpty(matchedGroups)) { + createFight(matchedGroups, needCount, robotIdList); + } + } + } + + private void createFight(List groupList, int canCreateCount, List robotIdList) { + if (CollectionUtils.isEmpty(groupList) || CollectionUtils.isEmpty(robotIdList)) { + return; + } + + // 创建指定数量的房间 + for (int i = 0; i < canCreateCount; i++) { + // 随机取一个机器人 + Integer robotId = robotIdList.get(RandomUtil.rand(robotIdList.size())); + TtUser player = ttUserMapper.selectById(robotId); + // 随机取一个宝箱分组 + ApiRobotFightGroupVO apiRobotFightGroupVO = groupList.get(RandomUtil.rand(groupList.size())); + log.info("机器人{}使用宝箱分组{}", robotId, apiRobotFightGroupVO.getGroupId()); + // 创建对战 + CreateFightBody createFightBody = new CreateFightBody(); + createFightBody.setModel(apiRobotFightGroupVO.getModel()); + createFightBody.setPlayerNumber(apiRobotFightGroupVO.getSeatNum()); + Map boxIdAndNumber = new HashMap<>(); + for (TtRobotFightGroupBox ttRobotFightGroupBox : apiRobotFightGroupVO.getTtRobotFightGroupBoxList()) { + boxIdAndNumber.put(ttRobotFightGroupBox.getBoxId(), ttRobotFightGroupBox.getBoxNum()); + } + createFightBody.setBoxIdAndNumber(boxIdAndNumber); + apiFightService.createFight(createFightBody, player); + } + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/RollTask.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/RollTask.java new file mode 100644 index 0000000..1be6027 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/RollTask.java @@ -0,0 +1,134 @@ +package com.ruoyi.playingmethod.scheduled; + + +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.TtBoxRecordsMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtRollService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.rabbitmq.config.DelayedQueueConfig; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.domain.entity.roll.TtRoll; +import com.ruoyi.playingmethod.service.ApiFightService; +import com.ruoyi.playingmethod.service.ApiRollService; +import com.ruoyi.playingmethod.websocket.WsFightHall; +import com.ruoyi.playingmethod.websocket.WsFightRoom; +import com.ruoyi.playingmethod.websocket.util.WsResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageProperties; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +import static com.ruoyi.domain.common.constant.TtboxRecordStatus.IN_PACKSACK_ON; +import static com.ruoyi.playingmethod.websocket.constant.SMsgKey.ALL_FIGHT_ROOM; + +@Slf4j +@Configuration // 1.主要用于标记配置类,兼备Component的效果。 +@EnableScheduling // 2.开启定时任务 +public class RollTask { + + @Value("${mkcsgo.fight.roundTime}") + private final Integer fightRoundTime = null; + + @Autowired + private ApiFightService fightService; + + @Autowired + private TtBoxRecordsMapper boxRecordsMapper; + + @Autowired + private TtBoxRecordsService boxRecordsService; + + @Autowired + private TtRollService ttRollService; + + @Autowired + private Executor customThreadPoolExecutor; + + @Autowired + private RabbitTemplate rabbitTemplate; + + @Autowired + private ApiRollService apiRollService; + + // 定时检查即将开奖的roll房(临界区:1分钟) + @Scheduled(cron = "0 */1 * * * ?") + private void refreshDayTask() { + + // System.out.println("11111111111111111"); + + //System.out.println("roll 开奖临界区检查"); + + // 查询两分钟以内将要开奖的roll房 + Calendar c = Calendar.getInstance(); + c.add(Calendar.MINUTE, 2); + Timestamp criticalTime = new Timestamp(c.getTimeInMillis()); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .eq(TtRoll::getRollStatus, 0) + .eq(TtRoll::getDelFlag, 0) + .lt(TtRoll::getEndTime, criticalTime); + List criticalRoll = ttRollService.list(wrapper); + + if (criticalRoll.isEmpty()) return; + + log.info("roll开奖临界区检查,{}个roll房进入临界区", criticalRoll.size()); + + // 加入延时队列 + for (TtRoll roll : criticalRoll) { + + if (DateUtils.getNowDate().compareTo(roll.getEndTime()) > 0) { + + log.info("roll房{}已经超时,直接开奖。", roll.getId()); + + R r = apiRollService.endROLL(roll.getId()); + if (!r.getCode().equals(200)) { + log.error("roll开奖 消费失败,尝试消息补发再次消费!" + r.getMsg()); + } + if (ObjectUtil.isNotEmpty(r.getMsg())) log.info("{}", r.getMsg()); + // 已超时直接开奖,不再加入延时队列,避免重复开奖 + continue; + } + + // 相差时间(未超时的roll房才加入延时队列) + long betweenDay = DateUtil.between(DateUtils.getNowDate(), roll.getEndTime(), DateUnit.MS); + + System.out.println("设置消息超时时间"); + log.info("设置消息超时时间:{}",betweenDay); + MessageProperties messageProperties = new MessageProperties(); + messageProperties.setExpiration(String.valueOf(betweenDay)); // 消息的过期属性,单位 ms + Message message = new Message(String.valueOf(roll.getId()).getBytes(), messageProperties); + + // 延时队列实现开奖 + rabbitTemplate.convertAndSend( + DelayedQueueConfig.OPEN_ROLL_EXCHANGE, + DelayedQueueConfig.OPEN_ROLL_KEY, + message); + + log.info("Roll房{}-{}加入临界区,{}秒后开奖", roll.getId(), roll.getRollName(), betweenDay / 1000); + } + + } + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/fightTask.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/fightTask.java new file mode 100644 index 0000000..174eb4e --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/scheduled/fightTask.java @@ -0,0 +1,102 @@ +package com.ruoyi.playingmethod.scheduled; + + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.TtBoxRecordsMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.playingmethod.service.ApiFightService; +import com.ruoyi.playingmethod.websocket.WsFightHall; +import com.ruoyi.playingmethod.websocket.WsFightRoom; +import com.ruoyi.playingmethod.websocket.util.WsResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +import static com.ruoyi.domain.common.constant.TtboxRecordStatus.IN_PACKSACK_ON; +import static com.ruoyi.playingmethod.websocket.constant.SMsgKey.ALL_FIGHT_ROOM; + +@Slf4j +@Configuration //1.主要用于标记配置类,兼备Component的效果。 +@EnableScheduling // 2.开启定时任务 +public class fightTask { + + @Value("${mkcsgo.fight.roundTime}") + private final Integer fightRoundTime = null; + + @Autowired + private ApiFightService fightService; + + @Autowired + private TtBoxRecordsMapper boxRecordsMapper; + + @Autowired + private TtBoxRecordsService boxRecordsService; + + @Autowired + private Executor customThreadPoolExecutor; + + // 定时更新超时未结束的对局 + @Scheduled(cron = "0/6 * * * * ?") + private void refreshDayTask() { + log.info("定时更新超时未结束的对局"); + // Timestamp now = new Timestamp(System.currentTimeMillis()); + LambdaQueryWrapper fightQuery = new LambdaQueryWrapper<>(); + fightQuery + .eq(TtFight::getStatus,1) + .and(wrapper->{ + wrapper.isNull(TtFight::getEndTime); + }) + .and(wrapper->{ + wrapper.isNotNull(TtFight::getBeginTime); + }) + // TODO: 2024/4/13 优化sql + // 对局开始时间早于当前时间减去一定时间间隔(再减去6秒),就会被认为是超时的对局 + // begin_time < DATE_SUB(NOW(), INTERVAL 100 SECOND) + .last("AND begin_time < DATE_SUB(NOW(), INTERVAL " + fightRoundTime / 1000 + "* round_number + 60"+" second)"); + + List list = fightService.list(fightQuery); + + if (list.isEmpty()) return; + + List fightIds = new ArrayList<>(); + // 更新fight + list.stream().forEach(item->{ + item.setEndTime(new Timestamp(System.currentTimeMillis())); + item.setStatus(3); + item.setRemark("超时强制更新结束状态"); + fightIds.add(item.getId()); + + // 断开房间所有连接 + WsFightRoom.batchClose(item); + }); + fightService.updateBatchById(list); + + // 异步更新对局数据 + CompletableFuture.runAsync(() -> { + WsFightHall.broadcast(WsResult.ok(ALL_FIGHT_ROOM.name(), list)); + }, customThreadPoolExecutor); + + // 更新openBoxRecords + new LambdaUpdateChainWrapper<>(boxRecordsMapper) + // .eq(TtBoxRecords::getStatus,IN_PACKSACK_ON.getCode()) + .in(TtBoxRecords::getFightId,fightIds) + .set(TtBoxRecords::getStatus,IN_PACKSACK_ON.getCode()) + .update(); + + log.info("更新 {} 条超时未结束的对局记录",list.size()); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiBattleRoyaleService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiBattleRoyaleService.java new file mode 100644 index 0000000..5c10a00 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiBattleRoyaleService.java @@ -0,0 +1,4 @@ +package com.ruoyi.playingmethod.service; + +public interface ApiBattleRoyaleService { +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiBindBoxService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiBindBoxService.java new file mode 100644 index 0000000..28f08d6 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiBindBoxService.java @@ -0,0 +1,35 @@ +package com.ruoyi.playingmethod.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtBoxA; +import com.ruoyi.domain.other.TtOrnamentsA; +import com.ruoyi.domain.other.TtBoxVO; +import com.ruoyi.playingmethod.domain.vo.CompValueVO; + +import java.util.List; + +public interface ApiBindBoxService { + + TtBoxA getBoxData(Integer boxId); + + List getCompBoxData(Integer boxId); + + List getBoxList(Integer boxTypeId, String homeFlag, String boxType); + + List groupByBoxType(List boxData); + + List openBox(TtBox ttBox, Integer num, TtUser ttUser); + + List addBoxRecord(TtUser ttUser, TtBox ttBox, Integer num); + + List openBoxArithmetic(Integer fightId,TtUser ttUser, TtBox ttBox, Integer num); + + // List getBindBoxHistory(ApiBindBoxController.boxHistoryParam param); + + R blindBox(TtUser ttUser, TtBox ttBox, Integer num); + + CompValueVO getCompValue(TtUser ttUser, TtBox ttBox); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiBoxOrnamentsService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiBoxOrnamentsService.java new file mode 100644 index 0000000..4416e9f --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiBoxOrnamentsService.java @@ -0,0 +1,8 @@ +package com.ruoyi.playingmethod.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtBoxOrnaments; + +public interface ApiBoxOrnamentsService extends IService { + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiBoxRecordsService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiBoxRecordsService.java new file mode 100644 index 0000000..17cbe95 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiBoxRecordsService.java @@ -0,0 +1,16 @@ +package com.ruoyi.playingmethod.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.dto.boxRecords.TenTopQuery; +import com.ruoyi.domain.dto.boxRecords.queryCondition; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO; + +import java.util.List; + +public interface ApiBoxRecordsService extends IService { + + List byCondition(queryCondition condition); + + List tenTopQuery(TenTopQuery query); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiCompoundService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiCompoundService.java new file mode 100644 index 0000000..754eee8 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiCompoundService.java @@ -0,0 +1,14 @@ +package com.ruoyi.playingmethod.service; + +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.vo.UserPackSackDataVO; + +import java.util.List; + +public interface ApiCompoundService { + + R compound(List packSackIds, TtUser ttUser); + + List getUserCompoundRecord(Integer userId); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiExponentService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiExponentService.java new file mode 100644 index 0000000..0e7dcf7 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiExponentService.java @@ -0,0 +1,38 @@ +package com.ruoyi.playingmethod.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.exponent.ExponentJoinParam; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtBox; + +public interface ApiExponentService { + /** + * 加入指数 + * @param param + * @return + */ + R join(TtUser ttUser, ExponentJoinParam param); + + /** + * 宝箱信息 + */ + R box(TtUser ttUser, Integer id); + + /** + * 最近十期指数 + */ + R recenttenperiod(); + + /** + * 返回信息 + * @param ttUser + * @return + */ + R info(TtUser ttUser); + + R records(TtUser ttUser); + + R mybox(TtUser ttUser); + + R openbox(TtUser ttUser, TtBox ttBox, Integer num); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiFightService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiFightService.java new file mode 100644 index 0000000..456aa99 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiFightService.java @@ -0,0 +1,54 @@ +package com.ruoyi.playingmethod.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.fight.FightDetailParam; +import com.ruoyi.domain.dto.fight.FightOnMyOwnParam; +import com.ruoyi.domain.other.CreateFightBody; +import com.ruoyi.domain.other.TtBoxVO; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.vo.ApiFightListDataVO; +import com.ruoyi.domain.vo.FightResultDataVO; +import com.ruoyi.domain.vo.fight.FightResultVO; +import com.ruoyi.domain.vo.fight.TtFightVO; +import com.ruoyi.playingmethod.model.vo.ApiFightRankingVO; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +public interface ApiFightService extends IService { + + R createFight(CreateFightBody createFightParamVO, TtUser ttUser); + + R joinFight(Integer fightId, TtUser player); + + List getFightBoxList(Integer boxTypeId); + + List getFightList(String model, String status, Integer userId, Integer fightId); + + FightResultDataVO getFightRecord(Integer fightId, Integer round, Integer rounds); + + R fightBegin(Integer fightId, TtUser player); + + R audience(Integer fightId); + + R fightEnd(Integer fightId); + + R seatrReady(Integer fightId, TtUser player); + + R fightRoomExit(Integer fightId, TtUser player); + + List getFightList(FightOnMyOwnParam param); + + R fightDetail(FightDetailParam param); + + R earlierHistory(FightDetailParam param); + + // AjaxResult joinFightRoom(Integer userId,Integer fightId); + + List getFightRankingByDate(String date); + + Map> tenTopFight(Integer maxType); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiFightUserService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiFightUserService.java new file mode 100644 index 0000000..65f8545 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiFightUserService.java @@ -0,0 +1,7 @@ +package com.ruoyi.playingmethod.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.other.TtFightUser; + +public interface ApiFightUserService extends IService { +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiLuckyUpgradeService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiLuckyUpgradeService.java new file mode 100644 index 0000000..ac69793 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiLuckyUpgradeService.java @@ -0,0 +1,18 @@ +package com.ruoyi.playingmethod.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.other.ApiLuckyUpgradeBody; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.UpgradeBodyA; +import com.ruoyi.domain.vo.ApiLuckyOrnamentsDataVO; + +import java.util.List; + +public interface ApiLuckyUpgradeService { + + List getOrnamentsList(ApiLuckyUpgradeBody apiLuckyUpgradeBody); + + R upgrade(TtUser ttUser, UpgradeBodyA upgradeBodyA); + + // List getUpgradeRecord(getUpgradeRecord param); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiRedPackService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiRedPackService.java new file mode 100644 index 0000000..40cb67a --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiRedPackService.java @@ -0,0 +1,22 @@ +package com.ruoyi.playingmethod.service; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.domain.other.TtRedPack; +import com.ruoyi.domain.other.TtRedPacketRecord; +import com.ruoyi.thirdparty.wechat.domain.PtLoginUser; + +import java.util.List; + +public interface ApiRedPackService { + + List getRedPackList(PtLoginUser user, Integer type); + + AjaxResult getRedPack(Long userId, Long redPackId); + + List getActivityDataHis(PtLoginUser user); + + AjaxResult getRedPackDataDetail(PtLoginUser user, Long recordId); + + AjaxResult getRedPackByKey(LoginUser user, String redPackKey); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiRollService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiRollService.java new file mode 100644 index 0000000..404ebf7 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiRollService.java @@ -0,0 +1,37 @@ +package com.ruoyi.playingmethod.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.roll.GetRollOpenPrizeParam; +import com.ruoyi.domain.dto.roll.GetRollPlayersParam; +import com.ruoyi.domain.dto.roll.GetRollPrizePool; +import com.ruoyi.domain.entity.roll.TtRoll; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.vo.RollDetailsDataVO; +import com.ruoyi.domain.vo.RollListDataVO; +import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO; +import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsVO; +import com.ruoyi.domain.vo.roll.RollUserVO; +import com.ruoyi.playingmethod.controller.ApiRollController.GetRollListParam; +import com.ruoyi.playingmethod.controller.ApiRollController.JoinRollParam; + +import java.util.List; + +public interface ApiRollService extends IService { + + R joinRoll(JoinRollParam param, TtUser player); + + R endROLL(Integer rollId); + + // List getRollList(GetRollListParam param); + + List getRollList(GetRollListParam param); + + R getRollDetails(Integer rollId); + + R> getRollPlayers(GetRollPlayersParam param); + + R> getRollPrizePool(GetRollPrizePool param); + + R> getRollOpenPrize(GetRollOpenPrizeParam param); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiUpgradeRecordService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiUpgradeRecordService.java new file mode 100644 index 0000000..880b07c --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiUpgradeRecordService.java @@ -0,0 +1,16 @@ +package com.ruoyi.playingmethod.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.upgrade.UpgradeCondition; +import com.ruoyi.domain.other.*; + + +import java.util.List; + +public interface ApiUpgradeRecordService extends IService { + + R historyDetail(UpgradeCondition param); + + R tenTopDetail(Boolean isVictory); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiUserBlendErcashService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiUserBlendErcashService.java new file mode 100644 index 0000000..a21f7e0 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiUserBlendErcashService.java @@ -0,0 +1,8 @@ +package com.ruoyi.playingmethod.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.entity.TtUserBlendErcash; + +public interface ApiUserBlendErcashService extends IService { + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiWelfareMonthlyRechargesService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiWelfareMonthlyRechargesService.java new file mode 100644 index 0000000..1627aa4 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiWelfareMonthlyRechargesService.java @@ -0,0 +1,30 @@ +package com.ruoyi.playingmethod.service; + +import com.ruoyi.domain.vo.OpenBoxVO; +import com.ruoyi.playingmethod.model.ApiWelfare; +import com.ruoyi.playingmethod.model.vo.ApiWelfareMonthlyRechargesVO; + +import java.util.List; + +public interface ApiWelfareMonthlyRechargesService { + + /** + * 获取福利列表 + */ + List getWelfareList(Long userId); + + /** + * 领取福利 + */ + OpenBoxVO claimWelfare(Integer welfareId, Long userId); + + /** + * 检查是否符合领取条件 + */ + boolean checkEligible(Integer welfareId, Long userId); + + /** + * 检查是否已领取 + */ + boolean checkClaimed(Integer welfareId, Long userId); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiWelfareService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiWelfareService.java new file mode 100644 index 0000000..6270c4d --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/ApiWelfareService.java @@ -0,0 +1,32 @@ +package com.ruoyi.playingmethod.service; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.vo.OpenBoxVO; +import com.ruoyi.playingmethod.model.ApiWelfare; +import com.ruoyi.playingmethod.model.ApiWelfareRecord; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface ApiWelfareService { + + /** + * 获取福利列表 + */ + List getWelfareList(Long userId); + + /** + * 领取福利 + */ + OpenBoxVO claimWelfare(Integer welfareId, Long userId); + + /** + * 检查是否不符合领取条件 + */ + boolean checkNotEligible(Integer welfareId, Long userId); + + /** + * 检查是否已领取 + */ + boolean checkClaimed(Integer welfareId, Long userId); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/IApiAttendanceRecordService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/IApiAttendanceRecordService.java new file mode 100644 index 0000000..c58f01c --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/IApiAttendanceRecordService.java @@ -0,0 +1,14 @@ +package com.ruoyi.playingmethod.service; + +import com.ruoyi.playingmethod.entity.TtAttendanceRecord; + +import java.util.List; + +public interface IApiAttendanceRecordService { + + TtAttendanceRecord selectByUid(int uid); + + int insert(int uid); + + List selectSevenAttendance(int uid); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/IApiReplacementRecordService.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/IApiReplacementRecordService.java new file mode 100644 index 0000000..e9711d4 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/IApiReplacementRecordService.java @@ -0,0 +1,11 @@ +package com.ruoyi.playingmethod.service; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; + +import java.util.List; + +public interface IApiReplacementRecordService { + + AjaxResult synthesizeItems(LoginUser user, List itemIds); +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiAttendanceRecordServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiAttendanceRecordServiceImpl.java new file mode 100644 index 0000000..44c39c2 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiAttendanceRecordServiceImpl.java @@ -0,0 +1,109 @@ +package com.ruoyi.playingmethod.service.impl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import com.ruoyi.playingmethod.entity.TtAttendanceRecord; +import com.ruoyi.playingmethod.mapper.ApiAttendanceRecordMapper; +import com.ruoyi.playingmethod.service.IApiAttendanceRecordService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.text.SimpleDateFormat; +import java.util.*; + +@Service +@Slf4j +public class ApiAttendanceRecordServiceImpl implements IApiAttendanceRecordService{ + private final ApiAttendanceRecordMapper apiTtAttendanceRecordMapper; + + public ApiAttendanceRecordServiceImpl(ApiAttendanceRecordMapper apiTtAttendanceRecordMapper) { + this.apiTtAttendanceRecordMapper = apiTtAttendanceRecordMapper; + } + + + @Override + public TtAttendanceRecord selectByUid(int uid) { + return apiTtAttendanceRecordMapper.selectByUid(uid); + } + + @Override + public int insert(int uid) { + TtAttendanceRecord ttAttendanceRecord = new TtAttendanceRecord(); + ttAttendanceRecord.setUserId(uid); + ttAttendanceRecord.setCreateTime(new Date()); + ttAttendanceRecord.setCoin(1); + return apiTtAttendanceRecordMapper.insertAttendanceRecord(ttAttendanceRecord); + + } + + @Override + public List selectSevenAttendance(int uid) { + List ttAttendanceRecords = new ArrayList<>(); + //周一 + DateTime dateTime = DateUtil.beginOfWeek(new Date()); + System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(dateTime)); + TtAttendanceRecord zhouYittAttendanceRecor = new TtAttendanceRecord(); + + if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(dateTime)) != null )zhouYittAttendanceRecor.setIsStatus("1"); + zhouYittAttendanceRecor.setCreateTime(dateTime); + ttAttendanceRecords.add(zhouYittAttendanceRecor); + //周二 + Calendar calendar = new GregorianCalendar(); + calendar.setTime(dateTime); + calendar.add(Calendar.DATE, 1); + System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())); + + TtAttendanceRecord zhouErttAttendanceRecord = new TtAttendanceRecord(); + if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())) != null)zhouErttAttendanceRecord.setIsStatus("1"); + zhouErttAttendanceRecord.setCreateTime(calendar.getTime()); + ttAttendanceRecords.add(zhouErttAttendanceRecord); + + //周三 + calendar.add(Calendar.DATE, 1); + System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())); + + TtAttendanceRecord zhouSanttAttendanceRecord = new TtAttendanceRecord(); + if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())) != null )zhouSanttAttendanceRecord.setIsStatus("1"); + zhouSanttAttendanceRecord.setCreateTime(calendar.getTime()); + ttAttendanceRecords.add(zhouSanttAttendanceRecord); + + //周四 + calendar.add(Calendar.DATE, 1); + System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())); + + TtAttendanceRecord zhouSittAttendanceRecord = new TtAttendanceRecord(); + if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())) != null )zhouSittAttendanceRecord.setIsStatus("1"); + zhouSittAttendanceRecord.setCreateTime(calendar.getTime()); + ttAttendanceRecords.add(zhouSittAttendanceRecord); + + //周五 + calendar.add(Calendar.DATE, 1); + System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())); + + TtAttendanceRecord zhouWuttAttendanceRecord = new TtAttendanceRecord(); + if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())) != null )zhouWuttAttendanceRecord.setIsStatus("1"); + zhouWuttAttendanceRecord.setCreateTime(calendar.getTime()); + ttAttendanceRecords.add(zhouWuttAttendanceRecord); + + //周六 + calendar.add(Calendar.DATE, 1); + System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())); + + TtAttendanceRecord zhouLiuttAttendanceRecord = new TtAttendanceRecord(); + if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())) != null )zhouLiuttAttendanceRecord.setIsStatus("1"); + zhouLiuttAttendanceRecord.setCreateTime(calendar.getTime()); + ttAttendanceRecords.add(zhouLiuttAttendanceRecord); + + //周天 + calendar.add(Calendar.DATE, 1); + System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())); + + TtAttendanceRecord zhouRittAttendanceRecord = new TtAttendanceRecord(); + if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())) != null )zhouRittAttendanceRecord.setIsStatus("1"); + zhouRittAttendanceRecord.setCreateTime(calendar.getTime()); + ttAttendanceRecords.add(zhouRittAttendanceRecord); + + return ttAttendanceRecords; + } + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiBattleRoyaleServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiBattleRoyaleServiceImpl.java new file mode 100644 index 0000000..502d8c9 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiBattleRoyaleServiceImpl.java @@ -0,0 +1,8 @@ +package com.ruoyi.playingmethod.service.impl; + +import com.ruoyi.playingmethod.service.ApiBattleRoyaleService; +import org.springframework.stereotype.Service; + +@Service +public class ApiBattleRoyaleServiceImpl implements ApiBattleRoyaleService { +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiBindBoxServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiBindBoxServiceImpl.java new file mode 100644 index 0000000..d528262 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiBindBoxServiceImpl.java @@ -0,0 +1,481 @@ +package com.ruoyi.playingmethod.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.mapper.TtBoxOrnamentsMapper; +import com.ruoyi.admin.mapper.TtBoxRecordsMapper; +import com.ruoyi.admin.mapper.TtBoxTypeMapper; +import com.ruoyi.admin.mapper.TtCompRecordMapper; +import com.ruoyi.admin.model.UpdateUserAccountBo; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtBoxService; +import com.ruoyi.admin.service.TtOrnamentsLevelService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.util.core.fight.LotteryMachine; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.common.constant.TtboxRecordSource; +import com.ruoyi.domain.dto.boxRecords.TtBoxRecordsNum; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.*; +import com.ruoyi.domain.vo.OpenBoxVO; +import com.ruoyi.playingmethod.domain.vo.CompValueVO; +import com.ruoyi.playingmethod.mapper.ApiBindBoxMapper; +import com.ruoyi.playingmethod.mapper.ApiTtUserBlendErcashMapper; +import com.ruoyi.playingmethod.service.ApiBindBoxService; +import com.ruoyi.playingmethod.utils.customException.OrnamentNullException; +import com.ruoyi.playingmethod.websocket.WsBindBox; +import com.ruoyi.playingmethod.websocket.constant.SMsgKey; +import com.ruoyi.playingmethod.websocket.util.WsResult; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.Timestamp; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.TtboxRecordStatus.IN_PACKSACK_ON; +import static java.math.BigDecimal.ROUND_HALF_UP; + +@Service +@Slf4j +public class ApiBindBoxServiceImpl implements ApiBindBoxService { + + private final TtUserService userService; + private final TtBoxService boxService; + private final TtBoxTypeMapper boxTypeMapper; + private final ApiBindBoxMapper bindBoxMapper; + private final TtBoxOrnamentsMapper boxOrnamentsMapper; + private final TtBoxRecordsService boxRecordsService; + private final RedisCache redisCache; + private final Executor customThreadPoolExecutor; + + @Autowired + private RabbitTemplate rabbitTemplate; + + @Autowired + private LotteryMachine lotteryMachine; + + @Autowired + private TtCompRecordMapper ttCompRecordMapper; + + @Autowired + private ApiTtUserBlendErcashMapper apiTtUserBlendErcashMapper; + + @Autowired + private TtBoxRecordsMapper ttBoxRecordsMapper; + + @Autowired + private TtOrnamentsLevelService ornamentsLevelService; + + public ApiBindBoxServiceImpl(TtUserService userService, + TtBoxService boxService, + TtBoxTypeMapper boxTypeMapper, + ApiBindBoxMapper bindBoxMapper, + TtBoxOrnamentsMapper boxOrnamentsMapper, + TtBoxRecordsService boxRecordsService, + RedisCache redisCache, + Executor customThreadPoolExecutor) { + this.userService = userService; + this.boxService = boxService; + this.boxTypeMapper = boxTypeMapper; + this.bindBoxMapper = bindBoxMapper; + this.boxOrnamentsMapper = boxOrnamentsMapper; + this.boxRecordsService = boxRecordsService; + this.redisCache = redisCache; + this.customThreadPoolExecutor = customThreadPoolExecutor; + } + + //@PostConstruct + public void postConstruct() { + List boxIds = boxOrnamentsMapper.selectBoxIdList(); + if (ObjectUtils.isEmpty(boxIds)) return; + for (Integer boxId : boxIds) { + boxService.isReplenishment(boxId); + } + } + + @Override + public TtBoxA getBoxData(Integer boxId) { + + TtBoxA boxData = bindBoxMapper.getBoxData(boxId); + if (ObjectUtils.isEmpty(boxData)) return null; + + List boxOrnamentsList = bindBoxMapper.getBoxOrnamentsList(boxId); + if (ObjectUtils.isEmpty(boxOrnamentsList)) return null; + + // 计算概率 + BigDecimal sum = new BigDecimal(boxOrnamentsList.stream().mapToInt(TtOrnamentsA::getOdds).sum()); + BigDecimal oneHundred = new BigDecimal("100").setScale(2); + BigDecimal max = BigDecimal.ZERO; + Integer maxId = null; + BigDecimal add = BigDecimal.ZERO; + List ornamentIds = new ArrayList<>(); + for (TtOrnamentsA vo : boxOrnamentsList) { + Integer odds = vo.getOdds(); + BigDecimal oddsResult = new BigDecimal(odds).divide(sum, 4, ROUND_HALF_UP).multiply(oneHundred).setScale(2); + add = oddsResult.add(add); + if (max.compareTo(oddsResult) <= 0) { + max = oddsResult; + maxId = vo.getOrnamentId(); + } + vo.setOddsResult(oddsResult); + ornamentIds.add(Long.valueOf(vo.getOrnamentId())); + } + + // 平差 + BigDecimal erroValue = oneHundred.subtract(add); + for (TtOrnamentsA vo : boxOrnamentsList) { + if (vo.getOrnamentId().equals(maxId)) { + vo.setOddsResult(vo.getOddsResult().add(erroValue)); + break; + } + } + + if (ObjectUtils.isEmpty(boxOrnamentsList)) return null; + boxOrnamentsList = boxOrnamentsList.stream().peek(boxOrnamentsDataVO -> boxOrnamentsDataVO.setOdds(null)).collect(Collectors.toList()); + List probabilityDistribution = bindBoxMapper.getProbabilityDistribution(boxId); + boxOrnamentsList.sort((o1, o2) -> o2.getUsePrice().compareTo(o1.getUsePrice())); + boxData.setBoxOrnamentsList(boxOrnamentsList); + boxData.setProbabilityDistribution(probabilityDistribution); + // 计算真实开奖 + List boxRecordsNums = ttBoxRecordsMapper.selectBoxRecordsByOrnamentIds(boxId, ornamentIds, null); + Map ornamentIdNum = new HashMap<>(); + sum = new BigDecimal(0); + add = BigDecimal.ZERO; + max = BigDecimal.ZERO; + maxId = null; + oneHundred = new BigDecimal("100").setScale(2); + if (CollectionUtils.isNotEmpty(boxRecordsNums)) { + for (TtBoxRecordsNum num : boxRecordsNums) { + ornamentIdNum.put(num.getOrnamentId().intValue(), num.getNum()); + sum = sum.add(new BigDecimal(num.getNum())); + } + } + boxData.setTotalOpenOdds(sum.intValue()); + // 某个颜色等级真是开奖的数量 + Map levelNum = new HashMap<>(); + for (TtOrnamentsA vo : boxOrnamentsList) { + Integer openOdds = ornamentIdNum.getOrDefault(vo.getOrnamentId(), 0); + vo.setOpenOdds(openOdds); + levelNum.put(vo.getOrnamentsLevelId(), levelNum.getOrDefault(vo.getOrnamentsLevelId(), 0) + openOdds); + BigDecimal openOddsResult = new BigDecimal(0); + if (sum.compareTo(new BigDecimal(0)) > 0) { + openOddsResult = new BigDecimal(openOdds).divide(sum, 4, RoundingMode.HALF_UP).multiply(oneHundred).setScale(2); + } + add = openOddsResult.add(add); + if (max.compareTo(openOddsResult) <= 0) { + max = openOddsResult; + maxId = vo.getOrnamentId(); + } + vo.setOpenOddsResult(openOddsResult); + } + + // 平差 + erroValue = oneHundred.subtract(add); + for (TtOrnamentsA vo : boxOrnamentsList) { + if (vo.getOrnamentId().equals(maxId)) { + vo.setOpenOddsResult(vo.getOpenOddsResult().add(erroValue)); + break; + } + } + List openProbabilityDistribution = new ArrayList<>(); + List levels = ornamentsLevelService.list(); + if (CollectionUtils.isNotEmpty(levels)) { + for (TtOrnamentsLevel level : levels) { + TtBoxLevelA a = new TtBoxLevelA(); + a.setLevel(level.getLevel()); + a.setOrnamentsLevelId(level.getId()); + BigDecimal probability = new BigDecimal(0); + if (sum.compareTo(new BigDecimal(0)) > 0) { + probability = new BigDecimal(levelNum.getOrDefault(level.getId(), 0)).divide(sum, 4, RoundingMode.HALF_UP).multiply(oneHundred).setScale(2); + } + a.setProbability(probability.toString()); + openProbabilityDistribution.add(a); + } + } + boxData.setOpenProbabilityDistribution(openProbabilityDistribution); + return boxData; + } + + @Override + public List getCompBoxData(Integer boxId) { + List boxOrnamentsList = bindBoxMapper.getBoxOrnamentsList(boxId); + if (ObjectUtils.isEmpty(boxOrnamentsList)) + return null; + // 剔除非补偿物品 + boxOrnamentsList.removeIf(ttOrnamentsA -> ttOrnamentsA.getCompOdds() == 0); + // 计算概率 + BigDecimal sum = new BigDecimal(boxOrnamentsList.stream().mapToInt(TtOrnamentsA::getCompOdds).sum()); + BigDecimal oneHundred = new BigDecimal("100").setScale(2); + BigDecimal max = BigDecimal.ZERO; + Integer maxId = null; + BigDecimal add = BigDecimal.ZERO; + for (TtOrnamentsA ttOrnamentsA : boxOrnamentsList) { + Integer compOdds = ttOrnamentsA.getCompOdds(); + BigDecimal compOddsResult = new BigDecimal(compOdds).divide(sum, 4, ROUND_HALF_UP).multiply(oneHundred).setScale(2); + add = compOddsResult.add(add); + if (max.compareTo(compOddsResult) <= 0) { + max = compOddsResult; + maxId = ttOrnamentsA.getOrnamentId(); + } + ttOrnamentsA.setCompOddsResult(compOddsResult); + } + // 平差 + BigDecimal errorValue = oneHundred.subtract(add); + for (TtOrnamentsA vo : boxOrnamentsList) { + if (vo.getOrnamentId().equals(maxId)) { + vo.setOddsResult(vo.getCompOddsResult().add(errorValue)); + break; + } + } + return boxOrnamentsList; + } + + @Override + public List getBoxList(Integer boxTypeId, String homeFlag, String boxType) { + + List boxList = bindBoxMapper.getBoxList(boxTypeId, homeFlag, boxType); + + // 过滤空箱子 + List collect = boxList.stream().filter(item -> { + + List list = new LambdaQueryChainWrapper<>(boxOrnamentsMapper) + .eq(TtBoxOrnaments::getBoxId, item.getBoxId()) + .list(); + if (ObjectUtil.isEmpty(list) || list.isEmpty()) return false; + + boolean flag = false; + for (TtBoxOrnaments ornaments : list) { + if (ornaments.getRealOdds() > 0) { + return !flag; + } + } + return flag; + + }).collect(Collectors.toList()); + + return groupByBoxType(collect); + } + + @Override + public List groupByBoxType(List boxData) { + Map> collect = boxData.stream().collect(Collectors.groupingBy(TtBoxA::getBoxTypeId)); + List resultList = new ArrayList<>(); + for (Map.Entry> entry : collect.entrySet()) { + Integer key = entry.getKey(); + List value = entry.getValue(); + TtBoxType ttBoxType = boxTypeMapper.selectById(key); + if (StringUtils.isNull(ttBoxType)) continue; + TtBoxVO boxTypeA = TtBoxVO.builder().build(); + boxTypeA.setBoxTypeId(ttBoxType.getId()); + boxTypeA.setBoxTypeName(ttBoxType.getName()); + boxTypeA.setIcon(ttBoxType.getIcon()); + boxTypeA.setBoxList(value); + resultList.add(boxTypeA); + } + return resultList; + } + + @Override + public List openBox(TtBox ttBox, Integer num, TtUser ttUser) { + return null; + } + + @Override + public List addBoxRecord(TtUser ttUser, TtBox ttBox, Integer num) { + return null; + } + + @Override + public List openBoxArithmetic(Integer fightId, TtUser ttUser, TtBox ttBox, Integer num) { + return null; + } + + + @Override + @Transactional + public R blindBox(TtUser user, TtBox box, Integer num) { + + // 同步扣款 + BigDecimal totalmoney = box.getPrice().multiply(new BigDecimal(num)); + R r = upgradeAccounting(totalmoney, user); + if (R.isError(r)) { + return R.fail(r.getMsg()); + } + + // 抽奖 + List boxRecords = new ArrayList<>(); + Map boxToOrnament = new HashMap<>(); + for (int i = 0; i < num; i++) { + + String ornamentId = lotteryMachine.singleLottery(user, box); + + if (StringUtils.isBlank(ornamentId)) { + throw new OrnamentNullException("单次抽奖无结果#"+box.getBoxId()+"#"+ornamentId); + //return R.fail("系统繁忙,请稍后重试。"); + } + + // 宝箱详细信息 + TtOrnamentsA ornamentsData = bindBoxMapper.ornamentsInfo(box.getBoxId(), ornamentId); + + if (ObjectUtil.isEmpty(ornamentsData)){ + throw new OrnamentNullException("没有符合条件的宝箱信息#"+box.getBoxId()+"#"+ornamentId); + } + + // 比对补偿箱类型开箱价格和奖品价值,记录补偿值。如果饰品大于等于门票,清空补偿值 + if ("3".equals(box.getBoxType()) && ornamentsData.getUsePrice().compareTo(box.getPrice()) < 0) { + compRecord(user.getUserId(), box.getBoxId()); + } else { + ttCompRecordMapper.deleteTtCompRecord(user.getUserId(), box.getBoxId()); + } + + // 抽奖成功,构建开箱记录数据 + TtBoxRecords boxRecord = TtBoxRecords.builder() + .userId(user.getUserId()) + + .boxId(box.getBoxId()) + .boxName(box.getBoxName()) + .boxPrice(box.getPrice()) + + .ornamentId(Long.valueOf(ornamentId)) + // .marketHashName(ornamentsData.) + .ornamentName(ObjectUtil.isNotEmpty(ornamentsData.getName()) ? ornamentsData.getName() : ObjectUtil.isNotEmpty(ornamentsData.getShortName()) ? ornamentsData.getShortName() : "无名称") + .imageUrl(ornamentsData.getImageUrl()) + .ornamentsPrice(ornamentsData.getUsePrice()) + .ornamentsLevelId(ornamentsData.getOrnamentsLevelId()) + .ornamentLevelImg(ornamentsData.getLevelImg()) + + .holderUserId(user.getUserId()) + + .source(TtboxRecordSource.BLIND_BOX.getCode()) + .status(IN_PACKSACK_ON.getCode()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + boxRecords.add(boxRecord); + boxToOrnament.put(Long.valueOf(ornamentId), ornamentsData); + } + + // 同步保存游戏记录 + boxRecordsService.saveBatch(boxRecords); + + // 返回值 + List openBoxVOs = boxRecords.stream().map(item -> { + OpenBoxVO openBoxVO = new OpenBoxVO(); + BeanUtil.copyProperties(item, openBoxVO); + TtOrnamentsA ornamentsData = boxToOrnament.get(item.getOrnamentId()); + if (ornamentsData != null) { + openBoxVO.setShortName(ornamentsData.getShortName()); + openBoxVO.setExteriorName(ornamentsData.getExteriorName()); + } + openBoxVO.setUsePrice(openBoxVO.getOrnamentsPrice()); + openBoxVO.setLevelImg(item.getOrnamentLevelImg()); + return openBoxVO; + }).collect(Collectors.toList()); + + // 异步任务 广播开箱消息 + broadcastToBoxRoom(boxRecords); + + return R.ok(openBoxVOs); + } + + @Override + public CompValueVO getCompValue(TtUser ttUser, TtBox ttBox) { + CompValueVO compValueVO = new CompValueVO(); + // 查询宝箱设定的补偿值 + compValueVO.setDesiredCompValue(ttBox.getCompAmount()); + // 查询用户当前补偿值 + TtCompRecord ttCompRecord = ttCompRecordMapper.selectTtCompRecord(ttUser.getUserId(), ttBox.getBoxId()); + if (Objects.isNull(ttCompRecord)) + compValueVO.setCurrentCompValue(0); + else + compValueVO.setCurrentCompValue(ttCompRecord.getCompAmount()); + return compValueVO; + } + + // 扣款 + public R upgradeAccounting(BigDecimal consumption, TtUser player) { + + // 再次检查余额 + player = userService.getById(player.getUserId()); + if (player.getAccountAmount() == null || player.getAccountAmount().compareTo(consumption) < 0) { + return R.fail("余额不足"); + } + return userService.updateUserAccount(player.getUserId(), consumption.negate(), TtAccountRecordSource.GAME_TYPE_01); + } + + // 异步任务 广播开箱消息 + public void broadcastToBoxRoom(List boxRecords) { + + CompletableFuture.runAsync(() -> { + + // 数据按boxId排序 + Collections.sort(boxRecords, new Comparator() { + @Override + public int compare(TtBoxRecords o1, TtBoxRecords o2) { + return o1.getBoxId().compareTo(o2.getBoxId()); + } + }); + + // 缓冲区 + List cache = new ArrayList<>(); + + Integer flagBoxId = -1; + for (TtBoxRecords record : boxRecords) { + + if (cache.isEmpty()) { + // 缓冲区为空,直接加入 + cache.add(record); + flagBoxId = record.getBoxId(); + continue; + } else { + // 不为空 + if (record.getBoxId().equals(flagBoxId)) { + cache.add(record); + } else { + Integer bid = cache.get(0).getBoxId(); + // 广播最新数据 + WsBindBox.broadcastToBoxRoom(bid, WsResult.ok(SMsgKey.Blind_Box_Current_Data.name(), cache, "开盒子更新。")); + cache.clear(); + } + } + } + Integer bid = cache.get(0).getBoxId(); + // 广播最新数据 + WsBindBox.broadcastToBoxRoom(bid, WsResult.ok(SMsgKey.Blind_Box_Current_Data.name(), cache, "开盒子更新。")); + + }, customThreadPoolExecutor); + } + + // 记录补偿值 + public void compRecord(Integer userId, Integer boxId) { + // 查询当前补偿值 + TtCompRecord ttCompRecord = ttCompRecordMapper.selectTtCompRecord(userId, boxId); + if (!Objects.isNull(ttCompRecord)) { + // 补偿值+1 + ttCompRecord.setCompAmount(ttCompRecord.getCompAmount() + 1); + ttCompRecordMapper.updateTtCompRecord(ttCompRecord); + } + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiBoxOrnamentsServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiBoxOrnamentsServiceImpl.java new file mode 100644 index 0000000..5157564 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiBoxOrnamentsServiceImpl.java @@ -0,0 +1,11 @@ +package com.ruoyi.playingmethod.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.other.TtBoxOrnaments; +import com.ruoyi.playingmethod.mapper.ApiBoxOrnamentsMapper; +import com.ruoyi.playingmethod.service.ApiBoxOrnamentsService; +import org.springframework.stereotype.Service; + +@Service +public class ApiBoxOrnamentsServiceImpl extends ServiceImpl implements ApiBoxOrnamentsService { +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiBoxRecordsServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiBoxRecordsServiceImpl.java new file mode 100644 index 0000000..fc9ed2f --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiBoxRecordsServiceImpl.java @@ -0,0 +1,75 @@ +package com.ruoyi.playingmethod.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.domain.dto.boxRecords.TenTopQuery; +import com.ruoyi.domain.dto.boxRecords.queryCondition; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO; +import com.ruoyi.playingmethod.service.ApiBoxRecordsService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +@Slf4j +public class ApiBoxRecordsServiceImpl extends ServiceImpl implements ApiBoxRecordsService { + @Override + public List byCondition(queryCondition condition) { + + condition.setLimit((condition.getPage() - 1) * condition.getSize()); + if (ObjectUtil.isNull(condition.getOrderByFie())) condition.setOrderByFie(1); + + List list = this.baseMapper.byCondition( + condition.getUserId(), + condition.getLimit(), + condition.getSize(), + condition.getOrderByFie(), + condition.getBoxRecordId(), + condition.getBoxId(), + condition.getUserType(), + condition.getSource(), + condition.getStatus(), + condition.getOrnamentPriceMin(), + condition.getOrnamentPriceMax(), + condition.getOrnamentLevelIds() + ); + + // 排序 + // Comparator comparator = null; + // if (ObjectUtil.isNull(condition.getOrderByFie()) || condition.getOrderByFie().equals(1)) { + // comparator = new Comparator() { + // @Override + // public int compare(TtBoxRecordsVO o1, TtBoxRecordsVO o2) { + // return o1.getCreateTime().compareTo(o2.getCreateTime()); + // } + // }; + // } else if (condition.getOrderByFie().equals(0)) { + // comparator = new Comparator() { + // @Override + // public int compare(TtBoxRecordsVO o1, TtBoxRecordsVO o2) { + // return o2.getCreateTime().compareTo(o1.getCreateTime()); + // } + // }; + // } + // + // Collections.sort(list, comparator); + + return list; + + } + + @Override + public List tenTopQuery(TenTopQuery query) { + query.setLimit((query.getPage() - 1) * query.getSize()); + + return this.baseMapper.tenTopQuery( + query.getLimit(), + query.getSize(), + query.getSource(), + query.getStatus() + ); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiCompoundServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiCompoundServiceImpl.java new file mode 100644 index 0000000..1ff03a9 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiCompoundServiceImpl.java @@ -0,0 +1,121 @@ +package com.ruoyi.playingmethod.service.impl; + +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.other.TtOrnamentsLevel; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtOrnamentsLevelService; +import com.ruoyi.admin.service.WebsitePropertyService; +import com.ruoyi.admin.util.RandomUtils; +import com.ruoyi.domain.vo.WebsitePropertyDataVO; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.playingmethod.mapper.ApiCompoundMapper; +import com.ruoyi.playingmethod.service.ApiCompoundService; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.user.service.ApiUserPackSackService; +import com.ruoyi.domain.vo.UserPackSackDataVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.TtboxRecordSource.MALL_EXCHANGE; + +@Service +@Slf4j +public class ApiCompoundServiceImpl implements ApiCompoundService { + + private final ISysConfigService configService; + private final ApiUserPackSackService userPackSackService; + private final WebsitePropertyService websitePropertyService; + private final TtBoxRecordsService boxRecordsService; + private final TtOrnamentMapper ornamentsMapper; + private final TtOrnamentsLevelService ornamentsLevelService; + private final ApiCompoundMapper apiCompoundMapper; + + public ApiCompoundServiceImpl(ISysConfigService configService, + ApiUserPackSackService userPackSackService, + WebsitePropertyService websitePropertyService, + TtBoxRecordsService boxRecordsService, + TtOrnamentMapper ornamentsMapper, + TtOrnamentsLevelService ornamentsLevelService, + ApiCompoundMapper apiCompoundMapper) { + this.configService = configService; + this.userPackSackService = userPackSackService; + this.websitePropertyService = websitePropertyService; + this.boxRecordsService = boxRecordsService; + this.ornamentsMapper = ornamentsMapper; + this.ornamentsLevelService = ornamentsLevelService; + this.apiCompoundMapper = apiCompoundMapper; + } + + @Override + public R compound(List packSackIds, TtUser ttUser) { + List boxRecordsList = userPackSackService.packSackHandle(packSackIds, ttUser, 3); + if (ObjectUtils.isEmpty(boxRecordsList)) return R.fail("请选择饰品后再进行合成!"); + List ornamentsIds = boxRecordsList.stream().map(TtBoxRecords::getOrnamentId).collect(Collectors.toList()); + List websitePropertyIds = websitePropertyService.list().stream().map(WebsitePropertyDataVO::getId).collect(Collectors.toList()); + websitePropertyIds.retainAll(ornamentsIds); + if (!websitePropertyIds.isEmpty()) return R.fail("选择的饰品中存在网站专属道具,请重新选择!"); + String maxCompoundNumStr = configService.selectConfigByKey("maxCompoundNum"); + int maxCompoundNum = Integer.parseInt(maxCompoundNumStr); + if (boxRecordsList.size() > maxCompoundNum) return R.fail("最多选择" + maxCompoundNum + "个饰品!"); + BigDecimal compoundBeanTotal = boxRecordsList.stream().map(TtBoxRecords::getOrnamentsPrice).reduce(BigDecimal.ZERO, BigDecimal::add); + String compoundMinPriceStr = configService.selectConfigByKey("compoundMinPrice"); // 获取配置中的汰换最低价 + BigDecimal compoundMinPrice = new BigDecimal(compoundMinPriceStr); + if (compoundBeanTotal.compareTo(compoundMinPrice) < 0) + return R.fail("选择的饰品总价必须大于" + compoundMinPrice + "游戏币才可以汰换哦!"); + Optional maxPriceOptional = boxRecordsList.stream().map(TtBoxRecords::getOrnamentsPrice).max(Comparator.naturalOrder()); + if (!maxPriceOptional.isPresent()) return null; + try { + String compoundMinPremiumRateStr = configService.selectConfigByKey("compoundMinPremiumRate"); + BigDecimal minPremiumRatePrice = new BigDecimal(compoundMinPremiumRateStr).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP) + .multiply(maxPriceOptional.get()); + String compoundMaxPremiumRateStr = configService.selectConfigByKey("compoundMaxPremiumRate"); + BigDecimal maxPremiumRatePrice = new BigDecimal(compoundMaxPremiumRateStr).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP) + .multiply(compoundBeanTotal); + BigDecimal minPrice = maxPriceOptional.get().subtract(minPremiumRatePrice); + BigDecimal maxPrice = compoundBeanTotal.subtract(maxPremiumRatePrice); + List ornamentsList = new LambdaQueryChainWrapper<>(ornamentsMapper).in(TtOrnament::getType, 2, 3, 4, 5) + .between(TtOrnament::getUsePrice, minPrice, maxPrice).list(); + if (StringUtils.isNull(ornamentsList) || ornamentsList.isEmpty()) + return R.fail("汰换失败,饰品池中未筛选出符合您价格区间的饰品!"); + int randomIndex = RandomUtils.getRandomIndex(ornamentsList.size()); + TtOrnament ttOrnament = ornamentsList.get(randomIndex); + List levelIds = ornamentsLevelService.list().stream().map(TtOrnamentsLevel::getId).collect(Collectors.toList()); + Integer ornamentsLevelId = levelIds.get(RandomUtils.getRandomIndex(levelIds.size())); + boxRecordsService.updateBatchById(boxRecordsList, 1); + TtBoxRecords boxRecords = TtBoxRecords.builder().build(); + boxRecords.setUserId(ttUser.getUserId()); + boxRecords.setOrnamentId(ttOrnament.getId()); + boxRecords.setOrnamentsPrice(ttOrnament.getUsePrice()); + boxRecords.setOrnamentsLevelId(ornamentsLevelId); + boxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode()); + boxRecords.setCreateTime(new Date()); + boxRecords.setSource(MALL_EXCHANGE.getCode()); //// TODO: 2024/4/1 类型不对 + boxRecords.setHolderUserId(ttUser.getUserId()); + boxRecordsService.save(boxRecords); + UserPackSackDataVO compoundData = apiCompoundMapper.selectCompoundDataById(boxRecords.getId()); + return R.ok(compoundData); + } catch (Exception e) { + return R.fail("数据异常!"); + } + } + + @Override + public List getUserCompoundRecord(Integer userId) { + return apiCompoundMapper.selectCompoundRecordByUserId(userId); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiExponentServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiExponentServiceImpl.java new file mode 100644 index 0000000..6cba3e1 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiExponentServiceImpl.java @@ -0,0 +1,555 @@ +package com.ruoyi.playingmethod.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.mapper.TtBoxMapper; +import com.ruoyi.admin.mapper.TtExponentMapper; +import com.ruoyi.admin.mapper.TtExponentUserBoxMapper; +import com.ruoyi.admin.mapper.TtExponentUserMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtBoxService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.util.core.fight.LotteryMachine; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.common.constant.TtboxRecordSource; +import com.ruoyi.domain.dto.exponent.*; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.exponent.TtExponent; +import com.ruoyi.domain.entity.exponent.TtExponentUser; +import com.ruoyi.domain.entity.exponent.TtExponentUserBox; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtBoxA; +import com.ruoyi.domain.other.TtOrnamentsA; +import com.ruoyi.domain.vo.OpenBoxVO; +import com.ruoyi.playingmethod.mapper.ApiBindBoxMapper; +import com.ruoyi.playingmethod.mapper.ApiTtUserBlendErcashMapper; +import com.ruoyi.playingmethod.service.ApiExponentService; +import com.ruoyi.playingmethod.utils.customException.OrnamentNullException; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.TtboxRecordStatus.IN_PACKSACK_ON; + +@Service +@Slf4j +public class ApiExponentServiceImpl implements ApiExponentService { + + @Autowired + private TtExponentMapper ttExponentMapper; + + @Autowired + private TtExponentUserMapper ttExponentUserMapper; + + @Autowired + private TtBoxService boxService; + + @Autowired + private TtUserService userService; + + @Autowired + private LotteryMachine lotteryMachine; + + @Autowired + private ApiBindBoxMapper bindBoxMapper; + + @Autowired + private TtBoxRecordsService boxRecordsService; + + @Autowired + private ApiTtUserBlendErcashMapper apiTtUserBlendErcashMapper; + + @Autowired + private TtBoxMapper ttBoxMapper; + + @Autowired + private TtExponentUserBoxMapper ttExponentUserBoxMapper; + + @Override + public R join(TtUser ttUser, ExponentJoinParam param) { + if (param == null || param.getId() == null || param.getBoxId() == null || param.getForward() == null) { + return R.fail("参数不合法"); + } + TtBox ttBox = boxService.getById(param.getBoxId()); + if (ttBox == null) { + return R.fail("宝箱参数不合法"); + } + // 同步扣款 + R> mapR = upgradeAccounting(ttBox.getPrice(), ttUser); + if (!mapR.getCode().equals(200)) { + return mapR; + } + TtExponentUser exponentUser = new TtExponentUser(); + exponentUser.setExponentId(param.getId()); + exponentUser.setBoxId(param.getBoxId()); + exponentUser.setUserId(ttUser.getUserId()); + exponentUser.setPrice(ttBox.getPrice()); + exponentUser.setForward(param.getForward()); + exponentUser.setStatus(0); + exponentUser.setBoxRecordId(0L); + exponentUser.setCreateTime(new Date()); + exponentUser.setUpdateTime(new Date()); + ttExponentUserMapper.insert(exponentUser); + return R.ok(); + } + + // 扣款 + public R> upgradeAccounting(BigDecimal consumption, TtUser player) { + // 再次检查余额 + player = userService.getById(player.getUserId()); + if (player.getAccountAmount() == null || player.getAccountAmount().compareTo(consumption) < 0) { + return R.fail("余额不足"); + } + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate.eq(TtUser::getUserId, player.getUserId()); + Map map; + if (player.getAccountAmount().compareTo(consumption) >= 0) { + userUpdate.set(TtUser::getAccountAmount, player.getAccountAmount().subtract(consumption)); + map = MapUtil.builder("Amount", consumption).map(); + } else { + BigDecimal subtract = consumption.subtract(player.getAccountAmount()); + userUpdate + .set(TtUser::getAccountAmount, 0) + .set(TtUser::getAccountCredits, player.getAccountCredits().subtract(subtract)); + map = MapUtil.builder("Amount", player.getAccountAmount()).map(); + map.put("Credits", subtract); + } + userService.update(userUpdate); + + // 综合消费日志 + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(player.getUserId()) + .amount(null) + .finalAmount(null) + .credits(null) + .finalCredits(null) + .total(consumption.negate()) // 收支合计 + .type(TtAccountRecordType.OUTPUT.getCode()) + .source(TtAccountRecordSource.EXPONENT_GAME_TYPE_01.getCode()) + .remark(TtAccountRecordSource.EXPONENT_GAME_TYPE_01.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + apiTtUserBlendErcashMapper.insert(blendErcash); + + return R.ok(map); + } + + @Override + public R box(TtUser ttUser, Integer id) { + List boxList = bindBoxMapper.getBoxList(null, null, "5"); + if (boxList.isEmpty()) { + return R.ok(new ArrayList<>(), "没有匹配的数据。"); + } + List boxVos = new ArrayList<>(); + List boxIds = new ArrayList<>(); + for (TtBoxA ttBoxA : boxList) { + ExponentBoxVo boxVo = new ExponentBoxVo(); + BeanUtils.copyProperties(ttBoxA, boxVo); + boxVo.setBoxId(ttBoxA.getBoxId()); + boxIds.add(ttBoxA.getBoxId()); + boxVos.add(boxVo); + } + + // 查看当前登录者是否购买本期指数 + LambdaQueryWrapper wrapper1 = Wrappers.lambdaQuery(); + wrapper1.in(TtExponentUser::getBoxId, boxIds); + wrapper1.eq(TtExponentUser::getExponentId, id); + wrapper1.eq(TtExponentUser::getUserId, ttUser.getUserId()); + List exponentUsers = ttExponentUserMapper.selectList(wrapper1); + Map boxIdToUp = new HashMap<>(); + Map boxIdToDown = new HashMap<>(); + if (CollectionUtils.isNotEmpty(exponentUsers)) { + for (TtExponentUser user : exponentUsers) { + if (user == null) { + continue; + } + if (Objects.equals(user.getForward(), 1)) { + int num = boxIdToUp.getOrDefault(user.getBoxId(), 0); + boxIdToUp.put(user.getBoxId(), num + 1); + } + if (Objects.equals(user.getForward(), 2)) { + int num = boxIdToDown.getOrDefault(user.getBoxId(), 0); + boxIdToDown.put(user.getBoxId(), num + 1); + } + } + } + for (ExponentBoxVo vo : boxVos) { + vo.setBuy_forward_up(boxIdToUp.getOrDefault(vo.getBoxId(), 0)); + vo.setBuy_forward_down(boxIdToDown.getOrDefault(vo.getBoxId(), 0)); + } + return R.ok(boxVos); + } + + @Override + public R recenttenperiod() { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.orderByDesc(TtExponent::getId); + wrapper.last("limit 10"); + List db = ttExponentMapper.selectList(wrapper); + return R.ok(db); + } + + /** + * + */ + @Override + public R info(TtUser ttUser) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.orderByDesc(TtExponent::getId); + wrapper.last("limit 1"); + TtExponent db = ttExponentMapper.selectOne(wrapper); + if (db == null) { + return R.fail("指数信息不存在"); + } + ExponentInfoVo vo = new ExponentInfoVo(); + Integer id = db.getId() + 1; + vo.setId(id); + vo.setExponent(0); + // 创建日期格式化对象,指定日期格式 + SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); + // 获取Calendar实例 + Calendar calendar = Calendar.getInstance(); + // 设置时间为今天凌晨 + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + // 获取今天凌晨时间 + Date todayMidnight = calendar.getTime(); + String period = ""; + if (db.getCreateTime().after(todayMidnight)) { + period = formatter.format(new Date()) + String.format("%04d", (db.getNumber() + 1)); + } else { + period = formatter.format(new Date()) + "0001"; + } + vo.setPeriod(period); + int createTime = (int) (db.getCreateTime().getTime() / 1000); + vo.setEnd_time(createTime + 60); + vo.setPoints(""); + vo.setState(0); + vo.setNumber(db.getNumber() + 1); + vo.setForward(0); + vo.setCreatetime(createTime + 30); + vo.setUpdatetime(createTime + 30); + int nowTime = (int) (System.currentTimeMillis() / 1000); + vo.setTimer(vo.getCreatetime() - nowTime); + + // 查看当前登录者是否购买本期指数 + LambdaQueryWrapper wrapper2 = Wrappers.lambdaQuery(); + wrapper2.eq(TtExponentUser::getExponentId, id); + wrapper2.eq(TtExponentUser::getUserId, ttUser.getUserId()); + List exponentUsers2 = ttExponentUserMapper.selectList(wrapper2); + // 若购买 + if (CollectionUtils.isNotEmpty(exponentUsers2)) { + vo.setBuy(exponentUsers2.size()); + BigDecimal up = BigDecimal.ZERO; + BigDecimal down = BigDecimal.ZERO; + for (TtExponentUser exponentUser : exponentUsers2) { + if (exponentUser == null) { + continue; + } + if (Objects.equals(exponentUser.getForward(), 1)) { + up = up.add(exponentUser.getPrice()); + } + if (Objects.equals(exponentUser.getForward(), 2)) { + down = down.add(exponentUser.getPrice()); + } + } + vo.setBuy_forward_up(up); + vo.setBuy_forward_down(down); + } + + ExponentInfoVo last = new ExponentInfoVo(); + last.setId(db.getId()); + last.setExponent(db.getExponent()); + period = formatter.format(db.getCreateTime()) + String.format("%04d", db.getNumber()); + last.setPeriod(period); + int lastCreateTime = (int) (db.getCreateTime().getTime() / 1000); + last.setEnd_time(lastCreateTime + 30); + last.setPoints(db.getPoints()); + last.setState(2); + last.setNumber(db.getNumber()); + last.setForward(db.getForward()); + last.setCreatetime(lastCreateTime); + last.setUpdatetime(lastCreateTime); + + vo.setLast(last); + + // 查看当前登录者是否购买本期指数 + LambdaQueryWrapper wrapper1 = Wrappers.lambdaQuery(); + wrapper1.eq(TtExponentUser::getExponentId, db.getId()); + wrapper1.eq(TtExponentUser::getUserId, ttUser.getUserId()); + List exponentUsers = ttExponentUserMapper.selectList(wrapper1); + ExponentJieguoVo jieguo = new ExponentJieguoVo(); + jieguo.setPeriod(period); + jieguo.setExponent(db.getExponent()); + jieguo.setForward(db.getForward()); + // 若购买 + if (CollectionUtils.isNotEmpty(exponentUsers)) { + jieguo.setSkinarr(openExponent(db.getForward(), ttUser, exponentUsers)); + jieguo.setBuy(exponentUsers.size()); + BigDecimal up = BigDecimal.ZERO; + BigDecimal down = BigDecimal.ZERO; + BigDecimal price = BigDecimal.ZERO; + for (TtExponentUser exponentUser : exponentUsers) { + if (exponentUser == null) { + continue; + } + price = price.add(exponentUser.getPrice()); + if (Objects.equals(exponentUser.getForward(), 1)) { + up = up.add(exponentUser.getPrice()); + } + if (Objects.equals(exponentUser.getForward(), 2)) { + down = down.add(exponentUser.getPrice()); + } + } + jieguo.setBuy_money(price); + jieguo.setBuy_forward_up(up); + jieguo.setBuy_forward_down(down); + } + vo.setJieguo(jieguo); + return R.ok(vo); + } + + /** + * 指数开箱 + */ + private List openExponent(int forward, TtUser ttUser, List exponentUsers) { + Map boxIdToNum = new HashMap<>(); + // 判断箱子是否已判定结果 + Map boxIdToOpen = new HashMap<>(); + for (TtExponentUser user : exponentUsers) { + if (user == null) { + continue; + } + // 等于1,则代表指数结果已判定结果 + if (Objects.equals(user.getStatus(), 1)) { + boxIdToOpen.put(user.getBoxId(), true); + int num = boxIdToNum.getOrDefault(user.getBoxId(), 0); + boxIdToNum.put(user.getBoxId(), num + 1); + continue; + } + // 等于2,则代表指数结果已判定结果 + if (Objects.equals(user.getStatus(), 2)) { + boxIdToOpen.put(user.getBoxId(), true); + boxIdToNum.put(user.getBoxId(), 0); + continue; + } + if (Objects.equals(user.getForward(), forward)) { + int num = boxIdToNum.getOrDefault(user.getBoxId(), 0); + boxIdToNum.put(user.getBoxId(), num + 1); + user.setStatus(1); + } else { + user.setStatus(2); + } + user.setUpdateTime(new Date()); + ttExponentUserMapper.updateById(user); + } + if (MapUtils.isEmpty(boxIdToNum)) { + return new ArrayList<>(); + } + // 查询箱子信息 + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.in(TtBox::getBoxId, boxIdToNum.keySet()); + List ttBoxes = ttBoxMapper.selectList(wrapper); + Map ttBoxMap = ttBoxes.stream().collect(Collectors.toMap(TtBox::getBoxId, Function.identity())); + + // 查询该用户已拥有的箱子信息 + LambdaQueryWrapper wrapper1 = Wrappers.lambdaQuery(); + wrapper1.in(TtExponentUserBox::getBoxId, boxIdToNum.keySet()); + wrapper1.eq(TtExponentUserBox::getUserId, ttUser.getUserId()); + List userBoxes = ttExponentUserBoxMapper.selectList(wrapper1); + Map userBoxeMap = userBoxes.stream().collect(Collectors.toMap(TtExponentUserBox::getBoxId, Function.identity())); + + List vos = new ArrayList<>(); + for (Map.Entry entry : boxIdToNum.entrySet()) { + ExponentSkinarrVo vo = new ExponentSkinarrVo(); + vo.setId(entry.getKey()); + vo.setCount(entry.getValue()); + TtBox ttBox = ttBoxMap.get(entry.getKey()); + if (ttBox != null) { + vo.setImage(ttBox.getBoxImg01()); + vo.setImage2(ttBox.getBoxImg02()); + } + vos.add(vo); + // 若指数没有判定过,则进行放入箱子数量 + if (!boxIdToOpen.getOrDefault(entry.getKey(), false)) { + TtExponentUserBox userBox = userBoxeMap.get(entry.getKey()); + if (userBox == null) { + userBox = new TtExponentUserBox(); + userBox.setBoxId(entry.getKey()); + userBox.setUserId(ttUser.getUserId()); + userBox.setNumber(entry.getValue()); + userBox.setCreateTime(new Date()); + userBox.setUpdateTime(new Date()); + ttExponentUserBoxMapper.insert(userBox); + } else { + userBox.setNumber(userBox.getNumber() + entry.getValue()); + userBox.setUpdateTime(new Date()); + ttExponentUserBoxMapper.updateById(userBox); + } + } + } + return vos; + } + + @Override + public R records(TtUser ttUser) { + List vos = ttExponentMapper.exponentOpenBox(ttUser.getUserId()); + if (CollectionUtils.isEmpty(vos)) { + return R.ok(new ArrayList<>()); + } + List boxIds = new ArrayList<>(); + for (ExponentOpenBoxVo vo : vos) { + boxIds.add(vo.getBoxId()); + if (vo.getFailed() != null && vo.getFailed() > 0) { + vo.setState(2); + } + if (vo.getSuccess() != null && vo.getSuccess() > 0) { + vo.setState(1); + } + } + + // 查询箱子信息 + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.in(TtBox::getBoxId, boxIds); + List ttBoxes = ttBoxMapper.selectList(wrapper); + Map ttBoxMap = ttBoxes.stream().collect(Collectors.toMap(TtBox::getBoxId, Function.identity())); + for (ExponentOpenBoxVo vo : vos) { + TtBox ttBox = ttBoxMap.get(vo.getBoxId()); + if (ttBox != null) { + vo.setImage(ttBox.getBoxImg01()); + vo.setImage2(ttBox.getBoxImg02()); + } + } + return R.ok(vos); + } + + @Override + public R mybox(TtUser ttUser) { + // 查询该用户已拥有的箱子信息 + LambdaQueryWrapper wrapper1 = Wrappers.lambdaQuery(); + wrapper1.eq(TtExponentUserBox::getUserId, ttUser.getUserId()); + List userBoxes = ttExponentUserBoxMapper.selectList(wrapper1); + if (CollectionUtils.isEmpty(userBoxes)) { + return R.ok(new ArrayList<>()); + } + List vos = new ArrayList<>(); + List boxIds = new ArrayList<>(); + for (TtExponentUserBox userBox : userBoxes) { + ExponentSkinarrVo vo = new ExponentSkinarrVo(); + vo.setId(userBox.getBoxId()); + boxIds.add(userBox.getBoxId()); + vo.setCount(userBox.getNumber()); + vos.add(vo); + } + // 查询箱子信息 + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.in(TtBox::getBoxId, boxIds); + List ttBoxes = ttBoxMapper.selectList(wrapper); + Map ttBoxMap = ttBoxes.stream().collect(Collectors.toMap(TtBox::getBoxId, Function.identity())); + for (ExponentSkinarrVo vo : vos) { + TtBox ttBox = ttBoxMap.get(vo.getId()); + if (ttBox != null) { + vo.setImage(ttBox.getBoxImg01()); + vo.setImage2(ttBox.getBoxImg02()); + } + } + return R.ok(vos); + } + + @Override + public R openbox(TtUser user, TtBox box, Integer num) { + // 查询该用户已拥有的箱子信息 + LambdaQueryWrapper wrapper1 = Wrappers.lambdaQuery(); + wrapper1.eq(TtExponentUserBox::getUserId, user.getUserId()); + wrapper1.eq(TtExponentUserBox::getBoxId, box.getBoxId()); + List userBoxes = ttExponentUserBoxMapper.selectList(wrapper1); + if (CollectionUtils.isEmpty(userBoxes) || userBoxes.get(0) == null || userBoxes.get(0).getNumber() < num) { + return R.ok(new ArrayList<>()); + } + TtExponentUserBox userBox = userBoxes.get(0); + // 抽奖 + List boxRecords = new ArrayList<>(); + Map boxToOrnament = new HashMap<>(); + for (int i = 0; i < num; i++) { + String ornamentId = lotteryMachine.singleLottery(user, box); + if (StringUtils.isBlank(ornamentId)) { + throw new OrnamentNullException("单次抽奖无结果#"+box.getBoxId()+"#"+ornamentId); + //return R.fail("系统繁忙,请稍后重试。"); + } + // 宝箱详细信息 + TtOrnamentsA ornamentsData = bindBoxMapper.ornamentsInfo(box.getBoxId(), ornamentId); + if (ObjectUtil.isEmpty(ornamentsData)){ + throw new OrnamentNullException("没有符合条件的宝箱信息#"+box.getBoxId()+"#"+ornamentId); + } + // 抽奖成功,构建开箱记录数据 + TtBoxRecords boxRecord = TtBoxRecords.builder() + .userId(user.getUserId()) + + .boxId(box.getBoxId()) + .boxName(box.getBoxName()) + .boxPrice(box.getPrice()) + + .ornamentId(Long.valueOf(ornamentId)) + // .marketHashName(ornamentsData.) + .ornamentName(ObjectUtil.isNotEmpty(ornamentsData.getName()) ? ornamentsData.getName() : ObjectUtil.isNotEmpty(ornamentsData.getShortName()) ? ornamentsData.getShortName() : "无名称") + .imageUrl(ornamentsData.getImageUrl()) + .ornamentsPrice(ornamentsData.getUsePrice()) + .ornamentsLevelId(ornamentsData.getOrnamentsLevelId()) + .ornamentLevelImg(ornamentsData.getLevelImg()) + .holderUserId(user.getUserId()) + .source(TtboxRecordSource.EXPONENT_BOX.getCode()) + .status(IN_PACKSACK_ON.getCode()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + boxRecords.add(boxRecord); + boxToOrnament.put(Long.valueOf(ornamentId), ornamentsData); + } + // 同步保存游戏记录 + boxRecordsService.saveBatch(boxRecords); + // 返回值 + List openBoxVOs = boxRecords.stream().map(item -> { + OpenBoxVO openBoxVO = new OpenBoxVO(); + BeanUtil.copyProperties(item, openBoxVO); + TtOrnamentsA ornamentsData = boxToOrnament.get(item.getOrnamentId()); + if (ornamentsData != null) { + openBoxVO.setShortName(ornamentsData.getShortName()); + openBoxVO.setExteriorName(ornamentsData.getExteriorName()); + } + openBoxVO.setUsePrice(openBoxVO.getOrnamentsPrice()); + openBoxVO.setLevelImg(item.getOrnamentLevelImg()); + return openBoxVO; + }).collect(Collectors.toList()); + + userBox.setNumber(userBox.getNumber() - num); + userBox.setUpdateTime(new Date()); + ttExponentUserBoxMapper.updateById(userBox); + + return R.ok(openBoxVOs); + } + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiFightServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiFightServiceImpl.java new file mode 100644 index 0000000..e4df698 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiFightServiceImpl.java @@ -0,0 +1,2116 @@ +package com.ruoyi.playingmethod.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.admin.model.UpdateUserAccountBo; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtFightResultService; +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.admin.service.TtOrnamentsLevelService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.util.core.fight.LotteryMachine; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.redis.config.RedisLock; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtboxRecordSource; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.dto.fight.FightDetailParam; +import com.ruoyi.domain.dto.fight.FightOnMyOwnParam; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.entity.fight.FightSeat; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.BoxDataBodyA; +import com.ruoyi.domain.other.CreateFightBody; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtBoxA; +import com.ruoyi.domain.other.TtBoxOrnaments; +import com.ruoyi.domain.other.TtBoxVO; +import com.ruoyi.domain.other.TtFightResult; +import com.ruoyi.domain.other.TtFightUser; +import com.ruoyi.domain.other.TtOrnamentsA; +import com.ruoyi.domain.other.TtOrnamentsLevel; +import com.ruoyi.domain.vo.ApiFightListDataVO; +import com.ruoyi.domain.vo.FightResultDataVO; +import com.ruoyi.domain.vo.PlayerGainsOrnamentsDataVO; +import com.ruoyi.domain.vo.TtBoxOrnamentsDataVO; +import com.ruoyi.domain.vo.UserPackSackDataVO; +import com.ruoyi.domain.vo.fight.AudienceVO; +import com.ruoyi.domain.vo.fight.FightBoxVO; +import com.ruoyi.domain.vo.fight.FightResultVO; +import com.ruoyi.domain.vo.fight.TtFightVO; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.websocket.WebSocketUsers; +import com.ruoyi.framework.websocket.pojo.ResultData; +import com.ruoyi.playingmethod.mapper.ApiBindBoxMapper; +import com.ruoyi.playingmethod.mapper.ApiFightMapper; +import com.ruoyi.playingmethod.mapper.ApiTtUserBlendErcashMapper; +import com.ruoyi.playingmethod.model.vo.ApiFightRankingVO; +import com.ruoyi.playingmethod.model.vo.ApiFightTenTopVo; +import com.ruoyi.playingmethod.service.ApiBindBoxService; +import com.ruoyi.playingmethod.service.ApiFightService; +import com.ruoyi.playingmethod.service.ApiFightUserService; +import com.ruoyi.playingmethod.websocket.WsFightHall; +import com.ruoyi.playingmethod.websocket.WsFightRoom; +import com.ruoyi.playingmethod.websocket.util.WsResult; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.common.service.ApiNoticeService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; + +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static com.ruoyi.admin.config.RedisConstants.JOIN_FIGHT_BEGIN_LOCK; +import static com.ruoyi.admin.config.RedisConstants.JOIN_FIGHT_END_LOCK; +import static com.ruoyi.admin.config.RedisConstants.JOIN_FIGHT_LOCK; +import static com.ruoyi.admin.config.RedisConstants.JOIN_FIGHT_SEAT_READY_LOCK; +import static com.ruoyi.domain.common.constant.TtboxRecordStatus.IN_PACKSACK_OFF; +import static com.ruoyi.domain.common.constant.TtboxRecordStatus.IN_PACKSACK_ON; +import static com.ruoyi.domain.common.constant.TtboxRecordStatus.RESOLVE; +import static com.ruoyi.playingmethod.websocket.constant.SMsgKey.ALL_FIGHT_ROOM; +import static com.ruoyi.playingmethod.websocket.constant.SMsgKey.FIGHT_RESULT; +import static com.ruoyi.playingmethod.websocket.constant.SMsgKey.FIGHT_ROOM_INFO; + +@Service +@Slf4j +public class ApiFightServiceImpl extends ServiceImpl implements ApiFightService { + + @Value("${mkcsgo.fight.roundTime}") + private final Integer fightRoundTime = null; + + @Autowired + private PlatformTransactionManager transactionManager; + + @Autowired + private TtBoxRecordsMapper boxRecordsMapper; + + @Autowired + private TtBoxOrnamentsMapper boxOrnamentsMapper; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ApiBindBoxMapper bindBoxMapper; + + @Autowired + private LotteryMachine lotteryMachine; + + @Autowired + private TtOrnamentMapper ttOrnamentMapper; + + @Autowired + private TtOrnamentsYYMapper ttOrnamentsYYMapper; + + @Autowired + private ApiTtUserBlendErcashMapper apiTtUserBlendErcashMapper; + + @Autowired + private ApiFightUserService fightUserService; + + @Autowired + private ISysConfigService configService; + + @Autowired + private TtFightResultService ttFightResultService; + + @Autowired + private ApiNoticeService apiNoticeService; + + private final TtBoxMapper boxMapper; + private final TtBoxRecordsService ttBoxRecordsService; + @Autowired + private TtBoxRecordsMapper ttBoxRecordsMapper; + private final TtUserService userService; + + private final RabbitTemplate rabbitTemplate; + private final TtBoxRecordsService boxRecordsService; + private final TtFightMapper fightMapper; + private final ApiFightMapper apiFightMapper; + private final TtFightUserMapper fightUserMapper; + private final ApiBindBoxService apiBindBoxService; + private final TtFightResultMapper fightResultMapper; + private final RedisLock redisLock; + private final Executor customThreadPoolExecutor; + private final TtOrnamentService ttOrnamentsZBTService; + + + private final TtOrnamentsLevelService ttOrnamentsLevelService; + @Autowired + private TtUserMapper ttUserMapper; + + public ApiFightServiceImpl(TtBoxMapper boxMapper, + TtUserService userService, + TtBoxRecordsService ttBoxRecordsService, + TtBoxRecordsService boxRecordsService, + TtFightMapper fightMapper, + RabbitTemplate rabbitTemplate, + TtOrnamentsLevelService ttOrnamentsLevelService, + TtOrnamentService ttOrnamentsZBTService, + ApiFightMapper apiFightMapper, + TtFightUserMapper fightUserMapper, + ApiBindBoxService apiBindBoxService, + TtFightResultMapper fightResultMapper, + RedisLock redisLock, + Executor customThreadPoolExecutor) { + this.boxMapper = boxMapper; + this.ttOrnamentsLevelService = ttOrnamentsLevelService; + this.userService = userService; + this.ttBoxRecordsService = ttBoxRecordsService; + this.boxRecordsService = boxRecordsService; + this.fightMapper = fightMapper; + this.rabbitTemplate = rabbitTemplate; + this.ttOrnamentsZBTService = ttOrnamentsZBTService; + this.apiFightMapper = apiFightMapper; + this.fightUserMapper = fightUserMapper; + this.apiBindBoxService = apiBindBoxService; + this.fightResultMapper = fightResultMapper; + this.redisLock = redisLock; + this.customThreadPoolExecutor = customThreadPoolExecutor; + } + + + private AjaxResult joinFightRoomCheck(Integer userId, Integer fightId) { + + TtUser player = userService.getById(userId); + TtFight ttFight = new LambdaQueryChainWrapper<>(fightMapper) + .eq(TtFight::getId, fightId) + .eq(TtFight::getStatus, "0") + .one(); + List roomPlayers = new LambdaQueryChainWrapper<>(fightUserMapper) + .eq(TtFightUser::getFightId, fightId) + .eq(TtFightUser::getUserId, player.getUserId()) + .list(); + + if (player.getAccountAmount().compareTo(ttFight.getBoxPriceTotal()) < 0) + return AjaxResult.error("加入房间失败,您的账户余额不足!"); + if (ObjectUtils.isEmpty(ttFight)) return AjaxResult.error("加入对战失败,该房间已结束!"); + if (roomPlayers.size() >= 1) return AjaxResult.error("您已成功加入该房间,请勿重复加入!"); + + return AjaxResult.success(); + } + + + @Override + // @Transactional + public R createFight(CreateFightBody createFightParam, TtUser player) { + + // log.info("初始化对局---------------------------------------------------------------------- 1"); + // 检查数据 + R fightCheck = createFightCheck(createFightParam, player); + if (!fightCheck.getCode().equals(200)) return fightCheck; + + TtFight fight = (TtFight) fightCheck.getData(); + + // 账户结算 + R r = readyFightAccounting(fight, player); + if (R.isError(r)) { + return R.fail(r.getMsg()); + } + + // 初始化对局 + fight.setModel(createFightParam.getModel()); + // 直接加入 + List seats = fight.getSeats(); + FightSeat ready = seats.remove(0).sitDown(player.getUserId()).ready(); + // 补充用户信息 + ready.setAvatar(player.getAvatar()); + ready.setNickName(player.getNickName()); + seats.add(ready); + Collections.reverse(seats); + + save(fight); + + // 同步保存参与人员信息 + TtFightUser fightUser = TtFightUser.builder() + .fightId(fight.getId()) + .userId(player.getUserId()) + .joinPrice(fight.getBoxPriceTotal()) + .createTime(new Date()) + .build(); + fightUserService.save(fightUser); + + + // ws广播最新对局(异步) + CompletableFuture.runAsync(() -> { + WsFightHall.broadcast(WsResult.ok(ALL_FIGHT_ROOM.name(), Arrays.asList(fight), "对战房间最新信息")); + log.info("用户{}【创建房间】{}", player.getUserId(), fight.getId()); + }, customThreadPoolExecutor); + + return R.ok(fight, "创建成功,可以建立 fightRoom ws"); + } + + public R createFightCheck(CreateFightBody createFightParam, TtUser player) { + + if (ObjectUtil.isEmpty(createFightParam.getModel())) { + return R.fail("模式不能为空"); + } + if (!createFightParam.getModel().equals("0") && !createFightParam.getModel().equals("1")) { + return R.fail("非法的模式类型"); + } + + Map boxIdAndNumber = createFightParam.getBoxIdAndNumber(); + + // 本局箱子总数 + Integer boxNum = 0; + LinkedHashMap boxMap = new LinkedHashMap<>(); + // 计算整局游戏箱子总价 + BigDecimal boxTotalPrice = BigDecimal.ZERO; + for (Integer boxId : boxIdAndNumber.keySet()) { + TtBox box = new LambdaQueryChainWrapper<>(boxMapper) + .eq(TtBox::getBoxId, boxId) + .eq(TtBox::getIsFight, "0") + .eq(TtBox::getStatus, "0") + .one(); + if (ObjectUtils.isEmpty(box)) { + return R.fail("不存在的箱子!"); + } + + boxMap.put(String.valueOf(boxId), new FightBoxVO(boxId, boxIdAndNumber.get(boxId), box.getBoxImg01(), box.getBoxImg02())); + + BigDecimal multiply = BigDecimal.valueOf(boxIdAndNumber.get(boxId)).multiply(box.getPrice()); + boxTotalPrice = boxTotalPrice.add(multiply); + boxNum = boxNum + boxIdAndNumber.get(boxId); + } + + // 回合数最多为15回合 + if (boxNum > 15 || boxNum <= 0) return R.fail("非法参数值 boxNum【" + boxNum + "】,宝箱数量范围1-15个!"); + if (player.getAccountAmount() == null || player.getAccountAmount().compareTo(boxTotalPrice) < 0) + return R.fail("您的账户余额不足!"); + + // ObjectMapper objectMapper = new ObjectMapper(); + + // 初始化座位信息 + ArrayList seats = new ArrayList<>(); + for (int i = 0; i < createFightParam.getPlayerNumber(); i++) { + FightSeat seat = FightSeat.builder() + .code(i) + .status(0) + .awardTotalPrices(BigDecimal.ZERO) + .build(); + seats.add(seat); + } + + TtFight fight = TtFight.builder() + .boxData(boxMap) + .boxPriceTotal(boxTotalPrice) + .status(0) + .roundNumber(boxNum) + .userId(player.getUserId()) + .playerNum(createFightParam.getPlayerNumber()) + .seats(seats) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + + return R.ok(fight); + } + + private R joinFightCheck(Integer fightId, TtUser player) { + + ObjectMapper objectMapper = new ObjectMapper(); + + // 查询对局信息(把mybatis一级缓存关掉) + TtFight fight = null; + fight = new LambdaQueryChainWrapper<>(fightMapper) + .eq(TtFight::getId, fightId) + .eq(TtFight::getStatus, "0") + .one(); + + if (ObjectUtils.isEmpty(fight)) return R.fail("此对局已结束!"); + + // 检查主播不能进入普通玩家房间 + /*if ("02".equals(createFightUser.getUserType()) && "01".equals(player.getUserType())) { + return R.fail("主播不可进入普通玩家房间"); + }*/ + + // 如果已报名,直接进入房间 + List list = JSONUtil.toList(JSON.toJSONString(fight.getSeats()), FightSeat.class); + for (FightSeat seat : list) { + if (ObjectUtil.isNotEmpty(seat.getPlayerId()) && seat.getPlayerId().equals(player.getUserId())) { + return R.ok(0, "已报名,再次进入房间。"); + } + } + + // 检查余额 + if (player.getAccountAmount() == null || player.getAccountAmount().compareTo(fight.getBoxPriceTotal()) < 0) + return R.fail("加入房间失败,您的账户余额不足!"); + + // 是否有空位,有空位就直接入座 + List seats = fight.getSeats(); + boolean f = false; + Integer joinIndex = null; + for (int i = 0; i < seats.size(); i++) { + FightSeat seat = objectMapper.convertValue(seats.get(i), FightSeat.class); + seats.set(i, seat); + if (seat.getStatus().equals(0) && !f) { + seat.sitDown(player.getUserId()); + seat.setAvatar(player.getAvatar()); + seat.setNickName(player.getNickName()); + f = !f; + joinIndex = i; + continue; + } + if (ObjectUtil.isNotEmpty(seat.getPlayerId()) && seat.getPlayerId().equals(player.getUserId())) { + if (ObjectUtil.isNotEmpty(joinIndex)) seats.get(joinIndex).sitUp(); + return R.fail(2, "不可重复加入对局。"); + } + } + if (!f) return R.fail("对局人满。"); + + try { + // 返回新的座位信息 + return R.ok(objectMapper.writeValueAsString(seats)); + } catch (JsonProcessingException e) { + log.warn("加入对局失败,请稍后重试。"); + return R.fail("加入对局失败,请稍后重试。"); + } + } + + // 加入房间 + @Override + @Transactional + public R joinFight(Integer fightId, TtUser player) { + // 加入 + Boolean lock = false; + for (int i = 0; i < 10; i++) { + lock = redisLock.tryLock(JOIN_FIGHT_LOCK + fightId, 3L, 7L, TimeUnit.SECONDS); + if (lock) { + break; + } + } + + if (!lock) { + return R.fail("服务器繁忙,稍后重试。"); + } + + try { + // 复查 + R check = joinFightCheck(fightId, player); + if (!check.getCode().equals(200)) { + redisLock.unlock(JOIN_FIGHT_LOCK + fightId); + return check; + } + if (ObjectUtil.isNotEmpty(check.getData()) && check.getData().equals(0)) { + return check; + } + + ObjectMapper objectMapper = new ObjectMapper(); + List seats = objectMapper.readValue((String) check.getData(), new com.fasterxml.jackson.core.type.TypeReference>() { + }); + LambdaUpdateWrapper fightUpdate = new LambdaUpdateWrapper<>(); + fightUpdate + .eq(TtFight::getId, fightId) + .eq(TtFight::getStatus, 0) + .set(TtFight::getSeats, check.getData()); + if (seatsIsFull(seats)) { + System.out.println("人满"); + // fightUpdate.set(TtFight::getStatus, 1); + } + update(fightUpdate); + log.info("玩家{}加入对局{}", player.getUserId(), fightId); + + TtFight fight = getById(fightId); + + if (MapUtils.isNotEmpty(fight.getBoxData())) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TtBox::getBoxId, fight.getBoxData().keySet()); + List boxes = boxMapper.selectList(queryWrapper); + Map boxMap = boxes.stream().filter(Objects::nonNull).collect(Collectors.toMap(TtBox::getBoxId, TtBox::getPrice)); + fight.getBoxData().keySet().forEach(boxId -> { + Object value = fight.getBoxData().get(boxId); + FightBoxVO a = null; + if (value instanceof LinkedHashMap) { + Map map = (LinkedHashMap) value; + a = new FightBoxVO(map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null, + map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null, + map.get("boxImg01") != null ? map.get("boxImg01").toString() : null, + map.get("boxImg02") != null ? map.get("boxImg02").toString() : null); + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } else { + a = (FightBoxVO) value; + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } + fight.getBoxData().put(boxId, a); + }); + } + + // ws广播参加对局玩家信息(异步) + CompletableFuture.runAsync(() -> { + WsFightRoom.broadcastFight(fight.getId(), WsResult.ok(FIGHT_ROOM_INFO.name(), fight, "对战房间最新信息")); + }, customThreadPoolExecutor); + + if (seatsIsFull(seats)) { + return R.ok(1, "加入对战成功。房间已满人"); + } + return R.ok(0, "加入对战成功。"); + + } catch (Exception e) { + e.printStackTrace(); + return R.fail("加入对战失败!"); + } finally { + redisLock.unlock(JOIN_FIGHT_LOCK + fightId); + } + } + + public R fightBeginCheck(Integer fightId, TtUser player) { + + TtFight fight = new LambdaQueryChainWrapper(fightMapper) + .eq(TtFight::getId, fightId) + // .eq(TtFight::getStatus, 0) + .one(); + if (ObjectUtil.isEmpty(fight)) return R.fail(null, "不存在的对局房间"); + if (fight.getStatus().equals(1)) return R.fail(fight, "对局已开始"); + if (fight.getStatus().equals(2) || fight.getStatus().equals(3)) return R.fail(fight, "对局已结束"); + + ObjectMapper objectMapper = new ObjectMapper(); + Map boxData = objectMapper.convertValue(fight.getBoxData(), new com.fasterxml.jackson.core.type.TypeReference>() { + }); + List seats = objectMapper.convertValue(fight.getSeats(), new com.fasterxml.jackson.core.type.TypeReference>() { + }); + + for (FightSeat seat : seats) { + if (!seat.getStatus().equals(2)) return R.fail("用户" + seat.getPlayerId() + "未准备。"); + } + + fight.setBoxData(boxData); + fight.setSeats(seats); + return R.ok(fight); + + } + + // 开始游戏 + @Override + public R fightBegin(Integer fightId, TtUser player) { + // TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition); + + Boolean lock = false; + for (int i = 0; i < 4; i++) { + + lock = redisLock.tryLock(JOIN_FIGHT_BEGIN_LOCK + fightId, 2L, 7L, TimeUnit.SECONDS); + + if (lock) break; + + } + + ObjectMapper objectMapper = new ObjectMapper(); + + // 多次尝试失败,检查一下房间状态 + if (!lock) { + TtFight fight = new LambdaQueryChainWrapper<>(fightMapper) + .eq(TtFight::getId, fightId) + .one(); + if (fight.getStatus().equals(0)) return R.fail("系统繁忙,请稍后重试。"); + return R.ok(null, "游戏已经开始"); + } + + // 抢到锁 + try { + + // 检查对局信息 + R check = fightBeginCheck(fightId, player); + if (!check.getCode().equals(200)) { + if (ObjectUtil.isEmpty(check.getData())) return check; + TtFight fight = objectMapper.convertValue(check.getData(), TtFight.class); + if (fight.getStatus().equals(1)) return R.ok(fight, "对局已开始"); + if (fight.getStatus().equals(2) || fight.getStatus().equals(3)) return R.ok(fight, "对局已结束"); + return check; + } + + TtFight fight = objectMapper.convertValue(check.getData(), TtFight.class); + + // 抽奖 计算开箱结果 + List result = newComputerFight(fight); + + // if (ObjectUtil.isEmpty(fightResult) || fightResult.isEmpty()) return R.fail("系统维护中,请联系管理员。"); + + // 计算胜负 + List winnerIds = computerWinner(fight.getModel(), fight.getSeats(), result); + + List fightResult = new ArrayList<>(); + // 分配战利品 + // 新逻辑, 若多个胜利者,且胜利者个数小于总玩家,代表着剩余失败的奖励要平分 + if (fight.getSeats().size() > winnerIds.size() && winnerIds.size() > 1) { + BigDecimal total = new BigDecimal(0); + for (TtBoxRecords item : result) { + if (ObjectUtil.isEmpty(item.getOrnamentId())) { + // 过滤掉抽空的情况 + item.setHolderUserId(null); + fightResult.add(item); + continue; + } + // 胜利者的钱包分配给胜利者 + if (winnerIds.contains(item.getUserId())) { + item.setHolderUserId(item.getUserId()); + fightResult.add(item); + continue; + } + item.setHolderUserId(null); + fightResult.add(item); + // 失败者的钱要加起来平分 + total = total.add(item.getOrnamentsPrice()); + } + BigDecimal bisect = total.divide(new BigDecimal(winnerIds.size()), 2, RoundingMode.HALF_UP); + fightResult.addAll(bisectPrize(fight, bisect, winnerIds)); + } else { + // 旧逻辑 + int count = 0; + for (TtBoxRecords item : result) { + if (ObjectUtil.isEmpty(item.getOrnamentId())) { + // 过滤掉抽空的情况 + item.setHolderUserId(null); + fightResult.add(item); + continue; + } + + if (winnerIds.contains(item.getUserId())) { + item.setHolderUserId(item.getUserId()); + } else { + int i = count % winnerIds.size(); + item.setHolderUserId(winnerIds.get(i)); + count++; + } + fightResult.add(item); + } + } + + // 分配失败者奖励 + List losePrize = loserPrize(fight, winnerIds); + + // 合并所有对战出货 + fightResult.addAll(losePrize); + + // 将对战结果临时存入Redis供进行中查询(写入数据库耗时长导致刚开始对战时进入的用户查询到空数据) + String key = "fight_result:fight_" + fightId; + redisCache.setCacheObject(key, fightResult, 3, TimeUnit.MINUTES); + + // 同步保存游戏出货结果 + boxRecordsService.saveBatch(fightResult); + + // ===== 新增:对战结果通知(过滤机器人用户)===== + AsyncManager.me().run(() -> sendFightResultNotice(fight, fightResult, winnerIds)); + // ===== 新增对战结果通知 结束 ===== + + // 同步更新对局状态 + LambdaUpdateWrapper fightUpdate = new LambdaUpdateWrapper<>(); + fightUpdate + .eq(TtFight::getId, fightId) + .eq(TtFight::getStatus, 0) + .set(TtFight::getWinnerIds, JSON.toJSONString(winnerIds)) + .set(TtFight::getStatus, 1) + .set(TtFight::getBeginTime, new Timestamp(System.currentTimeMillis())); + update(fightUpdate); + + // transactionManager.commit(transaction); + + // 异步推送对局数据 + CompletableFuture.runAsync(() -> { + + // 构建结果集 + // 根据宝箱id查询关联的所有饰品 + Map boxData = fight.getBoxData(); + ArrayList fightBoxVOList = new ArrayList<>(); + boxData.keySet().forEach(boxId -> { + List boxOrnamentsVOS = boxOrnamentsMapper.selectTtBoxOrnamentsList(Integer.valueOf(boxId)); + boxData.get(boxId).setOrnaments(boxOrnamentsVOS); + fightBoxVOList.add(boxData.get(boxId)); + }); + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TtBox::getBoxId, boxData.keySet()); + List boxes = boxMapper.selectList(queryWrapper); + Map boxMap = boxes.stream().filter(Objects::nonNull).collect(Collectors.toMap(TtBox::getBoxId, TtBox::getPrice)); + + FightResultVO resultVO = FightResultVO.builder() + .winnerIds(winnerIds) + .fightResult(fightResult) + .fightBoxVOList(fightBoxVOList) + .build(); + + // 推送对局结果数据 + TtFight newFight = new LambdaQueryChainWrapper<>(fightMapper) + .eq(TtFight::getId, fightId) + .eq(TtFight::getStatus, 1) + .one(); + + newFight.getBoxData().keySet().forEach(boxId -> { + Object value = newFight.getBoxData().get(boxId); + FightBoxVO a = null; + if (value instanceof LinkedHashMap) { + Map map = (LinkedHashMap) value; + a = new FightBoxVO(map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null, + map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null, + map.get("boxImg01") != null ? map.get("boxImg01").toString() : null, + map.get("boxImg02") != null ? map.get("boxImg02").toString() : null); + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } else { + a = (FightBoxVO) value; + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } + newFight.getBoxData().put(boxId, a); + }); + + resultVO.setFight(newFight); + // System.out.println("对战结果集:"+JSON.toJSONString(resultVO)); + WsFightRoom.broadcastFight(fightId, WsResult.ok(FIGHT_RESULT.name(), resultVO)); + // WsFightRoom.broadcastFight(fightId, WsResult.ok(FIGHT_ROOM_INFO.name(), newFight)); + + // 推送大厅最新的房间信息 + LambdaQueryWrapper fightQuery = new LambdaQueryWrapper<>(); + fightQuery + .eq(TtFight::getId, fightId) + .eq(TtFight::getStatus, 1); + TtFight fight1 = this.getOne(fightQuery); + + fight1.getBoxData().keySet().forEach(boxId -> { + Object value = fight1.getBoxData().get(boxId); + FightBoxVO a = null; + if (value instanceof LinkedHashMap) { + Map map = (LinkedHashMap) value; + a = new FightBoxVO(map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null, + map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null, + map.get("boxImg01") != null ? map.get("boxImg01").toString() : null, + map.get("boxImg02") != null ? map.get("boxImg02").toString() : null); + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } else { + a = (FightBoxVO) value; + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } + fight1.getBoxData().put(boxId, a); + }); + + WsFightHall.broadcast(WsResult.ok(ALL_FIGHT_ROOM.name(), Arrays.asList(fight1))); + + // 房间完成数据传输直接断开 + try { + Thread.sleep(200); + if (WsFightRoom.batchClose(newFight)) { + log.info("房间{}对战结果广播完成,已成功断开所有房间内连接。", newFight.getId()); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + }, customThreadPoolExecutor); + + return R.ok(null, "游戏开始。"); + + } catch (Exception e) { + e.printStackTrace(); + log.error("开始游戏失败,对战房间号{} 异常信息:{}", fightId, e.getMessage(), e); + return R.fail("系统繁忙,请稍后重试。"); + } finally { + redisLock.unlock(JOIN_FIGHT_BEGIN_LOCK + fightId); + } + } + + /** + * 对战结果通知(过滤机器人用户) + * 示例:对战结果:张三 胜利,最高价值饰品:AK-47(350元),本局共获得价值 820 元的饰品。 + */ + private void sendFightResultNotice(TtFight fight, List fightResult, List winnerIds) { + try { + List allPlayerIds = fight.getSeats().stream() + .map(FightSeat::getPlayerId) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (allPlayerIds.isEmpty()) return; + + // 查询真实用户,过滤机器人 + List realPlayers = new LambdaQueryChainWrapper<>(ttUserMapper) + .in(TtUser::getUserId, allPlayerIds) + .ne(TtUser::getUserType, UserType.ROBOT_USER.getCode()) + .list(); + + // 按持有人统计获得饰品 + Map> holderMap = fightResult.stream() + .filter(r -> r.getHolderUserId() != null && r.getOrnamentId() != null) + .collect(Collectors.groupingBy(r -> r.getHolderUserId().intValue())); + + for (TtUser realUser : realPlayers) { + Integer playerId = realUser.getUserId(); + boolean isWinner = winnerIds.contains(playerId); + List myItems = holderMap.getOrDefault(playerId, Collections.emptyList()); + + // 计算总获得价值 + BigDecimal totalValue = myItems.stream() + .map(TtBoxRecords::getOrnamentsPrice) + .filter(Objects::nonNull) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + // 找最高价值物品 + TtBoxRecords bestItem = myItems.stream() + .filter(r -> r.getOrnamentsPrice() != null) + .max(Comparator.comparing(TtBoxRecords::getOrnamentsPrice)) + .orElse(null); + + // 拼接通知内容 + String resultStr = isWinner ? "胜利" : "失败"; + StringBuilder content = new StringBuilder(); + content.append(" 对战结果:") + .append(realUser.getNickName()) + .append(resultStr) + .append(","); + if (bestItem != null) { + content.append("最高价值饰品:") + .append(bestItem.getOrnamentName()) + .append("(").append(bestItem.getOrnamentsPrice()).append("元),"); + } + content.append("本局共获得价值 ").append(totalValue).append(" 元的饰品。"); + apiNoticeService.sendNotice(Long.valueOf(playerId), "对战结果通知", content.toString()); + } + } catch (Exception e) { + log.error("对战通知推送失败,fightId={}, error={}", fight.getId(), e.getMessage(), e); + } + } + + private List bisectPrize(TtFight fight, BigDecimal bisect, List winnerIds) { + + List boxRecords = new ArrayList<>(); + + // 获取平台奖励 + String bisectPrize = configService.selectConfigByKey("fightLoserPrize"); + + List ornamentIds = JSONUtil.parseArray(bisectPrize).toList(Integer.class); + + if (ornamentIds.isEmpty()) return boxRecords; + + List loserPrizeList = new LambdaQueryChainWrapper<>(ttOrnamentMapper) + .in(TtOrnament::getId, ornamentIds) + .list(); + if (ObjectUtil.isEmpty(loserPrizeList) || loserPrizeList.isEmpty()) return boxRecords; + + for (Integer winnerId : winnerIds) { + + List records = loserPrizeList.stream().map(ornament -> { + return TtBoxRecords.builder() + .userId(winnerId) + + .ornamentId(ornament.getId()) + .marketHashName("对战平分奖") + .ornamentName("对战平分奖") + .imageUrl(ornament.getImageUrl()) + .ornamentsPrice(bisect) + + .holderUserId(winnerId) + + .source(TtboxRecordSource.FIGHT.getCode()) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .fightId(fight.getId()) + // .isShow(0) + .status(IN_PACKSACK_OFF.getCode()) + .build(); + + }).collect(Collectors.toList()); + + boxRecords.addAll(records); + + } + + return boxRecords; + } + + private List loserPrize(TtFight fight, List winnerIds) { + + List loserBoxRecords = new ArrayList<>(); + + List seats = fight.getSeats(); + + List losers = new ArrayList<>(); + for (FightSeat seat : seats) { + if (winnerIds.contains(seat.getPlayerId())) continue; + losers.add(seat.getPlayerId()); + } + + // 获取失败奖励 + String loserPrize = configService.selectConfigByKey("fightLoserPrize"); + + List ornamentIds = JSONUtil.parseArray(loserPrize).toList(Integer.class); + + // List ornamentIds = strToList(loserPrize); + + if (ornamentIds.isEmpty()) return loserBoxRecords; + + List loserPrizeList = new LambdaQueryChainWrapper<>(ttOrnamentMapper) + .in(TtOrnament::getId, ornamentIds) + .list(); + if (ObjectUtil.isEmpty(loserPrizeList) || loserPrizeList.isEmpty()) return loserBoxRecords; + + for (Integer loserId : losers) { + + List records = loserPrizeList.stream().map(ornament -> { + return TtBoxRecords.builder() + .userId(loserId) + + .ornamentId(ornament.getId()) + .marketHashName(ornament.getMarketHashName()) + .ornamentName(ornament.getShortName()) + .imageUrl(ornament.getImageUrl()) + .ornamentsPrice(ornament.getUsePrice()) + + .holderUserId(loserId) + + .source(TtboxRecordSource.FIGHT.getCode()) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .fightId(fight.getId()) + // .isShow(0) + .status(IN_PACKSACK_OFF.getCode()) + .build(); + + }).collect(Collectors.toList()); + + loserBoxRecords.addAll(records); + + } + + return loserBoxRecords; + } + + public List computerWinner(String model, List seats, List fightResult) { + + List winnerIds = new ArrayList<>(); + if (model.equals("0")) { + BigDecimal max = BigDecimal.ZERO; + for (FightSeat seat : seats) { + for (TtBoxRecords record : fightResult) { + if (record.getHolderUserId().equals(seat.getPlayerId())) { + seat.setAwardTotalPrices(seat.getAwardTotalPrices().add(record.getOrnamentsPrice())); + } + } + if (max.compareTo(seat.getAwardTotalPrices()) < 0) { + max = seat.getAwardTotalPrices(); + winnerIds.clear(); + winnerIds.add(seat.getPlayerId()); + } else if (max.compareTo(seat.getAwardTotalPrices()) == 0) { + winnerIds.add(seat.getPlayerId()); + } + } + } else if (model.equals("1")) { + + log.info("计算胜负,非酋模式。"); + + BigDecimal min = null; + for (FightSeat seat : seats) { + for (TtBoxRecords record : fightResult) { + if (record.getHolderUserId().equals(seat.getPlayerId())) { + seat.setAwardTotalPrices(seat.getAwardTotalPrices() + .add(ObjectUtil.isNotEmpty(record.getOrnamentsPrice()) ? record.getOrnamentsPrice() : BigDecimal.ZERO)); + } + } + if (ObjectUtil.isEmpty(min) || min.compareTo(BigDecimal.ZERO) == 0) { + min = seat.getAwardTotalPrices(); + } + if (min.compareTo(seat.getAwardTotalPrices()) > 0) { + min = seat.getAwardTotalPrices(); + winnerIds.clear(); + winnerIds.add(seat.getPlayerId()); + } else if (min.compareTo(seat.getAwardTotalPrices()) == 0) { + winnerIds.add(seat.getPlayerId()); + } + } + + log.info("赢家{}", winnerIds); + + } else { + log.warn("非法的对战模式编号"); + return winnerIds; + } + + return winnerIds; + } + + // 观战,返回当前对局进行到了第几回个 + @Override + public R audience(Integer fightId) { + + // TtFight fight = getById(fightId); + TtFight fight = new LambdaQueryChainWrapper<>(fightMapper).eq(TtFight::getId, fightId).one(); + if (ObjectUtil.isNull(fight)) return R.fail("不存在的对局。"); + if (fight.getStatus().equals(0)) return R.fail(601, "对局尚未开始。"); + // if (!fight.getStatus().equals(2)) return R.fail(602,"对局已结束。"); + + Long currentRound = -1L; + if (!fight.getStatus().equals(2) && !fight.getStatus().equals(3)) { + + // 时间差 计算当前进行到第几回合 + LocalDateTime now = LocalDateTime.now(); + Timestamp beginTime = fight.getBeginTime(); + LocalDateTime beginTime1 = beginTime.toLocalDateTime(); + + Duration duration = Duration.between(beginTime1, now); + currentRound = duration.toMillis() / fightRoundTime; // 这个秒要和前端的 【每回合时间】 同步 + + } + + // 游戏结果 + // 根据宝箱id查询关联的所有饰品 + ArrayList fightBoxVOList = new ArrayList<>(); + + Map boxData = fight.getBoxData(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TtBox::getBoxId, boxData.keySet()); + List boxes = boxMapper.selectList(queryWrapper); + var boxMapObj = boxes.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(TtBox::getBoxId)); + + boxData.keySet().forEach(boxId -> { + List boxOrnamentsVOS = boxOrnamentsMapper.selectTtBoxOrnamentsList(Integer.valueOf(boxId)); + FightBoxVO fightBoxVO = JSONUtil.toBean(JSONUtil.toJsonStr(boxData.get(boxId)), FightBoxVO.class); + fightBoxVO.setOrnaments(boxOrnamentsVOS); + if (boxMapObj.get(Integer.valueOf(boxId)) != null) { + fightBoxVO.setBoxName(boxMapObj.get(Integer.valueOf(boxId)).getFirst().getBoxName()); + } + fightBoxVOList.add(fightBoxVO); + }); + + List list = new LambdaQueryChainWrapper<>(boxRecordsMapper) + .eq(TtBoxRecords::getFightId, fightId) + .list(); + + AudienceVO result = AudienceVO.builder() + .currentRound(currentRound.intValue()) + .winnerIds(fight.getWinnerIds()) + .fightResult(list) + .fight(fight) + .fightBoxVOList(fightBoxVOList) + .build(); + + return R.ok(result, "房间游戏数据"); + } + + @Override + public R fightEnd(Integer fightId) { + + TtFight fight = new LambdaQueryChainWrapper<>(fightMapper) + .eq(TtFight::getId, fightId) + .one(); + if (ObjectUtil.isEmpty(fight)) { + return R.ok(null, "不存在的对局"); + } + if (fight.getStatus().equals(0)) { + return R.ok(null, "对局未开始"); + } + if (fight.getStatus().equals(2) || fight.getStatus().equals(3)) { + return R.ok(fight, "对局已结束"); + } + + Boolean lock = false; + for (int i = 0; i < 4; i++) { + + lock = redisLock.tryLock(JOIN_FIGHT_END_LOCK + fightId, 2L, 7L, TimeUnit.SECONDS); + + if (!lock) { + continue; + } + break; + } + + if (!lock) return R.ok("系统繁忙。请稍后查看对战结果"); + + try { + // 校验时间 + LocalDateTime now = LocalDateTime.now(); + Timestamp beginTime = fight.getBeginTime(); + LocalDateTime beginTime1 = beginTime.toLocalDateTime(); + Duration duration = Duration.between(beginTime1, now); + Long second = duration.toMillis() / 1000; + Integer roundTime = fightRoundTime / 1000; + // if (fight.getRoundNumber() * roundTime > second.intValue()) return R.fail("对局尚未结束,请稍后重试。"); + + // 更新游戏状态 + Timestamp endTime = new Timestamp(System.currentTimeMillis()); + new LambdaUpdateChainWrapper<>(fightMapper) + .eq(TtFight::getId, fightId) + .set(TtFight::getStatus, 2) + .set(TtFight::getEndTime, endTime) + .update(); + + // 更新背包 + LambdaUpdateWrapper boxRecordsUpdate = new LambdaUpdateWrapper<>(); + boxRecordsUpdate + .eq(TtBoxRecords::getFightId, fightId) + // .set(TtBoxRecords::getIsShow, 1); + .set(TtBoxRecords::getStatus, IN_PACKSACK_ON.getCode()); + boxRecordsService.update(boxRecordsUpdate); + + if (MapUtils.isNotEmpty(fight.getBoxData())) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TtBox::getBoxId, fight.getBoxData().keySet()); + List boxes = boxMapper.selectList(queryWrapper); + Map boxMap = boxes.stream().filter(Objects::nonNull).collect(Collectors.toMap(TtBox::getBoxId, TtBox::getPrice)); + fight.getBoxData().keySet().forEach(boxId -> { + Object value = fight.getBoxData().get(boxId); + FightBoxVO a = null; + if (value instanceof LinkedHashMap) { + Map map = (LinkedHashMap) value; + a = new FightBoxVO(map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null, + map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null, + map.get("boxImg01") != null ? map.get("boxImg01").toString() : null, + map.get("boxImg02") != null ? map.get("boxImg02").toString() : null); + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } else { + a = (FightBoxVO) value; + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } + fight.getBoxData().put(boxId, a); + }); + } + + fight.setStatus(2); + fight.setEndTime(endTime); + + // 异步更新对局数据 + CompletableFuture.runAsync(() -> { + WsFightHall.broadcast(WsResult.ok(ALL_FIGHT_ROOM.name(), Arrays.asList(fight))); + }, customThreadPoolExecutor); + + return R.ok(fight); + + } catch (Exception e) { + e.printStackTrace(); + log.warn("客户端【主动结束】游戏异常。"); + return R.ok(fight, "服务器繁忙。"); + } finally { + redisLock.unlock(JOIN_FIGHT_END_LOCK + fightId); + } + } + + public R seatrReadyCheck(Integer fightId, TtUser player) { + + TtFight fight = new LambdaQueryChainWrapper<>(fightMapper) + .eq(TtFight::getId, fightId) + // .eq(TtFight::getStatus, 0) + .one(); + if (ObjectUtil.isEmpty(fight)) return R.fail("不存在的对局。"); + if (fight.getStatus().equals(1)) return R.fail("对局进行中。"); + if (fight.getStatus().equals(2) || fight.getStatus().equals(3)) return R.fail("对局已结束。"); + + ObjectMapper objectMapper = new ObjectMapper(); + List seats = objectMapper.convertValue(fight.getSeats(), new com.fasterxml.jackson.core.type.TypeReference>() { + @Override + public Type getType() { + return super.getType(); + } + }); + fight.setSeats(seats); + + Map boxData = objectMapper.convertValue(fight.getBoxData(), new com.fasterxml.jackson.core.type.TypeReference>() { + @Override + public Type getType() { + return super.getType(); + } + }); + fight.setBoxData(boxData); + + return R.ok(fight); + } + + // 玩家准备游戏 + @Transactional + @Override + public R seatrReady(Integer fightId, TtUser player) { + + // 检查 + R check = seatrReadyCheck(fightId, player); + if (!check.getCode().equals(200)) return check; + + // fight + ObjectMapper objectMapper = new ObjectMapper(); + TtFight fight = objectMapper.convertValue(check.getData(), TtFight.class); + + if (MapUtils.isNotEmpty(fight.getBoxData())) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TtBox::getBoxId, fight.getBoxData().keySet()); + List boxes = boxMapper.selectList(queryWrapper); + Map boxMap = boxes.stream().filter(Objects::nonNull).collect(Collectors.toMap(TtBox::getBoxId, TtBox::getPrice)); + fight.getBoxData().keySet().forEach(boxId -> { + Object value = fight.getBoxData().get(boxId); + FightBoxVO a = null; + if (value instanceof LinkedHashMap) { + Map map = (LinkedHashMap) value; + a = new FightBoxVO(map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null, + map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null, + map.get("boxImg01") != null ? map.get("boxImg01").toString() : null, + map.get("boxImg02") != null ? map.get("boxImg02").toString() : null); + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } else { + a = (FightBoxVO) value; + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } + fight.getBoxData().put(boxId, a); + }); + } + + // 更新座位状态 + boolean flag = false; + for (FightSeat seat : fight.getSeats()) { + + if (flag) break; + + //找到自己的座位 + if (seat.getPlayerId().equals(player.getUserId())) { + + String lock = JOIN_FIGHT_SEAT_READY_LOCK + fightId + ":" + seat.getCode(); + + Boolean tryLock = false; + for (int i = 0; i < 2; i++) { + tryLock = redisLock.tryLock(lock, 2L, 7L, TimeUnit.SECONDS); + if (tryLock) break; + } + if (!tryLock) return R.fail("系统繁忙,请稍后重试。"); + + // if (ObjectUtil.isEmpty(seat.ready())) return R.fail("座位状态异常。"); + // updateById(fight); + + try { + //座位准备 + // if (ObjectUtil.isEmpty(seat.ready())) return R.fail("座位状态异常。"); + if (seat.getStatus().equals(2)) return R.fail("已准备,请勿重复操作。"); + if (ObjectUtil.isEmpty(seat.ready())) return R.fail("座位状态(0)异常。"); + updateById(fight); + + // 账户结算 + R mapR = readyFightAccounting(fight, player); + if (R.isError(mapR)) { + seat.readyCancel().sitUp(); + updateById(fight); + log.info("对战{},用户{}加入扣款异常,踢出座位{}。", fightId, player.getUserId(), seat.getCode()); + return mapR; + } + + // 同步保存参与人员信息 + TtFightUser build = TtFightUser.builder() + .fightId(fightId) + .userId(player.getUserId()) + .joinPrice(fight.getBoxPriceTotal()) + .createTime(new Date()) + .updateTime(new Date()) + .build(); + fightUserService.save(build); + } catch (Exception e) { + log.error("fight happen exception is {}", e.getMessage(), e); + seat.readyCancel().sitUp(); + updateById(fight); + e.printStackTrace(); + } finally { + redisLock.unlock(lock); + } + + // 广播房间消息 + CompletableFuture.runAsync(() -> { + WsFightRoom.broadcastFight(fightId, WsResult.ok(FIGHT_ROOM_INFO.name(), fight)); + }, customThreadPoolExecutor); + + flag = true; + return R.ok(); + } + } + + return R.fail("你不在此对局座位中"); + } + + @Override + public R fightRoomExit(Integer fightId, TtUser player) { + + TtFight fight = new LambdaQueryChainWrapper<>(fightMapper) + .eq(TtFight::getId, fightId) + .eq(TtFight::getStatus, 0) + .one(); + if (ObjectUtil.isEmpty(fight)) return R.fail("对局已开始,不能退出。"); + + List seats = JSONUtil.toList(JSONUtil.toJsonStr(fight.getSeats()), FightSeat.class); + for (int i = 0; i < seats.size(); i++) { + + Integer playerId = seats.get(i).getPlayerId(); + if (ObjectUtil.isEmpty(playerId)) { + continue; + } + + if (playerId.equals(player.getUserId())) { + if (seats.get(i).getStatus().equals(2)) { + // seat.sitUp(); + // updateById(fight); + // //广播房间消息 + // CompletableFuture.runAsync(() -> { + // WsFightRoom.broadcast(WsResult.ok(FIGHT_ROOM_INFO.name(), fight)); + // }, customThreadPoolExecutor); + return R.ok("退出成功,消费不退回。"); + } else if (seats.get(i).getStatus().equals(1)) { + if (!seats.get(i).sitUp()) return R.fail("退出失败,请稍后重试。"); + fight.setSeats(seats); + updateById(fight); + // 广播房间消息 + CompletableFuture.runAsync(() -> { + List list = new LambdaQueryChainWrapper<>(fightMapper) + .eq(TtFight::getStatus, 0) + .orderByDesc(TtFight::getCreateTime) + .list(); + WsFightHall.broadcast(WsResult.ok(ALL_FIGHT_ROOM.name(), list, "对战房间最新信息")); + WsFightRoom.broadcast(WsResult.ok(FIGHT_ROOM_INFO.name(), fight)); + }, customThreadPoolExecutor); + return R.ok("退出成功。"); + } else { + return R.fail("异常的座位状态0"); + } + } + } + + return R.fail("状态异常,你不在本对局中。"); + + } + + @Override + public List getFightList(FightOnMyOwnParam param) { + + Page pageInfo = new Page<>(param.getPage(), param.getSize()); + pageInfo.setOptimizeCountSql(false); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .eq(ObjectUtil.isNotEmpty(param.getFightId()), TtFight::getId, param.getFightId()) + .eq(ObjectUtil.isNotEmpty(param.getModel()), TtFight::getModel, param.getModel()) + .in(ObjectUtil.isNotEmpty(param.getStatusList()), TtFight::getStatus, param.getStatusList()); + + // 我参与的 + if (ObjectUtil.isNotNull(param.getPlayerId())) { + List myOwnFights = fightUserMapper.myOwnFights(param.getPlayerId()); + if (myOwnFights.size() > 0) { + wrapper.in(TtFight::getId, myOwnFights); + } else { + wrapper.eq(TtFight::getId, "non_existing_value"); + } + } + wrapper.orderByDesc(TtFight::getCreateTime); + + List page = this.page(pageInfo, wrapper).getRecords(); + if (CollectionUtils.isEmpty(page)) { + return Collections.emptyList(); + } + + List boxIds = new ArrayList<>(); + for (TtFight fight : page) { + if (fight == null || MapUtils.isEmpty(fight.getBoxData())) { + continue; + } + for (Map.Entry entry : fight.getBoxData().entrySet()) { + Object value = entry.getValue(); + if (value == null) { + continue; + } + Integer boxId; + if (value instanceof LinkedHashMap) { + Map map = (LinkedHashMap) value; + boxId = map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null; + } else { + boxId = ((FightBoxVO) value).getBoxId(); + } + if (boxId == null) { + continue; + } + boxIds.add(boxId); + } + } + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TtBox::getBoxId, boxIds); + List boxes = boxMapper.selectList(queryWrapper); + Map boxMap = boxes.stream().filter(Objects::nonNull).collect(Collectors.toMap(TtBox::getBoxId, TtBox::getPrice)); + for (TtFight fight : page) { + if (fight == null || MapUtils.isEmpty(fight.getBoxData())) { + continue; + } + for (Map.Entry entry : fight.getBoxData().entrySet()) { + Object value = entry.getValue(); + if (value == null) { + continue; + } + FightBoxVO vo; + Integer boxId; + if (value instanceof LinkedHashMap) { + Map map = (LinkedHashMap) value; + vo = new FightBoxVO(map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null, + map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null, + map.get("boxImg01") != null ? map.get("boxImg01").toString() : null, + map.get("boxImg02") != null ? map.get("boxImg02").toString() : null); + vo.setPrice(boxMap.getOrDefault(vo.getBoxId(), new BigDecimal(0))); + } else { + vo = (FightBoxVO) value; + vo.setPrice(boxMap.getOrDefault(vo.getBoxId(), new BigDecimal(0))); + } + fight.getBoxData().put(vo.getBoxId().toString(), vo); + } + } + + var result = page.stream().map(fight -> { + ApiFightListDataVO build = ApiFightListDataVO.builder().build(); + BeanUtil.copyProperties(fight, build); + build.setBoxData(JSONUtil.toJsonStr(fight.getBoxData())); + build.setSeats(fight.getSeats()); + build.setRoundNumber(fight.getRoundNumber()); + return build; + }).collect(Collectors.toList()); + + List fightIds = result.stream().filter(Objects::nonNull).map(ApiFightListDataVO::getId).collect(Collectors.toList()); + List voList = apiFightMapper.fightOrnamentsPrice(fightIds); + Map fightIdToPrice = voList.stream() + .filter(Objects::nonNull) // 过滤掉空值 + .collect(Collectors.toMap( + ApiFightTenTopVo::getId, + ApiFightTenTopVo::getTotalOrnamentsPrice + )); + for (ApiFightListDataVO vo : result) { + vo.setTotalOrnamentsPrice(fightIdToPrice.getOrDefault(vo.getId(), BigDecimal.ZERO)); + } + + return result; + } + + @Override + public R fightDetail(FightDetailParam param) { + + TtFight fight = this.getById(param.getFightId()); + if (!fight.getStatus().equals(2) && !fight.getStatus().equals(3)) { + return R.fail("对局未结束"); + } + + TtFightVO vo = new TtFightVO(); + BeanUtil.copyProperties(fight, vo); + Map boxData = fight.getBoxData(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TtBox::getBoxId, boxData.keySet()); + List boxes = boxMapper.selectList(queryWrapper); + Map boxMap = boxes.stream().filter(Objects::nonNull).collect(Collectors.toMap(TtBox::getBoxId, TtBox::getPrice)); + boxData.keySet().forEach(boxId -> { + Object value = boxData.get(boxId); + FightBoxVO a = null; + if (value instanceof LinkedHashMap) { + Map map = (LinkedHashMap) value; + a = new FightBoxVO(map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null, + map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null, + map.get("boxImg01") != null ? map.get("boxImg01").toString() : null, + map.get("boxImg02") != null ? map.get("boxImg02").toString() : null); + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } else { + a = (FightBoxVO) value; + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } + boxData.put(boxId, a); + }); + vo.setBoxData(boxData); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(TtBoxRecords::getFightId, param.getFightId()); + + List list = ttBoxRecordsService.list(wrapper); + vo.setFightResult(list); + + return R.ok(vo); + } + + @Override + public R earlierHistory(FightDetailParam param) { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TtFight::getId, param.getFightId()); + + TtFight one = this.getOne(queryWrapper); + Date createTime = one.getCreateTime(); + + Page pageInfo = new Page<>(param.getPage(), param.getSize()); + pageInfo.setOptimizeCountSql(false); + queryWrapper.clear(); + queryWrapper + .le(TtFight::getCreateTime, createTime) + // .and(qw->qw.eq(TtFight::getStatus,0)) + .eq(TtFight::getStatus, 0); + + pageInfo = this.page(pageInfo, queryWrapper); + + return R.ok(pageInfo.getRecords()); + } + + @Override + public List getFightRankingByDate(String date) { + return apiFightMapper.getFightRankingByDate(date); + } + + // 加入对战账户结算 + public R readyFightAccounting(TtFight fight, TtUser player) { + // 扣款 + BigDecimal totalMoney = fight.getBoxPriceTotal(); + R r = userService.updateUserAccount(player.getUserId(), totalMoney.negate(), TtAccountRecordSource.GAME_TYPE_02); + if (R.isError(r)) { + return R.fail(r.getMsg()); + } + + return R.ok(); + } + + public List computerFight(TtFight fight, TtBox box, Integer number) { + + // 计算结果 + ArrayList result = new ArrayList<>(); + List seats = fight.getSeats(); + for (FightSeat seat : seats) { + TtUser player = userService.getById(seat.getPlayerId()); + List ttBoxRecords = apiBindBoxService.openBoxArithmetic(fight.getId(), player, box, number); + // 补充图片 + ttBoxRecords.stream().forEach(boxRecord -> { + TtOrnament ornament = ttOrnamentsZBTService.getById(boxRecord.getOrnamentId()); + boxRecord.setImageUrl(ornament.getImageUrl()); + }); + result.addAll(ttBoxRecords); + } + return result; + } + + public List computerFight1(TtFight fight) { + + List result = new ArrayList<>(); + + List seats = fight.getSeats(); + Map boxs = fight.getBoxData(); + for (FightSeat seat : seats) { + Integer roundNumber = 1; + for (String boxId : boxs.keySet()) { + TtBox box = boxMapper.selectById(boxId); + // 一种箱子一个人的所有结果 + TtUser player = userService.getById(seat.getPlayerId()); + List ttBoxRecords = apiBindBoxService.openBoxArithmetic(fight.getId(), player, box, boxs.get(boxId).getNumber()); + // 补充图片 + for (TtBoxRecords boxRecord : ttBoxRecords) { + TtOrnament ornament = ttOrnamentsZBTService.getById(boxRecord.getOrnamentId()); + boxRecord.setImageUrl(ornament.getImageUrl()); + boxRecord.setFightRoundNumber(roundNumber); + roundNumber++; + } + result.addAll(ttBoxRecords); + } + } + + return result; + } + + // 新开箱算法 + public List newComputerFight(TtFight fight) { + + List result = new ArrayList<>(); + + Map boxs = fight.getBoxData(); + // 抽奖人列表 + ArrayList userList = new ArrayList<>(); + for (FightSeat seat : fight.getSeats()) { + userList.add(seat.getPlayerId()); + } + + Integer round = 1; + for (String boxId : boxs.keySet()) { + // 获取宝箱详细信息 + TtBox box = boxMapper.selectById(boxId); + for (int i = 0; i < boxs.get(boxId).getNumber(); i++) { + // 一个宝箱一回合 + List roundResult = roundLottery(round, box, userList); + if (ObjectUtil.isEmpty(roundResult) || roundResult.isEmpty()) return null; + result.addAll(roundResult); + round++; + } + + // 统计宝箱历史开箱数量 + new LambdaUpdateChainWrapper<>(boxMapper) + .eq(TtBox::getBoxId, boxId) + .set(TtBox::getOpenNum, box.getOpenNum() + boxs.get(boxId).getNumber()) + .update(); + } + + // 补充信息 + result.stream().forEach(item -> { + item.setFightId(fight.getId()); + }); + + // 保存信息 + return result; + } + + // 抽奖(一回合) + public List roundLottery(Integer round, TtBox box, ArrayList userList) { + + ArrayList result = new ArrayList<>(); + + // 循环所有人 + for (Integer uid : userList) { + + TtUser user = userService.getById(uid); + + // 抽奖 + String ornamentId = lotteryMachine.singleLottery(user, box); + if (StringUtils.isBlank(ornamentId)) { + log.info("用户{}抽奖失败", user.getUserId()); + return null; + } + + // 饰品详情 + // TtOrnamentsA ornamentsData = bindBoxMapper.getOrnamentsData(box.getBoxId(), Integer.valueOf(hashName), null); + TtOrnamentsA ornamentsData = bindBoxMapper.ornamentsInfo(box.getBoxId(), ornamentId); + + // 抽奖成功,构建开箱记录数据 + TtBoxRecords boxRecord = TtBoxRecords.builder() + .userId(user.getUserId()) + .boxId(box.getBoxId()) + .boxName(box.getBoxName()) + .boxPrice(box.getPrice()) + .ornamentId(Long.valueOf(ornamentId)) + // .marketHashName(ornamentsData.) + .ornamentName(ornamentsData.getShortName()) + .imageUrl(ornamentsData.getImageUrl()) + .ornamentsPrice(ornamentsData.getUsePrice()) + .ornamentsLevelId(ornamentsData.getOrnamentsLevelId()) + .ornamentLevelImg(ornamentsData.getLevelImg()) + .holderUserId(user.getUserId()) + .source(TtboxRecordSource.FIGHT.getCode()) + .status(IN_PACKSACK_ON.getCode()) + .fightRoundNumber(round) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + // .fightId() 后续补充 + // .isShow(0) + .status(IN_PACKSACK_OFF.getCode()) + .build(); + result.add(boxRecord); + + } + + return result; + } + + // 座位是否人满 + public Boolean seatsIsFull(List seats) { + boolean f = true; + for (FightSeat seat : seats) { + if (seat.getStatus().equals(0)) return !f; + } + return f; + } + + + @Override + public List getFightBoxList(Integer boxTypeId) { + + List boxList = apiFightMapper.getFightBoxList(boxTypeId); + + // 补充物品信息 + // for (TtBoxA box : boxList){ + // List list = new LambdaQueryChainWrapper<>(boxOrnamentsMapper) + // .eq(TtBoxOrnaments::getBoxId, box.getBoxId()) + // .list(); + // } + + // 过滤空箱子 + List collect = boxList.stream().filter(item -> { + + List list = new LambdaQueryChainWrapper<>(boxOrnamentsMapper) + .eq(TtBoxOrnaments::getBoxId, item.getBoxId()) + .list(); + if (ObjectUtil.isEmpty(list) || list.isEmpty()) return false; + + boolean flag = false; + for (TtBoxOrnaments ornaments : list) { + if (ornaments.getRealOdds() > 0) { + return !flag; + } + } + return flag; + + }).collect(Collectors.toList()); + + List ttBoxVOS = apiBindBoxService.groupByBoxType(collect); + + return ttBoxVOS; + } + + @Override + public List getFightList(String model, String status, Integer userId, Integer fightId) { + List vos = apiFightMapper.getFightList(model, status, userId, fightId); + if (CollectionUtils.isEmpty(vos)) { + return vos; + } + + List fightIds = vos.stream().filter(Objects::nonNull).map(ApiFightListDataVO::getId).collect(Collectors.toList()); + List voList = apiFightMapper.fightOrnamentsPrice(fightIds); + Map fightIdToPrice = voList.stream() + .filter(Objects::nonNull) // 过滤掉空值 + .collect(Collectors.toMap( + ApiFightTenTopVo::getId, + ApiFightTenTopVo::getTotalOrnamentsPrice + )); + Set boxIds = new HashSet<>(); + for (ApiFightListDataVO vo : vos) { + Map boxData = JSONUtil.toBean(vo.getBoxData(), new cn.hutool.core.lang.TypeReference>() { + }, false); + if (MapUtils.isEmpty(boxData)) { + continue; + } + for (Map.Entry entry : boxData.entrySet()) { + if (entry == null || entry.getValue() == null || entry.getValue().getBoxId() == null) { + continue; + } + boxIds.add(entry.getValue().getBoxId()); + } + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TtBox::getBoxId, boxIds); + List boxes = boxMapper.selectList(queryWrapper); + Map boxMap = boxes.stream().filter(Objects::nonNull).collect(Collectors.toMap(TtBox::getBoxId, TtBox::getPrice)); + for (ApiFightListDataVO vo : vos) { + Map boxData = JSONUtil.toBean(vo.getBoxData(), new cn.hutool.core.lang.TypeReference>() { + }, false); + if (MapUtils.isNotEmpty(boxData)) { + for (Map.Entry entry : boxData.entrySet()) { + if (entry == null || entry.getValue() == null || entry.getValue().getBoxId() == null) { + continue; + } + entry.getValue().setPrice(boxMap.getOrDefault(entry.getValue().getBoxId(), new BigDecimal(0))); + } + } + vo.setBoxData(JSONUtil.toJsonStr(boxData)); + vo.setTotalOrnamentsPrice(fightIdToPrice.getOrDefault(vo.getId(), BigDecimal.ZERO)); + } + return vos; + } + + @Override + public FightResultDataVO getFightRecord(Integer fightId, Integer round, Integer rounds) { + FightResultDataVO fightResultDataVO = new FightResultDataVO(); + fightResultDataVO.setFightId(fightId); + + TtFightResult ttFightResult = fightResultMapper.selectOne(new QueryWrapper() + .eq("fight_id", fightId)); + if (ObjectUtil.isEmpty(ttFightResult)) return null; + // 对局正式开始时间 - 客户端获取本回合结果时间 + long hhsj = new Date().getTime() - ttFightResult.getCreateTime().getTime(); + + if (round != null) { + fightResultDataVO.setRound(round); + } else { + // 当前回合数 + round = (int) hhsj / 6000 + 1; + if (round > rounds) { + // 时间已过,房间关闭 + return fightResultDataVO; + } else { + fightResultDataVO.setRound(round); + } + } + List playerGainsOrnamentsDataVOList = new ArrayList<>(); + + // 计算当前回合数奖品 + List list = fightUserMapper + .selectList(new QueryWrapper() + .select("fight_id", "user_id", "join_seat_num") + .eq("fight_id", fightId) + .groupBy("fight_id", "user_id", "join_seat_num")); + + for (int i = 0; i < list.size(); i++) { + + PlayerGainsOrnamentsDataVO playerGainsOrnamentsDataVO = new PlayerGainsOrnamentsDataVO(); + // 获取奖品信息 + List ttBoxRecords = ttBoxRecordsService.list(new QueryWrapper() + .eq("user_id", list.get(i).getUserId()) + .eq("fight_id", list.get(i).getFightId())); + TtOrnament ttOrnament = ttOrnamentsZBTService.getById(ttBoxRecords.get(round - 1).getOrnamentId()); + + playerGainsOrnamentsDataVO.setUserId(ttBoxRecords.get(round - 1).getUserId()); + playerGainsOrnamentsDataVO.setJoinSeatNum(list.get(i).getJoinSeatNum()); + + UserPackSackDataVO userPackSackDataVO = new UserPackSackDataVO(); + userPackSackDataVO.setImageUrl(ttOrnament.getImageUrl()); + userPackSackDataVO.setOrnamentsPrice(ttOrnament.getPrice()); + userPackSackDataVO.setItemName(ttOrnament.getName()); + userPackSackDataVO.setShortName(ttOrnament.getShortName()); + + if (ttOrnament.getQuality() != null) ttOrnament.setQuality("1"); + + TtOrnamentsLevel ttOrnamentsLevel = ttOrnamentsLevelService.getById(ttOrnament.getQuality()); + userPackSackDataVO.setLevelImg(ttOrnamentsLevel.getLevelImg()); + + playerGainsOrnamentsDataVO.setOrnamentsData(userPackSackDataVO); + + playerGainsOrnamentsDataVOList.add(playerGainsOrnamentsDataVO); + } + fightResultDataVO.setPlayerGainsOrnamentsData(playerGainsOrnamentsDataVOList); + + return fightResultDataVO; + } + + private void sendResult(TtFight ttFight, Integer rounds) { + + log.info("sendResult"); + log.info("对局信息 " + ttFight.getPlayerNum()); + + int num = 25; + while (num > 0) { + try { + List ttBoxRecordsList = new LambdaQueryChainWrapper<>(boxRecordsService.getBaseMapper()) + .eq(TtBoxRecords::getStatus, "4") + .eq(TtBoxRecords::getSource, "1") + .eq(TtBoxRecords::getFightId, ttFight.getId()) + .list(); + + Map boxDataJsonString = ttFight.getBoxData(); + if (StringUtils.isEmpty(boxDataJsonString)) return; + + List boxDataList = JSONObject.parseObject(JSON.toJSONString(boxDataJsonString), new TypeReference>() { + }); + + int boxNumTotal = boxDataList.stream().mapToInt(BoxDataBodyA::getBoxNum).sum(); + + // 等待开箱结果 + if (boxNumTotal * ttFight.getPlayerNum() > ttBoxRecordsList.size()) { + log.info("等待开箱结果 " + ttBoxRecordsList.size()); + // TimeUnit.MILLISECONDS.sleep(1000); + TimeUnit.MILLISECONDS.sleep(400); + // TimeUnit.MILLISECONDS.sleep(100); + num--; + continue; + } + + Map> boxRecordsGroupingByUserId = ttBoxRecordsList.stream().collect(Collectors.groupingBy(TtBoxRecords::getUserId)); + List playerGainsOrnamentsDataList = new ArrayList<>(); + for (Integer userId : boxRecordsGroupingByUserId.keySet()) { + PlayerGainsOrnamentsDataVO playerGainsOrnamentsData = getPlayerGainsOrnamentsData(userId, ttFight.getId()); + playerGainsOrnamentsDataList.add(playerGainsOrnamentsData); + } + FightResultDataVO fightResultData = new FightResultDataVO(); + fightResultData.setFightId(ttFight.getId()); + fightResultData.setPlayerGainsOrnamentsData(playerGainsOrnamentsDataList); + List winnerGainsOrnamentsData = allotResult(boxRecordsGroupingByUserId, ttFight); + fightResultData.setWinnerGainsOrnamentsData(winnerGainsOrnamentsData); + new LambdaUpdateChainWrapper<>(fightMapper).eq(TtFight::getId, ttFight.getId()).set(TtFight::getStatus, "1") + .set(TtFight::getUpdateTime, new Date()).update(); + + // 关闭对战房间 + TtFightResult ttFightResult = fightResultMapper.selectOne(new QueryWrapper().eq("fight_id", ttFight.getId())); + if (ttFightResult != null) { + + // rabbitTemplate.convertAndSend(FightQueueConfig.FIGHT_EXCHANGE, FightQueueConfig.FIGHT_ROUTING_KEY, ttFight.getId(), + // message -> { + // // 注意这里时间可以使long,而且是设置header + // message.getMessageProperties().setDelay((rounds * 6000)); + // return message; + // } + // ); + + log.info(ttFight.getId() + "将在{}ms后执行", rounds * 6000); +// rabbitProduc.sendDelayMessage(ttRoll.getId().intValue() + "", difference); + + } + + + new LambdaUpdateChainWrapper<>(fightUserMapper).eq(TtFightUser::getFightId, ttFight.getId()).set(TtFightUser::getStatus, "2") + .set(TtFightUser::getUpdateTime, new Date()).update(); + ResultData resultData = new ResultData<>(); + resultData.setCode(1); + resultData.setTypeName("success"); + resultData.setData(fightResultData); + WebSocketUsers.sendMessageToUsersByText(JSON.toJSONString(resultData)); + TtFightResult fightResult = TtFightResult.builder().fightId(ttFight.getId()).createTime(new Date()).fightResult(JSON.toJSONString(fightResultData)).build(); + + log.info("=====================保存对战房间" + ttFight.getId() + "结果==================="); + fightResultMapper.insert(fightResult); + return; + } catch (Exception e) { + return; + } + } + } + + private List allotResult(Map> boxRecordsGroupingByUserId, TtFight ttFight) { + Map> dataA = new HashMap<>(); + for (Map.Entry> entry : boxRecordsGroupingByUserId.entrySet()) { + Integer userId = entry.getKey(); + List boxRecordsList = entry.getValue(); + TtFightUser ttFightUser = new LambdaQueryChainWrapper<>(fightUserMapper).eq(TtFightUser::getFightId, ttFight.getId()) + .eq(TtFightUser::getUserId, userId).eq(TtFightUser::getStatus, "0").one(); + PlayerGainsOrnamentsDataVO playerGainsOrnamentsData = getPlayerGainsOrnamentsData(userId, ttFight.getId()); + Map data = new HashMap<>(); + BigDecimal reduce = boxRecordsList.stream().map(TtBoxRecords::getOrnamentsPrice).reduce(BigDecimal.ZERO, BigDecimal::add); + data.put("priceTotal", JSON.toJSONString(reduce)); + data.put("userId", JSON.toJSONString(userId)); + data.put("playerGainsOrnamentsData", JSON.toJSONString(playerGainsOrnamentsData)); + data.put("boxRecordsList", JSON.toJSONString(boxRecordsList)); + dataA.put(ttFightUser.getJoinSeatNum(), data); + } + Map balanceMap = dataA.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> JSONObject + .parseObject(entry.getValue().get("priceTotal"), new TypeReference() { + }))); + BigDecimal balanceA = balanceMap.values().stream().max(BigDecimal::compareTo).orElse(BigDecimal.ZERO); + List maxJoinSeatNumList = balanceMap.entrySet().stream().filter(entry -> entry.getValue().equals(balanceA)) + .map(Map.Entry::getKey).collect(Collectors.toList()); + BigDecimal balanceB = balanceMap.values().stream().min(BigDecimal::compareTo).orElse(BigDecimal.ZERO); + List minJoinSeatNumList = balanceMap.entrySet().stream().filter(entry -> entry.getValue().equals(balanceB)) + .map(Map.Entry::getKey).collect(Collectors.toList()); + Map> collectA = dataA.entrySet().stream().filter(entry -> !maxJoinSeatNumList.contains(entry.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + Map> collectB = dataA.entrySet().stream().filter(entry -> !minJoinSeatNumList.contains(entry.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + if ("0".equals(ttFight.getModel())) { + return singlePlayer(ttFight, dataA, maxJoinSeatNumList, collectA); + } else if ("1".equals(ttFight.getModel())) { + return singlePlayer(ttFight, dataA, minJoinSeatNumList, collectB); + } else if ("2".equals(ttFight.getModel())) { + if (ttFight.getPlayerNum() == 4 && dataA.size() == 4) { + return null; + } + } + return null; + } + + private List singlePlayer(TtFight ttFight, Map> dataTabulate, + List joinSeatNumList, Map> collect) { + if (ttFight.getPlayerNum() == 2 && dataTabulate.size() == 2) { + if (joinSeatNumList.size() == 1) return onePersonWin(ttFight, dataTabulate, joinSeatNumList, collect); + else if (joinSeatNumList.size() == 2) return dogFall(ttFight, dataTabulate, joinSeatNumList); + } else if (ttFight.getPlayerNum() == 3 && dataTabulate.size() == 3) { + if (joinSeatNumList.size() == 1) return onePersonWin(ttFight, dataTabulate, joinSeatNumList, collect); + else if (joinSeatNumList.size() == 2) { + List playerGainsOrnamentsDataVOList = dogFall(ttFight, dataTabulate, joinSeatNumList); + divideEquallyLosePrice(collect, playerGainsOrnamentsDataVOList); + return playerGainsOrnamentsDataVOList; + } else if (joinSeatNumList.size() == 3) return dogFall(ttFight, dataTabulate, joinSeatNumList); + } else if (ttFight.getPlayerNum() == 4 && dataTabulate.size() == 4) { + if (joinSeatNumList.size() == 1) return onePersonWin(ttFight, dataTabulate, joinSeatNumList, collect); + else if (joinSeatNumList.size() == 2) { + List playerGainsOrnamentsDataVOList = dogFall(ttFight, dataTabulate, joinSeatNumList); + List losePriceList = collect.values().stream() + .map(stringStringMap -> JSONObject.parseObject(stringStringMap.get("priceTotal"), new TypeReference() { + })) + .collect(Collectors.toList()); + if (losePriceList.get(0).compareTo(losePriceList.get(1)) == 0) { + List playerGainsOrnamentsData = collect.values().stream() + .map(stringStringMap -> JSONObject.parseObject(stringStringMap.get("playerGainsOrnamentsData"), + new TypeReference() { + })) + .collect(Collectors.toList()); + for (int i = 0; i < playerGainsOrnamentsData.size(); i++) { + playerGainsOrnamentsDataVOList.get(i).getOrnamentsDataList().addAll(playerGainsOrnamentsData.get(i).getOrnamentsDataList()); + new LambdaUpdateChainWrapper<>(boxRecordsService.getBaseMapper()).eq(TtBoxRecords::getFightId, ttFight.getId()) + .eq(TtBoxRecords::getUserId, playerGainsOrnamentsData.get(i).getUserId()).set(TtBoxRecords::getStatus, "0") + .set(TtBoxRecords::getUpdateTime, new Date()).set(TtBoxRecords::getHolderUserId, playerGainsOrnamentsDataVOList.get(i) + .getUserId()).update(); + } + } else { + divideEquallyLosePrice(collect, playerGainsOrnamentsDataVOList); + } + return playerGainsOrnamentsDataVOList; + } else if (joinSeatNumList.size() == 3) { + List playerGainsOrnamentsDataVOList = dogFall(ttFight, dataTabulate, joinSeatNumList); + divideEquallyLosePrice(collect, playerGainsOrnamentsDataVOList); + return playerGainsOrnamentsDataVOList; + } else if (joinSeatNumList.size() == 4) return dogFall(ttFight, dataTabulate, joinSeatNumList); + } + return null; + } + + private void divideEquallyLosePrice(Map> collect, + List playerGainsOrnamentsDataVOList) { + BigDecimal reduce = collect.values().stream().map(stringStringMap -> JSONObject.parseObject(stringStringMap + .get("priceTotal"), new TypeReference() { + })).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal divide = reduce.divide(new BigDecimal(playerGainsOrnamentsDataVOList.size()), 2, RoundingMode.HALF_UP); + for (PlayerGainsOrnamentsDataVO playerGainsOrnamentsDataVO : playerGainsOrnamentsDataVOList) { + userService.updateUserAccount(playerGainsOrnamentsDataVO.getUserId(), divide, TtAccountRecordSource.GAME_TYPE_02_AWARD); + } + List boxRecordsList = new ArrayList<>(); + for (Map value : collect.values()) { + boxRecordsList.addAll(JSONObject.parseObject(value.get("boxRecordsList"), new TypeReference>() { + })); + } + boxRecordsList = boxRecordsList.stream().peek(ttBoxRecords -> { + ttBoxRecords.setStatus(RESOLVE.getCode()); + ttBoxRecords.setUpdateTime(new Date()); + }).collect(Collectors.toList()); + boxRecordsService.updateBatchById(boxRecordsList, 1); + } + + private List dogFall(TtFight ttFight, + Map> dataTabulate, + List joinSeatNumList) { + List result = new ArrayList<>(); + for (Integer i : joinSeatNumList) { + PlayerGainsOrnamentsDataVO playerGainsOrnamentsData = JSONObject.parseObject( + dataTabulate.get(i).get("playerGainsOrnamentsData"), + new TypeReference() { + }); + result.add(playerGainsOrnamentsData); + new LambdaUpdateChainWrapper<>(boxRecordsService.getBaseMapper()).eq(TtBoxRecords::getFightId, ttFight.getId()) + .eq(TtBoxRecords::getUserId, playerGainsOrnamentsData.getUserId()).set(TtBoxRecords::getStatus, "0") + .set(TtBoxRecords::getUpdateTime, new Date()).set(TtBoxRecords::getHolderUserId, playerGainsOrnamentsData.getUserId()).update(); + } + return result; + } + + private List onePersonWin(TtFight ttFight, + Map> dataTabulate, + List joinSeatNumList, + Map> collect) { + List result = new ArrayList<>(); + List losePackSackDataList = new ArrayList<>(); + List> loseOrnamentsDataList = collect.values().stream().map(stringStringMap -> JSONObject.parseObject( + stringStringMap.get("playerGainsOrnamentsData"), new TypeReference() { + }).getOrnamentsDataList()) + .collect(Collectors.toList()); + for (List loseOrnamentsData : loseOrnamentsDataList) { + losePackSackDataList.addAll(loseOrnamentsData); + } + PlayerGainsOrnamentsDataVO playerGainsOrnamentsData = JSONObject.parseObject(dataTabulate.get( + joinSeatNumList.get(0)).get("playerGainsOrnamentsData"), + new TypeReference() { + }); + playerGainsOrnamentsData.getOrnamentsDataList().addAll(losePackSackDataList); + result.add(playerGainsOrnamentsData); + for (Map.Entry> entry : dataTabulate.entrySet()) { + Integer userId = JSONObject.parseObject(entry.getValue().get("userId"), new TypeReference() { + }); + new LambdaUpdateChainWrapper<>(boxRecordsService.getBaseMapper()).eq(TtBoxRecords::getFightId, ttFight.getId()) + .eq(TtBoxRecords::getUserId, userId).set(TtBoxRecords::getStatus, "0").set(TtBoxRecords::getUpdateTime, new Date()) + .set(TtBoxRecords::getHolderUserId, playerGainsOrnamentsData.getUserId()).update(); + } + return result; + } + + private PlayerGainsOrnamentsDataVO getPlayerGainsOrnamentsData(Integer userId, Integer fightId) { + TtUser ttUser = new LambdaQueryChainWrapper<>(userService.getBaseMapper()).eq(TtUser::getUserId, userId).one(); + TtFightUser ttFightUser = new LambdaQueryChainWrapper<>(fightUserMapper).eq(TtFightUser::getFightId, fightId) + .eq(TtFightUser::getUserId, userId).eq(TtFightUser::getStatus, "0").one(); + List ornamentsDataList = apiFightMapper.selectOrnamentsDataListByUserIdAndFightId(userId, fightId); + PlayerGainsOrnamentsDataVO playerGainsOrnamentsData = new PlayerGainsOrnamentsDataVO(); + playerGainsOrnamentsData.setUserId(userId); + playerGainsOrnamentsData.setUserName(ttUser.getUserName()); + playerGainsOrnamentsData.setNickName(ttUser.getNickName()); + playerGainsOrnamentsData.setAvatar(ttUser.getAvatar()); + playerGainsOrnamentsData.setOrnamentsDataList(ornamentsDataList); + playerGainsOrnamentsData.setJoinSeatNum(ttFightUser.getJoinSeatNum()); + return playerGainsOrnamentsData; + } + + private void openBox(TtFight ttFight, TtUser ttUser) { + log.info("开箱"); + String boxDataJsonString = JSON.toJSONString(ttFight.getBoxData()); + List boxDataList = JSONObject.parseObject(boxDataJsonString, new TypeReference>() { + }); + List boxRecordsList = new ArrayList<>(); + + for (BoxDataBodyA boxData : boxDataList) { + TtBox ttBox = new LambdaQueryChainWrapper<>(boxMapper) + .eq(TtBox::getBoxId, boxData.getBoxId()) + .eq(TtBox::getIsFight, "0") + .eq(TtBox::getStatus, "0") + .one(); + if (StringUtils.isNull(ttBox)) throw new RuntimeException("数据异常!"); + + // 计算开箱结果、保存开箱记录、返回开箱记录 + List ttBoxRecords = apiBindBoxService.addBoxRecord(ttUser, ttBox, boxData.getBoxNum()); + boxRecordsList.addAll(ttBoxRecords); + + // 统计箱子的历史开箱次数 + ttBox.setOpenNum(ttBox.getOpenNum() + boxData.getBoxNum()); + boxMapper.updateById(ttBox); + } + + if (boxRecordsList.isEmpty()) throw new RuntimeException("数据异常!"); + + boxRecordsList = boxRecordsList.stream().peek(ttBoxRecords -> { + ttBoxRecords.setStatus(IN_PACKSACK_ON.getCode()); + ttBoxRecords.setSource(TtboxRecordSource.FIGHT.getCode()); + ttBoxRecords.setFightId(ttFight.getId()); + ttBoxRecords.setHolderUserId(ttUser.getUserId()); + }).collect(Collectors.toList()); + log.info("更新boxRecord状态为4"); + boxRecordsService.updateBatchById(boxRecordsList, 1); + } + + @Override + public Map> tenTopFight(Integer maxType) { + if (maxType == null || maxType < 1 || maxType > 3) { + return Collections.emptyMap(); + } + long beginTime = System.currentTimeMillis(); + // 创建日期格式化对象,指定日期格式 + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + // 获取Calendar实例 + Calendar calendar = Calendar.getInstance(); + // 设置时间为今天凌晨 + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + // 获取今天凌晨时间 + Date todayMidnight = calendar.getTime(); + String formattedTodayMidnight = formatter.format(todayMidnight); + // 获取明天凌晨时间 + calendar.add(Calendar.DAY_OF_MONTH, 1); + Date tomorrowMidnight = calendar.getTime(); + String formattedTomorrowMidnight = formatter.format(tomorrowMidnight); + // 获取昨天凌晨时间 + calendar.add(Calendar.DAY_OF_MONTH, -2); // 从今天凌晨减去2天 + + List todayFightResult = new ArrayList<>(); + + List todayTenTopFight = apiFightMapper.tenTopFight(formattedTodayMidnight, formattedTomorrowMidnight, maxType); + log.info("today time is {}", System.currentTimeMillis() - beginTime); + beginTime = System.currentTimeMillis(); + if (CollectionUtils.isNotEmpty(todayTenTopFight)) { + for (ApiFightTenTopVo ten : todayTenTopFight) { + if (ten == null || ten.getId() == null) { + continue; + } + FightResultVO vo = ttFightResultService.getFightResult(ten.getId()); + if (vo == null) { + continue; + } + vo.setTotalOrnamentsPrice(ten.getTotalOrnamentsPrice()); + todayFightResult.add(vo); + } + } + log.info("today for time is {}", System.currentTimeMillis() - beginTime); + + Map> map = new HashMap<>(); + map.put("todayFightResult", todayFightResult); + return map; + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiFightUserServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiFightUserServiceImpl.java new file mode 100644 index 0000000..3d3c595 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiFightUserServiceImpl.java @@ -0,0 +1,11 @@ +package com.ruoyi.playingmethod.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.mapper.TtFightUserMapper; +import com.ruoyi.domain.other.TtFightUser; +import com.ruoyi.playingmethod.service.ApiFightUserService; +import org.springframework.stereotype.Service; + +@Service +public class ApiFightUserServiceImpl extends ServiceImpl implements ApiFightUserService { +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiLuckyUpgradeServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiLuckyUpgradeServiceImpl.java new file mode 100644 index 0000000..2e8e93e --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiLuckyUpgradeServiceImpl.java @@ -0,0 +1,489 @@ +package com.ruoyi.playingmethod.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.config.RedisConstants; +import com.ruoyi.admin.mapper.TtBoxRecordsMapper; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.admin.mapper.TtOrnamentsLevelMapper; +import com.ruoyi.admin.mapper.TtUpgradeFailOrnamentsMapper; +import com.ruoyi.admin.mapper.TtUpgradeOrnamentsMapper; +import com.ruoyi.admin.mapper.TtUpgradeRecordMapper; +import com.ruoyi.admin.model.UpdateUserAccountBo; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtUpgradeOrnamentsService; +import com.ruoyi.admin.service.TtUpgradeRecordService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.redis.config.RedisLock; +import com.ruoyi.domain.common.constant.LockKey.UpgradeLock; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtboxRecordSource; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.ApiLuckyUpgradeBody; +import com.ruoyi.domain.other.TtUpgradeOrnaments; +import com.ruoyi.domain.other.TtUpgradeRecord; +import com.ruoyi.domain.other.UpgradeBodyA; +import com.ruoyi.domain.vo.ApiLuckyOrnamentsDataVO; +import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO; +import com.ruoyi.playingmethod.mapper.ApiLuckyUpgradeMapper; +import com.ruoyi.playingmethod.mapper.ApiTtUserBlendErcashMapper; +import com.ruoyi.playingmethod.service.ApiLuckyUpgradeService; +import com.ruoyi.system.service.ISysConfigService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class ApiLuckyUpgradeServiceImpl implements ApiLuckyUpgradeService { + + @Value("${mkcsgo.upgrade.defaultRequired}") + private Integer defaultRequired; + + @Value("${mkcsgo.upgrade.anchorDefaultRequired}") + private Integer anchorDefaultRequired; + + @Autowired + private Executor customThreadPoolExecutor; + + @Autowired + private ApiTtUserBlendErcashMapper apiTtUserBlendErcashMapper; + + private final TtUserService userService; + private final ISysConfigService configService; + private final ApiLuckyUpgradeMapper apiLuckyUpgradeMapper; + private final TtUpgradeOrnamentsMapper upgradeOrnamentsMapper; + private final TtUpgradeFailOrnamentsMapper upgradeFailOrnamentsMapper; + private final TtUpgradeRecordMapper upgradeRecordMapper; + private final TtOrnamentMapper ornamentsMapper; + private final TtBoxRecordsMapper boxRecordsMapper; + private final TtOrnamentsLevelMapper ornamentsLevelMapper; + + @Autowired + private RabbitTemplate rabbitTemplate; + + @Autowired + private RedisLock redisLock; + + @Autowired + private RedisCache redisCache; + + public ApiLuckyUpgradeServiceImpl(TtUserService userService, + ISysConfigService configService, + ApiLuckyUpgradeMapper apiLuckyUpgradeMapper, + TtUpgradeOrnamentsMapper upgradeOrnamentsMapper, + TtUpgradeFailOrnamentsMapper upgradeFailOrnamentsMapper, + TtUpgradeRecordMapper upgradeRecordMapper, + TtOrnamentMapper ornamentsMapper, + TtBoxRecordsMapper boxRecordsMapper, + TtOrnamentsLevelMapper ornamentsLevelMapper) { + this.userService = userService; + this.configService = configService; + this.apiLuckyUpgradeMapper = apiLuckyUpgradeMapper; + this.upgradeOrnamentsMapper = upgradeOrnamentsMapper; + this.upgradeFailOrnamentsMapper = upgradeFailOrnamentsMapper; + this.upgradeRecordMapper = upgradeRecordMapper; + this.ornamentsMapper = ornamentsMapper; + this.boxRecordsMapper = boxRecordsMapper; + this.ornamentsLevelMapper = ornamentsLevelMapper; + } + + @Autowired + private TtUpgradeOrnamentsService ttUpgradeOrnamentsService; + + @Autowired + private TtUpgradeOrnamentsMapper ttUpgradeOrnamentsMapper; + + @Autowired + private TtBoxRecordsService ttBoxRecordsService; + + @Autowired + private TtUpgradeRecordService ttUpgradeRecordService; + + @Override + public List getOrnamentsList(ApiLuckyUpgradeBody apiLuckyUpgradeBody) { + apiLuckyUpgradeBody.setType(null); + return apiLuckyUpgradeMapper.getOrnamentsList(apiLuckyUpgradeBody); + } + + public R upgradeCheck(TtUser player, UpgradeBodyA param) { + + if (player.getAccountAmount() == null || player.getAccountAmount().compareTo(param.getPrice()) <= 0) { + return R.fail("余额不足"); + } + + if (!player.getUserType().equals(UserType.ANCHOR.getCode()) && !player.getUserType().equals(UserType.COMMON_USER.getCode())) { + return R.fail("非法的用户类型"); + } + return R.ok(); + } + + @Override + @Transactional + public R upgrade(TtUser ttUser, UpgradeBodyA upgradeParam) { + + TtUpgradeOrnaments upgradeOrnament = new LambdaQueryChainWrapper<>(upgradeOrnamentsMapper) + .eq(TtUpgradeOrnaments::getId, upgradeParam.getUpgradeOrnamentId()) + .eq(TtUpgradeOrnaments::getStatus, "0") + .one(); + if (ObjectUtil.isNull(upgradeOrnament)) { + return R.fail("该饰品未在幸运升级开放。"); + } + + // 检查 + R check = upgradeCheck(ttUser, upgradeParam); + if (!check.getCode().equals(200)) return check; + + // lock_key + Integer upgradeOrnamentId = upgradeParam.getUpgradeOrnamentId(); + String userType = ttUser.getUserType(); + String upgradeLock = UpgradeLock.UPGRADE_LOCK.getLock() + upgradeOrnamentId + ":" + userType; + + // 2 尝试获取锁 + Boolean lock = false; + for (int t = 0; t < 2; t++) { + + lock = redisLock.tryLock(upgradeLock, 2L, 7L, TimeUnit.SECONDS); + + if (!lock) { + + } else { + break; + } + } + + if (!lock) return R.fail("系统繁忙,请稍后重试。"); + + // 3 扣款 + R> mapR = upgradeAccounting(upgradeParam.getPrice(), ttUser); + if (!mapR.getCode().equals(200)) { + redisLock.unlock(upgradeLock); + return mapR; + } + + // 4 计算游戏 + boolean isVictory; + try { + + // 升级核心 + isVictory = playing(ttUser, upgradeOrnament, upgradeParam); + + } catch (Exception e) { + e.printStackTrace(); + return R.fail("系统繁忙,请稍后重试。"); + } finally { + redisLock.unlock(upgradeLock); + } + + // 5 同步保存本次结果。 + TtUpgradeRecord upgradeRecord; // 升级记录 + List boxRecords; // 获得物品记录 + if (isVictory) { + + Long ornamentId = upgradeOrnament.getOrnamentsId(); + + // 胜 + List prizeList = ornamentsMapper.simpleOrnamentInfo(Arrays.asList(ornamentId)); + SimpleOrnamentVO ornament = prizeList.get(0); + + // 奖励集合 并统计奖励总价值 + // List prizeList = Arrays.asList(ornament); + BigDecimal prizeTotal = BigDecimal.ZERO; + for (SimpleOrnamentVO vo : prizeList) prizeTotal = prizeTotal.add(vo.getOrnamentPrice()); + + // 构造升级记录 + upgradeRecord = TtUpgradeRecord.builder() + .userId(ttUser.getUserId()) + .userType(ttUser.getUserType()) + .nickName(ObjectUtil.isNotEmpty(ttUser.getNickName()) ? ttUser.getNickName() : ttUser.getUserName()) + .isVictory(isVictory) + + .amountConsumed(upgradeParam.getPrice()) + .probability(upgradeParam.getProbability()) + + .targetUpgradeId(upgradeOrnament.getId()) + .targetOrnamentId(ornamentId) + .targetOrnamentPrice(ornament.getOrnamentPrice()) + + .gainOrnamentList(JSONUtil.toJsonStr(prizeList)) + .gainOrnamentsPrice(prizeTotal) + + .openTime(new Date()) + .build(); + + // 构造获得物品记录 + boxRecords = Arrays.asList(TtBoxRecords.builder() + .source(TtboxRecordSource.UPGRADE.getCode()) + .ornamentId(ornamentId) + .ornamentName(ornament.getOrnamentName()) + .userId(ttUser.getUserId()) + .holderUserId(ttUser.getUserId()) + .imageUrl(ornament.getOrnamentImgUrl()) + .ornamentsPrice(ornament.getOrnamentPrice()) + .status(TtboxRecordStatus.IN_PACKSACK_ON.getCode()) + .marketHashName(ornament.getOrnamentHashName()) + .createTime(new Date()) + .updateTime(new Date()) + .build()); + + } else { + + // 负 + // 奖励集合 并统计奖励总价值 + List failPrize = upgradeFailOrnamentsMapper.ornamentInfoByUpgradeId(upgradeOrnament.getId()); + + BigDecimal prizeTotal = BigDecimal.ZERO; + if (!failPrize.isEmpty()) { + for (SimpleOrnamentVO item : failPrize) { + prizeTotal = prizeTotal.add(item.getOrnamentPrice().multiply(new BigDecimal(item.getOrnamentNumber()))); + } + } + + // 构造升级记录 + upgradeRecord = TtUpgradeRecord.builder() + .userId(ttUser.getUserId()) + .userType(ttUser.getUserType()) + .nickName(ObjectUtil.isNotEmpty(ttUser.getNickName()) ? ttUser.getNickName() : ttUser.getUserName()) + + .amountConsumed(upgradeParam.getPrice()) + .probability(upgradeParam.getProbability()) + + .isVictory(isVictory) + + .targetUpgradeId(upgradeOrnament.getId()) + .targetOrnamentId(upgradeOrnament.getOrnamentsId()) + .targetOrnamentPrice(upgradeOrnament.getOrnamentPrice()) + + .gainOrnamentList(failPrize.isEmpty() ? "" : JSONUtil.toJsonStr(failPrize)) + .gainOrnamentsPrice(prizeTotal) + + .openTime(new Date()) + .build(); + + // 构造获得物品记录 + boxRecords = failPrize.stream().map(item -> { + return TtBoxRecords.builder() + .source(TtboxRecordSource.UPGRADE.getCode()) + .ornamentId(item.getOrnamentId()) + .ornamentName(item.getOrnamentName()) + .userId(ttUser.getUserId()) + .holderUserId(ttUser.getUserId()) + .imageUrl(item.getOrnamentImgUrl()) + .ornamentsPrice(item.getOrnamentPrice()) + .status(TtboxRecordStatus.IN_PACKSACK_ON.getCode()) + .marketHashName(item.getOrnamentHashName()) + .createTime(new Date()) + .updateTime(new Date()) + .build(); + }).collect(Collectors.toList()); + + } + + // 游戏记录 + ttUpgradeRecordService.save(upgradeRecord); + ttBoxRecordsService.saveBatch(boxRecords); + + return R.ok(upgradeRecord); + + } + + public Boolean playing(TtUser ttUser, TtUpgradeOrnaments upgradeOrnament, UpgradeBodyA upgradeParam) { + + Integer upgradeOrnamentId = upgradeOrnament.getId(); + String userType = ttUser.getUserType(); + + String rangeFixedKey = RedisConstants.UPGRADE_RANGE_FIXED + upgradeOrnamentId + ":" + userType; + String rangeFloatKey = RedisConstants.UPGRADE_RANGE + upgradeOrnamentId + ":" + userType; + + // 读redis的固定概率 [x,y] x进度,y空间大小 + Object rangeFixedObj = redisCache.getCacheObject(rangeFixedKey); + String rangeFixedStr = JSONUtil.toJsonStr(rangeFixedObj); + Integer[] rangeFixed = JSONUtil.parseArray(rangeFixedStr).toArray(new Integer[2]); + // 读redis的饰品概率区间 [begin,end] + Object rangeFloatObj = redisCache.getCacheObject(rangeFloatKey); + String rangeFloatStr = JSONUtil.toJsonStr(rangeFloatObj); + Integer[] rangeFloat = JSONUtil.parseArray(rangeFloatStr).toArray(new Integer[2]); + + if (ObjectUtil.isNotEmpty(rangeFixed)) { + // 抽奖 + return doUpgrade( + ttUser, + upgradeOrnament, + rangeFixed, + upgradeParam, + rangeFixedKey, + rangeFloatKey, + rangeFloat); + } + + // 没有,使用redis的概率区间新建, + if (ObjectUtil.isNotEmpty(rangeFloat)) { + + // 创建固定概率, + Integer[] newRangeFixed = new Integer[]{0, null}; + for (int i = 0; i < 5; i++) { + Random random = new Random(); + int r = random.nextInt(rangeFloat[1] - rangeFloat[0]) + rangeFloat[0]; + newRangeFixed[1] = r; + } + + // 抽奖 + return doUpgrade( + ttUser, + upgradeOrnament, + newRangeFixed, + upgradeParam, + rangeFixedKey, + rangeFloatKey, + rangeFloat); + } + + // 也没有概率区间,用mysql的数据,创建固定概率,抽奖 + if (userType.equals(UserType.ANCHOR.getCode())) { + String anchorLuckSection = upgradeOrnament.getAnchorLuckSection(); + List list = JSONUtil.toList(anchorLuckSection, Integer.class); + rangeFloat = list.toArray(new Integer[2]); + } else if (userType.equals(UserType.COMMON_USER.getCode())) { + String luckSection = upgradeOrnament.getLuckSection(); + List list = JSONUtil.toList(luckSection, Integer.class); + rangeFloat = list.toArray(new Integer[2]); + } + + rangeFixed = createRangeFixed(rangeFloat, 0); + + // 缓存固定概率 + // redisCache.setCacheList(rangeFixedKey, Arrays.asList(rangeFixed)); + // redisCache.setCacheObject(rangeFixedKey, Arrays.asList(rangeFixed)); + // 缓存概率区间 + redisCache.setCacheObject(rangeFloatKey, rangeFloat, 1, TimeUnit.DAYS); + + // 抽奖 + return doUpgrade( + ttUser, + upgradeOrnament, + rangeFixed, + upgradeParam, + rangeFixedKey, + rangeFloatKey, + rangeFloat); + + } + + /** + * @param rangeFixed 本次的固定概率 + * @param param http请求参数 + * @param rangeFixedKey redis固定概率key + * @param rangeFloat 升级饰品的概率区间 + * @return + */ + public Boolean doUpgrade(TtUser ttUser, + TtUpgradeOrnaments upgradeOrnament, + Integer[] rangeFixed, + UpgradeBodyA param, + String rangeFixedKey, + String rangeFloatKey, + Integer[] rangeFloat) { + + // 计算进度 + Integer currentV = rangeFixed[0]; + Integer fullV = rangeFixed[1]; + + int sub = fullV - (param.getProbability() + currentV); + + if (sub > 0) { + // 失败,记录进度 + int i = param.getProbability() + currentV; + rangeFixed[0] = i; + // redisCache.setCacheList(rangeFixedKey, Arrays.asList(rangeFixed)); + redisCache.setCacheObject(rangeFixedKey, rangeFixed, 1, TimeUnit.DAYS); + return false; + } + + // 成功 + // 如果没有概率区间,新建 + if (ObjectUtil.isNotEmpty(rangeFloat)) { + + if (ttUser.getUserType().equals(UserType.ANCHOR.getCode())) { + String anchorLuckSection = upgradeOrnament.getAnchorLuckSection(); + List list = JSONUtil.toList(anchorLuckSection, Integer.class); + rangeFloat = list.toArray(new Integer[2]); + } else if (ttUser.getUserType().equals(UserType.COMMON_USER.getCode())) { + String luckSection = upgradeOrnament.getLuckSection(); + List list = JSONUtil.toList(luckSection, Integer.class); + rangeFloat = list.toArray(new Integer[2]); + } + + // 缓存概率区间 + redisCache.setCacheObject(rangeFloatKey, rangeFloat, 1, TimeUnit.DAYS); + + // rangeFixed = createRangeFixed(rangeFloat,0); + + } + + // 成功,随机创建新的固定概率 + Integer[] newRangeFixed = createRangeFixed(rangeFloat, Math.abs(sub)); + + // 保存新的固定概率 + // redisCache.setCacheList(rangeFixedKey, Arrays.asList(newRangeFixed)); + redisCache.setCacheObject(rangeFixedKey, newRangeFixed, 1, TimeUnit.DAYS); + return true; + + } + + + public Integer[] createRangeFixed(Integer[] rangeFloat, Integer initVal) { + + // 创建固定概率, + Integer[] newRangeFixed = new Integer[2]; + newRangeFixed[0] = initVal; + int r = -1; + for (int i = 0; i < 5; i++) { + Random random = new Random(); + r = random.nextInt(rangeFloat[1] - rangeFloat[0]) + rangeFloat[0]; + } + newRangeFixed[1] = r; + + return newRangeFixed; + } + + // 扣款 + public R> upgradeAccounting(BigDecimal consumption, TtUser player) { + R updateResult = userService.updateUserAccount(player.getUserId(), consumption.negate(), + TtAccountRecordSource.GOLD_DEVICE); + if (R.isError(updateResult)) { + return R.fail(updateResult.getMsg()); + } + + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate.eq(TtUser::getUserId, player.getUserId()); + + Map map = new HashMap<>(); + map.put("Amount", updateResult.getData().getAccountAmount()); + map.put("Credits", updateResult.getData().getAccountCredits()); + map.put("total", consumption); + + return R.ok(map); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiRedPackServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiRedPackServiceImpl.java new file mode 100644 index 0000000..67ad884 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiRedPackServiceImpl.java @@ -0,0 +1,282 @@ +package com.ruoyi.playingmethod.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.ruoyi.admin.mapper.TtRedPacketMapper; +import com.ruoyi.admin.mapper.TtRedPacketRecordMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.TtRedPackService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.util.RandomUtils; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.redis.config.RedisLock; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtRedPack; +import com.ruoyi.domain.other.TtRedPacket; +import com.ruoyi.domain.other.TtRedPacketRecord; +import com.ruoyi.domain.param.RedPackRecordParam; +import com.ruoyi.playingmethod.service.ApiRedPackService; +import com.ruoyi.thirdparty.wechat.domain.PtLoginUser; +import com.ruoyi.thirdparty.wechat.entity.TtCoinRechargeParam; +import com.ruoyi.thirdparty.wechat.entity.TtCoinRechargeRecord; +import com.ruoyi.thirdparty.wechat.service.ITtCoinRechargeRecordService; +import com.ruoyi.thirdparty.wechat.service.ITtCoinRecordService; +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.math.BigDecimal; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class ApiRedPackServiceImpl implements ApiRedPackService { + + @Autowired + private TtRedPackService ttRedPackService; + + @Autowired + private ITtCoinRechargeRecordService coinRechargeRecordService; + + @Autowired + private ITtCoinRecordService recordService; + + @Autowired + private TtUserService ttUserService; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + @Autowired + private TtRedPacketRecordMapper ttRedPacketRecordMapper; + + @Autowired + private TtRedPacketMapper ttRedPacketMapper; + + @Autowired + private TtUserService userService; + + @Autowired + private RedisLock redisLock; + + @Override + public List getRedPackList(PtLoginUser user, Integer type) { + TtRedPack redPackParam = new TtRedPack(); + // redPackParam.setStatus(1L); + // redPackParam.setPackType(type + ""); + return ttRedPackService.selectTtRedPackList(redPackParam); + } + + @Override + @Transactional + public AjaxResult getRedPack(Long userId, Long redPackId) { + TtUser user = ttUserService.getById(userId); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + TtRedPack redPack = ttRedPackService.selectTtRedPackById(redPackId); + TtCoinRechargeParam rechargeParam = new TtCoinRechargeParam(); + rechargeParam.setUid(user.getUserId().longValue()); + rechargeParam.setBeginTime(format.format(redPack.getBeginTime())); + rechargeParam.setEndTime(format.format(redPack.getEndTime())); + rechargeParam.setCoin(redPack.getRechargeLimit()); + + // 查询用户在指定时间内的大于最小充值金额的充值记录 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TtUserBlendErcash::getUserId, rechargeParam.getUid()) + .eq(TtUserBlendErcash::getType, 1) + .eq(TtUserBlendErcash::getSource, 0) + .gt(TtUserBlendErcash::getTotal, rechargeParam.getCoin()) + .gt(TtUserBlendErcash::getCreateTime, rechargeParam.getBeginTime()) + .lt(TtUserBlendErcash::getCreateTime, rechargeParam.getEndTime()); + List rechargeRecords = ttUserBlendErcashMapper.selectList(queryWrapper); + + // 根据红包ID和用户ID查询红包记录,检查用户是否已经领取过某个特定的红包 + LambdaQueryWrapper ttRedPacketRecordWrapper = new LambdaQueryWrapper<>(); + ttRedPacketRecordWrapper + .eq(TtRedPacketRecord::getRedPacketId, redPack.getId()) + .eq(TtRedPacketRecord::getUserId, user.getUserId()); + List redPackRecords = ttRedPacketRecordMapper.selectList(ttRedPacketRecordWrapper); + if (redPackRecords.size() >= rechargeRecords.size()) { + return AjaxResult.error("可领取次数不足"); + } + // TODO 减红包剩余数 + String lockName = "RED_PACK_LOCK:" + redPackId; + boolean lock = false; + try { + // 尝试获取锁,最多尝试10次,每次等待3秒,锁定时间7秒 + for (int i = 0; i < 10; i++) { + lock = redisLock.tryLock(lockName, 3, 7, TimeUnit.SECONDS); + if (lock) { + break; + } + } + + if (!lock) { + return AjaxResult.error("服务器繁忙,稍后重试。"); + } + + // 执行业务逻辑 + TtRedPack ttRedPack = ttRedPackService.selectTtRedPackById(redPackId); + if (ttRedPack.getValidCount() <= 0) { + return AjaxResult.error("红包已被领完"); + } else { + ttRedPack.setValidCount(ttRedPack.getValidCount() - 1); + ttRedPackService.updateTtRedPack(ttRedPack); + } + } finally { + if (lock) { + redisLock.unlock(lockName); + } + } + + TtRedPacketRecord ttRedPackRecord = new TtRedPacketRecord(); + + BigDecimal randomValue = RandomUtils.getRandomaBigdecimal(redPack.getValueMin(), redPack.getValueMax()); + + ttRedPackRecord.setRedPacketId(redPackId.intValue()); + ttRedPackRecord.setUserId(user.getUserId()); + ttRedPackRecord.setReceiveAmount(randomValue); + ttRedPackRecord.setReceiveTime(new Date()); + + ttRedPacketRecordMapper.insert(ttRedPackRecord); + + // 加钱 + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, user.getUserId()) + .setSql("account_credits = account_credits + " + randomValue); + userService.update(userUpdate); + + // 综合消费日志 + TtUser ttUser = userService.getById(user.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(user.getUserId()) + .credits(randomValue.compareTo(BigDecimal.ZERO) > 0 ? randomValue : null) + .finalCredits(randomValue.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountCredits().add(randomValue) : null) + .total(randomValue.add(randomValue)) // 收支合计 + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.CHARGE_RED_PACKET.getCode()) + .remark(TtAccountRecordSource.CHARGE_RED_PACKET.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + ttUserBlendErcashMapper.insert(blendErcash); + + return AjaxResult.success(ttRedPackRecord); + } + + @Override + @Transactional + public AjaxResult getRedPackByKey(LoginUser user, String redPackKey) { + + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + // 根据KEY查询红包 + LambdaUpdateWrapper selectRedPackByKeyWrapper = new LambdaUpdateWrapper<>(); + selectRedPackByKeyWrapper + .eq(TtRedPacket::getPassword, redPackKey); + TtRedPacket redPack = ttRedPacketMapper.selectOne(selectRedPackByKeyWrapper); + if (redPack == null){ + return AjaxResult.error("未找到红包,请检查口令是否正确"); + } + TtCoinRechargeParam rechargeParam = new TtCoinRechargeParam(); + rechargeParam.setUid(user.getUserId()); + rechargeParam.setBeginTime(format.format(new Date()) + " 00:00:00"); + rechargeParam.setEndTime(format.format(new Date()) + " 23:59:59"); + + + LambdaQueryWrapper ttRedPacketRecordWrapper = new LambdaQueryWrapper<>(); + ttRedPacketRecordWrapper + .eq(TtRedPacketRecord::getRedPacketId, redPack.getId()) + .eq(TtRedPacketRecord::getUserId, user.getUserId().intValue()); + List redPackRecords = ttRedPacketRecordMapper.selectList(ttRedPacketRecordWrapper); + if (redPackRecords.size() > 0){ + return AjaxResult.error("您已经领取过。"); + } + + TtRedPacketRecord ttRedPackRecord = new TtRedPacketRecord(); + + // 此处解析最小值和最大值[1, 5] + String amount = redPack.getAmount(); + // 去除方括号和空格,得到 "1,5" + String cleanString = amount.replaceAll("\\[|\\]|\\s", ""); + // 分割字符串,得到字符串数组 ["1", "5"] + String[] parts = cleanString.split(","); + // 转换为 BigDecimal + BigDecimal minValue = new BigDecimal(parts[0]); + BigDecimal maxValue = new BigDecimal(parts[1]); + BigDecimal randomValue = RandomUtils.getRandomaBigdecimal(minValue, maxValue); + + ttRedPackRecord.setRedPacketId(redPack.getId()); + ttRedPackRecord.setUserId(user.getUserId().intValue()); + ttRedPackRecord.setReceivePassword(redPackKey); + ttRedPackRecord.setReceiveAmount(randomValue); + ttRedPackRecord.setReceiveTime(new Date()); + + ttRedPacketRecordMapper.insert(ttRedPackRecord); + + // 加钱 + userService.updateOnlyUserAccount(user.getUserId().intValue(), randomValue, TtAccountRecordSource.RECEIVE_RED_PACKET); + + return AjaxResult.success(ttRedPackRecord); + } + + @Override + public List getActivityDataHis(PtLoginUser user) { + RedPackRecordParam redPackRecordParam = new RedPackRecordParam(); + redPackRecordParam.setuId(user.getUserId().intValue()); + LambdaQueryWrapper ttRedPacketRecordWrapper = new LambdaQueryWrapper<>(); + ttRedPacketRecordWrapper + .eq(TtRedPacketRecord::getUserId, user.getUserId().intValue()); + List redPackRecords = ttRedPacketRecordMapper.selectList(ttRedPacketRecordWrapper); + return redPackRecords; + } + + @Override + public AjaxResult getRedPackDataDetail(PtLoginUser user, Long recordId) { + + TtRedPacketRecord ttRedPackRecord = ttRedPacketRecordMapper.selectById(recordId); + if (ttRedPackRecord.getReceivePassword() != null) { + TtRedPacket redPackKey = ttRedPacketMapper.selectById(ttRedPackRecord.getRedPacketId()); + // TODO 缺少属性 + // ttRedPackRecord.setRedPackKey(redPackKey); + } else { + TtRedPack redPack = ttRedPackService.selectTtRedPackById(ttRedPackRecord.getRedPacketId().longValue()); + // TODO 缺少属性 + // ttRedPackRecord.setRedPack(redPack); + } + + return AjaxResult.success(ttRedPackRecord); + } + + /** + * 解析红包最小值和最大值 + */ + public static List parseBigDecimalList(String input) { + // 去掉中括号和空格 + String cleanInput = input.replace("[", "").replace("]", "").replaceAll("\\s+", ""); + + // 按逗号分割成字符串数组 + String[] parts = cleanInput.split(","); + + // 转换为 BigDecimal 对象列表 + List bigDecimals = Arrays.stream(parts) + .map(BigDecimal::new) // 使用 BigDecimal 的构造方法转换为 BigDecimal 对象 + .collect(Collectors.toList()); + + return bigDecimals; + } + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiReplacementRecordServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiReplacementRecordServiceImpl.java new file mode 100644 index 0000000..bfbd848 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiReplacementRecordServiceImpl.java @@ -0,0 +1,198 @@ +package com.ruoyi.playingmethod.service.impl; + +import com.ruoyi.admin.mapper.TtBoxMapper; +import com.ruoyi.admin.mapper.TtBoxRecordsMapper; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.common.constant.TtboxRecordSource; +import com.ruoyi.domain.dto.packSack.PackSackCondition; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtReplacementRecord; +import com.ruoyi.domain.vo.UserPackSackDataVO; +import com.ruoyi.playingmethod.mapper.ApiReplacementRecordMapper; +import com.ruoyi.playingmethod.service.IApiReplacementRecordService; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.wechat.domain.PtLoginUser; +import com.ruoyi.user.mapper.ApiUserPackSackMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.*; + +/** + * 业务端汰换逻辑实现 + * + * @author junhai + */ +@Service +public class ApiReplacementRecordServiceImpl implements IApiReplacementRecordService { + + @Autowired + private ApiReplacementRecordMapper apiReplacementRecordMapper; + + @Autowired + private TtBoxRecordsService ttBoxRecordsService; + + @Autowired + private TtBoxRecordsMapper ttBoxRecordsMapper; + + @Autowired + private ISysConfigService sysConfigService; + + @Autowired + private TtOrnamentMapper ttOrnamentMapper; + + // 概率配置数组 + private static final double[][] percent = { + {0.5, 0.6}, + {0.6, 0.7}, + {0.7, 0.8}, + {0.8, 0.9}, + {0.9, 1}, + {1, 2} + }; + + + @Override + @Transactional + public AjaxResult synthesizeItems(LoginUser user, List itemIds) { + int size = 0; + int size1 = 0; + if (itemIds != null) { + size = itemIds.size(); + } + + int allCount = size + size1; + if (allCount < 3 || allCount > 9) { + return AjaxResult.error("汰换数量在3~9个"); + } + + BigDecimal totalPrice = new BigDecimal(0); + + //查询饰品列表 + // 1.先查询宝箱记录里面的饰品 + if(!itemIds.isEmpty()){ + // 从我的背包中查询饰品总价值 + List boxs = apiReplacementRecordMapper + .selectUserPackSack(user.getUserId().intValue(), itemIds); + for (int i = 0; i < boxs.size(); i++) { + BigDecimal bigDecimal = boxs.get(i).getOrnamentsPrice(); + totalPrice = totalPrice.add(bigDecimal); + } + } + //累加装备总价值 + String status = "8"; + //更新记录的状态 updateStatusByIds + if(!itemIds.isEmpty()){ + ttBoxRecordsMapper.updateStatusByIds(itemIds, status); + } + + //获取最小汰换金额 + BigDecimal minPrice = new BigDecimal(sysConfigService.selectConfigByKey("minExchangeAmount")); + // 汰换饰品合计总价不得低于 + if (totalPrice.compareTo(minPrice) < 0) { + return AjaxResult.error("汰换饰品合计总价不得低于" + minPrice); + } + + //获取饰品的id + Long id = getOId(totalPrice); + //获取饰品详情 + TtOrnament ttOrnament = ttOrnamentMapper.selectOrnamentById(id); + //进行汰换的日志记录 + TtReplacementRecord replacementRecord = new TtReplacementRecord(); + replacementRecord.setUid(user.getUserId()); + replacementRecord.setUname(user.getUserData().getNickName()); + replacementRecord.setAwardOid(ttOrnament.getId()); + replacementRecord.setAwardOname(ttOrnament.getName()); + replacementRecord.setAwardOimg(ttOrnament.getImageUrl()); + replacementRecord.setAwardOprice(ttOrnament.getUsePrice()); + replacementRecord.setTime(DateUtils.getNowDate()); + replacementRecord.setCreateTime(DateUtils.getNowDate()); + apiReplacementRecordMapper.insertTtReplacementRecord(replacementRecord); + //将汰换的奖品加入背包 + TtBoxRecords ttBoxRecords = new TtBoxRecords(); + ttBoxRecords.setSource(TtboxRecordSource.REPLACEMENT.getCode()); + ttBoxRecords.setUserId(user.getUserId().intValue()); + ttBoxRecords.setHolderUserId(user.getUserId().intValue()); + ttBoxRecords.setBoxName("汰换奖品"); + ttBoxRecords.setOrnamentName(ttOrnament.getName()); + ttBoxRecords.setImageUrl(ttOrnament.getImageUrl()); + ttBoxRecords.setOrnamentId(ttOrnament.getId()); + ttBoxRecords.setOrnamentsPrice(ttOrnament.getUsePrice()); + ttBoxRecords.setStatus(0); + ttBoxRecords.setCreateTime(DateUtils.getNowDate()); + ttBoxRecordsService.save(ttBoxRecords); + + return AjaxResult.success(replacementRecord); + } + + + /** + * 随机产生饰品id + * + * @param bean + * @return + */ + public Long getOId(BigDecimal bean) { + List ornament = new ArrayList<>(); + initArray(bean, ornament); + Random random = new Random(); + int randomIndex = random.nextInt(ornament.size()); + return ornament.get(randomIndex).getId(); + } + + /** + * 初始化 饰品列表 + * + * @param bean + * @param ornaments + */ + private void initArray(BigDecimal bean, List ornaments) { + for (double[] range : percent) { + BigDecimal start = bean.multiply(BigDecimal.valueOf(range[0])); + BigDecimal end = bean.multiply(BigDecimal.valueOf(range[1])); + + Map map = new HashMap<>(); + map.put("start", start); + map.put("end", end); + + int configQuantity = 1; + + if (configQuantity > 0) { + List eligibleOrnaments = apiReplacementRecordMapper.findByPriceRange(map); + shuffleList(eligibleOrnaments); + int quantityToAdd = Math.min(configQuantity, eligibleOrnaments.size()); + + for (int i = 0; i < quantityToAdd; i++) { + ornaments.add(eligibleOrnaments.get(i)); + } + } + } + } + + /** + * 采用了 Fisher-Yates 随机洗牌算法, + * + * @param list + */ + private void shuffleList(List list) { + Random random = new Random(); + int n = list.size(); + for (int i = n - 1; i > 0; i--) { + int j = random.nextInt(i + 1); + TtOrnament temp = list.get(i); + list.set(i, list.get(j)); + list.set(j, temp); + } + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiRollServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiRollServiceImpl.java new file mode 100644 index 0000000..f72ad38 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiRollServiceImpl.java @@ -0,0 +1,651 @@ +package com.ruoyi.playingmethod.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.config.RedisConstants; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtOrnamentsLevelService; +import com.ruoyi.admin.service.TtRollUserService; +import com.ruoyi.admin.util.RandomUtils; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.redis.config.RedisLock; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtboxRecordSource; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.common.constant.roll.RollGetPrizeWay; +import com.ruoyi.domain.common.constant.roll.RollStatus; +import com.ruoyi.domain.common.constant.roll.RollType; +import com.ruoyi.domain.dto.roll.GetRollOpenPrizeParam; +import com.ruoyi.domain.dto.roll.GetRollPlayersParam; +import com.ruoyi.domain.dto.roll.GetRollPrizePool; +import com.ruoyi.domain.entity.*; +import com.ruoyi.domain.entity.roll.*; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.*; +import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO; +import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsVO; +import com.ruoyi.domain.vo.roll.RollUserPrizeVO; +import com.ruoyi.domain.vo.roll.RollUserVO; +import com.ruoyi.domain.vo.roll.SimpleRollOrnamentVO; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.playingmethod.controller.ApiRollController.GetRollListParam; +import com.ruoyi.playingmethod.controller.ApiRollController.JoinRollParam; +import com.ruoyi.playingmethod.mapper.ApiRollMapper; +import com.ruoyi.playingmethod.service.ApiRollService; +import com.ruoyi.domain.vo.RollDetailsDataVO; +import com.ruoyi.domain.vo.RollListDataVO; +import com.ruoyi.thirdparty.common.service.ApiNoticeService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.TtboxRecordStatus.IN_PACKSACK_ON; + +@Service +@Slf4j +public class ApiRollServiceImpl extends ServiceImpl implements ApiRollService { + + private final ApiRollMapper apiRollMapper; + private final TtUserMapper userMapper; + private final TtOrnamentMapper ornamentsMapper; + private final TtBoxRecordsService boxRecordsService; + private final TtRollMapper rollMapper; + private final TtRollUserService rollUserService; + private final TtRollJackpotOrnamentsMapper rollJackpotOrnamentsMapper; + private final TtRechargeRecordMapper rechargeRecordMapper; + private final RedisLock redisLock; + + public ApiRollServiceImpl(ApiRollMapper apiRollMapper, + TtUserMapper userMapper, + TtOrnamentMapper ornamentsMapper, + TtBoxRecordsService boxRecordsService, + TtRollMapper rollMapper, + TtRollUserService rollUserService, + TtRollJackpotOrnamentsMapper rollJackpotOrnamentsMapper, + TtRechargeRecordMapper rechargeRecordMapper, + RedisLock redisLock) { + this.apiRollMapper = apiRollMapper; + this.userMapper = userMapper; + this.ornamentsMapper = ornamentsMapper; + this.boxRecordsService = boxRecordsService; + this.rollMapper = rollMapper; + this.rollUserService = rollUserService; + this.rollJackpotOrnamentsMapper = rollJackpotOrnamentsMapper; + this.rechargeRecordMapper = rechargeRecordMapper; + this.redisLock = redisLock; + } + + @Autowired + private TtRollUserMapper ttRollUserMapper; + + @Autowired + private ApiNoticeService apiNoticeService; + + @Autowired + private TtRollUserPrizeMapper ttRollUserPrizeMapper; + + @Autowired + private TtRollJackpotMapper ttRollJackpotMapper; + + @Autowired + private TtRollJackpotOrnamentsMapper ttRollJackpotOrnamentsMapper; + + @Autowired + private TtRollMapper ttRollMapper; + + @Autowired + private TtOrnamentMapper ttOrnamentMapper; + + @Autowired + private TtBoxRecordsService ttBoxRecordsService; + + @Autowired + private TtOrnamentsLevelMapper ttOrnamentsLevelMapper; + + @Autowired + private TtOrnamentsLevelService ttOrnamentsLevelService; + + @Autowired + private TtBoxRecordsMapper ttBoxRecordsMapper; + + public R joinRollCheck(TtRoll ttRoll, TtRollUser rollUser, TtUser player, JoinRollParam param) { + + if (StringUtils.isNull(ttRoll)) return R.fail("当前Roll房已结束"); + if (StringUtils.isNotNull(rollUser)) return R.fail("您当前已在该房间内,请勿重复加入房间!"); + + String password = ttRoll.getRollPassword(); + if (StringUtils.isNotEmpty(password)) { + if (StringUtils.isEmpty(param.getRollPassword())) return R.fail("请输入密码!"); + if (!password.equals(param.getRollPassword())) return R.fail("密码错误!"); + } + + // 检查加入条件:充值下限 + BigDecimal minRecharge = ttRoll.getMinRecharge(); + if (minRecharge.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal rechargeTotalAmount = getRechargeTotalAmount(player.getUserId(), ttRoll.getRechargeStartTime()); + if (minRecharge.compareTo(rechargeTotalAmount) > 0) return R.fail("未满足该房间的充值条件!"); + } + + return R.ok(); + } + + @Override + public R joinRoll(JoinRollParam param, TtUser player) { + + // roll房 + TtRoll ttRoll = new LambdaQueryChainWrapper<>(rollMapper) + .eq(TtRoll::getId, param.getRollId()) + .eq(TtRoll::getRollStatus, "0") + .gt(TtRoll::getEndTime, DateUtils.getNowDate()) + .one(); + // roll房成员 + TtRollUser rollUser = new LambdaQueryChainWrapper<>(rollUserService.getBaseMapper()) + .eq(TtRollUser::getUserId, player.getUserId()) + .eq(TtRollUser::getRollId, param.getRollId()) + .one(); + + // 检查 + R check = joinRollCheck(ttRoll, rollUser, player, param); + if (!check.getCode().equals(200)) return check; + + // 如果是主播房 + if (ttRoll.getRollType().equals(RollType.ANCHOR.getCode())) { + TtUser anchor = new LambdaQueryChainWrapper<>(userMapper) + .eq(TtUser::getUserId, ttRoll.getUserId()) + .eq(TtUser::getUserType, UserType.ANCHOR.getCode()) + .eq(TtUser::getDelFlag, "0") + .one(); + // 检查是否粉丝 + Integer parentId = player.getParentId(); + if (StringUtils.isNull(parentId) || !anchor.getUserId().equals(parentId)) { + return R.fail("您不是该主播的粉丝,请先加入主播粉丝团才可加入房间!"); + } + } + + // 加入房间 + Boolean lock = false; + for (int i = 0; i < 2; i++) { + + lock = redisLock.tryLock(RedisConstants.JOIN_ROLL_LOCK + param.getRollId(), 2L, 7L, TimeUnit.SECONDS); + + if (!lock) { + try { + TimeUnit.SECONDS.sleep(1L); + } catch (Exception e) { + log.warn("try lock sleep warn"); + } finally { + continue; + } + } else { + break; + } + + } + + if (lock) { + try { + + List rollUserList = new LambdaQueryChainWrapper<>(rollUserService.getBaseMapper()) + .eq(TtRollUser::getRollId, param.getRollId()) + .list(); + + if (ttRoll.getPeopleNum() <= rollUserList.size()) { + return R.fail("roll房人满。"); + } + + TtRollUser ttRollUser = TtRollUser.builder() + .rollId(param.getRollId()) + .userId(player.getUserId()) + .userName(player.getUserName()) + .nickName(player.getNickName()) + .avatar(player.getAvatar()) + .joinTime(DateUtils.getNowDate()) + .build(); + + rollUserService.getBaseMapper().insert(ttRollUser); + + return R.ok(); + + } catch (Exception e) { + e.printStackTrace(); + return R.fail("加入失败,请稍后重试。"); + } finally { + redisLock.unlock(RedisConstants.JOIN_ROLL_LOCK + param.getRollId()); + } + } + + redisLock.unlock(RedisConstants.JOIN_ROLL_LOCK + param.getRollId()); + return R.fail("服务器繁忙。"); + + } + + // roll房开奖 + @Override + public R endROLL(Integer rollId) { + + System.out.println("server roll房开奖" + rollId); + + // 1 检查 + TtRoll ttRoll = new LambdaQueryChainWrapper<>(rollMapper) + .eq(TtRoll::getId, rollId) + .eq(TtRoll::getDelFlag, "0") + .one(); + if (ObjectUtil.isEmpty(ttRoll)) return R.fail("不存在的roll房。"); + if(ttRoll.getRollStatus().equals("1")) return R.ok("已开奖,拒绝重复开奖"); + + // 用数据库乐观锁:将 rollStatus 从 0 改为 1(原子操作),防止并发重复开奖 + boolean locked = new LambdaUpdateChainWrapper<>(rollMapper) + .eq(TtRoll::getId, rollId) + .eq(TtRoll::getRollStatus, "0") + .set(TtRoll::getRollStatus, "1") + .update(); + if (!locked) { + return R.ok("已开奖,拒绝重复开奖"); + } + // 重新查询,final 变量供 lambda 使用 + final TtRoll finalRoll = new LambdaQueryChainWrapper<>(rollMapper) + .eq(TtRoll::getId, rollId) + .eq(TtRoll::getDelFlag, "0") + .one(); + ttRoll = finalRoll; + + List list = new LambdaQueryChainWrapper<>(rollUserService.getBaseMapper()) + .eq(TtRollUser::getRollId, rollId) + .list(); + if (ObjectUtil.isEmpty(list) || list.isEmpty()) { + ttRoll.setRollStatus(RollStatus.END.getCode()); + rollMapper.updateById(ttRoll); + return R.ok("roll房无人参与。"); + } + + // 2 分配系统指定,并返回剩余奖品 + List rollUsers = new LambdaQueryChainWrapper<>(ttRollUserMapper) + .eq(TtRollUser::getRollId, ttRoll.getId()) + .eq(TtRollUser::getGetPrizeWay, RollGetPrizeWay.SYS.getCode()) + .list(); + + log.info("所有参与人员" + rollUsers); + + Map surplusPrize = rollSurplusOrnaments(rollUsers, ttRoll); + + // 3 过滤出未分配奖品的用户 + LambdaQueryWrapper rollUserQuery = new LambdaQueryWrapper<>(); + rollUserQuery + .eq(TtRollUser::getRollId, rollId) + .eq(TtRollUser::getStatus, "0"); + if (!rollUsers.isEmpty()) { + List rollUserIds = rollUsers.stream() + .map(TtRollUser::getId) + .collect(Collectors.toList()); + rollUserQuery.notIn(TtRollUser::getId, rollUserIds); + } + List rollUserList = rollUserService.list(rollUserQuery); + + // 4 打乱用户顺序 + Collections.shuffle(rollUserList); + log.info("过滤系统指定以后" + rollUserList); + + // 5 分配奖品、 + List res = new ArrayList<>(); + + + // 一人拿一件 + boolean iGetIt = false; + for (TtRollUser rollUser : rollUserList) { + + // if (iGetIt) continue; + + Set> entries = surplusPrize.entrySet(); + + for (Map.Entry entry : entries) { + + Integer numb = entry.getValue().getOrnamentsNum(); + if (numb <= 0) continue; + + // 等级信息 + TtOrnamentsLevel level = ttOrnamentsLevelService.getById(entry.getValue().getOrnamentLevelId()); + + TtBoxRecords boxRecords = TtBoxRecords.builder() + .rollId(rollId) + .createTime(new Timestamp(System.currentTimeMillis())) + .holderUserId(rollUser.getUserId()) + .ornamentId(entry.getKey()) + .ornamentsPrice(entry.getValue().getPrice()) + .imageUrl(entry.getValue().getImgUrl()) + .ornamentName(entry.getValue().getOrnamentName()) + .ornamentLevelImg(level.getLevelImg()) + .source(TtboxRecordSource.ROLL.getCode()) + .userId(rollUser.getUserId()) + .updateTime(new Timestamp(System.currentTimeMillis())) + .status(IN_PACKSACK_ON.getCode()) + .build(); + res.add(boxRecords); + + log.info("{}获得{}", rollUser.getNickName(), entry.getValue().getOrnamentName()); + + // 扣减数量 + TtRollJackpotOrnaments value = entry.getValue(); + value.setOrnamentsNum(numb - 1); + surplusPrize.put(entry.getKey(), value); + + break; + + } + + } + + log.info("保存开奖结果:" + res); + + boxRecordsService.saveBatch(res); + + // ===== 新增:Roll房开奖通知(过滤机器人用户 userType=03)===== + TtRoll finalTtRoll = ttRoll; + AsyncManager.me().run(() -> sendRollResultNotice(res, finalTtRoll)); + // ===== 新增roll房开奖通知结束 ===== + + // 6 更新roll房状态 + new LambdaUpdateChainWrapper<>(rollMapper) + .eq(TtRoll::getId, ttRoll.getId()) + .set(TtRoll::getRollStatus, RollStatus.END.getCode()) + .set(TtRoll::getUpdateTime, new Date()) + .update(); + + System.out.println("server roll房开奖" + ttRoll.getId() + ttRoll.getRollName() + "成功"); + + return R.ok(); + + } + + + /** + * Roll房开奖结果通知(过滤机器人用户) + * 示例:【roll房名字】 张三的开奖结果:AK-47(120元)、蝴蝶刀(500元),共获得价值 620 元的饰品。 + */ + private void sendRollResultNotice(List res, TtRoll ttRoll) { + try { + if (res.isEmpty()) return; + // 按用户分组饰品 + Map> userItemsMap = res.stream() + .filter(r -> r.getHolderUserId() != null) + .collect(Collectors.groupingBy(TtBoxRecords::getHolderUserId)); + if (userItemsMap.isEmpty()) return; + + // 过滤机器人,只取真实用户 + List userIds = new ArrayList<>(userItemsMap.keySet()); + List realUsers = new LambdaQueryChainWrapper<>(userMapper) + .in(TtUser::getUserId, userIds) + .ne(TtUser::getUserType, UserType.ROBOT_USER.getCode()) + .list(); + + String rollName = ttRoll.getRollName(); + for (TtUser user : realUsers) { + List items = userItemsMap.get(user.getUserId()); + if (items == null || items.isEmpty()) continue; + // 拼接通知内容 + StringBuilder contentBuilder = new StringBuilder(); + contentBuilder.append("【").append(rollName).append("】 ") + .append(user.getNickName()).append("的开奖结果:"); + BigDecimal totalPrice = BigDecimal.ZERO; + for (TtBoxRecords item : items) { + contentBuilder.append(item.getOrnamentName()) + .append("(").append(item.getOrnamentsPrice()).append("元)、"); + if (item.getOrnamentsPrice() != null) { + totalPrice = totalPrice.add(item.getOrnamentsPrice()); + } + } + String contentStr = contentBuilder.toString(); + if (contentStr.endsWith("、")) { + contentStr = contentStr.substring(0, contentStr.length() - 1); + } + contentStr += ",共获得价值 " + totalPrice + " 元的饰品。"; + apiNoticeService.sendNotice(Long.valueOf(user.getUserId()), "Roll房开奖通知", contentStr); + } + } catch (Exception e) { + log.error("Roll房开奖通知推送失败,rollId={}, error={}", ttRoll.getId(), e.getMessage(), e); + } + } + + //{ornamentId:TtRollJackpotOrnaments} TtRollJackpotOrnaments中有数量 + public Map rollSurplusOrnaments(List rollUsers, TtRoll ttRoll) { + + // 奖池所有物品 + List jackpotOrnaments = new LambdaQueryChainWrapper<>(rollJackpotOrnamentsMapper) + .eq(TtRollJackpotOrnaments::getJackpotId, ttRoll.getJackpotId()) + .list(); + + HashMap AllPrizePool = new HashMap<>(); + for (TtRollJackpotOrnaments i : jackpotOrnaments) { + AllPrizePool.put(i.getOrnamentsId(), i); + } + + // 没有系统指定,直接返回奖池所有物品 + if (rollUsers.isEmpty()) return AllPrizePool; + + List rollUserIds = rollUsers.stream() + .map(TtRollUser::getId) + .collect(Collectors.toList()); + // 系统指定信息 + List RollUserPrizeVOs = ttRollUserPrizeMapper.byRollUserIds(rollUserIds); + // 没有系统指定,直接返回奖池所有物品 + if (RollUserPrizeVOs.isEmpty()) return AllPrizePool; + + // 分配系统指定 + List res = new ArrayList<>(); + for (RollUserPrizeVO userPrize : RollUserPrizeVOs) { + + // 分配奖品 + for (int i = 0; i < userPrize.getNumber(); i++) { + + TtBoxRecords boxRecords = TtBoxRecords.builder() + .rollId(ttRoll.getId()) + .createTime(new Timestamp(System.currentTimeMillis())) + .holderUserId(userPrize.getUserId()) + .ornamentsPrice(userPrize.getPrice()) + .imageUrl(userPrize.getImgUrl()) + .ornamentId(userPrize.getOrnamentId()) + .source(TtboxRecordSource.ROLL.getCode()) + .userId(userPrize.getUserId()) + .updateTime(new Timestamp(System.currentTimeMillis())) + .status(IN_PACKSACK_ON.getCode()) + .build(); + res.add(boxRecords); + + } + + // 在所有奖品中减去当前用户获得的奖品 + for (Long ornamentId : AllPrizePool.keySet()) { + if (ornamentId.equals(userPrize.getOrnamentId())) { + // 减去 + TtRollJackpotOrnaments ornaments = AllPrizePool.get(ornamentId); + int i = ornaments.getOrnamentsNum() - userPrize.getNumber(); + ornaments.setOrnamentsNum(Math.max(i, 0)); + AllPrizePool.put(ornamentId, ornaments); + break; + } + } + + } + + // 保存已分配 + boxRecordsService.saveBatch(res, 1); + + return AllPrizePool; + } + + @Override + public List getRollList(GetRollListParam param) { + + List rollList = apiRollMapper.getRollList(param); + + if (CollectionUtils.isEmpty(rollList)) { + return new ArrayList<>(); + } + // 对数据进行处理 + rollList.forEach(item -> { + // 补充奖池信息 + TtRoll roll = this.getById(item.getId()); + List prizeList = ttRollJackpotOrnamentsMapper.rollShow(roll.getJackpotId()); + item.setOrnamentsList(JSONUtil.toJsonStr(prizeList)); + if (CollectionUtils.isNotEmpty(prizeList)) { + Integer ornamentsNumberOfRoll = ttRollJackpotOrnamentsMapper.ornamentsNumberOfRoll(prizeList.get(0).getJackpotId()); + item.setOrnamentsNum(ornamentsNumberOfRoll); + } + + if (!StringUtils.isBlank(item.getRollPassword())) { + item.setHasPW(true); + item.setRollPassword(null); + } else { + item.setHasPW(false); + } + }); + + return rollList; + } + + @Override + public R getRollDetails(Integer rollId) { + + TtRoll ttRoll = getById(rollId); + if (ObjectUtil.isEmpty(ttRoll)) return R.fail("不存在的roll房。"); + + RollDetailsDataVO data = apiRollMapper.getRollDetails(rollId); + data.setHasPW(!StringUtils.isBlank(ttRoll.getRollPassword())); + + // 成员 + // List list = new LambdaQueryChainWrapper<>(ttRollUserMapper) + // .eq(TtRollUser::getRollId, rollId) + // .list(); + // List players = list.stream().map(item -> { + // SimpleRollUserVO playerVo = new SimpleRollUserVO(); + // BeanUtil.copyProperties(item, playerVo); + // playerVo.setUserName(null); + // return playerVo; + // }).collect(Collectors.toList()); + // data.setPlayerList(players); + + // 奖池 + List prizeList = ttRollJackpotOrnamentsMapper.rollShow(ttRoll.getJackpotId()); + data.setJackpotOrnamentsDataList(prizeList); + + // 获奖名单 + // List boxRecords = ttBoxRecordsMapper.rollOpenPrize(rollId); + // data.setOpenPrizeList(boxRecords); + return R.ok(data); + } + + @Override + public R> getRollPlayers(GetRollPlayersParam param) { + + param.setLimit((param.getPage() - 1) * param.getSize()); + + List collect = ttRollUserMapper.pageByRollId(param); + + return R.ok(collect); + } + + @Override + public R> getRollPrizePool(GetRollPrizePool param) { + + param.setLimit((param.getPage() - 1) * param.getSize()); + + TtRoll one = new LambdaQueryChainWrapper<>(ttRollMapper) + .eq(TtRoll::getId, param.getRollId()) + .eq(TtRoll::getDelFlag, 0) + .one(); + if (ObjectUtil.isNull(one)) return R.fail("不存在的roll房。"); + + param.setJackpotId(one.getJackpotId()); + List list = ttRollJackpotOrnamentsMapper.listByRollId(param); + + return R.ok(list); + } + + @Override + public R> getRollOpenPrize(GetRollOpenPrizeParam param) { + + param.setLimit((param.getPage() - 1) * param.getSize()); + + // 获奖名单 + List boxRecords = ttBoxRecordsMapper.rollOpenPrize(param); + return R.ok(boxRecords); + } + + private TtBoxRecords addBoxRecordsData(Integer rollId, Long ornamentsId, Integer userId, TtRollJackpotOrnaments rollJackpotOrnaments) { + TtOrnament ttOrnament = new LambdaQueryChainWrapper<>(ornamentsMapper).eq(TtOrnament::getId, ornamentsId).one(); + TtBoxRecords boxRecords = TtBoxRecords.builder().build(); + boxRecords.setUserId(userId); + boxRecords.setOrnamentId(ornamentsId); + boxRecords.setOrnamentsPrice(ttOrnament.getUsePrice()); + boxRecords.setOrnamentsLevelId(rollJackpotOrnaments.getOrnamentLevelId()); + boxRecords.setStatus(IN_PACKSACK_ON.getCode()); + boxRecords.setCreateTime(DateUtils.getNowDate()); + boxRecords.setSource(TtboxRecordSource.ROLL.getCode()); + boxRecords.setRollId(rollId); + boxRecords.setHolderUserId(userId); + return boxRecords; + } + + private TtRollJackpotOrnaments getRollJackpotOrnaments(Long ornamentsId, Map map, Integer jackpotId) { + TtRollJackpotOrnaments ttRollJackpotOrnaments = map.get(ornamentsId); + if (ttRollJackpotOrnaments == null) { + ttRollJackpotOrnaments = new LambdaQueryChainWrapper<>(rollJackpotOrnamentsMapper).eq(TtRollJackpotOrnaments::getJackpotId, jackpotId) + .eq(TtRollJackpotOrnaments::getOrnamentsId, ornamentsId).one(); + map.put(ornamentsId, ttRollJackpotOrnaments); + } + return ttRollJackpotOrnaments; + } + + private List getSurplusOrnamentsList(Integer rollId, Integer jackpotId) { + + // roll房奖池 + List ornamentsList = new LambdaQueryChainWrapper<>(rollJackpotOrnamentsMapper) + .eq(TtRollJackpotOrnaments::getJackpotId, jackpotId) + .list(); + + Map data = new HashMap<>(); + for (TtRollJackpotOrnaments ornaments : ornamentsList) { + data.put(ornaments.getOrnamentsId(), ornaments.getOrnamentsNum()); + } + + // 打乱奖池 + List surplusOrnamentsList = RandomUtils.toList(data); + + // 系统指定的获奖者 + List allocatedRollUserList = new LambdaQueryChainWrapper<>(rollUserService.getBaseMapper()) + .eq(TtRollUser::getRollId, rollId) + .eq(TtRollUser::getStatus, "2") + .list(); + List allocated = allocatedRollUserList.stream().map(TtRollUser::getOrnamentsId).collect(Collectors.toList()); + + for (Long i : allocated) { + Iterator iterator = surplusOrnamentsList.iterator(); + while (iterator.hasNext()) { + Long next = iterator.next(); + if (Objects.equals(next, i)) { + iterator.remove(); + break; + } + } + } + return surplusOrnamentsList; + } + + private BigDecimal getRechargeTotalAmount(Integer userId, Date rechargeStartTime) { + LambdaQueryChainWrapper wrapper = new LambdaQueryChainWrapper<>(rechargeRecordMapper).eq(TtRechargeRecord::getUserId, userId); + if (StringUtils.isNotNull(rechargeStartTime)) wrapper.gt(TtRechargeRecord::getCreateTime, rechargeStartTime); + return wrapper.list().stream().map(TtRechargeRecord::getAmountActuallyPaid).reduce(BigDecimal.ZERO, BigDecimal::add); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiUpgradeRecordServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiUpgradeRecordServiceImpl.java new file mode 100644 index 0000000..6872e03 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiUpgradeRecordServiceImpl.java @@ -0,0 +1,100 @@ +package com.ruoyi.playingmethod.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.upgrade.UpgradeCondition; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.*; +import com.ruoyi.playingmethod.mapper.ApiUpgradeRecordMapper; + +import com.ruoyi.playingmethod.service.ApiUpgradeRecordService; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + + +@Service +@Slf4j +public class ApiUpgradeRecordServiceImpl extends ServiceImpl implements ApiUpgradeRecordService { + + + @Autowired + TtUserService userService; + + @Override + public R historyDetail(UpgradeCondition param) { + + Page pageInfo = new Page<>(param.getPage(), param.getSize()); + pageInfo.setOptimizeCountSql(false); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + wrapper.eq(ObjectUtil.isNotNull(param.getUpgradeRecordId()),TtUpgradeRecord::getId,param.getUpgradeRecordId()); + + wrapper.eq(ObjectUtil.isNotNull(param.getOrnamentId()),TtUpgradeRecord::getTargetOrnamentId,param.getOrnamentId()); + + wrapper.eq(ObjectUtil.isNotNull(param.getUserType()),TtUpgradeRecord::getUserType,param.getUserType()); + + wrapper.eq(ObjectUtil.isNotNull(param.getUserId()),TtUpgradeRecord::getUserId,param.getUserId()); + wrapper.orderByDesc(TtUpgradeRecord::getOpenTime); + + pageInfo = this.page(pageInfo, wrapper); + + if (pageInfo.getRecords().size() == 0){ + return R.ok(pageInfo); + } + + Set userIds = pageInfo.getRecords().stream().map(TtUpgradeRecord::getUserId).collect(Collectors.toSet()); + + //��װ�û�ͷ�� + List list = userService.list(Wrappers.lambdaQuery(TtUser.class) + .in(TtUser::getUserId, userIds)); + //ͷ��map + Map avatarMap = list.stream().collect(Collectors.toMap(TtUser::getUserId, TtUser::getAvatar)); + pageInfo.getRecords().forEach(r->{ + if (r.getUserId() !=null){ + r.setUserAvatar(avatarMap.get(r.getUserId())); + } + }); + + + return R.ok(pageInfo); + + } + + @Override + public R tenTopDetail(Boolean isVictory) { + Page pageInfo = new Page<>(1, 10); + pageInfo.setOptimizeCountSql(false); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(isVictory != null, TtUpgradeRecord::getIsVictory, isVictory); + wrapper.orderByDesc(TtUpgradeRecord::getTargetOrnamentPrice); + pageInfo = this.page(pageInfo, wrapper); + if (pageInfo.getRecords().size() == 0){ + return R.ok(pageInfo); + } + Set userIds = pageInfo.getRecords().stream().map(TtUpgradeRecord::getUserId).collect(Collectors.toSet()); + List list = userService.list(Wrappers.lambdaQuery(TtUser.class) + .in(TtUser::getUserId, userIds)); + Map avatarMap = list.stream().collect(Collectors.toMap(TtUser::getUserId, TtUser::getAvatar)); + pageInfo.getRecords().forEach(r->{ + if (r.getUserId() !=null){ + r.setUserAvatar(avatarMap.get(r.getUserId())); + } + }); + return R.ok(pageInfo); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiUserBlendErcashServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiUserBlendErcashServiceImpl.java new file mode 100644 index 0000000..9f3bbb0 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiUserBlendErcashServiceImpl.java @@ -0,0 +1,18 @@ +package com.ruoyi.playingmethod.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.playingmethod.mapper.ApiTtUserBlendErcashMapper; + +import com.ruoyi.playingmethod.service.ApiUserBlendErcashService; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.stereotype.Service; + + +@Service +@Slf4j +public class ApiUserBlendErcashServiceImpl extends ServiceImpl implements ApiUserBlendErcashService { +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiWelfareMonthlyRechargesServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiWelfareMonthlyRechargesServiceImpl.java new file mode 100644 index 0000000..26f2087 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiWelfareMonthlyRechargesServiceImpl.java @@ -0,0 +1,147 @@ +package com.ruoyi.playingmethod.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.ruoyi.admin.mapper.TtWelfareMonthlyRechargesMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtBoxService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.util.core.fight.LotteryMachine; +import com.ruoyi.domain.common.constant.TtboxRecordSource; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtOrnamentsA; +import com.ruoyi.domain.other.TtWelfareMonthlyRecharges; +import com.ruoyi.domain.vo.OpenBoxVO; +import com.ruoyi.playingmethod.mapper.ApiBindBoxMapper; +import com.ruoyi.playingmethod.mapper.ApiWelfareMonthlyRechargesMapper; +import com.ruoyi.playingmethod.model.ApiWelfare; +import com.ruoyi.playingmethod.model.ApiWelfareRecord; +import com.ruoyi.playingmethod.model.vo.ApiWelfareMonthlyRechargesVO; +import com.ruoyi.playingmethod.service.ApiWelfareMonthlyRechargesService; +import com.ruoyi.playingmethod.utils.customException.OrnamentNullException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +@Service +public class ApiWelfareMonthlyRechargesServiceImpl implements ApiWelfareMonthlyRechargesService { + + @Autowired + private ApiWelfareMonthlyRechargesMapper apiWelfareMonthlyRechargesMapper; + + @Autowired + private TtUserService ttUserService; + + @Autowired + private TtBoxService ttBoxService; + + @Autowired + private LotteryMachine lotteryMachine; + + @Autowired + private ApiBindBoxMapper bindBoxMapper; + + @Autowired + private TtBoxRecordsService boxRecordsService; + + @Autowired + private TtWelfareMonthlyRechargesMapper ttWelfareMonthlyRechargesMapper; + + @Override + public List getWelfareList(Long userId) { + List apiWelfareMonthlyRechargesVOS = + apiWelfareMonthlyRechargesMapper.getWelfareList(userId); + for (ApiWelfareMonthlyRechargesVO apiWelfareMonthlyRechargesVO : apiWelfareMonthlyRechargesVOS) { + // 检查领取条件 + // BigDecimal rechargeRecordsOfMonth = apiWelfareMonthlyRechargesMapper.selectRechargeRecordsOfMonth(userId); + // if (rechargeRecordsOfMonth.compareTo(apiWelfareMonthlyRechargesVO.getAmount()) >= 0) { + // apiWelfareMonthlyRechargesVO.setEligible("1"); + // } else { + // apiWelfareMonthlyRechargesVO.setEligible("0"); + // } + if (checkEligible(apiWelfareMonthlyRechargesVO.getWelfareId(), userId)) + apiWelfareMonthlyRechargesVO.setEligible("1"); + else + apiWelfareMonthlyRechargesVO.setEligible("0"); + // 检查是否领取 + boolean isClaimed = apiWelfareMonthlyRechargesMapper.checkClaimed( + apiWelfareMonthlyRechargesVO.getWelfareId(), + userId, + "2"); + apiWelfareMonthlyRechargesVO.setClaimStatus(isClaimed ? "1" : "0"); + } + return apiWelfareMonthlyRechargesVOS; + } + + @Override + public OpenBoxVO claimWelfare(Integer welfareId, Long userId) { + // 1.获取福利对应的宝箱信息 + Integer boxId = apiWelfareMonthlyRechargesMapper.getBoxIdByWelfareId(welfareId); + TtUser ttUser = ttUserService.getById(userId); + TtBox ttBox = ttBoxService.getById(boxId); + // 2.抽奖 + String ornamentId = lotteryMachine.singleLottery(ttUser, ttBox); + if (Objects.isNull(ornamentId)) { + throw new OrnamentNullException("福利开箱未抽中"); + } + // 3.获取宝箱详细信息 + TtOrnamentsA ornamentsData = bindBoxMapper.ornamentsInfo(boxId, ornamentId); + if (Objects.isNull(ornamentsData)) { + throw new OrnamentNullException("福利开箱未抽中"); + } + // 4.构建并存储开箱记录 + TtBoxRecords ttBoxRecords = new TtBoxRecords(); + ttBoxRecords.setUserId(ttUser.getUserId()); + ttBoxRecords.setBoxId(ttBox.getBoxId()); + ttBoxRecords.setBoxName(ttBox.getBoxName()); + ttBoxRecords.setBoxPrice(ttBox.getPrice()); + ttBoxRecords.setOrnamentId(Long.valueOf(ornamentId)); + ttBoxRecords.setOrnamentName(ornamentsData.getName()); + ttBoxRecords.setImageUrl(ornamentsData.getImageUrl()); + ttBoxRecords.setOrnamentsPrice(ornamentsData.getUsePrice()); + ttBoxRecords.setOrnamentsLevelId(ornamentsData.getOrnamentsLevelId()); + ttBoxRecords.setOrnamentLevelImg(ornamentsData.getLevelImg()); + ttBoxRecords.setHolderUserId(ttUser.getUserId()); + ttBoxRecords.setSource(TtboxRecordSource.BLIND_BOX.getCode()); + ttBoxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode()); + ttBoxRecords.setCreateTime(new Timestamp(System.currentTimeMillis())); + boxRecordsService.save(ttBoxRecords); + // 5.存储领取记录 + ApiWelfareRecord apiWelfareRecord = new ApiWelfareRecord(); + apiWelfareRecord.setWelfareId(welfareId); + apiWelfareRecord.setUserId(userId); + apiWelfareRecord.setType("2"); + apiWelfareRecord.setCreateTime(new Date()); + apiWelfareMonthlyRechargesMapper.saveClaimWelfareRecord(apiWelfareRecord); + // 6.返回前端数据 + OpenBoxVO openBoxVO = new OpenBoxVO(); + BeanUtil.copyProperties(ttBoxRecords, openBoxVO); + openBoxVO.setUsePrice(openBoxVO.getOrnamentsPrice()); + openBoxVO.setLevelImg(ttBoxRecords.getOrnamentLevelImg()); + return openBoxVO; + } + + @Override + public boolean checkEligible(Integer welfareId, Long userId) { + BigDecimal rechargeRecordsOfMonth = apiWelfareMonthlyRechargesMapper.selectRechargeRecordsOfMonth(userId); + if (Objects.isNull(rechargeRecordsOfMonth)) + return false; + TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges = ttWelfareMonthlyRechargesMapper.selectTtWelfareMonthlyRechargesById(welfareId); + TtUser ttUser = ttUserService.getById(userId); + boolean isRechargeAmountSufficient = rechargeRecordsOfMonth.compareTo(ttWelfareMonthlyRecharges.getAmount()) >= 0; + boolean isVipLevelSufficient = ttUser.getVipLevel() >= ttWelfareMonthlyRecharges.getVipLevel(); + return isRechargeAmountSufficient && isVipLevelSufficient; + } + + @Override + public boolean checkClaimed(Integer welfareId, Long userId) { + return apiWelfareMonthlyRechargesMapper.checkClaimed(welfareId, userId, "2"); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiWelfareServiceImpl.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiWelfareServiceImpl.java new file mode 100644 index 0000000..bb99ea7 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/service/impl/ApiWelfareServiceImpl.java @@ -0,0 +1,141 @@ +package com.ruoyi.playingmethod.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.ruoyi.admin.mapper.TtWelfareMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtBoxService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.util.core.fight.LotteryMachine; +import com.ruoyi.domain.common.constant.TtboxRecordSource; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.other.TtOrnamentsA; +import com.ruoyi.domain.other.TtWelfare; +import com.ruoyi.domain.vo.OpenBoxVO; +import com.ruoyi.playingmethod.mapper.ApiBindBoxMapper; +import com.ruoyi.playingmethod.mapper.ApiWelfareMapper; +import com.ruoyi.playingmethod.model.ApiWelfare; +import com.ruoyi.playingmethod.model.ApiWelfareRecord; +import com.ruoyi.playingmethod.service.ApiWelfareService; +import com.ruoyi.playingmethod.utils.customException.OrnamentNullException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +@Service +public class ApiWelfareServiceImpl implements ApiWelfareService { + + @Autowired + private ApiWelfareMapper apiWelfareMapper; + + @Autowired + private TtUserService ttUserService; + + @Autowired + private TtBoxService ttBoxService; + + @Autowired + private LotteryMachine lotteryMachine; + + @Autowired + private ApiBindBoxMapper bindBoxMapper; + + @Autowired + private TtBoxRecordsService boxRecordsService; + + @Autowired + private TtWelfareMapper ttWelfareMapper; + + @Override + public List getWelfareList(Long userId) { + List welfareList = apiWelfareMapper.getWelfareList(userId); + for (ApiWelfare apiWelfare : welfareList) { + if ("1".equals(apiWelfare.getType())) { + boolean isEligible = checkVipUpgradeEligibility(userId, apiWelfare.getVipLevel()); + apiWelfare.setEligible(isEligible ? "1" : "0"); + boolean isClaimed = apiWelfareMapper.checkClaimed(apiWelfare.getWelfareId(), userId, "1"); + apiWelfare.setClaimStatus(isClaimed ? "1" : "0"); + } + } + return welfareList; + } + + @Transactional + @Override + public OpenBoxVO claimWelfare(Integer welfareId, Long userId) { + // 1.获取福利对应的宝箱信息 + Integer boxId = apiWelfareMapper.getBoxIdByWelfareId(welfareId); + TtUser ttUser = ttUserService.getById(userId); + TtBox ttBox = ttBoxService.getById(boxId); + // 2.抽奖 + String ornamentId = lotteryMachine.singleLottery(ttUser, ttBox); + if (Objects.isNull(ornamentId)) { + throw new OrnamentNullException("福利开箱未抽中"); + } + // 3.获取宝箱详细信息 + TtOrnamentsA ornamentsData = bindBoxMapper.ornamentsInfo(boxId, ornamentId); + if (Objects.isNull(ornamentsData)) { + throw new OrnamentNullException("福利开箱未抽中"); + } + // 4.构建并存储开箱记录 + TtBoxRecords ttBoxRecords = new TtBoxRecords(); + ttBoxRecords.setUserId(ttUser.getUserId()); + ttBoxRecords.setBoxId(ttBox.getBoxId()); + ttBoxRecords.setBoxName(ttBox.getBoxName()); + ttBoxRecords.setBoxPrice(ttBox.getPrice()); + ttBoxRecords.setOrnamentId(Long.valueOf(ornamentId)); + ttBoxRecords.setOrnamentName(ornamentsData.getName()); + ttBoxRecords.setImageUrl(ornamentsData.getImageUrl()); + ttBoxRecords.setOrnamentsPrice(ornamentsData.getUsePrice()); + ttBoxRecords.setOrnamentsLevelId(ornamentsData.getOrnamentsLevelId()); + ttBoxRecords.setOrnamentLevelImg(ornamentsData.getLevelImg()); + ttBoxRecords.setHolderUserId(ttUser.getUserId()); + ttBoxRecords.setSource(TtboxRecordSource.BLIND_BOX.getCode()); + ttBoxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode()); + ttBoxRecords.setCreateTime(new Timestamp(System.currentTimeMillis())); + boxRecordsService.save(ttBoxRecords); + // 5.存储领取记录 + ApiWelfareRecord apiWelfareRecord = new ApiWelfareRecord(); + apiWelfareRecord.setWelfareId(welfareId); + apiWelfareRecord.setUserId(userId); + apiWelfareRecord.setType("1"); + apiWelfareRecord.setCreateTime(new Date()); + apiWelfareMapper.saveClaimWelfareRecord(apiWelfareRecord); + // 6.返回前端数据 + OpenBoxVO openBoxVO = new OpenBoxVO(); + BeanUtil.copyProperties(ttBoxRecords, openBoxVO); + openBoxVO.setUsePrice(openBoxVO.getOrnamentsPrice()); + openBoxVO.setLevelImg(ttBoxRecords.getOrnamentLevelImg()); + return openBoxVO; + } + + @Override + public boolean checkNotEligible(Integer welfareId, Long userId) { + TtWelfare ttWelfare = ttWelfareMapper.selectTtWelfareByWelfareId(welfareId); + if ("1".equals(ttWelfare.getType())) { + return !checkVipUpgradeEligibility(userId, ttWelfare.getVipLevel()); + } + return false; + } + + @Override + public boolean checkClaimed(Integer welfareId, Long userId) { + return apiWelfareMapper.checkClaimed(welfareId, userId, "1"); + } + + /** + * 检查是否具备VIP等级福利领取条件 + */ + private boolean checkVipUpgradeEligibility(Long userId, Integer vipLevel) { + TtUser ttUser = ttUserService.getById(userId); + return ttUser.getVipLevel() >= vipLevel; + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/utils/Constant/OPPS.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/utils/Constant/OPPS.java new file mode 100644 index 0000000..9f907e6 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/utils/Constant/OPPS.java @@ -0,0 +1,10 @@ +package com.ruoyi.playingmethod.utils.Constant; + +public enum OPPS { + // 展示爆率 + OPPS, + // 真实爆率 + REAL_OPPS, + // 主播爆率 + ANCHOR_ODDS; +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/utils/Constant/RedisLockKey.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/utils/Constant/RedisLockKey.java new file mode 100644 index 0000000..af9fc5b --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/utils/Constant/RedisLockKey.java @@ -0,0 +1,4 @@ +package com.ruoyi.playingmethod.utils.Constant; + +public enum RedisLockKey { +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/utils/customException/OrnamentNullException.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/utils/customException/OrnamentNullException.java new file mode 100644 index 0000000..9a073c1 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/utils/customException/OrnamentNullException.java @@ -0,0 +1,7 @@ +package com.ruoyi.playingmethod.utils.customException; + +public class OrnamentNullException extends RuntimeException { + public OrnamentNullException(String msg){ + super(msg); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WebSocketUsers.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WebSocketUsers.java new file mode 100644 index 0000000..68323c1 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WebSocketUsers.java @@ -0,0 +1,96 @@ +package com.ruoyi.playingmethod.websocket; + +import com.ruoyi.playingmethod.websocket.entity.SessionData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class WebSocketUsers { + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUsers.class); + + private static Map USERS = new ConcurrentHashMap(); + + public static void put(String key, SessionData sessionData) { + USERS.put(key, sessionData); + } + + public static boolean remove(SessionData sessionData) { + String key = null; + boolean flag = USERS.containsValue(sessionData); + if (flag) { + Set> entries = USERS.entrySet(); + for (Map.Entry entry : entries) { + SessionData value = entry.getValue(); + if (value.getSession().equals(sessionData.getSession())) { + key = entry.getKey(); + break; + } + } + } else { + return true; + } + return remove(key); + } + + public static boolean remove(String key) { + LOGGER.info("\n 正在移出用户 - {}", key); + SessionData remove = USERS.remove(key); + if (remove.getSession() != null) { + boolean containsValue = USERS.containsValue(remove); + LOGGER.info("\n 移出结果 - {}", containsValue ? "失败" : "成功"); + LOGGER.info("\n 当前人数 - {}", WebSocketUsers.getUsers().size()); + return containsValue; + } else { + return true; + } + } + + public static Map getUsers() { + return USERS; + } + + public static void sendMessageToUsersByText(String message) { + Collection values = USERS.values(); + for (SessionData value : values) { + try { + value.getSession().getBasicRemote().sendText(message); + } catch (IOException e) { + //LOGGER.error("\n[发送消息异常]", e); + LOGGER.error("\n[发送消息异常]"); + } + } + } + + public static void sendMessageToUserByText(Integer userId, String message) { + Collection values = USERS.values(); + if (userId == 0) { + for (SessionData value : values) { + if (value.getSession() != null && Objects.equals(value.getUserId(), userId)) { + try { + value.getSession().getBasicRemote().sendText(message); + } catch (IOException e) { + LOGGER.error("\n[发送消息异常]", e); + } + } + } + } else { + for (SessionData value : values) { + if (value.getSession() != null && Objects.equals(value.getUserId(), userId)) { + try { + value.getSession().getBasicRemote().sendText(message); + return; + } catch (IOException e) { + LOGGER.error("\n[发送消息异常]", e); + return; + } + } + } + } + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WsBindBox.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WsBindBox.java new file mode 100644 index 0000000..f831366 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WsBindBox.java @@ -0,0 +1,233 @@ +package com.ruoyi.playingmethod.websocket; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.TtBoxService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.common.constant.TtboxRecordSource; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.common.constant.sys.UserStatus; +import com.ruoyi.domain.dto.boxRecords.queryCondition; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO; +import com.ruoyi.playingmethod.service.ApiBoxRecordsService; +import com.ruoyi.playingmethod.websocket.constant.SMsgKey; +import com.ruoyi.playingmethod.websocket.util.WsResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import jakarta.websocket.*; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 绑定箱子 + */ +@Slf4j +@Component +@ServerEndpoint("/ws/bindBox/{boxId}/{userId}") +public class WsBindBox { + + // 用来记录当前连接数的变量 + private static volatile int onlineCount = 0; + // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象 + private static ConcurrentHashMap allRoomUserMap = new ConcurrentHashMap<>(); + // 与某个客户端的连接会话,需要通过它来与客户端进行数据收发 + private Session session; + private static ApiBoxRecordsService apiBoxRecordsService; + private static TtBoxService boxService; + private static TtUserService userService; + private static TtUserMapper userMapper; + private Integer userId = null; + private Integer boxId = null; + private String key = ""; + + @Autowired + public void ttFightService(ApiBoxRecordsService apiBoxRecordsService){ + WsBindBox.apiBoxRecordsService = apiBoxRecordsService; + } + + @Autowired + public void ttFightService(TtBoxService boxService){ + WsBindBox.boxService = boxService; + } + + @Autowired + public void ttFightService(TtUserService userService){ + WsBindBox.userService = userService; + } + + @Autowired + public void ttFightService(TtUserMapper userMapper){ + WsBindBox.userMapper = userMapper; + } + + @OnOpen + public void onOpen(Session session, + @PathParam("boxId") Integer boxId, + @PathParam("userId") Integer userId) throws IOException { + // 更新连接池 + addBoxRoomUser(userId, boxId, session); + + // 用户信息 + TtUser player = new LambdaQueryChainWrapper<>(userMapper) + .eq(TtUser::getUserId, userId) + .eq(TtUser::getStatus, UserStatus.NORMAL.getCode()) + .eq(TtUser::getDelFlag, 0) + .one(); + // 检查连接 + R check = connectCheck(player, boxId); + if (!check.getCode().equals(200)) { + session.getBasicRemote().sendText(check.getMsg()); + session.close(); + return; + } + + log.debug("/ws/bindBox > > onOpen"); + log.info("用户{}进入盲盒游戏房间,在线人数{}", userId, WsBindBox.onlineCount); + sendMessage("用户" + userId + "进入盲盒游戏房间,在线人数" + WsBindBox.onlineCount); + + // 首次连接获取最新的一组盲盒开箱数据 + List sources = Arrays.asList(TtboxRecordSource.BLIND_BOX.getCode()); + List status = Arrays.asList( + TtboxRecordStatus.IN_PACKSACK_ON.getCode(), + TtboxRecordStatus.DELIVERY_YET.getCode(), + TtboxRecordStatus.APPLY_DELIVERY.getCode(), + TtboxRecordStatus.RESOLVE.getCode()); + queryCondition param = queryCondition.builder() + .boxId(boxId) + // .userType(player.getUserType()) + .source(sources) + .status(status) + .orderByFie(0) + .page(1) + .size(10) + .build(); + List ttBoxRecordsVOS = apiBoxRecordsService.byCondition(param); + + sendMessage(WsResult.ok(SMsgKey.Blind_Box_Init_Data.name(),ttBoxRecordsVOS,"初始化历史开箱记录")); + } + + @OnClose + public void onClose(Session session) { + removeRoomUser(); + log.info("关闭连接,正常在线人数:" + WsBindBox.onlineCount); + } + + @OnMessage + public void onMessage(String message, Session session) { + log.info("收到消息"); + } + + @OnError + public void onError(Session session, Throwable exception) throws Exception { + log.info("出现错误"); + } + + /** + * 连接检查 + */ + private R connectCheck(TtUser player, Integer boxId) { + if (ObjectUtil.isNotEmpty(WsFightRoom.allRoomUserMap.get(player.getUserId() + "_" + boxId))) { + R.fail("用户" + player.getUserId() + "已经连接宝箱房间" + boxId + "。请勿重复调用。"); + } + TtBox box = boxService.getById(boxId); + if (ObjectUtil.isEmpty(box)) { + return R.fail("不存在的宝箱,id:" + boxId); + } + return R.ok(); + } + + /** + * 更新在线人数 + */ + private int addOnlineCount(int number) { + synchronized (this){ + WsBindBox.onlineCount += number; + return WsBindBox.onlineCount; + } + } + + /** + * 全局广播 + */ + public static void broadcast(Object message) { + + // ObjectMapper objectMapper = new ObjectMapper(); + String msg = JSON.toJSONString(message); + + Collection wss = WsBindBox.allRoomUserMap.values(); + for (WsBindBox ws : wss){ + try { + ws.session.getBasicRemote().sendText(msg); + } catch (IOException e) { + log.warn("WS大厅广播消息异常。错误信息:{}", msg); + } + } + } + + /** + * 房间广播 + */ + public static void broadcastToBoxRoom(Integer boxId, Object message) { + + String msg = JSON.toJSONString(message); + + Collection wslist = WsBindBox.allRoomUserMap.values(); + + for (WsBindBox ws : wslist) { + try { + if (!ws.boxId.equals(boxId)) continue; + // log.info(String.valueOf(ws.session.isOpen())); + if (!ws.session.isOpen()) continue; + RemoteEndpoint.Basic basicRemote = ws.session.getBasicRemote(); + basicRemote.sendText(msg); + } catch (IOException e) { + log.warn("WS推送广播给{}_{}消息异常。", ws.userId, ws.boxId); + } + } + } + + /** + * 实现服务器主动推送 + */ + public void sendMessage(Object message) { + try { + this.session.getBasicRemote().sendText(JSON.toJSONString(message)); + } catch (IOException e) { + log.warn("服务器推送消息异常。"); + } + } + + /** + * 添加连接用户 + */ + private WsBindBox addBoxRoomUser(Integer userId, Integer boxId, Session session) { + addOnlineCount(1); + this.userId = userId; + this.boxId = boxId; + this.key = userId + "_" + boxId; + this.session = session; + WsBindBox.allRoomUserMap.put(key, this); + return this; + } + + /** + * 移除连接用户 + */ + private WsBindBox removeRoomUser(){ + addOnlineCount(-1); + WsBindBox.allRoomUserMap.remove(this.key); + return this; + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WsFightHall.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WsFightHall.java new file mode 100644 index 0000000..f8de942 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WsFightHall.java @@ -0,0 +1,290 @@ +package com.ruoyi.playingmethod.websocket; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.admin.mapper.TtBoxMapper; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.vo.fight.FightBoxVO; +import com.ruoyi.playingmethod.service.ApiFightService; +import com.ruoyi.playingmethod.websocket.constant.SMsgKey; +import com.ruoyi.playingmethod.websocket.util.WsResult; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import jakarta.websocket.OnClose; +import jakarta.websocket.OnError; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 对战大厅 + */ +@Slf4j +@Component +@ServerEndpoint("/ws/fight/hall/{userId}") +public class WsFightHall { + + // 用来记录当前连接数的变量 + private static int onlineCount = 0; + + public static synchronized void increaseOnlineCount() { + onlineCount++; + } + + public static synchronized void decreaseOnlineCount() { + if (onlineCount > 0) { + onlineCount--; + } + } + + // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象 + private static ConcurrentHashMap allHallUserMap = new ConcurrentHashMap<>(); + // 与某个客户端的连接会话,需要通过它来与客户端进行数据收发 + private Session session; + private static ApiFightService apiFightService; + private String userId = ""; + + @Autowired + public void ttFightService(ApiFightService apiFightService) { + WsFightHall.apiFightService = apiFightService; + } + + private static TtBoxMapper ttBoxMapper; + + @Autowired + public void ttBoxMapper(TtBoxMapper ttBoxMapper) { + WsFightHall.ttBoxMapper = ttBoxMapper; + } + + @OnOpen + public void onOpen(Session session, @PathParam("userId") String userId) throws IOException { + // 获取请求参数 + Map> parameters = session.getRequestParameterMap(); + int pageNum = 1; + int pageSize = 10; + String status = null; + String model = null; + BigDecimal boxPriceTotalMin = null; + BigDecimal boxPriceTotalMax = null; + List pageNums = parameters.get("pageNum"); + if (!Objects.isNull(pageNums) && !pageNums.isEmpty()) { + pageNum = Integer.parseInt(pageNums.get(0)); + } + List pageSizes = parameters.get("pageSize"); + if (!Objects.isNull(pageSizes) && !pageSizes.isEmpty()) { + pageSize = Integer.parseInt(pageSizes.get(0)); + pageSize = Math.min(pageSize, 10); + } + List statuses = parameters.get("status"); + if (!Objects.isNull(statuses) && !statuses.isEmpty()) { + status = statuses.get(0); + } + List models = parameters.get("model"); + if (!Objects.isNull(models) && !models.isEmpty()) { + model = models.get(0); + } + List boxPriceTotalMines = parameters.get("boxPriceTotalMin"); + if (!Objects.isNull(boxPriceTotalMines) && !boxPriceTotalMines.isEmpty()) { + boxPriceTotalMin = new BigDecimal(boxPriceTotalMines.get(0)); + } + List boxPriceTotalMaxes = parameters.get("boxPriceTotalMax"); + if (!Objects.isNull(boxPriceTotalMaxes) && !boxPriceTotalMaxes.isEmpty()) { + boxPriceTotalMax = new BigDecimal(boxPriceTotalMaxes.get(0)); + } + + if (ObjectUtil.isNotEmpty(WsFightHall.allHallUserMap.get(userId))) { + session.getBasicRemote().sendText("用户" + userId + "已连接,请勿重复调用"); + session.close(); + return; + } + addHallUser(userId, session); + + + Page pageInfo = new Page<>(pageNum, pageSize); + pageInfo.setOptimizeCountSql(false); + + LambdaQueryWrapper fightQuery = new LambdaQueryWrapper<>(); + fightQuery + .eq(status != null, TtFight::getStatus, status) + .eq(model != null, TtFight::getModel, model) + .between(boxPriceTotalMin != null && boxPriceTotalMax != null, + TtFight::getBoxPriceTotal, boxPriceTotalMin, boxPriceTotalMax) + .ge(boxPriceTotalMin == null && boxPriceTotalMax != null, + TtFight::getBoxPriceTotal, boxPriceTotalMax) + .orderByDesc(TtFight::getCreateTime); + List fightList = apiFightService.page(pageInfo, fightQuery).getRecords(); + if (CollectionUtils.isEmpty(fightList)) { + Map map = new HashMap<>(); + map.put("rows", new ArrayList<>()); + map.put("total", 0); + sendMessage(WsResult.ok(SMsgKey.ALL_FIGHT_ROOM.name(), map, "所有对战房间")); + } else { + List boxIds = new ArrayList<>(); + for (TtFight fight : fightList) { + if (fight == null || MapUtils.isEmpty(fight.getBoxData())) { + continue; + } + for (Map.Entry entry : fight.getBoxData().entrySet()) { + Object value = entry.getValue(); + if (value == null) { + continue; + } + Integer boxId; + if (value instanceof LinkedHashMap) { + Map map = (LinkedHashMap) value; + boxId = map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null; + } else { + boxId = ((FightBoxVO) value).getBoxId(); + } + if (boxId == null) { + continue; + } + boxIds.add(boxId); + } + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TtBox::getBoxId, boxIds); + List boxes = ttBoxMapper.selectList(queryWrapper); + Map boxMap = boxes.stream().filter(Objects::nonNull).collect(Collectors.toMap(TtBox::getBoxId, TtBox::getPrice)); + for (TtFight fight : fightList) { + if (fight == null || MapUtils.isEmpty(fight.getBoxData())) { + continue; + } + for (Map.Entry entry : fight.getBoxData().entrySet()) { + Object value = entry.getValue(); + if (value == null) { + continue; + } + FightBoxVO vo; + Integer boxId; + if (value instanceof LinkedHashMap) { + Map map = (LinkedHashMap) value; + vo = new FightBoxVO(map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null, + map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null, + map.get("boxImg01") != null ? map.get("boxImg01").toString() : null, + map.get("boxImg02") != null ? map.get("boxImg02").toString() : null); + vo.setPrice(boxMap.getOrDefault(vo.getBoxId(), new BigDecimal(0))); + } else { + vo = (FightBoxVO) value; + vo.setPrice(boxMap.getOrDefault(vo.getBoxId(), new BigDecimal(0))); + } + fight.getBoxData().put(vo.getBoxId().toString(), vo); + } + } + Map map = new HashMap<>(); + map.put("rows", fightList); + map.put("total", apiFightService.page(pageInfo, fightQuery).getTotal()); + + sendMessage(WsResult.ok(SMsgKey.ALL_FIGHT_ROOM.name(), map, "所有对战房间")); + } + + + log.info("用户{}进入对战游戏大厅,广播{}个房间,在线人数{}", userId, fightList.size(), WsFightHall.onlineCount); + } + + @OnClose + public void onClose(Session session) { + // removeHallUser(); + removeHallUser(session.getPathParameters().get("userId")); + log.info("关闭连接,正常在线人数:" + WsFightHall.onlineCount); + } + + @OnMessage + public void onMessage(String message, Session session) { + log.info("收到消息"); + } + + @OnError + public void onError(Session session, Throwable exception) throws Exception { + log.error("出现错误", exception); + } + + /** + * 更新在线人数 + */ + private int addOnlineCount(int number) { + synchronized (new Object()) { + WsFightHall.onlineCount += number; + return WsFightHall.onlineCount; + } + } + + /** + * 全局广播 + */ + public static void broadcast(Object message) { + + // ObjectMapper objectMapper = new ObjectMapper(); + String msg = JSON.toJSONString(message); + + Collection wss = WsFightHall.allHallUserMap.values(); + + for (WsFightHall ws : wss) { + try { + ws.session.getBasicRemote().sendText(msg); + } catch (IOException e) { + log.warn("ws大厅广播给消息异常。msg:{}", msg); + } + } + } + + /** + * 实现服务器主动推送 + */ + public void sendMessage(Object message) { + try { + this.session.getBasicRemote().sendText(JSON.toJSONString(message)); + } catch (IOException e) { + log.warn("服务器推送消息异常"); + } + } + + /** + * 添加连接用户 + */ + private WsFightHall addHallUser(String userId, Session session) { + addOnlineCount(1); + this.userId = userId; + this.session = session; + WsFightHall.allHallUserMap.put(userId, this); + return this; + } + + /** + * 移除连接用户 + */ + private WsFightHall removeHallUser() { + addOnlineCount(-1); + WsFightHall.allHallUserMap.remove(userId); + return this; + } + + /** + * 移除连接用户 + */ + private WsFightHall removeHallUser(String userId) { + addOnlineCount(-1); + WsFightHall.allHallUserMap.remove(userId); + return this; + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WsFightRoom.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WsFightRoom.java new file mode 100644 index 0000000..7da2d71 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WsFightRoom.java @@ -0,0 +1,406 @@ +package com.ruoyi.playingmethod.websocket; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ruoyi.admin.mapper.TtBoxMapper; +import com.ruoyi.admin.mapper.TtBoxOrnamentsMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.fight.FightSeat; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.domain.other.FightBoutData; +import com.ruoyi.domain.other.TtBox; +import com.ruoyi.domain.vo.TtBoxOrnamentsDataVO; +import com.ruoyi.domain.vo.fight.FightBoxVO; +import com.ruoyi.domain.vo.fight.FightResultVO; +import com.ruoyi.playingmethod.service.ApiFightService; +import com.ruoyi.playingmethod.service.ApiFightUserService; +import com.ruoyi.playingmethod.websocket.constant.SMsgKey; +import com.ruoyi.playingmethod.websocket.util.WsResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import jakarta.websocket.*; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * 对战房间 + */ +@Slf4j +@Component +@ServerEndpoint("/ws/fight/room/{userId}/{fightId}") +public class WsFightRoom { + + static Map sessionMap = new ConcurrentHashMap<>(); + + static volatile AtomicInteger viewersCount = new AtomicInteger(0); + + // 用来记录当前连接数的变量 + private static volatile int onlineCount = 0; + // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象 + public static ConcurrentHashMap allRoomUserMap = new ConcurrentHashMap<>(); + // 与某个客户端的连接会话,需要通过它来与客户端进行数据收发 + private Session session; + private static ApiFightService apiFightService; + private static TtBoxRecordsService boxRecordsService; + private static TtBoxOrnamentsMapper boxOrnamentsMapper; + // private static TtBoxOrnamentsService ttBoxOrnamentsService; + private static ApiFightUserService apiFightUserService; + private static Integer fightRoundTime = null; // 战斗回合时间 + private Integer userId = null; + private Integer fightId = null; + private String key = ""; + private static RedisCache redisCache; + + private static TtBoxMapper ttBoxMapper; + + @Value("${mkcsgo.fight.roundTime}") + public void ttFightService(Integer fightRoundTime) { + WsFightRoom.fightRoundTime = fightRoundTime; + } + + @Autowired + public void ttFightService(ApiFightService apiFightService) { + WsFightRoom.apiFightService = apiFightService; + } + + @Autowired + public void boxOrnamentsMapper(TtBoxOrnamentsMapper boxOrnamentsMapper) { + WsFightRoom.boxOrnamentsMapper = boxOrnamentsMapper; + } + + @Autowired + public void boxRecordsService(TtBoxRecordsService boxRecordsService) { + WsFightRoom.boxRecordsService = boxRecordsService; + } + + public void apiFightUserService(ApiFightUserService apiFightUserService) { + WsFightRoom.apiFightUserService = apiFightUserService; + } + + @Autowired + public void ttBoxMapper(TtBoxMapper ttBoxMapper) { + WsFightRoom.ttBoxMapper = ttBoxMapper; + } + + @Autowired + public void redisCache(RedisCache redisCache) { + WsFightRoom.redisCache = redisCache; + } + + @OnOpen + public void onOpen(Session session, + @PathParam("userId") Integer userId, + @PathParam("fightId") Integer fightId) { + try { + addFightRoomUser(userId, fightId, session); + + R check = connectCheck(userId, fightId); + if (!check.getCode().equals(200)) { + session.getBasicRemote().sendText(check.getMsg()); + session.close(); + return; + } + + log.debug("/ws/fight/room > > onOpen"); + log.info("用户{}进入房间{}," + "在线人数{}", userId, fightId, WsFightRoom.onlineCount); + sendMsgToPlayers("用户:" + userId + "进入房间," + fightId + "在线人数" + WsFightRoom.onlineCount, null); + + // 首次连接获取房间最新数据 + LambdaQueryWrapper fightQuery = new LambdaQueryWrapper<>(); + fightQuery + .eq(TtFight::getId, fightId); + // .eq(TtFight::getStatus,0); + TtFight fight = apiFightService.getOne(fightQuery); + + // 构建结果集 + // 根据宝箱id查询关联的所有饰品 + Map boxData = fight.getBoxData(); + + ArrayList fightBoxVOList = new ArrayList<>(); + boxData.keySet().forEach(boxId -> { + List boxOrnamentsVOS = boxOrnamentsMapper.selectTtBoxOrnamentsList(Integer.valueOf(boxId)); + FightBoxVO fightBoxVO = JSONUtil.toBean(JSONUtil.toJsonStr(boxData.get(boxId)), FightBoxVO.class); + fightBoxVO.setOrnaments(boxOrnamentsVOS); + boxData.put(boxId,fightBoxVO); + fightBoxVOList.add(boxData.get(boxId)); + }); + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TtBox::getBoxId, boxData.keySet()); + List boxes = ttBoxMapper.selectList(queryWrapper); + var boxMapObj = boxes.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(TtBox::getBoxId)); + Map boxMap = boxes.stream().filter(Objects::nonNull).collect(Collectors.toMap(TtBox::getBoxId, TtBox::getPrice)); + + for (FightBoxVO vo : fightBoxVOList) { + vo.setPrice(boxMap.getOrDefault(vo.getBoxId(), new BigDecimal(0))); + if (boxMapObj.get(vo.getBoxId()) != null) { + vo.setBoxName(boxMapObj.get(vo.getBoxId()).getFirst().getBoxName()); + } + } + + fight.getBoxData().keySet().forEach(boxId -> { + Object value = fight.getBoxData().get(boxId); + FightBoxVO a = null; + if (value instanceof LinkedHashMap) { + Map map = (LinkedHashMap) value; + a = new FightBoxVO(map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null, + map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null, + map.get("boxImg01") != null ? map.get("boxImg01").toString() : null, + map.get("boxImg02") != null ? map.get("boxImg02").toString() : null); + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } else { + a = (FightBoxVO) value; + a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO)); + } + fight.getBoxData().put(boxId, a); + }); + + // 如果游戏结束,发送结果集数据 + if (fight.getStatus().equals(2) || fight.getStatus().equals(3)) { + + // 所有对战结果记录 + LambdaQueryWrapper boxRecordsQuery = new LambdaQueryWrapper<>(); + boxRecordsQuery + .eq(TtBoxRecords::getFightId, fightId); + List allBoxRecords = boxRecordsService.list(boxRecordsQuery); + + FightResultVO resultVO = FightResultVO.builder() + .currentRound(-1L) + .fight(fight) + .winnerIds(fight.getWinnerIds()) + .fightResult(allBoxRecords) + .fightBoxVOList(fightBoxVOList) + .build(); + + broadcastFight(fightId, WsResult.ok(SMsgKey.FIGHT_RESULT.name(), resultVO, "对局已结束,对局最新信息")); + log.info("room onOpen 广播数据成功。"); + + } else if (fight.getStatus().equals(1)) { + + // 所有对战结果记录 + LambdaQueryWrapper boxRecordsQuery = new LambdaQueryWrapper<>(); + boxRecordsQuery + .eq(TtBoxRecords::getFightId, fightId) + .eq(TtBoxRecords::getStatus, 0); + // 首先从Redis中查询对战结果,没有则在从数据库中查询 + List allBoxRecords; + String key = "fight_result:fight_" + fightId; + allBoxRecords = redisCache.getCacheObject(key); + + if (Objects.isNull(allBoxRecords)) { + allBoxRecords = boxRecordsService.list(boxRecordsQuery); + } + + // 时间差,计算当前进行到第几回合 + // LocalDateTime now = LocalDateTime.now(); + // Timestamp beginTime = fight.getBeginTime(); + // LocalDateTime beginTime1 = beginTime.toLocalDateTime(); + // Duration duration = Duration.between(beginTime1, now); + // Long currentRound = duration.toMillis() / fightRoundTime; // 这个秒要和前端的【每回合时间】同步 + + // 获取当前回合数 + Integer currentRound = 0; + FightBoutData fightBoutData = redisCache.getCacheObject("fight_bout_data:fight_" + fightId); + if (!Objects.isNull(fightBoutData)) { + currentRound = fightBoutData.getBoutNum(); + } + + FightResultVO resultVO = FightResultVO.builder() + .currentRound(currentRound.longValue()) + .fight(fight) + .winnerIds(fight.getWinnerIds()) + .fightResult(allBoxRecords) + .fightBoxVOList(fightBoxVOList) + .build(); + + // broadcastFight(fightId, WsResult.ok(SMsgKey.FIGHT_RESULT.name(), resultVO, "对局进行中,对局最新信息")); + sendMsgToPlayers(WsResult.ok(SMsgKey.FIGHT_RESULT.name(), resultVO, "对局进行中,对局最新信息"), null); + + log.info("room onOpen 广播数据成功。"); + } else if (fight.getStatus().equals(0)) { + // 没有结束,返回房间信息即可 + sendMsgToPlayers(WsResult.ok(SMsgKey.FIGHT_ROOM_INFO.name(), fight, "对局准备中,对战房间最新信息"), null); + log.info("room onOpen 广播数据成功。"); + } + } catch (Exception e) { + e.printStackTrace(); + log.warn("onopen warn"); + } + } + + @OnClose + public void onClose(Session session) { + removeFightRoomUser(); + log.info("onClose"); + log.info("正常在线人数:" + WsFightRoom.onlineCount); + } + + @OnError + public void onError(Session session, Throwable exception) throws Exception { + log.info("onError"); + } + + @OnMessage + public void onMessage(String message, Session session) { + log.info("onMessage"); + } + + /** + * 连接检查 + */ + private R connectCheck(Integer userId, Integer fightId) { + if (ObjectUtil.isNotEmpty(WsFightRoom.allRoomUserMap.get(userId + "_" + fightId))) { + R.fail("用户" + userId + "已经连接对局" + fightId + "。请勿重复调用。"); + } + TtFight fight = apiFightService.getById(fightId); + if (ObjectUtil.isEmpty(fight)) { + return R.fail("不存在的对局ID:" + fightId); + } + // if (fight.getStatus().equals(2) || fight.getStatus().equals(3)){ + // return R.fail("对局"+fightId+"已结束。"); + // } + return R.ok(); + } + + /** + * 更新在线人数 + */ + private int addOnlineCount(int number) { + synchronized (new Object()) { + WsFightRoom.onlineCount = WsFightRoom.onlineCount + number; + return WsFightRoom.onlineCount; + } + } + + /** + * 单点推送 + */ + public void sendMsgToPlayers(Object message, List keys) { + String msg = JSON.toJSONString(message); + + try { + if (ObjectUtil.isEmpty(keys) || keys.size() == 0) { + // 推送给自己 + this.session.getBasicRemote().sendText(msg); + } else { + for (String key : keys) { + WsFightRoom ws = WsFightRoom.allRoomUserMap.get(key); + ws.session.getBasicRemote().sendText(msg); + } + } + } catch (Exception e) { + log.warn("推送消息异常。msg:" + msg); + } + } + + /** + * 房间广播 + */ + public static void broadcastFight(Integer fightId, Object message) { + String msg = JSON.toJSONString(message); + + Collection wslist = WsFightRoom.allRoomUserMap.values(); + + for (WsFightRoom ws : wslist) { + try { + if (!ws.fightId.equals(fightId)) continue; + log.info(String.valueOf(ws.session.isOpen())); + if (!ws.session.isOpen()) continue; + RemoteEndpoint.Basic basicRemote = ws.session.getBasicRemote(); + basicRemote.sendText(msg); + } catch (IOException e) { + log.warn("ws推送广播给{}_{}消息异常。", ws.userId, ws.fightId); + } + } + } + + /** + * 全局广播 + */ + public static void broadcast(Object message) { + + ObjectMapper objectMapper = new ObjectMapper(); + String msg = ""; + try { + msg = objectMapper.writeValueAsString(message); + } catch (JsonProcessingException e) { + log.warn("ws解析广播消息异常。"); + } + + Collection wss = WsFightRoom.allRoomUserMap.values(); + for (WsFightRoom ws : wss) { + try { + ws.session.getBasicRemote().sendText(msg); + } catch (IOException e) { + log.warn("ws推送广播给{}_{}消息异常。", ws.userId, ws.fightId); + } + } + } + + /** + * 添加连接用户 + */ + private WsFightRoom addFightRoomUser(Integer userId, Integer fightId, Session session) { + addOnlineCount(1); + this.userId = userId; + this.fightId = fightId; + this.key = userId + "_" + fightId; + this.session = session; + WsFightRoom.allRoomUserMap.put(key, this); + return this; + } + + /** + * 移除连接用户 + */ + private WsFightRoom removeFightRoomUser() { + addOnlineCount(-1); + WsFightRoom.allRoomUserMap.remove(userId + "_" + fightId); + return this; + } + + /** + * 断开房间所有连接 + */ + public static Boolean batchClose(TtFight fight) { + List seats = fight.getSeats(); + + for (int i = 0; i < seats.size(); i++) { + FightSeat seat = JSONUtil.toBean(JSONUtil.toJsonStr(seats.get(i)), FightSeat.class); + + Integer playerId = seat.getPlayerId(); + String key = ObjectUtil.isNotEmpty(playerId) ? String.valueOf(playerId) : "" + "_" + fight.getId(); + + WsFightRoom ws = WsFightRoom.allRoomUserMap.get(key); + if (ObjectUtil.isEmpty(ws)) continue; + + try { + ws.session.close(); + } catch (IOException e) { + log.warn("关闭连接异常"); + } + } + return true; + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WsFightRoom2.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WsFightRoom2.java new file mode 100644 index 0000000..8eeed01 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/WsFightRoom2.java @@ -0,0 +1,256 @@ +package com.ruoyi.playingmethod.websocket; + +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.ruoyi.admin.mapper.TtBoxOrnamentsMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.fight.TtFight; +import com.ruoyi.domain.vo.TtBoxOrnamentsDataVO; +import com.ruoyi.domain.vo.fight.FightBoxVO; +import com.ruoyi.domain.vo.fight.FightResultVO; +import com.ruoyi.playingmethod.service.ApiFightService; +import com.ruoyi.playingmethod.websocket.constant.SMsgKey; +import com.ruoyi.playingmethod.websocket.util.WsResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import jakarta.websocket.*; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.sql.Timestamp; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + + +/** + * 对战房间 + */ +@Slf4j +@ServerEndpoint("/ws/fight/room2/{userId}/{fightId}") +@Component +public class WsFightRoom2 { + + static Map sessionMap = new ConcurrentHashMap<>(); + + static Map viewersSessionMap = new ConcurrentHashMap<>(); + + private String sessionKey; + + private static Integer fightRoundTime; + + private static ApiFightService apiFightService; + + private static TtBoxRecordsService ttBoxRecordsService; + + private static TtBoxOrnamentsMapper ttBoxOrnamentsMapper; + + @Value("${mkcsgo.fight.roundTime}") + public void setFightRoundTime(Integer fightRoundTime) { + WsFightRoom2.fightRoundTime = fightRoundTime; + } + + @Autowired + public void ttFightService(ApiFightService apiFightService) { + WsFightRoom2.apiFightService = apiFightService; + } + + @Autowired + public void boxRecordsService(TtBoxRecordsService ttBoxRecordsService) { + WsFightRoom2.ttBoxRecordsService = ttBoxRecordsService; + } + + @Autowired + public void boxOrnamentsMapper(TtBoxOrnamentsMapper ttBoxOrnamentsMapper) { + WsFightRoom2.ttBoxOrnamentsMapper = ttBoxOrnamentsMapper; + } + + @OnOpen + public void onOpen(Session session, + @PathParam("userId") Integer userId, + @PathParam("fightId") Integer fightId) { + + sessionKey = fightId.toString() + "#" + userId.toString(); + + // 检查用户是否已经进入某个房间 + if (!Objects.isNull(sessionMap.get(sessionKey))) { + try { + session.getBasicRemote().sendText("用户" + userId + "已加入" + fightId +"房间,不得重复进入"); + session.close(); + return; + } catch (IOException e) { + e.printStackTrace(); + } + } else { + sessionMap.put(sessionKey, session); + } + + // 首次连接获取房间最新数据 + LambdaQueryWrapper fightQuery = new LambdaQueryWrapper<>(); + fightQuery + .eq(TtFight::getId, fightId); + TtFight ttFight = apiFightService.getOne(fightQuery); + + // 检查房间是否存在 + if (Objects.isNull(ttFight)) { + try { + session.getBasicRemote().sendText("房间" + fightId + "不存在"); + session.close(); + return; + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 判断对战状态 + if (ttFight.getStatus() == 2) { + // 构建结果集 + // 根据宝箱ID查询关联的所有饰品 + Map boxData = ttFight.getBoxData(); + ArrayList fightBoxVOList = new ArrayList<>(); + boxData.keySet().forEach(boxId -> { + fightBoxVOList.add(boxData.get(boxId)); + }); + + // 所有对战结果记录 + LambdaQueryWrapper boxRecordsQuery = new LambdaQueryWrapper<>(); + boxRecordsQuery + .eq(TtBoxRecords::getFightId, fightId) + .eq(TtBoxRecords::getStatus, 0); + List allBoxRecords = ttBoxRecordsService.list(boxRecordsQuery); + + FightResultVO resultVO = FightResultVO.builder() + .currentRound(-1L) + .fight(ttFight) + .winnerIds(ttFight.getWinnerIds()) + .fightResult(allBoxRecords) + .fightBoxVOList(fightBoxVOList) + .build(); + + WsResult result = WsResult.ok(SMsgKey.FIGHT_RESULT.name(), resultVO, "对局已结束,对局最新信息"); + String message = JSON.toJSONString(result); + broadcastMessageToFight(fightId, message); + } else if (ttFight.getStatus() == 1) { + // 构建结果集 + // 根据宝箱ID查询关联的所有饰品 + Map boxData = ttFight.getBoxData(); + ArrayList fightBoxVOList = new ArrayList<>(); + boxData.keySet().forEach(boxId -> { + List boxOrnamentsVOS = ttBoxOrnamentsMapper.selectTtBoxOrnamentsList(Integer.valueOf(boxId)); + FightBoxVO fightBoxVO = JSONUtil.toBean(JSONUtil.toJsonStr(boxData.get(boxId)), FightBoxVO.class); + fightBoxVO.setOrnaments(boxOrnamentsVOS); + boxData.put(boxId,fightBoxVO); + fightBoxVOList.add(boxData.get(boxId)); + }); + + // 所有对战结果记录 + LambdaQueryWrapper boxRecordsQuery = new LambdaQueryWrapper<>(); + boxRecordsQuery + .eq(TtBoxRecords::getFightId, fightId) + .eq(TtBoxRecords::getStatus, 0); + List allBoxRecords = ttBoxRecordsService.list(boxRecordsQuery); + + // 时间差,计算当前进行到第几回合 + LocalDateTime now = LocalDateTime.now(); + Timestamp beginTime = ttFight.getBeginTime(); + LocalDateTime localDateTime = beginTime.toLocalDateTime(); + + Duration duration = Duration.between(localDateTime, now); + Long currentRound = duration.toMillis() / fightRoundTime; // 这个秒要和前端的【每回合时间】同步 + + FightResultVO resultVO = FightResultVO.builder() + .currentRound(currentRound) + .fight(ttFight) + .winnerIds(ttFight.getWinnerIds()) + .fightResult(allBoxRecords) + .fightBoxVOList(fightBoxVOList) + .build(); + + WsResult result = WsResult.ok(SMsgKey.FIGHT_RESULT.name(), resultVO, "对局进行中,对局最新信息"); + String message = JSON.toJSONString(result); + broadcastMessageToFight(fightId, message); + + // 存储观战人,广播房间内观战人数 + viewersSessionMap.put(sessionKey, session); + broadcastViewersCountToFight(fightId); + } else if (ttFight.getStatus() == 0) { + WsResult result = WsResult.ok(SMsgKey.FIGHT_ROOM_INFO.name(), ttFight, "对局准备中,对战房间最新信息"); + String message = JSON.toJSONString(result); + try { + session.getBasicRemote().sendText(message); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @OnMessage + public void onMessage(Session session, String message) { + } + + @OnClose + public void onClose(Session session) { + if (session.equals(sessionMap.get(sessionKey))) { + sessionMap.remove(sessionKey); + } + if (session.equals(viewersSessionMap.get(sessionKey))) { + viewersSessionMap.remove(sessionKey); + Integer fightId = Integer.parseInt(sessionKey.substring(0, sessionKey.indexOf('#'))); + broadcastViewersCountToFight(fightId); + } + } + + @OnError + public void onError(Session session, Throwable throwable) { + } + + // 向指定房间的用户广播消息 + private void broadcastMessageToFight(Integer fightId, String message) { + for (Map.Entry entry : sessionMap.entrySet()) { + String sessionKey = entry.getKey(); + Session session = entry.getValue(); + if (sessionKey.substring(0, sessionKey.indexOf('#')).equals(fightId.toString())) { + try { + session.getBasicRemote().sendText(message); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + // 向指定房间的用户广播观战人数 + private void broadcastViewersCountToFight(Integer fightId) { + int viewersCount = 0; + // 统计本房间的观战人数 + for (Map.Entry entry : viewersSessionMap.entrySet()) { + String sessionKey = entry.getKey(); + if (sessionKey.substring(0, sessionKey.indexOf('#')).equals(fightId.toString())) { + viewersCount++; + } + } + // 向该房间所有用户广播 + for (Map.Entry entry : sessionMap.entrySet()) { + String sessionKey = entry.getKey(); + Session session = entry.getValue(); + if (sessionKey.substring(0, sessionKey.indexOf('#')).equals(fightId.toString())) { + try { + Map map = new HashMap<>(); + map.put("viewersCount", viewersCount); + map.put("message", "当前房间观战人数"); + String message = JSON.toJSONString(map); + session.getBasicRemote().sendText(message); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/config/WebSocketConfig.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/config/WebSocketConfig.java new file mode 100644 index 0000000..9f4a449 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/config/WebSocketConfig.java @@ -0,0 +1,13 @@ +package com.ruoyi.playingmethod.websocket.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +@Configuration +public class WebSocketConfig { + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/constant/CMsgKey.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/constant/CMsgKey.java new file mode 100644 index 0000000..eb2f77f --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/constant/CMsgKey.java @@ -0,0 +1,8 @@ +package com.ruoyi.playingmethod.websocket.constant; + +// 客户端发送消息key +public enum CMsgKey { + + FIGHT_END; + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/constant/SMsgKey.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/constant/SMsgKey.java new file mode 100644 index 0000000..fa71cfb --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/constant/SMsgKey.java @@ -0,0 +1,21 @@ +package com.ruoyi.playingmethod.websocket.constant; + +// 服务端发送消息key +public enum SMsgKey { + + // 所有对战房间列表 + ALL_FIGHT_ROOM, + + // 对战结果 + FIGHT_RESULT, + + // 房间信息 + FIGHT_ROOM_INFO, + + // 盲盒房间连接第一次数据 + Blind_Box_Init_Data, + + // 盲盒房间实时数据 + Blind_Box_Current_Data; + +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/entity/ResultData.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/entity/ResultData.java new file mode 100644 index 0000000..b16a83c --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/entity/ResultData.java @@ -0,0 +1,17 @@ +package com.ruoyi.playingmethod.websocket.entity; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ResultData implements Serializable { + + private static final long serialVersionUID = 1L; + + private int code; + + private String typeName; + + private T data; +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/entity/SessionData.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/entity/SessionData.java new file mode 100644 index 0000000..b1e2a45 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/entity/SessionData.java @@ -0,0 +1,11 @@ +package com.ruoyi.playingmethod.websocket.entity; + +import lombok.Data; + +import jakarta.websocket.Session; + +@Data +public class SessionData { + private Integer userId = 0; + private Session session; +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/util/SemaphoreUtils.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/util/SemaphoreUtils.java new file mode 100644 index 0000000..be2cd8b --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/util/SemaphoreUtils.java @@ -0,0 +1,31 @@ +package com.ruoyi.playingmethod.websocket.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.Semaphore; + +public class SemaphoreUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(SemaphoreUtils.class); + + public static boolean tryAcquire(Semaphore semaphore) { + boolean flag = false; + + try { + flag = semaphore.tryAcquire(); + } catch (Exception e) { + LOGGER.error("获取信号量异常", e); + } + + return flag; + } + + public static void release(Semaphore semaphore) { + + try { + semaphore.release(); + } catch (Exception e) { + LOGGER.error("释放信号量异常", e); + } + } +} diff --git a/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/util/WsResult.java b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/util/WsResult.java new file mode 100644 index 0000000..abad1ac --- /dev/null +++ b/skins-service/service-playingmethod/src/main/java/com/ruoyi/playingmethod/websocket/util/WsResult.java @@ -0,0 +1,81 @@ +package com.ruoyi.playingmethod.websocket.util; + +import com.ruoyi.common.constant.HttpStatus; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 响应信息主体 + * + * @author ruoyi + */ + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WsResult implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 成功 */ + public static final int SUCCESS = HttpStatus.SUCCESS; + + /** 失败 */ + public static final int FAIL = HttpStatus.ERROR; + + private String key; + + private T data; + + private int code; + + private String msg; + + public static WsResult ok() + { + return new WsResult(null,null, SUCCESS, "操作成功"); + } + + public static WsResult ok(String key, T data) + { + return new WsResult(key,data, SUCCESS, "操作成功"); + } + + public static WsResult ok(String key,T data,String msg) + { + return new WsResult(key,data, SUCCESS, msg); + } + + public static WsResult ok(String msg) + { + return new WsResult(null,null, SUCCESS, msg); + } + + public static WsResult fail() + { + return new WsResult(null,null, FAIL, "操作失败"); + } + + public static WsResult fail(String key,T data,String msg) + { + return new WsResult(key,data, FAIL, msg); + } + + public static WsResult fail(String msg) + { + return new WsResult(null,null, FAIL, msg); + } + + public static Boolean isError(WsResult ret) + { + return !isSuccess(ret); + } + + public static Boolean isSuccess(WsResult ret) + { + return WsResult.SUCCESS == ret.getCode(); + } +} diff --git a/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiAttendanceRecordMapper.xml b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiAttendanceRecordMapper.xml new file mode 100644 index 0000000..22c4db9 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiAttendanceRecordMapper.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + insert into tt_attendance_record + + user_id, + coin, + create_time, + update_time, + + + #{userId}, + #{coin}, + #{createTime}, + #{updateTime}, + + + + + + + \ No newline at end of file diff --git a/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiBattleRoyaleMapper.xml b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiBattleRoyaleMapper.xml new file mode 100644 index 0000000..4889567 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiBattleRoyaleMapper.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiBindBoxMapper.xml b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiBindBoxMapper.xml new file mode 100644 index 0000000..4aeb8b6 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiBindBoxMapper.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiCompoundMapper.xml b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiCompoundMapper.xml new file mode 100644 index 0000000..efcd7d2 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiCompoundMapper.xml @@ -0,0 +1,41 @@ + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiFightMapper.xml b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiFightMapper.xml new file mode 100644 index 0000000..57294e5 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiFightMapper.xml @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiLuckyUpgradeMapper.xml b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiLuckyUpgradeMapper.xml new file mode 100644 index 0000000..e89ed1b --- /dev/null +++ b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiLuckyUpgradeMapper.xml @@ -0,0 +1,56 @@ + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiReplacementRecordMapper.xml b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiReplacementRecordMapper.xml new file mode 100644 index 0000000..55635cc --- /dev/null +++ b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiReplacementRecordMapper.xml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select id, uid, uname, oids, award_oid, award_oname, award_oprice,award_oimg, time, create_time, update_time + from tt_replacement_record + + + + insert into tt_replacement_record + + uid, + uname, + oids, + award_oid, + award_oname, + award_oprice, + award_oimg, + time, + create_time, + update_time, + + + #{uid}, + #{uname}, + #{oids}, + #{awardOid}, + #{awardOname}, + #{awardOprice}, + #{awardOimg}, + #{time}, + #{createTime}, + #{updateTime}, + + + + + select + id, + `name`, + use_price, + image_url, + market_hash_name, + item_id, + price, + quantity, + short_name, + `type`, + type_name, + quality, + quality_name, + quality_color, + rarity, + rarity_name, + rarity_color, + exterior, + exterior_name, + create_time, + update_time, + remark, + is_putaway, + is_proprietary_property, + zbt_id, + yyyouping_id, + type_hash_name, + exterior_hash_name, + quality_hash_name, + rarity_hash_name + from tt_ornament + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiRobotFightGroupMapper.xml b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiRobotFightGroupMapper.xml new file mode 100644 index 0000000..e87e876 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiRobotFightGroupMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiRollMapper.xml b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiRollMapper.xml new file mode 100644 index 0000000..3e460d8 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiRollMapper.xml @@ -0,0 +1,87 @@ + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiWelfareMapper.xml b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiWelfareMapper.xml new file mode 100644 index 0000000..2351701 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiWelfareMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO tt_welfare_record(welfare_id, user_id, type, create_time) + VALUES (#{welfareId}, #{userId}, #{type}, #{createTime}) + + \ No newline at end of file diff --git a/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiWelfareMonthlyRechargesMapper.xml b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiWelfareMonthlyRechargesMapper.xml new file mode 100644 index 0000000..ed98ee6 --- /dev/null +++ b/skins-service/service-playingmethod/src/main/resources/com/ruoyi/playingmethod/mapper/ApiWelfareMonthlyRechargesMapper.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO tt_welfare_record(welfare_id, user_id, type, create_time) + VALUES (#{welfareId}, #{userId}, #{type}, #{createTime}) + + \ No newline at end of file diff --git a/skins-service/service-task/pom.xml b/skins-service/service-task/pom.xml new file mode 100644 index 0000000..96ca576 --- /dev/null +++ b/skins-service/service-task/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + com.ruoyi + skins-service + 4.8.2 + + + service-task + + + 8 + 8 + UTF-8 + + + + + + + + + + + + com.ruoyi + ruoyi-common + + + + com.ruoyi + service-admin + 4.8.2 + + + + + \ No newline at end of file diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/MQReceiver/DirectReceiver.java b/skins-service/service-task/src/main/java/com/ruoyi/task/MQReceiver/DirectReceiver.java new file mode 100644 index 0000000..81c1ac5 --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/MQReceiver/DirectReceiver.java @@ -0,0 +1,29 @@ +package com.ruoyi.task.MQReceiver; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.rabbitmq.client.Channel; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.task.DTO.pWelfareMQData; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.*; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.math.BigDecimal; + +import static com.ruoyi.task.config.DirectExchangeConfig.*; + + +// 不用 +@Slf4j +// @Component +public class DirectReceiver { + +} diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/config/DirectExchangeConfig.java b/skins-service/service-task/src/main/java/com/ruoyi/task/config/DirectExchangeConfig.java new file mode 100644 index 0000000..2f096e0 --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/config/DirectExchangeConfig.java @@ -0,0 +1,48 @@ +package com.ruoyi.task.config; + +import org.springframework.amqp.core.AmqpAdmin; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Configurable; + + +@Configurable +public class DirectExchangeConfig { + + @Autowired + private AmqpAdmin amqpAdmin; + public static final String PROMOTION_WELFARE_QUEUE = "pWelfareQueue"; + public static final String PROMOTION_WELFARE_EXCHANGE = "pWelfareExchange"; + public static final String PROMOTION_WELFARE_KEY1 = "pwKey1"; + + // public static void main(String[] args) { + // System.out.println(Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); + // } + + + // @Bean + // public Queue directQueue() { + // return new Queue(DIRECT_QUEUE, true); + // } + // + // @Bean + // public DirectExchange directExchange() { + // return new DirectExchange(DIRECT_EXCHANGE, true, false); + // } + // + // @Bean + // public Binding bindingDirectExchange(Queue directQueue, DirectExchange directExchange) { + // return BindingBuilder.bind(directQueue).to(directExchange).with(DIRECT_ROUTING_KEY); + // } + + // @Bean + // public Queue directQueue2() { + // return new Queue(DIRECT_QUEUE2, true); + // } + + // @Bean + // public Binding bindingDirectExchange2(Queue directQueue2, DirectExchange directExchange) { + // return BindingBuilder.bind(directQueue2).to(directExchange).with(DIRECT_ROUTING_KEY); + // } + + +} diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/controller/MQController.java b/skins-service/service-task/src/main/java/com/ruoyi/task/controller/MQController.java new file mode 100644 index 0000000..0b77006 --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/controller/MQController.java @@ -0,0 +1,38 @@ +package com.ruoyi.task.controller; + +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.task.DTO.pWelfareMQData; +import com.ruoyi.domain.task.VO.TtTaskDoingVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.List; + +import static com.ruoyi.domain.task.constant.mq.MQMoudle.PROMOTION_WELFARE_EXCHANGE; +import static com.ruoyi.domain.task.constant.mq.MQMoudle.PROMOTION_WELFARE_KEY1; + +@Slf4j +@RestController +@RequestMapping("/api/mq") +public class MQController { + + @Autowired + private RabbitTemplate rabbitTemplate; + + @GetMapping("/t1") + @Anonymous + public AjaxResult t1() { + + log.debug("mq测试"); + return AjaxResult.success("mq测试"); + + } + +} diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/controller/TtTaskController.java b/skins-service/service-task/src/main/java/com/ruoyi/task/controller/TtTaskController.java new file mode 100644 index 0000000..7e52f32 --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/controller/TtTaskController.java @@ -0,0 +1,75 @@ +package com.ruoyi.task.controller; + +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.task.VO.TtTaskDoingVO; +import com.ruoyi.task.scheduled.TaskUpdate; +import com.ruoyi.task.service.TtTaskService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Api(tags = "任务模块") +@RestController +@RequestMapping("/api/ttTask") +public class TtTaskController extends BaseController { + + @Autowired + private TtTaskService ttTaskService; + + @Autowired + private TaskUpdate taskUpdate; + + @ApiOperation("关于我的任务") + @GetMapping("/taskOfme") + @Anonymous + // public List taskOfme(@PathVariable("page") Integer page,@PathVariable("size") Integer size) { + public List taskOfme() { + Long userId = getUserId(); + return ttTaskService.taskOfme(1, 5, Integer.valueOf(String.valueOf(userId))); //暂时不需要分页 + // return ttTaskService.taskOfme(1,5,1); + } + + // 领取奖励 + @GetMapping("/getAward/{tid}") + @Anonymous + public AjaxResult getAward(@PathVariable("tid") Integer taskDoingid) { + Long userId = getUserId(); + return ttTaskService.getAward(Integer.valueOf(String.valueOf(userId)), taskDoingid); + // return ttTaskService.getAward(1,1); + } + + // 接首次下载任务 + @GetMapping("/firsDownLoadTask") + @Anonymous + public AjaxResult firsDownLoadTask() { + Long userId = getUserId(); + return ttTaskService.firsDownLoadTask(Integer.valueOf(String.valueOf(userId))); + } + + // 接首次下载任务 + @GetMapping("/test") + @Anonymous + public AjaxResult firsDownLoadTask(@RequestParam("kind") String kind) { + if (kind == null) { + kind = ""; + } + + switch (kind) { + case "rank": + taskUpdate.blendErcashRankPrize(); + case "zhubo": + taskUpdate.pWelfarePrize(); + } + return AjaxResult.success("测试完成"); + } + +} diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/mapper/TtTaskDoingMapper.java b/skins-service/service-task/src/main/java/com/ruoyi/task/mapper/TtTaskDoingMapper.java new file mode 100644 index 0000000..d4c6578 --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/mapper/TtTaskDoingMapper.java @@ -0,0 +1,13 @@ +package com.ruoyi.task.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.task.TtTask; +import com.ruoyi.domain.task.TtTaskDoing; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +@Mapper +public interface TtTaskDoingMapper extends BaseMapper { + + TtTaskDoing isOwnUser(@Param("userId") Integer userId,@Param("tid") Integer tid); +} diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/mapper/TtTaskMapper.java b/skins-service/service-task/src/main/java/com/ruoyi/task/mapper/TtTaskMapper.java new file mode 100644 index 0000000..6115196 --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/mapper/TtTaskMapper.java @@ -0,0 +1,16 @@ +package com.ruoyi.task.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.task.TtTask; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface TtTaskMapper extends BaseMapper { + + TtTask byId(Integer id); + + List listByState(@Param("state") Integer state); +} diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/scheduled/TaskUpdate.java b/skins-service/service-task/src/main/java/com/ruoyi/task/scheduled/TaskUpdate.java new file mode 100644 index 0000000..f2a3d72 --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/scheduled/TaskUpdate.java @@ -0,0 +1,388 @@ +package com.ruoyi.task.scheduled; + + +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.mapper.TtPromotionUpdateMapper; +import com.ruoyi.admin.mapper.TtUserAmountRecordsMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.mapper.TtUserCreditsRecordsMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.TtRechargeRankingRewardService; +import com.ruoyi.admin.service.TtUserAmountRecordsService; +import com.ruoyi.admin.service.TtUserCreditsRecordsService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.service.TtVipLevelService; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.common.constant.sys.MoneyType; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtRechargeRankingReward; +import com.ruoyi.domain.other.TtVipLevel; +import com.ruoyi.domain.vo.TeamDetailVO; +import com.ruoyi.domain.vo.UserBERankVO; +import com.ruoyi.task.service.TtTaskDoingService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.TtAccountRecordSource.P_WELFARE; + +@Slf4j +@Configuration // 1.主要用于标记配置类,兼备Component的效果。 +@EnableScheduling // 2.开启定时任务 +public class TaskUpdate { + + @Autowired + private TtUserService userService; + + @Autowired + private TtTaskDoingService ttTaskDoingService; + + @Autowired + private TtUserService ttUserService; + + @Autowired + private TtUserCreditsRecordsService ttUserCreditsRecordsService; + + @Autowired + private TtUserCreditsRecordsMapper ttUserCreditsRecordsMapper; + + @Autowired + private TtUserAmountRecordsService ttUserAmountRecordsService; + + @Autowired + private TtUserAmountRecordsMapper ttUserAmountRecordsMapper; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + @Autowired + private TtUserMapper userMapper; + + @Autowired + private TtPromotionUpdateMapper ttPromotionUpdateMapper; + + @Autowired + private TtVipLevelService vipLevelService; + + @Autowired + private TtRechargeRankingRewardService ttRechargeRankingRewardService; + // 刷新每日任务 + //@Scheduled(cron = "0 0 0 * * ?") + // private void refreshDayTask() { + // + // log.info("刷新{}任务", "每日流水奖励"); + // + // Timestamp now = new Timestamp(System.currentTimeMillis()); + // + // // {每日流水奖励}任务数据重置 + // LambdaUpdateWrapper ttTaskDoingUpdate = new LambdaUpdateWrapper<>(); + // ttTaskDoingUpdate + // .eq(TtTaskDoing::getTaskId, 2) + // .set(TtTaskDoing::getBeginTime, now) + // .set(TtTaskDoing::getCompeteTime, null) + // .set(TtTaskDoing::getCompletionState, TaskCompletionState.DOING.getCode()) + // .set(TtTaskDoing::getProgress, 0); + // ttTaskDoingService.update(ttTaskDoingUpdate); + // + // } + + // 发放综合流水排行榜前十奖励 + @Scheduled(cron = "2 7 0 * * ?", zone = "Asia/Shanghai") + public void blendErcashRankPrize() { + + log.info("发放综合流水排行榜前十奖励。"); + + // 正式时间区间------------------------------------- + Calendar c = Calendar.getInstance(); + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + Timestamp end = new Timestamp(c.getTimeInMillis()); + c.add(Calendar.DAY_OF_MONTH, -1); + Timestamp begin = new Timestamp(c.getTimeInMillis()); + + // 测试时间区间--------------------------------- + // Calendar c = Calendar.getInstance(); + // Timestamp end = new Timestamp(c.getTimeInMillis()); + // c.set(Calendar.HOUR_OF_DAY, 0); + // c.set(Calendar.MINUTE, 0); + // c.set(Calendar.SECOND, 0); + // c.set(Calendar.MILLISECOND, 0); + // c.add(Calendar.MINUTE, -2); + // Timestamp begin = new Timestamp(c.getTimeInMillis()); + //------------------------------------------------- + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String beginT = dateFormat.format(begin); + String endT = dateFormat.format(end); + if (StringUtils.isBlank(beginT) || StringUtils.isBlank(beginT)) { + beginT = null; + endT = null; + } + + var source = TtAccountRecordSource.getGameConsumeCodes(); + + List rank = ttUserBlendErcashMapper.rank( + new ArrayList<>(source), + beginT, + endT, + 0, + 10); + + var rankingRewards = ttRechargeRankingRewardService.selectTtRechargeRankingRewardList(null); + var rankRewardMap = rankingRewards.stream() + .collect(Collectors.toMap(TtRechargeRankingReward::getId, t -> t)); + + for (int i = 1; i <= rank.size(); i++) { + var vo = rank.get(i - 1); + vo.setBeRank(i); + try { + var reward = rankRewardMap.get(vo.getBeRank()); + if (reward == null) { + log.warn("未找到排名奖励配置,排名:{}", vo.getBeRank()); + continue; + } + userService.updateOnlyUserAccount(vo.getUserId(), reward.getReward(), TtAccountRecordSource.RANK_BLEND_ERCASH); + } catch (Exception e) { + log.error("发放综合流水排行榜奖励失败,用户:{},异常:{}", vo.getUserId(), e.getMessage()); + } + } + + log.info("发放综合流水排行榜前十奖励 完成"); + } + + // 发放主播推广福利奖励 + @Scheduled(cron = "2 4 0 * * ?", zone = "Asia/Shanghai") // 每天0点15分2秒执行 + public void pWelfarePrize() { + + Long start = System.currentTimeMillis(); + log.info("发放推广福利奖励。"); + + // 正式时间段 ---------------------------------- + Calendar c = Calendar.getInstance(); + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + Timestamp end = new Timestamp(c.getTimeInMillis()); + c.add(Calendar.DAY_OF_MONTH, -1); + Timestamp begin = new Timestamp(c.getTimeInMillis()); + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String beginT = dateFormat.format(begin); + String endT = dateFormat.format(end); + + if (StringUtils.isBlank(beginT) || StringUtils.isBlank(endT)) { + log.warn("推广福利统计失败!"); + return; + } + + // 查询今天下级有消费的主播 + List bossIdList = ttUserAmountRecordsMapper.bossByHasConsumeEmployee(beginT, endT); + + // 循环所有boss发放福利 + for (Integer bossId : bossIdList) { + try { + boolean r = pWelfarePrizeToBoss(bossId, beginT, endT); + if (!r) { + log.error("推广福利统计失败,用户【{}】发奖失败!", bossId); + } + } catch (Exception e) { + log.error("推广福利统计失败,用户【{}】发奖异常!{}", bossId, e.getMessage()); + } + } + + Long stop = System.currentTimeMillis(); + log.info("每日推广福利发放完成。用时:{}分钟", (stop - start) / 60000); + + } + + // 发放昨日充值vip流水福利奖励 + @Scheduled(cron = "2 1 0 * * ?", zone = "Asia/Shanghai") // 每天0点1分2秒执行 + private void vipReward() { + Long start = System.currentTimeMillis(); + log.info("发放充值vip流水福利奖励"); + + LocalDate yesterday = LocalDate.now().minusDays(1); + LocalDateTime beginTime = yesterday.atStartOfDay(); + LocalDateTime endTime = yesterday.atTime(23, 59, 59); + + var records = new LambdaQueryChainWrapper<>(ttUserBlendErcashMapper).between(TtUserBlendErcash::getCreateTime, beginTime, endTime) + .in(TtUserBlendErcash::getSource, TtAccountRecordSource.getGameConsumeCodes()).list(); + + if (CollectionUtils.isEmpty(records)) { + log.info("昨日无充值VIP流水记录,无需发放奖励"); + return; + } + + Map> userRecordsMap = records.stream() + .collect(Collectors.groupingBy(TtUserBlendErcash::getUserId)); + + // 3. 遍历用户进行发放操作 + var ttVipLevelMap = vipLevelService.list().stream().collect(Collectors.toMap(TtVipLevel::getId, t -> t)); + for (Map.Entry> entry : userRecordsMap.entrySet()) { + Integer userId = entry.getKey(); + try { + List userRecords = entry.getValue(); + + // 计算该用户昨日VIP流水总额 + BigDecimal totalAmount = userRecords.stream() + .map(TtUserBlendErcash::getAmount) + .reduce(BigDecimal.ZERO, BigDecimal::add); + totalAmount = totalAmount.abs(); + + TtUser ttUser = userService.getById(userId); + + if (ttUser.getVipLevel() > 0) { + TtVipLevel ttVipLevel = ttVipLevelMap.get(ttUser.getVipLevel()); + if (ttVipLevel == null) { + log.warn("用户【{}】VIP等级不存在![{}]", ttUser.getUserId(), ttUser.getVipLevel()); + continue; + } + BigDecimal rebate = ttVipLevel.getCommissions().divide(BigDecimal.valueOf(100), 4, + RoundingMode.HALF_UP) + .multiply(totalAmount).setScale(2, RoundingMode.HALF_UP); + + userService.updateUserAccount(userId, rebate, TtAccountRecordSource.VIP_CONSUME_AWARD); + } + + log.debug("用户ID: {}, 昨日VIP流水总额: {}, 奖励发放处理中...", userId, totalAmount); + } catch (Exception e) { + log.error("发放VIP流水福利失败,用户ID: {},错误: {}", userId, e.getMessage()); + } + } + + + Long stop = System.currentTimeMillis(); + log.info("发放充值vip流水福利奖励完成。用时:{}分钟", (stop - start) / 60000); + } + + // 根据时间区间内下级的消费发放推广福利 + private boolean pWelfarePrizeToBoss(Integer bossId, String beginTimeStr, String endTimeStr) { + var user = userMapper.selectTtUserById(Long.valueOf(bossId)); + if (user == null) { + log.warn("推广福利统计失败,用户【{}】不存在!", bossId); + return false; + } + if (!UserType.ANCHOR.getCode().equals(user.getUserType())) { + log.warn("推广福利统计失败,用户【{}】不是主播!", bossId); + return false; + } + + // 解析时间 + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date beginTime = null; + try { + beginTime = dateFormat.parse(beginTimeStr); + } catch (ParseException e) { + log.warn("日期解析异常,【{}】发奖失败。", bossId); + return false; + } + + // 查询所有下级id + List allEmployeesId = userMapper.allEmployeesByParents(Arrays.asList(bossId)); + + // 查询下级的最近一个绑定时间 + List mit = ttPromotionUpdateMapper.latelyUpdate(allEmployeesId); + if (mit.size() < allEmployeesId.size()) log.warn("下级已绑定上级,但未写入更新日志,请及时检查!!!"); + // 如果最近绑定时间大于本次查询的起始时间,以最近绑定时间为准 + List empIds1 = new ArrayList<>(); + List empIds2 = new ArrayList<>(); + for (TeamDetailVO item : mit) { + Timestamp latelyTime = item.getBeginTime(); + if (latelyTime.compareTo(new Timestamp(beginTime.getTime())) < 0) { + empIds1.add(item.getEmployeeId()); + } else { + empIds2.add(item); + } + } + + // 昨日没有换过绑定的玩家数据 + List personBEList1 = new ArrayList<>(); + if (!empIds1.isEmpty()) { + personBEList1 = new LambdaQueryChainWrapper<>(ttUserBlendErcashMapper) + .in(TtUserBlendErcash::getUserId, empIds1) + .in(TtUserBlendErcash::getSource, TtAccountRecordSource.getWelfarePrizeCodes()) + .between(TtUserBlendErcash::getCreateTime, beginTimeStr, endTimeStr) + .list(); + } + + // 当日有 更换绑定的下级消费统计 + List personBEList2 = new ArrayList<>(); + if (!empIds2.isEmpty()) { + for (TeamDetailVO vo : empIds2) { + String beginT = dateFormat.format(vo.getBeginTime()); + var l = new LambdaQueryChainWrapper<>(ttUserBlendErcashMapper) + .eq(TtUserBlendErcash::getUserId, vo.getEmployeeId()) + .in(TtUserBlendErcash::getSource, TtAccountRecordSource.getWelfarePrizeCodes()) + .between(TtUserBlendErcash::getCreateTime, beginT, endTimeStr) + .list(); + personBEList2.addAll(l); + } + } + + personBEList1.addAll(personBEList2); + + var totalRecharge = personBEList1.stream().filter(v -> { + return Objects.equals(v.getSource(), TtAccountRecordSource.RECHARGE.getCode()); + }).map(v -> v.getAmount().abs()).reduce(BigDecimal.ZERO, BigDecimal::add); + + var allConsumeList = personBEList1.stream().filter(v -> { + return Objects.equals(v.getType(), TtAccountRecordType.OUTPUT.getCode()); + }).toList(); + + var totalNormalGameConsume = allConsumeList.stream().filter(v -> { + return TtAccountRecordSource.isNormalGameConsume(v.getSource()); + }).map(v -> v.getAmount().abs()).reduce(BigDecimal.ZERO, BigDecimal::add); + + var totalSpecialGameConsume = allConsumeList.stream().filter(v -> { + return TtAccountRecordSource.isSpecialGameConsume(v.getSource()); + }).map(v -> v.getAmount().abs()).reduce(BigDecimal.ZERO, BigDecimal::add); + + // 发奖 + // 0.02*总充值 + 0.03*普通游戏消费 + 0.01*特殊游戏消费 + BigDecimal prize = totalRecharge.multiply(new BigDecimal("0.02")).add(totalNormalGameConsume.multiply(new BigDecimal("0.03"))) + .add(totalSpecialGameConsume.multiply(new BigDecimal("0.01"))); + + // 更新账户 + userService.updateUserAccount(bossId, prize, P_WELFARE); + return true; + } + + // 奖励 + private void rankPrizeF(MoneyType prizeMoneyType, + LambdaUpdateWrapper wrapper, + Integer userId, + String value, + TtAccountRecordSource source) { + + userService.updateUserAccount(userId, new BigDecimal(value), source); + + } + +} diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/service/TtTaskDoingService.java b/skins-service/service-task/src/main/java/com/ruoyi/task/service/TtTaskDoingService.java new file mode 100644 index 0000000..f6554c8 --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/service/TtTaskDoingService.java @@ -0,0 +1,11 @@ +package com.ruoyi.task.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.domain.task.TtTask; +import com.ruoyi.domain.task.TtTaskDoing; + +import java.util.List; + +public interface TtTaskDoingService extends IService { + TtTaskDoing isOwnUser(Integer userId, Integer tid); +} diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/service/TtTaskService.java b/skins-service/service-task/src/main/java/com/ruoyi/task/service/TtTaskService.java new file mode 100644 index 0000000..e1c4f76 --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/service/TtTaskService.java @@ -0,0 +1,17 @@ +package com.ruoyi.task.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.task.TtTask; +import com.ruoyi.domain.task.TtTaskDoing; +import com.ruoyi.domain.task.VO.TtTaskDoingVO; + +import java.util.List; + +public interface TtTaskService extends IService { + List taskOfme(Integer page, Integer size, Integer userId); + + AjaxResult getAward(Integer userId, Integer taskDoingid); + + AjaxResult firsDownLoadTask(Integer userId); +} diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/service/impl/TtTaskDoingServiceImpl.java b/skins-service/service-task/src/main/java/com/ruoyi/task/service/impl/TtTaskDoingServiceImpl.java new file mode 100644 index 0000000..c6b2c49 --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/service/impl/TtTaskDoingServiceImpl.java @@ -0,0 +1,21 @@ +package com.ruoyi.task.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.domain.task.TtTaskDoing; +import com.ruoyi.task.mapper.TtTaskDoingMapper; +import com.ruoyi.task.service.TtTaskDoingService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class TtTaskDoingServiceImpl extends ServiceImpl implements TtTaskDoingService { + + @Autowired + private TtUserService userService; + + @Override + public TtTaskDoing isOwnUser(Integer userId, Integer tid) { + return baseMapper.isOwnUser(userId,tid); + } +} diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/service/impl/TtTaskServiceImpl.java b/skins-service/service-task/src/main/java/com/ruoyi/task/service/impl/TtTaskServiceImpl.java new file mode 100644 index 0000000..5ce817b --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/service/impl/TtTaskServiceImpl.java @@ -0,0 +1,498 @@ +package com.ruoyi.task.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.admin.service.TtUserCreditsRecordsService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.entity.recorde.TtUserCreditsRecords; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.task.TtTask; +import com.ruoyi.domain.task.TtTaskDoing; +import com.ruoyi.domain.task.VO.TtTaskDoingVO; +import com.ruoyi.domain.task.constant.*; +import com.ruoyi.task.mapper.TtTaskMapper; +import com.ruoyi.task.service.TtTaskDoingService; +import com.ruoyi.task.service.TtTaskService; +import com.ruoyi.task.utils.TasKUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class TtTaskServiceImpl extends ServiceImpl implements TtTaskService { + + @Autowired + private TtUserService userService; + + @Autowired + private TtTaskDoingService ttTaskDoingService; + + @Autowired + private TtUserService ttUserService; + + @Autowired + private TtUserCreditsRecordsService ttUserCreditsRecordsService; + + @Autowired + private TasKUtil tasKUtil; + + @Override + public List taskOfme(Integer page, Integer size, Integer userId) { + + // 用户信息 + TtUser userInfo = userService.getById(userId); + + // 尝试新接任务 + AjaxResult r1 = tryCreateNewTaskDoing(userInfo); + + // 维护任务 + AjaxResult r2 = maintainTask(userInfo); + + // 查询结果 + LambdaQueryWrapper ttTaskDoingQuery = new LambdaQueryWrapper<>(); + ttTaskDoingQuery + .eq(TtTaskDoing::getUserId,userInfo.getUserId()) + .eq(TtTaskDoing::getCompletionState,TaskState.UP.getCode()); + List ownTask = ttTaskDoingService.list(ttTaskDoingQuery); + return ownTask.stream().map((item)->{ + + TtTask task = this.getById(item.getTaskId()); + TtTaskDoingVO vo = new TtTaskDoingVO(); + BeanUtil.copyProperties(task,vo); + vo.setTaskId(task.getId()); + vo.setTaskDoingId(item.getId()); + vo.setUserId(item.getUserId()); + vo.setBeginTime(item.getBeginTime()); + vo.setCompeteTime(item.getCompeteTime()); + vo.setCompletionState(item.getCompletionState()); + vo.setProgress(item.getProgress()); + return vo; + + }).collect(Collectors.toList()); + + } + + private AjaxResult maintainTask(TtUser userInfo) { + + // 已接任务 + LambdaQueryWrapper ttTaskDoingQuery = new LambdaQueryWrapper<>(); + ttTaskDoingQuery + .eq(TtTaskDoing::getUserId,userInfo.getUserId()); + List ownTask = ttTaskDoingService.list(ttTaskDoingQuery); + + ownTask.stream().forEach((item)->{ + if (!tasKUtil.updateTask(item)){ + log.warn("任务{}-类型{} 数据更新失败,检查任务配置信息!",item.getId(),item.getTaskId()); + } + }); + + + // LambdaQueryWrapper ttTaskQuery = new LambdaQueryWrapper<>(); + // LambdaUpdateWrapper ttTaskDoingUpdate = new LambdaUpdateWrapper<>(); + // + // // 维护普通任务{首次下载奖励} + // ttTaskQuery + // .eq(TtTask::getId,1); + // TtTask task1 = ttTaskService.getOne(ttTaskQuery); + // + // ttTaskDoingUpdate + // .eq(TtTaskDoing::getUserId,userInfo.getUserId()) + // .eq(TtTaskDoing::getTaskId,1) + // .set(TtTaskDoing::getProgress,task1.getTargetValue()) + // .set(TtTaskDoing::getCompletionState,TaskCompletionState.COMPLETION.getCode()) + // .set(TtTaskDoing::getCompeteTime,new Timestamp(System.currentTimeMillis())); + // ttTaskDoingService.update(ttTaskDoingUpdate); + // + // + // // 维护每日任务{每日流水奖励} + // ttTaskQuery.clear(); + // ttTaskQuery + // .eq(TtTask::getId,2); + // TtTask task2 = ttTaskService.getOne(ttTaskQuery); + // + // Calendar nowDay = Calendar.getInstance(); + // nowDay.set(Calendar.HOUR_OF_DAY, 0); + // nowDay.set(Calendar.MINUTE, 0); + // nowDay.set(Calendar.SECOND, 0); + // nowDay.set(Calendar.MILLISECOND, 0); + // Timestamp toDay = new Timestamp(nowDay.getTimeInMillis()); + // + // LambdaQueryWrapper creditsRecordsQuery = new LambdaQueryWrapper<>(); + // creditsRecordsQuery + // .eq(TtUserCreditsRecords::getUserId,userInfo.getUserId()) + // .ge(TtUserCreditsRecords::getCreateTime,toDay); + // List todayRecords = ttUserCreditsRecordsService.list(creditsRecordsQuery); + // + // // 累计今日流水 + // BigDecimal credits = new BigDecimal("0"); + // todayRecords.stream().forEach((item)->{ + // if (item.getCredits().compareTo(BigDecimal.ZERO)>=0){ + // credits.add(item.getCredits()); + // } + // }); + // + // ttTaskDoingUpdate.clear(); + // ttTaskDoingUpdate + // .eq(TtTaskDoing::getUserId,userInfo.getUserId()) + // .eq(TtTaskDoing::getTaskId,2) + // .set(TtTaskDoing::getProgress,credits.intValue()) + // .set(credits.compareTo(new BigDecimal(task2.getTargetValue()))>=0,TtTaskDoing::getCompletionState,TaskCompletionState.COMPLETION.getCode()) + // .set(credits.compareTo(new BigDecimal(task2.getTargetValue()))>=0,TtTaskDoing::getCompeteTime,new Timestamp(System.currentTimeMillis())); + // ttTaskDoingService.update(ttTaskDoingUpdate); + + return AjaxResult.success(); + } + + private AjaxResult tryCreateNewTaskDoing(TtUser userInfo) { + + // 已接任务 + LambdaQueryWrapper ttTaskDoingQuery = new LambdaQueryWrapper<>(); + ttTaskDoingQuery + .eq(TtTaskDoing::getUserId,userInfo.getUserId()); + List ownTask = ttTaskDoingService.list(ttTaskDoingQuery); + + // 所有上架的任务 + List allTask = baseMapper.listByState(TaskState.UP.getCode()); + + // 筛选可接的新任务 + List filterT = filterTask(allTask,ownTask); + + // 自动接手新任务 + ArrayList newTaskDoings = new ArrayList<>(); + for (TtTask task:filterT){ + + TtTaskDoing newTaskDoing = TtTaskDoing.builder() + .taskId(task.getId()) + .userId(userInfo.getUserId()) + .completionState(TaskCompletionState.DOING.getCode()) + .beginTime(new Timestamp(System.currentTimeMillis())) + .progress(0) + .build(); + newTaskDoings.add(newTaskDoing); + + } + // 保存新接的任务 + for (TtTaskDoing taskDoing:newTaskDoings){ + ttTaskDoingService.save(taskDoing); + } + + return AjaxResult.success(); + } + + @Override + public AjaxResult getAward(Integer userId, Integer taskDoingid) { + + // 任务是否存在 + TtTaskDoing taskDoing = ttTaskDoingService.isOwnUser(userId,taskDoingid); + if (ObjectUtil.isEmpty(taskDoing)){ + return AjaxResult.error("任务不存在"); + } + + // 是否可领取 + AjaxResult r = taskIsComplete(taskDoing); + if (!r.isSuccess()){ + return r; + } + + // 领奖 + getPrize(taskDoing); + + return AjaxResult.success("操作成功"); + } + + @Override + public AjaxResult firsDownLoadTask(Integer userId) { + + // 是否已完成 + LambdaQueryWrapper ttTaskDoingQuery = new LambdaQueryWrapper<>(); + ttTaskDoingQuery + .eq(TtTaskDoing::getUserId,userId) + .eq(TtTaskDoing::getTaskId,1); + TtTaskDoing one = ttTaskDoingService.getOne(ttTaskDoingQuery); + if (ObjectUtil.isNotEmpty(one)){ + return AjaxResult.success("首次下载任务已经完成,不能重复执行。"); + } + + // 接手任务并直接完成 + TtTask task = baseMapper.byId(1); + TtTaskDoing taskDoing = new TtTaskDoing(); + BeanUtil.copyProperties(task,taskDoing); + taskDoing.setUserId(userId); + taskDoing.setTaskId(task.getId()); + taskDoing.setProgress(1); + taskDoing.setCompletionState(TaskCompletionState.COMPLETION.getCode()); + taskDoing.setCompeteTime(new Timestamp(System.currentTimeMillis())); + ttTaskDoingService.save(taskDoing); + + return AjaxResult.success("任务完成。"); + + } + + private AjaxResult getPrize(TtTaskDoing taskDoing) { + TtTask task = baseMapper.byId(taskDoing.getTaskId()); + if (task.getAwardType().equals(TaskAwardType.CREDITS.getCode())){ + + BigDecimal prize = BigDecimal.ZERO; + + if (ObjectUtil.isEmpty(task.getAwardValue())){ + // 暂时没有动态奖励的任务 + // 计算奖金 + // prize = computerPrize(task, taskDoing.getUserId()); + } + prize = new BigDecimal(task.getAwardValue()); + + TtUser user = ttUserService.getById(taskDoing.getUserId()); + BigDecimal add = user.getAccountCredits().add(prize); + user.setAccountCredits(add); + + LambdaUpdateWrapper ttUserUpdate = new LambdaUpdateWrapper<>(); + ttUserUpdate + .eq(TtUser::getUserId,user.getUserId()) + .set(TtUser::getAccountCredits,add); + ttUserService.update(ttUserUpdate); + userService.insertUserCreditsRecords(user.getUserId(), TtAccountRecordType.INPUT, TtAccountRecordSource.TASK, prize, add,taskDoing.getTaskId()); + + taskDoing.setCompletionState(TaskCompletionState.COMPLETION_PRIZE.getCode()); + ttTaskDoingService.updateById(taskDoing); + + return AjaxResult.success("领取奖励:积分"+add.toString()); + }else if (task.getAwardType().equals(TaskAwardType.OTHER.getCode())){ + System.out.println("其他奖励类型"); + return AjaxResult.error("其他奖励类型开发中。"); + }else { + System.out.println("非法的奖励类型"); + return AjaxResult.error("非法的奖励类型。"); + } + } + + private BigDecimal computerPrize(TtTask task,Integer userId){ + + // 流水奖励动态计算奖金 + if (task.getId().equals(2)){ + + Calendar nowDay = Calendar.getInstance(); + nowDay.set(Calendar.HOUR_OF_DAY, 0); + nowDay.set(Calendar.MINUTE, 0); + nowDay.set(Calendar.SECOND, 0); + nowDay.set(Calendar.MILLISECOND, 0); + Timestamp toDay = new Timestamp(nowDay.getTimeInMillis()); + + nowDay.add(Calendar.DAY_OF_MONTH,-1); + Timestamp yesterday = new Timestamp(nowDay.getTimeInMillis()); + + LambdaQueryWrapper creditsRecordsQuery = new LambdaQueryWrapper<>(); + creditsRecordsQuery + .eq(TtUserCreditsRecords::getUserId,userId) + .ge(TtUserCreditsRecords::getCreateTime,yesterday) + .le(TtUserCreditsRecords::getCreateTime,toDay); + List list = ttUserCreditsRecordsService.list(creditsRecordsQuery); + + BigDecimal prize = new BigDecimal("0"); + list.stream().forEach((item)->{ + if (item.getCredits().compareTo(BigDecimal.ZERO)>=0){ + prize.add(item.getCredits()); + } + }); + + prize.multiply(new BigDecimal("0.01")); + return prize; + + }else { + return BigDecimal.ZERO; + } + + } + + private AjaxResult taskIsComplete(TtTaskDoing taskDoing) { + + TtTask task = baseMapper.byId(taskDoing.getTaskId()); + if (task.getType().equals(TaskType.COMMON.getCode())){ + + if (taskDoing.getCompletionState().equals(TaskCompletionState.COMPLETION_PRIZE.getCode())){ + return AjaxResult.error("不可重复领取奖励。"); + } + if (taskDoing.getCompletionState().equals(TaskCompletionState.COMPLETION.getCode())){ + return AjaxResult.success(); + } + + // if (taskDoing.getProgress()>=task.getTargetValue()){ + // return AjaxResult.success(); + // } + return AjaxResult.error("任务未完成,不可领奖。"); + }else if (task.getType().equals(TaskType.DAY.getCode())){ + + if (taskDoing.getCompletionState().equals(TaskCompletionState.COMPLETION.getCode())){ + return AjaxResult.success(); + } + return AjaxResult.error("未完成/已领取"); + + }else if (task.getType().equals(TaskType.WEEK.getCode())){ + System.out.println("WEEK"); + return AjaxResult.error("非法taskType"); + }else if (task.getType().equals(TaskType.MONTH.getCode())){ + System.out.println("MONTH"); + return AjaxResult.error("非法taskType"); + }else { + System.out.println("非法任务类型"); + return AjaxResult.error("非法taskType"); + } + + } + + // 废弃方法 + private List getDoingTask(List filterT,List ownTask, TtUser userInfo) { + + ArrayList result = new ArrayList<>(); + + // 自动接手新任务 + ArrayList newTaskDoings = new ArrayList<>(); + for (TtTask task:filterT){ + + TtTaskDoing newTaskDoing = TtTaskDoing.builder() + .taskId(task.getId()) + .userId(userInfo.getUserId()) + .completionState(TaskCompletionState.DOING.getCode()) + .beginTime(new Timestamp(System.currentTimeMillis())) + .progress(0) + .build(); + newTaskDoings.add(newTaskDoing); + + TtTaskDoingVO ttTaskDoingVO = new TtTaskDoingVO(); + BeanUtil.copyProperties(task,ttTaskDoingVO); + ttTaskDoingVO.setCompletionState(newTaskDoing.getCompletionState()); + ttTaskDoingVO.setProgress(newTaskDoing.getProgress()); + result.add(ttTaskDoingVO); + + } + // 保存新接的任务 + for (TtTaskDoing taskDoing:newTaskDoings){ + ttTaskDoingService.save(taskDoing); + } + + List collect = ownTask.stream().map((item) -> { + TtTaskDoingVO vo = new TtTaskDoingVO(); + TtTask byId = baseMapper.byId(item.getTaskId()); + BeanUtil.copyProperties(byId, vo); + vo.setTaskId(byId.getId()); + vo.setTaskDoingId(item.getId()); + vo.setCompletionState(item.getCompletionState()); + vo.setProgress(item.getProgress()); + vo.setBeginTime(item.getBeginTime()); + vo.setCompeteTime(item.getCompeteTime()); + return vo; + }).collect(Collectors.toList()); + // 维护旧任务 + collect.stream().forEach((item)->{ + if (item.getType().equals(TaskType.DAY.getCode())){ + + // 当天零点时间戳 + Calendar c = Calendar.getInstance(); + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + Timestamp currenDay = new Timestamp(c.getTimeInMillis()); + + // 任务开始时间 + Timestamp beginTime = item.getBeginTime(); + // 任务完成时间 + Timestamp competeTime = item.getCompeteTime(); + + // 尝试重置当日任务 + // 完成时间为空或小于当天零点 1刷新开始时间,2状态更新为完成 + if (ObjectUtil.isEmpty(competeTime) || competeTime.compareTo(currenDay)<0){ + UpdateWrapper updateWrapper = new UpdateWrapper(); + updateWrapper + .eq("id",item.getTaskDoingId()) + .set("begin_time",new Timestamp(System.currentTimeMillis())) + .set("completion_state",TaskCompletionState.COMPLETION.getCode()); + ttTaskDoingService.update(); + } + // 开始时间小于当天零点 并 未完成 刷新开始时间,状态更新为完成 + // 开始时间大于当天零点 并 进行中 1状态更新为完成,2更新完成时间 + // if (beginTime.compareTo(currenDay)>=0 && item.getCompletionState().equals(TaskCompletionState.DOING.getCode())){ + // UpdateWrapper updateWrapper = new UpdateWrapper(); + // updateWrapper + // .eq("id",item.getTaskDoingId()) + // .set("completion_state",TaskCompletionState.COMPLETION.getCode()) + // .set("compete_time",new Timestamp(System.currentTimeMillis())); + // ttTaskDoingService.update(); + // } + // 开始时间大于当天零点 并 已完成 不处理 + + }else if (item.getType().equals(TaskType.WEEK.getCode())){ + + }else if (item.getType().equals(TaskType.MONTH.getCode())){ + + }else { + + } + }); + + // 合并所有正在进行的任务 + result.addAll(collect); + + return result; + } + + //筛选可接的新任务 + private List filterTask(List allTask,List ownTask) { + + ArrayList result = new ArrayList<>(); + + // 条件过滤 + for (TtTask task:allTask){ + + String condition = task.getTaskCondition(); + if (StringUtils.isBlank(condition)||condition.equals("{}")){ + result.add(task); + continue; + } + + JSONObject conditionObj = JSON.parseObject(condition); + if (conditionObj.get("key1").equals("key1")){ + System.out.println("key1"); + }else if (conditionObj.get("key2").equals("key2")){ + System.out.println("key2"); + } + + } + + // 筛选可接的新任务 + ArrayList canDoingTasks = new ArrayList<>(); + for (TtTask task:result){ + + TtTaskDoing flag = null; + for (TtTaskDoing taskDoing:ownTask){ + if (taskDoing.getTaskId().equals(task.getId())){ + flag = taskDoing; + break; + } + } + + if (ObjectUtil.isEmpty(flag)){ + canDoingTasks.add(task); + } + } + + return canDoingTasks; + } +} diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/utils/TasKUtil.java b/skins-service/service-task/src/main/java/com/ruoyi/task/utils/TasKUtil.java new file mode 100644 index 0000000..a72ddcf --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/utils/TasKUtil.java @@ -0,0 +1,100 @@ +package com.ruoyi.task.utils; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.ruoyi.admin.service.TtUserCreditsRecordsService; +import com.ruoyi.domain.entity.recorde.TtUserCreditsRecords; +import com.ruoyi.domain.task.TtTask; +import com.ruoyi.domain.task.TtTaskDoing; +import com.ruoyi.domain.task.constant.TaskCompletionState; +import com.ruoyi.task.mapper.TtTaskMapper; +import com.ruoyi.task.service.TtTaskDoingService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.List; + +@Component +public class TasKUtil { + + @Autowired + private TtTaskMapper ttTaskMapper; + + @Autowired + private TtTaskDoingService ttTaskDoingService; + + @Autowired + private TtUserCreditsRecordsService ttUserCreditsRecordsService; + + // 任务更新 + public boolean updateTask(TtTaskDoing ttTaskDoing) { + + Integer taskId = ttTaskDoing.getTaskId(); + + LambdaQueryWrapper ttTaskQuery = new LambdaQueryWrapper<>(); + LambdaUpdateWrapper ttTaskDoingUpdate = new LambdaUpdateWrapper<>(); + if (taskId.equals(1)) { + + // {首次下载奖励} + ttTaskQuery + .eq(TtTask::getId, 1); + TtTask task1 = ttTaskMapper.selectOne(ttTaskQuery); + + ttTaskDoingUpdate + .eq(TtTaskDoing::getUserId, ttTaskDoing.getUserId()) + .eq(TtTaskDoing::getTaskId, 1) + .set(TtTaskDoing::getProgress, task1.getTargetValue()) + .set(TtTaskDoing::getCompletionState, TaskCompletionState.COMPLETION.getCode()) + .set(TtTaskDoing::getCompeteTime, new Timestamp(System.currentTimeMillis())); + ttTaskDoingService.update(ttTaskDoingUpdate); + + return true; + } else if (taskId.equals(2)) { + + // {每日流水奖励} + ttTaskQuery.clear(); + ttTaskQuery + .eq(TtTask::getId, 2); + TtTask task2 = ttTaskMapper.selectOne(ttTaskQuery); + + Calendar nowDay = Calendar.getInstance(); + nowDay.set(Calendar.HOUR_OF_DAY, 0); + nowDay.set(Calendar.MINUTE, 0); + nowDay.set(Calendar.SECOND, 0); + nowDay.set(Calendar.MILLISECOND, 0); + Timestamp toDay = new Timestamp(nowDay.getTimeInMillis()); + + LambdaQueryWrapper creditsRecordsQuery = new LambdaQueryWrapper<>(); + creditsRecordsQuery + .eq(TtUserCreditsRecords::getUserId, ttTaskDoing.getUserId()) + .ge(TtUserCreditsRecords::getCreateTime, toDay); + List todayRecords = ttUserCreditsRecordsService.list(creditsRecordsQuery); + + // 累计今日流水 + BigDecimal credits = new BigDecimal("0"); + todayRecords.stream().forEach((item) -> { + if (item.getCredits().compareTo(BigDecimal.ZERO) >= 0) { + credits.add(item.getCredits()); + } + }); + + ttTaskDoingUpdate.clear(); + ttTaskDoingUpdate + .eq(TtTaskDoing::getUserId, ttTaskDoing.getUserId()) + .eq(TtTaskDoing::getTaskId, 2) + .set(TtTaskDoing::getProgress, credits.intValue()) + .set(credits.compareTo(new BigDecimal(task2.getTargetValue())) >= 0, TtTaskDoing::getCompletionState, TaskCompletionState.COMPLETION.getCode()) + .set(credits.compareTo(new BigDecimal(task2.getTargetValue())) >= 0, TtTaskDoing::getCompeteTime, new Timestamp(System.currentTimeMillis())); + ttTaskDoingService.update(ttTaskDoingUpdate); + + return true; + } else { + return false; + } + + } + +} diff --git a/skins-service/service-task/src/main/java/com/ruoyi/task/utils/task/PromotionWelfareTask.java b/skins-service/service-task/src/main/java/com/ruoyi/task/utils/task/PromotionWelfareTask.java new file mode 100644 index 0000000..1cea0d2 --- /dev/null +++ b/skins-service/service-task/src/main/java/com/ruoyi/task/utils/task/PromotionWelfareTask.java @@ -0,0 +1,69 @@ +package com.ruoyi.task.utils.task; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.task.DTO.pWelfareMQData; +import lombok.AllArgsConstructor; + +import java.math.BigDecimal; +import java.sql.Timestamp; + +// + +@AllArgsConstructor +public class PromotionWelfareTask implements Runnable { + + private TtUserService userService; + + private TtUser user; + + // 消费金额 + private BigDecimal price; + + @Override + public void run() { + // 统计推广福利 + pWelfareMQData msgDate = pWelfareMQData.builder() + .userId(user.getUserId()) + .account(price) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + + TtUser user = userService.getById(msgDate.getUserId()); + TtUser parent = userService.getById(user.getParentId()); + if (ObjectUtil.isNotEmpty(parent)){ + BigDecimal credits = null; + if ("01".equals(parent.getUserType())) { + credits = msgDate.getAccount().multiply(new BigDecimal("0.045")); + } else { + credits = msgDate.getAccount().multiply(new BigDecimal("0.01")); + } + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper + .eq(TtUser::getUserId,parent.getUserId()) + .set(TtUser::getAccountCredits,parent.getAccountCredits().add(credits)); + // .set(TtUser::getAccountAmount,parent.getAccountAmount().add(credits)); + userService.update(wrapper); + // userService.insertUserCreditsRecords(parent.getUserId(), + // TtAccountRecordType.INPUT, + // TtAccountRecordSource.P_WELFARE, + // credits, + // parent.getAccountCredits().add(credits), + // user.getUserId(), + // user.getNickName(), + // msgDate.getAccount()); + userService.insertUserAmountRecords(parent.getUserId(), + TtAccountRecordType.INPUT, + TtAccountRecordSource.P_WELFARE, + credits, + parent.getAccountCredits().add(credits), + user.getUserId(), + user.getNickName(), + msgDate.getAccount()); + } + } +} diff --git a/skins-service/service-task/src/main/resources/com/ruoyi/task/mapper/TtTaskDoingMapper.xml b/skins-service/service-task/src/main/resources/com/ruoyi/task/mapper/TtTaskDoingMapper.xml new file mode 100644 index 0000000..a6acdaa --- /dev/null +++ b/skins-service/service-task/src/main/resources/com/ruoyi/task/mapper/TtTaskDoingMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-task/src/main/resources/com/ruoyi/task/mapper/TtTaskMapper.xml b/skins-service/service-task/src/main/resources/com/ruoyi/task/mapper/TtTaskMapper.xml new file mode 100644 index 0000000..356c5d5 --- /dev/null +++ b/skins-service/service-task/src/main/resources/com/ruoyi/task/mapper/TtTaskMapper.xml @@ -0,0 +1,37 @@ + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-thirdparty/pom.xml b/skins-service/service-thirdparty/pom.xml new file mode 100644 index 0000000..b8bbc4d --- /dev/null +++ b/skins-service/service-thirdparty/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + com.ruoyi + skins-service + 4.8.2 + + + service-thirdparty + + + 8 + 8 + UTF-8 + + + + + + com.ruoyi + ruoyi-framework + + + + + com.ruoyi + service-admin + 4.8.2 + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + + + + + com.google.zxing + javase + 3.3.3 + + + + + com.alipay.sdk + alipay-sdk-java + 4.38.161.ALL + + + + org.apache.httpcomponents + httpclient + 4.5.1 + + + + \ No newline at end of file diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/config/MYConfig.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/config/MYConfig.java new file mode 100644 index 0000000..27628df --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/config/MYConfig.java @@ -0,0 +1,26 @@ +package com.ruoyi.thirdparty.MaYi.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "mayi") +public class MYConfig { + + private String apiKey; + private String gateway; + private String memberid; + private String notifyBaseUrl; + private String callBackUrl; + // (银行)通道编码 + private String payBankCode; + + // 下单接口 + public static final String ApiPayAddOrder = "/Pay_AddOrder"; + + // 查询订单接口 + public static final String ApiQueryTrans = "/Pay_Trade_query.html"; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/controller/MYController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/controller/MYController.java new file mode 100644 index 0000000..f759acb --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/controller/MYController.java @@ -0,0 +1,90 @@ +package com.ruoyi.thirdparty.MaYi.controller; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.mayi.PayNotifyRequest; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.thirdparty.MaYi.config.MYConfig; +import com.ruoyi.thirdparty.MaYi.service.MYService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Map; + +@Api(tags = "聚合支付") +@Slf4j +@RestController +@RequestMapping("api/mayi") +public class MYController extends BaseController { + + @Autowired + private MYService myService; + + @Autowired + private TtUserService userService; + + // 下单 + @ApiOperation("预下单") + @PostMapping("/ApiAddTrans") + @UpdateUserCache + public R ApiAddTrans(HttpServletRequest request, @RequestBody @Validated CreateOrderParam param) { + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) return checkLogin; + Integer userId = ((Long) checkLogin.getData()).intValue(); + + TtUser user = userService.getById(userId); + // 是否实名认证0未认证,1已认证' + if (user.getIsRealCheck().equals("0")) { + return R.fail("请实名认证后再进行充值!"); + } + return myService.ApiAddTrans(param, user, request); + } + + // 支付回调 + @ApiOperation("支付回调") + @PostMapping("/notify") + public String addTransNotify(@RequestBody PayNotifyRequest data) { + log.info("聚合支付支付回调 {}", JSONUtil.toJsonStr(data)); + return myService.payNotify(data); + } + + // 查询订单接口 + // @PostMapping("/ApiQueryTrans") + // public String ApiQueryTrans(HttpServletRequest request, Map param) { + // return "success"; + // } + // + // // 查询余额接口 + // @PostMapping("/ApiQueryBalancePHP") + // public String ApiQueryBalancePHP(HttpServletRequest request, Map param) { + // return "success"; + // } + + public R checkLogin() { + Long userId; + try { + userId = getUserId(); + if (ObjectUtil.isEmpty(userId)) return R.fail(401,"登录过期,请重新登录。"); + return R.ok(userId); + } catch (Exception e) { + return R.fail("登录过期,请重新登录。"); + } + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/service/Impl/MYServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/service/Impl/MYServiceImpl.java new file mode 100644 index 0000000..1c6cc89 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/service/Impl/MYServiceImpl.java @@ -0,0 +1,424 @@ +package com.ruoyi.thirdparty.MaYi.service.Impl; + +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.domain.common.constant.PayOrderStatus; +import com.ruoyi.domain.common.constant.PayType; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.dto.mayi.ApiPayAddOrderResponse; +import com.ruoyi.domain.dto.mayi.PayNotifyRequest; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.TtPromotionLevel; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.MaYi.config.MYConfig; +import com.ruoyi.thirdparty.MaYi.service.MYService; +import com.ruoyi.thirdparty.MaYi.utils.signUtil; +import com.ruoyi.thirdparty.common.service.RechargeSuccessfulNoticeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.servlet.http.HttpServletRequest; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +@Slf4j +@Service +public class MYServiceImpl implements MYService { + + @Autowired + private MYConfig myConfig; + + @Autowired + private TtOrderMapper orderMapper; + + @Autowired + private Executor customThreadPoolExecutor; + + @Autowired + private TtUserService userService; + + @Autowired + private TtUserMapper userMapper; + + @Autowired + private TtRechargeProdMapper rechargeListMapper; + + @Autowired + private TtUserBlendErcashMapper userBlendErcashMapper; + + @Autowired + private TtRechargeProdMapper rechargeProdMapper; + + @Autowired + private RechargeSuccessfulNoticeService rechargeSuccessfulNoticeService; + + @Autowired + private ISysConfigService configService; + + @Autowired + private TtPromotionLevelMapper ttPromotionLevelMapper; + + @Autowired + private TtFirstRechargeMapper ttFirstRechargeMapper; + + @Override + public R ApiAddTrans(@Validated CreateOrderParam param, TtUser user, HttpServletRequest request) { + + // 查询商品信息 + TtRechargeProd goods = new LambdaQueryChainWrapper<>(rechargeProdMapper) + .eq(TtRechargeProd::getId, param.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + if (ObjectUtil.isEmpty(goods)) return R.fail("不存在的商品。"); + if (goods.getPrice().compareTo(param.getGoodsPrice()) != 0) return R.fail("商品价格不一致。"); + + // 总价值 + BigDecimal totalAmount = param.getGoodsPrice().multiply(new BigDecimal(param.getGoodsNum())); + + // 构建完整参数 + Map map = buildBaseParam(new HashMap<>(), request); + + Snowflake snowflake = IdUtil.getSnowflake(1, 1); + String orderId = String.valueOf(snowflake.nextId()); + map.put("pay_orderid", orderId); + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String tranDateTime = dateFormat.format(new Date()); + map.put("pay_applydate", tranDateTime); + + map.put("pay_bankcode", myConfig.getPayBankCode()); + + // 回调地址 + map.put("pay_notifyurl", myConfig.getNotifyBaseUrl()); + + // 同步跳转 + map.put("pay_callbackurl", myConfig.getCallBackUrl() + "/api/mayi/notify"); + + map.put("pay_amount", totalAmount.toString()); + + // 签名 + String sign = signUtil.getSign(map, myConfig.getApiKey()); + map.put("pay_md5sign", sign); + map.put("pay_clientip", IpUtils.getIpAddr()); + map.put("pay_productname", goods.getName()); + map.put("type", "json"); + + // System.out.println(JSONUtil.toJsonStr(map)); + + // 发送请求 + HttpRequest post = HttpUtil.createPost(myConfig.getGateway() + MYConfig.ApiPayAddOrder); + post.header("Content-Type","application/x-www-form-urlencoded"); + post.formStr(map); + HttpResponse res = post.execute(); + + // 解析响应 + ApiPayAddOrderResponse resBody = JSONUtil.toBean(res.body(), ApiPayAddOrderResponse.class); + if (!resBody.getStatus().equals("1")) { + return R.fail(resBody); + } + + // 创建订单 + TtOrder order = new TtOrder(); + order.setOrderId(orderId); + order.setOutTradeNo(resBody.getPay_orderid()); + + order.setUserId(user.getUserId()); + order.setType(PayType.JU_HE_ZHI_FU.getCode()); + + order.setGoodsId(param.getGoodsId()); + order.setGoodsPrice(param.getGoodsPrice()); + order.setGoodsNum(param.getGoodsNum()); + order.setTotalAmount(totalAmount); + + order.setSign(sign); + order.setStatus(PayOrderStatus.NO_PAY.getCode()); + + order.setPayUrl(resBody.getPayUrl()); + + order.setCreateTime(new Date()); + order.setUpdateTime(new Date()); + + orderMapper.insert(order); + return R.ok(order); + } + + @Override + public String payNotify(PayNotifyRequest data) { + TtOrder order = null; + // TODO: 2024/4/12 最好再查询一下第三方平台的订单信息 + try { + // 查询订单信息 + order = new LambdaQueryChainWrapper<>(orderMapper) + .eq(TtOrder::getOrderId, data.getOrderid()) + .eq(TtOrder::getStatus, PayOrderStatus.NO_PAY.getCode()) + .one(); + + if (ObjectUtil.isEmpty(order)) { + log.warn("支付回调异常,不存在的有效订单。"); + return "fail"; + } + + // 查询用户信息 + TtUser user = new LambdaQueryChainWrapper<>(userMapper) + .eq(TtUser::getUserId, order.getUserId()) + .eq(TtUser::getDelFlag, 0) + .one(); + if (ObjectUtil.isEmpty(user)) { + log.warn("支付回调异常,不存在的有效用户。"); + return "fail"; + } + + if (order.getStatus().equals(PayOrderStatus.PAY_COMPLE.getCode()) || order.getStatus().equals(PayOrderStatus.PAY_YET.getCode())) { + log.warn("重复的回调!该订单已完成。"); + return "success"; + } + + // 查询商品信息 + TtRechargeProd goods = new LambdaQueryChainWrapper<>(rechargeListMapper) + .eq(TtRechargeProd::getId, order.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + if (ObjectUtil.isEmpty(goods)) { + log.warn("支付回调异常,不存在的商品。"); + return "fail"; + } + + // 账户结算 + if (ObjectUtil.isNull(goods.getProductA())) goods.setProductA(BigDecimal.ZERO); + if (ObjectUtil.isNull(goods.getProductC())) goods.setProductC(BigDecimal.ZERO); + payNotifyAccounting(order, user, goods, order.getGoodsNum()); + + // 首充赠送 + firstChargeGiftAmount(user, goods, order.getGoodsNum()); + + // 推广等级充值赠送 + promotionLevelChargeGiftAmount(user, goods, order.getGoodsNum()); + + // 推广充值返佣 + promotionChargeCommission(user, goods, order.getGoodsNum()); + + // 更新订单 + new LambdaUpdateChainWrapper<>(orderMapper) + .eq(TtOrder::getId, order.getId()) + .set(TtOrder::getStatus, PayOrderStatus.PAY_COMPLE.getCode()) + .set(TtOrder::getUpdateTime, new Date()) + .update(); + + // 发送充值成功通知 + rechargeSuccessfulNoticeService.sendRechargeSuccessNotice(user.getUserId().toString(), order.getGoodsPrice()); + + return "success"; + } catch (Exception e) { + e.printStackTrace(); + + log.warn("聚合支付,回调异常"); + + if (ObjectUtil.isNotNull(order)) { + new LambdaUpdateChainWrapper<>(orderMapper) + .eq(TtOrder::getId, order.getId()) + .set(TtOrder::getStatus, PayOrderStatus.CALL_BACK_ERRO.getCode()) + .set(TtOrder::getUpdateTime, new Date()) + .update(); + } + return "fail"; + } + } + + @Override + public R ApiQueryTrans(Map param, TtUser user) { + return null; + } + + @Override + public R ApiPropayTrans(Map param, TtUser user) { + return null; + } + + @Override + public R ApiQueryBalancePHP(Map param, TtUser user) { + return null; + } + + // 构建基本请求信息 + private Map buildBaseParam(Map map, HttpServletRequest request) { + map.put("pay_memberid", myConfig.getMemberid()); + return map; + } + + // 账户结算 + public void payNotifyAccounting(TtOrder order, TtUser user, TtRechargeProd goods, Integer goodsNumber) { + + // 加钱 + // BigDecimal totalAmount = order.getTotalAmount(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal totalCredits = goods.getProductC().multiply(new BigDecimal(goodsNumber)); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, user.getUserId()) + .setSql("account_amount = account_amount + " + totalAmount.toString() + + ",account_credits = account_credits + " + totalCredits.toString()); + userService.update(userUpdate); + + user = userService.getById(user.getUserId()); + // 综合消费日志 + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(user.getUserId()) + + .amount(totalAmount.compareTo(BigDecimal.ZERO) > 0 ? totalAmount : null) + .finalAmount(totalAmount.compareTo(BigDecimal.ZERO) > 0 ? user.getAccountAmount().add(totalAmount) : null) + + .credits(totalCredits.compareTo(BigDecimal.ZERO) > 0 ? totalCredits : null) + .finalCredits(totalCredits.compareTo(BigDecimal.ZERO) > 0 ? user.getAccountCredits().add(totalCredits) : null) + + .total(totalAmount.add(totalCredits)) // 收支合计 + + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.RECHARGE.getCode()) + .remark(TtAccountRecordSource.RECHARGE.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + userBlendErcashMapper.insert(blendErcash); + + // 更新VIP等级 + userService.updateUserVIPLevel(user.getUserId()); + } + + /** + * 首充赠送 + */ + public void firstChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + // 1.判断是否为首充 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .eq(TtUserBlendErcash::getSource, 1) + .eq(TtUserBlendErcash::getUserId, ttUser.getUserId()); + List ttUserBlendErcashes = userBlendErcashMapper.selectList(wrapper); + if (ttUserBlendErcashes.size() > 0) { + return; + } + + // 2.加钱 + // 充值金额 + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + // 查询赠送比例 + BigDecimal firstChargeAmountRatio = ttFirstRechargeMapper.selectRatioByMinAmount(totalAmount); + if (Objects.isNull(firstChargeAmountRatio)) { + return; + } + // 计算赠送金额 + BigDecimal giftAmount = totalAmount.multiply(firstChargeAmountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 3.记录 + ttUser = userService.getById(ttUser.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.FIRST_CHARGE.getCode()) + .remark(TtAccountRecordSource.FIRST_CHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } + + /** + * 推广等级充值赠送 + */ + public void promotionLevelChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + // 1.查询用户推广等级对应的赠送比例 + TtPromotionLevel ttPromotionLevel = ttPromotionLevelMapper.selectById(ttUser.getPromotionLevel()); + + // 2.加钱 + BigDecimal amountRatio = ttPromotionLevel.getRechargeGiftRatio(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal giftAmount = totalAmount.multiply(amountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 3.记录 + ttUser = userService.getById(ttUser.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_LEVEL_CHARGE.getCode()) + .remark(TtAccountRecordSource.PROMOTION_LEVEL_CHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } + + /** + * 推广充值返佣 + */ + private void promotionChargeCommission(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + if (Objects.isNull(ttUser.getParentId())) { + return; + } + // 查询用户推广等级信息 + TtPromotionLevel ttPromotionLevel = ttPromotionLevelMapper.selectById(ttUser.getPromotionLevel()); + + // 给上级加钱 + BigDecimal amountRatio = ttPromotionLevel.getCommissions(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal giftAmount = totalAmount.multiply(amountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getParentId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 记录 + ttUser = userService.getById(ttUser.getParentId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_CHARGE_COMMISSION.getCode()) + .remark(TtAccountRecordSource.PROMOTION_CHARGE_COMMISSION.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/service/MYService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/service/MYService.java new file mode 100644 index 0000000..b28b64b --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/service/MYService.java @@ -0,0 +1,35 @@ +package com.ruoyi.thirdparty.MaYi.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.mayi.PayNotifyRequest; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Map; + +public interface MYService { + + R ApiAddTrans(CreateOrderParam param, TtUser user, HttpServletRequest request); + R ApiQueryTrans(Map param, TtUser user); + R ApiPropayTrans(Map param, TtUser user); + R ApiQueryBalancePHP(Map param, TtUser user); + + public void payNotifyAccounting(TtOrder order, TtUser user, TtRechargeProd goods, Integer goodsNumber); + + String payNotify(PayNotifyRequest data); + + /** + * �׳����� + */ + public void firstChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber); + + /** + * �ƹ�ȼ���ֵ���� + */ + public void promotionLevelChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber); + + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/utils/ZYHTTPRes.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/utils/ZYHTTPRes.java new file mode 100644 index 0000000..941fc44 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/utils/ZYHTTPRes.java @@ -0,0 +1,47 @@ +package com.ruoyi.thirdparty.MaYi.utils; + +// zy返回值 + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class ZYHTTPRes { + + public enum resCode{ + + success("0000"), + DOING("0001"); + + private String code; + + resCode(String code){ + this.code = code; + } + public String getCode(){ + return this.code; + } + } + + private String respCode; + //返回码说明 + private String respDesc; + //返回码 + private String payCode; + private String payDesc; + private String amt; + private String tranTime; + private String tranType; + //扩展信息 + private String msgExt; + private String payUrl; + // 交易流水号 + private String merReqNo; + //服务端响应流水号 + private String serverRspNo; + private String sign; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/utils/signUtil.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/utils/signUtil.java new file mode 100644 index 0000000..7e08625 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/MaYi/utils/signUtil.java @@ -0,0 +1,39 @@ +package com.ruoyi.thirdparty.MaYi.utils; + +import cn.hutool.crypto.digest.DigestAlgorithm; +import cn.hutool.crypto.digest.Digester; +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; + +import java.security.MessageDigest; +import java.util.*; + +public class signUtil { + + public static String getSign(Map map, String key) { + + // 排序 + List> entryList = new ArrayList<>(map.entrySet()); + Collections.sort(entryList, new Comparator>() { + @Override + public int compare(Map.Entry o1, Map.Entry o2) { + return o1.getKey().compareTo(o2.getKey()); + } + }); + + String str = ""; + for (int i = 0; i < entryList.size(); i++) { + // if (i == entryList.size()-1) { + // str = str + entryList.get(i).getKey() + "=" + entryList.get(i).getValue(); + // continue; + // } + str = str + entryList.get(i).getKey() + "=" + entryList.get(i).getValue() + "&"; + } + str = str + "key=" +key; + + // MD5加密 + Digester md5 = new Digester(DigestAlgorithm.MD5); + return md5.digestHex(str).toUpperCase(); + + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/config/AliCilent.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/config/AliCilent.java new file mode 100644 index 0000000..7610fa6 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/config/AliCilent.java @@ -0,0 +1,32 @@ +package com.ruoyi.thirdparty.alipay.config; + +import com.alipay.api.AlipayClient; +import com.alipay.api.DefaultAlipayClient; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +@Configuration +@EnableConfigurationProperties(value = AliProperties.class) +public class AliCilent { + + @Bean + @Qualifier("RealNameAuthentication") + public AlipayClient RealNameAuthentication(AliProperties aliProperties) { + List merchants = aliProperties.getMerchants(); + if (merchants.isEmpty()) throw new IllegalArgumentException("No merchant configuration provided"); + MerchantConfig merchantConfig = merchants.get(1); + return new DefaultAlipayClient( + aliProperties.getServerUrl(), + merchantConfig.getAppId(), + merchantConfig.getPrivateKey(), + aliProperties.getFormat(), + aliProperties.getCharset(), + merchantConfig.getAlipayPublicKey(), + aliProperties.getSignType() + ); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/config/AliProperties.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/config/AliProperties.java new file mode 100644 index 0000000..46ebcdb --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/config/AliProperties.java @@ -0,0 +1,32 @@ +package com.ruoyi.thirdparty.alipay.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.List; + +@Data +@ConfigurationProperties(prefix = "alipay") +public class AliProperties { + + private String serverUrl; + + private String format; + + private String charset; + + private String signType; + + private List merchants; + + private String notifyUrl; + + private String realNameAuthenticationReturnUrl; + + private String authenticationHost; + + private String authenticationPath; + + private String authenticationAppcode; + +} \ No newline at end of file diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/config/MerchantConfig.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/config/MerchantConfig.java new file mode 100644 index 0000000..5a29e84 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/config/MerchantConfig.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.alipay.config; + +import lombok.Data; + +@Data +public class MerchantConfig { + + private String appId; + + private String privateKey; + + private String alipayPublicKey; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/controller/AliPayController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/controller/AliPayController.java new file mode 100644 index 0000000..d320b85 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/controller/AliPayController.java @@ -0,0 +1,55 @@ +package com.ruoyi.thirdparty.alipay.controller; + +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.UserPermission; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.thirdparty.alipay.service.AliPayService; +import com.ruoyi.thirdparty.wechat.service.ApiTokenService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@Api(tags = "支付宝") +@RestController +@RequestMapping("/api/alipay") +public class AliPayController { + + private final ApiTokenService tokenService; + private final AliPayService aliPayService; + private final TtUserService ttUserService; + + public AliPayController(ApiTokenService apiTokenService, + AliPayService aliPayService, + TtUserService ttUserService) { + this.tokenService = apiTokenService; + this.aliPayService = aliPayService; + this.ttUserService = ttUserService; + } + + @ApiOperation("支付") + @UserPermission + @PostMapping(value = "/trade/page/pay") + public R pay(@RequestBody @Validated CreateOrderParam param) { + Long userId = SecurityUtils.getUserId(); + TtUser ttUser = ttUserService.getById(userId); + // 是否实名认证(0未认证 1已认证) + if ("0".equals(ttUser.getIsRealCheck())) { + return R.fail("未实名认证"); + } + String ip = IpUtils.getIpAddr(); + return aliPayService.pay(param, ttUser, ip); + } + + @PostMapping(value = "/callBack") + public String callBack(@RequestParam Map params) { + return aliPayService.callBack(params); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/service/AliPayService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/service/AliPayService.java new file mode 100644 index 0000000..02d0377 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/service/AliPayService.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.alipay.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; + +import java.util.Map; + +public interface AliPayService { + + R pay(CreateOrderParam param, TtUser ttUser, String ip); + + String callBack(Map params); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/service/RealNameAuthenticationService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/service/RealNameAuthenticationService.java new file mode 100644 index 0000000..bef6154 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/service/RealNameAuthenticationService.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.alipay.service; + +public interface RealNameAuthenticationService { + + String authInitialize(String realName, String idNum); + + String startCertify(String certifyId); + + String queryCertifyResult(String certifyId); + + String authentication(String realName, String idNum); + + String authentication2(String realName, String idNum, String phoneNum); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/service/impl/AliPayServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/service/impl/AliPayServiceImpl.java new file mode 100644 index 0000000..8fe443d --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/service/impl/AliPayServiceImpl.java @@ -0,0 +1,389 @@ +package com.ruoyi.thirdparty.alipay.service.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alipay.api.AlipayApiException; +import com.alipay.api.AlipayClient; +import com.alipay.api.AlipayConstants; +import com.alipay.api.DefaultAlipayClient; +import com.alipay.api.domain.AlipayTradePrecreateModel; +import com.alipay.api.internal.util.AlipaySignature; +import com.alipay.api.request.AlipayTradePrecreateRequest; +import com.alipay.api.response.AlipayTradePrecreateResponse; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.common.constant.PayOrderStatus; +import com.ruoyi.domain.common.constant.PayType; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.TtPromotionLevel; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.alipay.config.AliProperties; +import com.ruoyi.thirdparty.alipay.service.AliPayService; +import com.ruoyi.thirdparty.common.service.RechargeSuccessfulNoticeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Slf4j +@Service +public class AliPayServiceImpl implements AliPayService { + + @Autowired + private TtRechargeProdMapper rechargeProdMapper; + + @Autowired + private AliProperties aliProperties; + + @Autowired + private TtOrderMapper orderMapper; + + @Autowired + private TtUserBlendErcashMapper userBlendErcashMapper; + + @Autowired + private TtUserService userService; + + @Autowired + private TtUserMapper userMapper; + + @Autowired + private RechargeSuccessfulNoticeService rechargeSuccessfulNoticeService; + + @Autowired + private TtRechargeProdMapper rechargeListMapper; + + @Autowired + private ISysConfigService configService; + + @Autowired + private TtPromotionLevelMapper ttPromotionLevelMapper; + + @Autowired + private TtFirstRechargeMapper ttFirstRechargeMapper; + + @Override + public R pay(CreateOrderParam param, TtUser ttUser, String ip) { + // 查询商品信息 + TtRechargeProd goods = new LambdaQueryChainWrapper<>(rechargeProdMapper) + .eq(TtRechargeProd::getId, param.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + if (ObjectUtil.isEmpty(goods)) { + return R.fail("不存在的商品。"); + } + if (goods.getPrice().compareTo(param.getGoodsPrice()) != 0) { + return R.fail("商品价格不一致。"); + } + + // 计算总价值 + BigDecimal totalAmount = param.getGoodsPrice().multiply(new BigDecimal(param.getGoodsNum())); + + // 构建支付 + String serverUrl = aliProperties.getServerUrl(); + String appId = aliProperties.getMerchants().get(0).getAppId(); + String privateKey = aliProperties.getMerchants().get(0).getPrivateKey(); + String format = aliProperties.getFormat(); + String charset = aliProperties.getCharset(); + String alipayPublicKey = aliProperties.getMerchants().get(0).getAlipayPublicKey(); + String signType = aliProperties.getSignType(); + String notifyUrl = aliProperties.getNotifyUrl(); + + // 初始化SDK + AlipayClient alipayClient = new DefaultAlipayClient(serverUrl, appId, privateKey, format, charset, alipayPublicKey, signType); + + // 构造请求参数以调用接口 + AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); + AlipayTradePrecreateModel model = new AlipayTradePrecreateModel(); + + // 设置通知地址 + request.setNotifyUrl(notifyUrl); + + // 设置订单标题 + model.setSubject(goods.getName()); + // 设置商户订单号 + String orderId = String.valueOf(IdUtil.getSnowflake(1, 1).nextId()); + model.setOutTradeNo(orderId); + // 设置订单总金额 + model.setTotalAmount(totalAmount.toString()); + request.setBizModel(model); + + try { + AlipayTradePrecreateResponse response = alipayClient.execute(request); + if (response.isSuccess()) { + // 创建订单 + TtOrder order = new TtOrder(); + order.setOrderId(orderId); + order.setOutTradeNo(response.getOutTradeNo()); + order.setUserId(ttUser.getUserId()); + order.setType(PayType.ZFB.getCode()); + order.setGoodsId(param.getGoodsId()); + order.setGoodsPrice(param.getGoodsPrice()); + order.setGoodsNum(param.getGoodsNum()); + order.setTotalAmount(totalAmount); + order.setStatus(PayOrderStatus.NO_PAY.getCode()); + order.setCreateTime(new Date()); + orderMapper.insert(order); + JSONObject jsonObject = JSON.parseObject(response.getBody()); + return R.ok(jsonObject); + } + } catch (AlipayApiException e) { + e.printStackTrace(); + } + return R.fail("支付失败"); + } + + @Override + public String callBack(Map params) { + log.info("支付成功通知"); + String result = "failure"; + String alipayPublicKey = aliProperties.getMerchants().get(0).getAlipayPublicKey(); + try { + // 异步通知校验 + boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayPublicKey, + AlipayConstants.CHARSET_UTF8, + AlipayConstants.SIGN_TYPE_RSA2); + if (!signVerified) { + log.error("支付成功,异步通知验签失败!"); + return result; + } + // 进行二次校验 + String tradeStatus = params.get("trade_status"); + if (!"TRADE_SUCCESS".equals(tradeStatus)) { + log.error("交易失败"); + return result; + } + // 处理业务 + // 查询订单信息 + String outTradeNo = params.get("out_trade_no"); + TtOrder order = new LambdaQueryChainWrapper<>(orderMapper) + .eq(TtOrder::getOutTradeNo, outTradeNo) + .eq(TtOrder::getStatus, PayOrderStatus.NO_PAY.getCode()) + .one(); + // 防止重复通知 + if (!PayOrderStatus.NO_PAY.getCode().equals(order.getStatus())) { + return result; + } + // 查询用户信息 + TtUser user = new LambdaQueryChainWrapper<>(userMapper) + .eq(TtUser::getUserId, order.getUserId()) + .eq(TtUser::getDelFlag, 0) + .one(); + // 查询商品信息 + TtRechargeProd goods = new LambdaQueryChainWrapper<>(rechargeListMapper) + .eq(TtRechargeProd::getId, order.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + // 账户结算 + if (ObjectUtil.isNull(goods.getProductA())) goods.setProductA(BigDecimal.ZERO); + if (ObjectUtil.isNull(goods.getProductC())) goods.setProductC(BigDecimal.ZERO); + payNotifyAccounting(order, user, goods, order.getGoodsNum()); + + try { + // 首充赠送 + firstChargeGiftAmount(user, goods, order.getGoodsNum()); + + // 推广等级充值赠送 + promotionLevelChargeGiftAmount(user, goods, order.getGoodsNum()); + + // 推广充值返佣 + promotionChargeCommission(user, goods, order.getGoodsNum()); + } catch (Exception e) { + e.printStackTrace(); + } + + // 更新订单 + boolean update = new LambdaUpdateChainWrapper<>(orderMapper) + .eq(TtOrder::getId, order.getId()) + .set(TtOrder::getStatus, PayOrderStatus.PAY_COMPLE.getCode()) + .set(TtOrder::getUpdateTime, new Date()) + .update(); + if (update) { + result = "success"; + } + + // 发送充值成功通知 + rechargeSuccessfulNoticeService.sendRechargeSuccessNotice(order.getUserId().toString(), order.getGoodsPrice()); + } catch (AlipayApiException e) { + e.printStackTrace(); + } + return result; + } + + // 账户结算 + public void payNotifyAccounting(TtOrder order, TtUser user, TtRechargeProd goods, Integer goodsNumber) { + + // 加钱 + // BigDecimal totalAmount = order.getTotalAmount(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal totalCredits = goods.getProductC().multiply(new BigDecimal(goodsNumber)); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, user.getUserId()) + .setSql("account_amount = account_amount + " + totalAmount.toString() + + ",account_credits = account_credits + " + totalCredits.toString()); + userService.update(userUpdate); + + user = userService.getById(user.getUserId()); + // 综合消费日志 + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(user.getUserId()) + + .amount(totalAmount.compareTo(BigDecimal.ZERO) > 0 ? totalAmount : null) + .finalAmount(totalAmount.compareTo(BigDecimal.ZERO) > 0 ? user.getAccountAmount().add(totalAmount) : null) + + .credits(totalCredits.compareTo(BigDecimal.ZERO) > 0 ? totalCredits : null) + .finalCredits(totalCredits.compareTo(BigDecimal.ZERO) > 0 ? user.getAccountCredits().add(totalCredits) : null) + + .total(totalAmount.add(totalCredits)) // 收支合计 + + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.RECHARGE.getCode()) + .remark(TtAccountRecordSource.RECHARGE.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + userBlendErcashMapper.insert(blendErcash); + + // 更新VIP等级 + userService.updateUserVIPLevel(user.getUserId()); + } + + /** + * 首充赠送 + */ + private void firstChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + // 1.判断是否为首充 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .eq(TtUserBlendErcash::getSource, 1) + .eq(TtUserBlendErcash::getUserId, ttUser.getUserId()); + List ttUserBlendErcashes = userBlendErcashMapper.selectList(wrapper); + if (ttUserBlendErcashes.size() > 0) { + return; + } + + // 2.加钱 + // 充值金额 + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + // 查询赠送比例 + BigDecimal firstChargeAmountRatio = ttFirstRechargeMapper.selectRatioByMinAmount(totalAmount); + if (Objects.isNull(firstChargeAmountRatio)) { + return; + } + // 计算赠送金额 + BigDecimal giftAmount = totalAmount.multiply(firstChargeAmountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 3.记录 + ttUser = userService.getById(ttUser.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.FIRST_CHARGE.getCode()) + .remark(TtAccountRecordSource.FIRST_CHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } + + /** + * 推广等级充值赠送 + */ + private void promotionLevelChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + // 1.查询用户推广等级对应的赠送比例 + TtPromotionLevel ttPromotionLevel = ttPromotionLevelMapper.selectById(ttUser.getPromotionLevel()); + if (Objects.isNull(ttPromotionLevel)) + return; + + // 2.加钱 + BigDecimal amountRatio = ttPromotionLevel.getRechargeGiftRatio(); + if (Objects.isNull(amountRatio)) + return; + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal giftAmount = totalAmount.multiply(amountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 3.记录 + ttUser = userService.getById(ttUser.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_LEVEL_CHARGE.getCode()) + .remark(TtAccountRecordSource.PROMOTION_LEVEL_CHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } + + /** + * 推广充值返佣 + */ + private void promotionChargeCommission(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + if (Objects.isNull(ttUser.getParentId())) { + return; + } + // 查询用户推广等级信息 + TtPromotionLevel ttPromotionLevel = ttPromotionLevelMapper.selectById(ttUser.getPromotionLevel()); + + // 给上级加钱 + BigDecimal amountRatio = ttPromotionLevel.getCommissions(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal giftAmount = totalAmount.multiply(amountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getParentId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 记录 + ttUser = userService.getById(ttUser.getParentId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_CHARGE_COMMISSION.getCode()) + .remark(TtAccountRecordSource.PROMOTION_CHARGE_COMMISSION.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/service/impl/RealNameAuthenticationServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/service/impl/RealNameAuthenticationServiceImpl.java new file mode 100644 index 0000000..87fac42 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/service/impl/RealNameAuthenticationServiceImpl.java @@ -0,0 +1,219 @@ +package com.ruoyi.thirdparty.alipay.service.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson2.JSONObject; +import com.alipay.api.AlipayApiException; +import com.alipay.api.AlipayClient; +import com.alipay.api.request.AlipayUserCertifyOpenCertifyRequest; +import com.alipay.api.request.AlipayUserCertifyOpenInitializeRequest; +import com.alipay.api.request.AlipayUserCertifyOpenQueryRequest; +import com.alipay.api.response.AlipayUserCertifyOpenCertifyResponse; +import com.alipay.api.response.AlipayUserCertifyOpenInitializeResponse; +import com.alipay.api.response.AlipayUserCertifyOpenQueryResponse; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.thirdparty.alipay.config.AliProperties; +import com.ruoyi.thirdparty.alipay.service.RealNameAuthenticationService; +import com.ruoyi.thirdparty.alipay.utils.HttpUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpResponse; +import org.apache.http.util.EntityUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@Service +@Slf4j +public class RealNameAuthenticationServiceImpl implements RealNameAuthenticationService { + + @Qualifier("RealNameAuthentication") + private final AlipayClient RealNameAuthentication; + private final AliProperties aliProperties; + + @Autowired + private RestTemplate restTemplate; + + public RealNameAuthenticationServiceImpl(AlipayClient realNameAuthentication, + AliProperties aliProperties) { + RealNameAuthentication = realNameAuthentication; + this.aliProperties = aliProperties; + } + + @Override + public String authInitialize(String realName, String idNum) { + AlipayUserCertifyOpenInitializeRequest request = new AlipayUserCertifyOpenInitializeRequest(); + JSONObject identityObj = new JSONObject(); + identityObj.put("identity_type", "CERT_INFO"); + identityObj.put("cert_type", "IDENTITY_CARD"); + identityObj.put("cert_name", realName); + identityObj.put("cert_no", idNum); + JSONObject merchantConfigObj = new JSONObject(); + try { + merchantConfigObj.put("return_url", aliProperties.getRealNameAuthenticationReturnUrl()); + JSONObject bizContentObj = new JSONObject(); + bizContentObj.put("outer_order_no", IdUtil.simpleUUID()); + bizContentObj.put("biz_code", "FACE"); + bizContentObj.put("identity_param", identityObj); + bizContentObj.put("merchant_config", merchantConfigObj); + request.setBizContent(bizContentObj.toString()); + AlipayUserCertifyOpenInitializeResponse response = RealNameAuthentication.execute(request); + if (response.isSuccess()) { + return response.getCertifyId(); + } else { + return null; + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public String startCertify(String certifyId) { + AlipayUserCertifyOpenCertifyRequest request = new AlipayUserCertifyOpenCertifyRequest(); + JSONObject bizContentObj = new JSONObject(); + bizContentObj.put("certify_id", certifyId); + request.setBizContent(bizContentObj.toString()); + try { + AlipayUserCertifyOpenCertifyResponse response = RealNameAuthentication.pageExecute(request, "GET"); + if (response.isSuccess()) { + return "alipays://platformapi/startapp?appId=20000067&url=" + URLEncoder.encode(response.getBody(), "UTF-8"); + } else { + return null; + } + } catch (AlipayApiException | UnsupportedEncodingException e) { + return null; + } + } + + @Override + public String queryCertifyResult(String certifyId) { + AlipayUserCertifyOpenQueryRequest request = new AlipayUserCertifyOpenQueryRequest(); + JSONObject bizContentObj = new JSONObject(); + bizContentObj.put("certify_id", certifyId); + request.setBizContent(bizContentObj.toString()); + try { + AlipayUserCertifyOpenQueryResponse response = RealNameAuthentication.execute(request); + if (StrUtil.equals(response.getCode(), "40004")) { + return response.getSubMsg(); + } + if (response.isSuccess()) { + String body = response.getBody(); + log.info("返回的结构体:{}", body); + if (StringUtils.isNotBlank(body)) { + JSONObject jsonObject = JSONObject.parseObject(body); + JSONObject queryResponse = jsonObject.getJSONObject("alipay_user_certify_open_query_response"); + if (StrUtil.equals(queryResponse.getString("code"), "10000") && StrUtil.equals(queryResponse.getString("passed"), "T")) { + return ""; + } else return "认证失败,请重新认证!"; + } + } else return "认证查询服务调用失败!"; + } catch (AlipayApiException e) { + return "开始认证服务调用失败!"; + } + return "认证失败,请重新认证!"; + } + + @Override + public String authentication(String realName, String idNum) { + log.info("realName is {}, idNum is {}", realName, idNum); + String method = "POST"; + Map headers = new HashMap(); + //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105 + headers.put("Authorization", "APPCODE " + aliProperties.getAuthenticationAppcode()); + Map querys = new HashMap(); + Map bodys = new HashMap(); + bodys.put("idCardNo", idNum); + bodys.put("name", realName); + try { + /** + * 重要提示如下: + * HttpUtils请从 + * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java + * 下载 + * + * 相应的依赖请参照 + * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml + */ + HttpResponse response = HttpUtils.doPost(aliProperties.getAuthenticationHost(), aliProperties.getAuthenticationPath(), method, headers, querys, bodys); + log.info("authentication response code is {}", response.getStatusLine().getStatusCode()); + //获取response的body + String result = EntityUtils.toString(response.getEntity()); + log.info("authentication result is {}", result); + JSONObject jsonObject = JSONObject.parseObject(result); + int code = jsonObject.getIntValue("code"); + if (code == 400) { + return "身份证格式不正确!"; + } + if (code != 200) { + return "认证失败,请重新认证!"; + } + JSONObject data = jsonObject.getJSONObject("data"); + int res = data.getIntValue("result"); + if (res == 1) { + return "认证失败,请重新认证!"; + } + } catch (Exception e) { + log.error("authentication is {}", e.getMessage(), e); + return "认证失败,请重新认证!"; + } + return ""; + } + + + @Override + public String authentication2(String realName, String idNum, String phoneNum) { + log.info("realName is {}, idNum is {}, phoneNum is {}", realName, idNum, phoneNum); + try { + HttpHeaders headers = new HttpHeaders(); + headers.set("X-APISpace-Token", "3q25hxre8uu9rbeuqtfwf4sxmjh4dach"); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add("name", realName); + map.add("idNum", idNum); + map.add("mobile", phoneNum); + + // 5. 组装请求实体 + HttpEntity> request = new HttpEntity<>(map, headers); + + // 6. 发送 POST 请求并获取响应 + ResponseEntity response = restTemplate.postForEntity("https://eolink.o.apispace.com/carriers/carriers-auth", + request, String.class); + + log.info("authentication response code is {}", response.getStatusCode()); + log.info("authentication result is {}", response.getBody()); + + JSONObject jsonObject = JSONObject.parseObject(response.getBody()); + if (jsonObject == null) { + return "认证失败,请重新认证!"; + } + String code = jsonObject.getString("code"); + if (!Objects.equals(code, "200000")) { + return "认证失败,请重新认证!"; + } + JSONObject data = jsonObject.getJSONObject("data"); + String res = data.getString("result"); + if (!Objects.equals(res, "01")) { + return "认证失败,请重新认证!"; + } + } catch (Exception e) { + log.error("authentication is {}", e.getMessage(), e); + return "认证失败,请重新认证!"; + } + return ""; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/utils/HttpUtils.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/utils/HttpUtils.java new file mode 100644 index 0000000..866dac4 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/alipay/utils/HttpUtils.java @@ -0,0 +1,311 @@ +package com.ruoyi.thirdparty.alipay.utils; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.HttpClient; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class HttpUtils { + + /** + * get + * + * @param host + * @param path + * @param method + * @param headers + * @param querys + * @return + * @throws Exception + */ + public static HttpResponse doGet(String host, String path, String method, + Map headers, + Map querys) + throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpGet request = new HttpGet(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + return httpClient.execute(request); + } + + /** + * post form + * + * @param host + * @param path + * @param method + * @param headers + * @param querys + * @param bodys + * @return + * @throws Exception + */ + public static HttpResponse doPost(String host, String path, String method, + Map headers, + Map querys, + Map bodys) + throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpPost request = new HttpPost(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + if (bodys != null) { + List nameValuePairList = new ArrayList(); + + for (String key : bodys.keySet()) { + nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key))); + } + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8"); + formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8"); + request.setEntity(formEntity); + } + + return httpClient.execute(request); + } + + /** + * Post String + * + * @param host + * @param path + * @param method + * @param headers + * @param querys + * @param body + * @return + * @throws Exception + */ + public static HttpResponse doPost(String host, String path, String method, + Map headers, + Map querys, + String body) + throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpPost request = new HttpPost(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + if (StringUtils.isNotBlank(body)) { + request.setEntity(new StringEntity(body, "utf-8")); + } + + return httpClient.execute(request); + } + + /** + * Post stream + * + * @param host + * @param path + * @param method + * @param headers + * @param querys + * @param body + * @return + * @throws Exception + */ + public static HttpResponse doPost(String host, String path, String method, + Map headers, + Map querys, + byte[] body) + throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpPost request = new HttpPost(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + if (body != null) { + request.setEntity(new ByteArrayEntity(body)); + } + + return httpClient.execute(request); + } + + /** + * Put String + * @param host + * @param path + * @param method + * @param headers + * @param querys + * @param body + * @return + * @throws Exception + */ + public static HttpResponse doPut(String host, String path, String method, + Map headers, + Map querys, + String body) + throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpPut request = new HttpPut(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + if (StringUtils.isNotBlank(body)) { + request.setEntity(new StringEntity(body, "utf-8")); + } + + return httpClient.execute(request); + } + + /** + * Put stream + * @param host + * @param path + * @param method + * @param headers + * @param querys + * @param body + * @return + * @throws Exception + */ + public static HttpResponse doPut(String host, String path, String method, + Map headers, + Map querys, + byte[] body) + throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpPut request = new HttpPut(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + if (body != null) { + request.setEntity(new ByteArrayEntity(body)); + } + + return httpClient.execute(request); + } + + /** + * Delete + * + * @param host + * @param path + * @param method + * @param headers + * @param querys + * @return + * @throws Exception + */ + public static HttpResponse doDelete(String host, String path, String method, + Map headers, + Map querys) + throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpDelete request = new HttpDelete(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + return httpClient.execute(request); + } + + private static String buildUrl(String host, String path, Map querys) throws UnsupportedEncodingException { + StringBuilder sbUrl = new StringBuilder(); + sbUrl.append(host); + if (!StringUtils.isBlank(path)) { + sbUrl.append(path); + } + if (null != querys) { + StringBuilder sbQuery = new StringBuilder(); + for (Map.Entry query : querys.entrySet()) { + if (0 < sbQuery.length()) { + sbQuery.append("&"); + } + if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) { + sbQuery.append(query.getValue()); + } + if (!StringUtils.isBlank(query.getKey())) { + sbQuery.append(query.getKey()); + if (!StringUtils.isBlank(query.getValue())) { + sbQuery.append("="); + sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8")); + } + } + } + if (0 < sbQuery.length()) { + sbUrl.append("?").append(sbQuery); + } + } + + return sbUrl.toString(); + } + + private static HttpClient wrapClient(String host) { + HttpClient httpClient = new DefaultHttpClient(); + if (host.startsWith("https://")) { + sslClient(httpClient); + } + + return httpClient; + } + + private static void sslClient(HttpClient httpClient) { + try { + SSLContext ctx = SSLContext.getInstance("TLS"); + X509TrustManager tm = new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + public void checkClientTrusted(X509Certificate[] xcs, String str) { + + } + public void checkServerTrusted(X509Certificate[] xcs, String str) { + + } + }; + ctx.init(null, new TrustManager[] { tm }, null); + SSLSocketFactory ssf = new SSLSocketFactory(ctx); + ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + ClientConnectionManager ccm = httpClient.getConnectionManager(); + SchemeRegistry registry = ccm.getSchemeRegistry(); + registry.register(new Scheme("https", 443, ssf)); + } catch (KeyManagementException ex) { + throw new RuntimeException(ex); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/config/BaiduConfig.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/config/BaiduConfig.java new file mode 100644 index 0000000..6d91f3d --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/config/BaiduConfig.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.baidu.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "baidu") +public class BaiduConfig { + private String baseUrl; + private String token; + private String logidUrl; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/domain/body/BaiduConversionType.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/domain/body/BaiduConversionType.java new file mode 100644 index 0000000..c9206e6 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/domain/body/BaiduConversionType.java @@ -0,0 +1,9 @@ +package com.ruoyi.thirdparty.baidu.domain.body; + +import lombok.Data; + +@Data +public class BaiduConversionType { + private String logidUrl; + private Integer newType; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/domain/body/BaiduUploadConvertData.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/domain/body/BaiduUploadConvertData.java new file mode 100644 index 0000000..0474439 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/domain/body/BaiduUploadConvertData.java @@ -0,0 +1,11 @@ +package com.ruoyi.thirdparty.baidu.domain.body; + +import lombok.Data; + +import java.util.List; + +@Data +public class BaiduUploadConvertData { + private String token; + private List conversionTypes; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/service/BaiduService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/service/BaiduService.java new file mode 100644 index 0000000..7fd8e25 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/service/BaiduService.java @@ -0,0 +1,6 @@ +package com.ruoyi.thirdparty.baidu.service; + +public interface BaiduService { + + void upload(Integer userId, Integer newType, String bdVid); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/service/impl/BaiduServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/service/impl/BaiduServiceImpl.java new file mode 100644 index 0000000..d518f96 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baidu/service/impl/BaiduServiceImpl.java @@ -0,0 +1,67 @@ +package com.ruoyi.thirdparty.baidu.service.impl; + +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.thirdparty.baidu.config.BaiduConfig; +import com.ruoyi.thirdparty.baidu.domain.body.BaiduConversionType; +import com.ruoyi.thirdparty.baidu.domain.body.BaiduUploadConvertData; +import com.ruoyi.thirdparty.baidu.service.BaiduService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Slf4j +@Service +public class BaiduServiceImpl implements BaiduService { + + @Autowired + private BaiduConfig baiduConfig = new BaiduConfig(); + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + //注册49 + //购买10 + @Override + public void upload(Integer userId, Integer newType, String bdVid) { + log.info("userId is {}, newType is {}, bdVid is {}", userId, newType, bdVid); + if (StringUtils.isBlank(bdVid) || newType == null || userId == null) { + return; + } + if (newType != 10 && newType != 49) { + return; + } + if (Objects.equals(10, newType)) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(TtUserBlendErcash::getUserId, userId) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode()) + ; + // 大于1非首次充值 + List ercashes = ttUserBlendErcashMapper.selectList(queryWrapper); + if (ercashes.size() != 1) { + return; + } + } + // 构建请求头 + Map headers = new HashMap<>(); + BaiduUploadConvertData data = new BaiduUploadConvertData(); + data.setToken(baiduConfig.getToken()); + BaiduConversionType type = new BaiduConversionType(); + type.setNewType(newType); + type.setLogidUrl(baiduConfig.getLogidUrl() + bdVid); + data.setConversionTypes(Collections.singletonList(type)); + String body = JSON.toJSONString(data); + String result = HttpUtils.sendPostJSONString(baiduConfig.getBaseUrl(), body, headers); + log.info("body is {}, result is {}", body, result); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/config/BaiweiPayConfig.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/config/BaiweiPayConfig.java new file mode 100644 index 0000000..f475569 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/config/BaiweiPayConfig.java @@ -0,0 +1,36 @@ +package com.ruoyi.thirdparty.baiweipay.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "baiweipay") +public class BaiweiPayConfig { + + /** + * 服务器URL + */ + private String serverUrl; + + /** + * 商户ID + */ + private String pid; + + /** + * 签名 + */ + private String sign; + + /** + * 通知URL + */ + private String notifyUrl; + + /** + * 返回URL + */ + private String returnUrl; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/controller/BaiweiPayController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/controller/BaiweiPayController.java new file mode 100644 index 0000000..78ec400 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/controller/BaiweiPayController.java @@ -0,0 +1,50 @@ +package com.ruoyi.thirdparty.baiweipay.controller; + +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.UserPermission; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.thirdparty.baiweipay.service.BaiweiPayService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +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.Map; + +@Api(tags = "百威支付") +@Slf4j +@RestController +@RequestMapping("api/baiweipay") +public class BaiweiPayController { + + @Autowired + private TtUserService ttUserService; + + @Autowired + private BaiweiPayService baiweiPayService; + + @ApiOperation("支付") + @UserPermission + @PostMapping(value = "/pay") + public R pay(@RequestBody @Validated CreateOrderParam param) { + Long userId = SecurityUtils.getUserId(); + TtUser ttUser = ttUserService.getById(userId); + // 是否实名认证(0未认证 1已认证) + if ("0".equals(ttUser.getIsRealCheck())) { + return R.fail("未实名认证"); + } + String ip = IpUtils.getIpAddr(); + return baiweiPayService.pay(param, ttUser, ip); + } + + @GetMapping("/notify") + public String notify(@RequestParam Map params) { + return baiweiPayService.notify(params); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/domain/BaiweiPayResponse.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/domain/BaiweiPayResponse.java new file mode 100644 index 0000000..c712d4d --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/domain/BaiweiPayResponse.java @@ -0,0 +1,19 @@ +package com.ruoyi.thirdparty.baiweipay.domain; + +import lombok.Data; + +@Data +public class BaiweiPayResponse { + + private String code; + + private String msg; + + private String tradeNo; + + private String payurl; + + private String qrcode; + + private String urlscheme; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/service/BaiweiPayService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/service/BaiweiPayService.java new file mode 100644 index 0000000..e99038f --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/service/BaiweiPayService.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.baiweipay.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; + +import java.util.Map; + +public interface BaiweiPayService { + + R pay(CreateOrderParam param, TtUser ttUser, String ip); + + String notify(Map params); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/service/impl/BaiweiPayServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/service/impl/BaiweiPayServiceImpl.java new file mode 100644 index 0000000..0920bae --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/service/impl/BaiweiPayServiceImpl.java @@ -0,0 +1,419 @@ +package com.ruoyi.thirdparty.baiweipay.service.impl; + +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.common.constant.PayOrderStatus; +import com.ruoyi.domain.common.constant.PayType; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.TtPromotionLevel; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.domain.other.TtRechargeRecord; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.baiweipay.config.BaiweiPayConfig; +import com.ruoyi.thirdparty.baiweipay.service.BaiweiPayService; +import com.ruoyi.thirdparty.common.service.RechargeSuccessfulNoticeService; +import com.ruoyi.thirdparty.baiweipay.domain.BaiweiPayResponse; +import com.ruoyi.thirdparty.baiweipay.util.BaiweiPayUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.*; + +@Slf4j +@Service +public class BaiweiPayServiceImpl implements BaiweiPayService { + + @Autowired + private TtRechargeProdMapper rechargeProdMapper; + + @Autowired + private BaiweiPayConfig baiweiPayConfig; + + @Autowired + private TtOrderMapper orderMapper; + + @Autowired + private TtUserMapper userMapper; + + @Autowired + private TtRechargeProdMapper rechargeListMapper; + + @Autowired + private TtUserService userService; + + @Autowired + private TtUserBlendErcashMapper userBlendErcashMapper; + + @Autowired + private RechargeSuccessfulNoticeService rechargeSuccessfulNoticeService; + + @Autowired + private TtRechargeRecordMapper ttRechargeRecordMapper; + + @Autowired + private ISysConfigService configService; + + @Autowired + private TtPromotionLevelMapper ttPromotionLevelMapper; + + @Autowired + private TtFirstRechargeMapper ttFirstRechargeMapper; + + @Autowired + private RestTemplate restTemplate; + + @Override + public R pay(CreateOrderParam param, TtUser ttUser, String ip) { + // 1.查询商品信息 + TtRechargeProd goods = new LambdaQueryChainWrapper<>(rechargeProdMapper) + .eq(TtRechargeProd::getId, param.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + if (ObjectUtil.isEmpty(goods)) { + return R.fail("不存在的商品"); + } + if (goods.getPrice().compareTo(param.getGoodsPrice()) != 0) { + return R.fail("商品价格不一致"); + } + + // 计算总价值 + BigDecimal totalAmount = param.getGoodsPrice().multiply(new BigDecimal(param.getGoodsNum())); + + // 2.构建支付参数 + Map map = new HashMap<>(); + map.put("pid", baiweiPayConfig.getPid()); + map.put("type", "alipay"); + Snowflake snowflake = IdUtil.getSnowflake(1, 1); + String orderId = String.valueOf(snowflake.nextId()); + map.put("out_trade_no", orderId); + map.put("notify_url", baiweiPayConfig.getNotifyUrl()); + map.put("return_url", baiweiPayConfig.getReturnUrl()); + map.put("name", goods.getName()); + map.put("money", String.valueOf(totalAmount)); + map.put("clientip", ip); + String key = baiweiPayConfig.getSign(); + BaiweiPayUtil signUtil = new BaiweiPayUtil(key); + String sign = signUtil.getSign(map); + map.put("sign", sign); + map.put("sign_type", "MD5"); + + // 3.发送请求 + // HttpRequest post = HttpUtil.createPost(baiweiPayConfig.getServerUrl()); + // post.header("Content-Type","application/x-www-form-urlencoded"); + // post.formStr(map); + // HttpResponse res = post.execute(); + + // 切换请求 + // 获取服务器 URL + String url = baiweiPayConfig.getServerUrl(); + + // 构建请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + // 构建表单数据 + MultiValueMap formParams = new LinkedMultiValueMap<>(); + for (Map.Entry entry : map.entrySet()) { + formParams.add(entry.getKey(), entry.getValue()); + } + + // 构建请求实体 + HttpEntity> requestEntity = new HttpEntity<>(formParams, headers); + + // 发送 POST 请求 + ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + + // 获取响应 + String result = response.getBody(); + + // 4.解析响应 + BaiweiPayResponse resBody = JSONUtil.toBean(result, BaiweiPayResponse.class); + if (!resBody.getCode().equals("1")) { + return R.fail(resBody.getMsg()); + } + + // 5.创建订单 + TtOrder order = new TtOrder(); + order.setOrderId(orderId); + order.setOutTradeNo(resBody.getTradeNo()); + order.setUserId(ttUser.getUserId()); + order.setType(PayType.GFHT_PAY.getCode()); + order.setGoodsId(param.getGoodsId()); + order.setGoodsPrice(param.getGoodsPrice()); + order.setGoodsNum(param.getGoodsNum()); + order.setTotalAmount(totalAmount); + order.setSign(sign); + order.setStatus(PayOrderStatus.NO_PAY.getCode()); + order.setCreateTime(new Date()); + orderMapper.insert(order); + + return R.ok(resBody); + } + + @Override + public String notify(Map params) { + log.info("支付成功通知"); + String result = "failure"; + + // 校验 + String tradeStatus = params.get("trade_status"); + if (!"TRADE_SUCCESS".equals(tradeStatus)) { + log.error("交易失败"); + return result; + } + + // 查询订单信息 + String outTradeNo = params.get("trade_no"); + TtOrder order = new LambdaQueryChainWrapper<>(orderMapper) + .eq(TtOrder::getOutTradeNo, outTradeNo) + .eq(TtOrder::getStatus, PayOrderStatus.NO_PAY.getCode()) + .one(); + // 防止重复通知 + if (!PayOrderStatus.NO_PAY.getCode().equals(order.getStatus())) { + return result; + } + + // 查询用户信息 + TtUser user = new LambdaQueryChainWrapper<>(userMapper) + .eq(TtUser::getUserId, order.getUserId()) + .eq(TtUser::getDelFlag, 0) + .one(); + + // 3.查询商品信息 + TtRechargeProd goods = new LambdaQueryChainWrapper<>(rechargeListMapper) + .eq(TtRechargeProd::getId, order.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + + // 4.账户结算 + if (ObjectUtil.isNull(goods.getProductA())) goods.setProductA(BigDecimal.ZERO); + if (ObjectUtil.isNull(goods.getProductC())) goods.setProductC(BigDecimal.ZERO); + payNotifyAccounting(order, user, goods, order.getGoodsNum()); + + try { + // 首充赠送 + firstChargeGiftAmount(user, goods, order.getGoodsNum()); + + // 推广等级充值赠送 + promotionLevelChargeGiftAmount(user, goods, order.getGoodsNum()); + + // 推广充值返佣 + promotionChargeCommission(user, goods, order.getGoodsNum()); + } catch (Exception e) { + e.printStackTrace(); + } + + // 5.更新订单 + boolean update = new LambdaUpdateChainWrapper<>(orderMapper) + .eq(TtOrder::getId, order.getId()) + .set(TtOrder::getStatus, PayOrderStatus.PAY_COMPLE.getCode()) + .set(TtOrder::getUpdateTime, new Date()) + .update(); + if (update) { + result = "success"; + } + + // 6.发送充值通知 + rechargeSuccessfulNoticeService.sendRechargeSuccessNotice(order.getUserId().toString(), order.getGoodsPrice()); + + return result; + } + + // 账户结算 + public void payNotifyAccounting(TtOrder order, TtUser user, TtRechargeProd goods, Integer goodsNumber) { + + // 加钱 + // BigDecimal totalAmount = order.getTotalAmount(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal totalCredits = goods.getProductC().multiply(new BigDecimal(goodsNumber)); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, user.getUserId()) + .setSql("account_amount = account_amount + " + totalAmount.toString() + + ",account_credits = account_credits + " + totalCredits.toString()); + userService.update(userUpdate); + + user = userService.getById(user.getUserId()); + // 综合消费日志 + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(user.getUserId()) + + .amount(totalAmount.compareTo(BigDecimal.ZERO) > 0 ? totalAmount : null) + .finalAmount(totalAmount.compareTo(BigDecimal.ZERO) > 0 ? user.getAccountAmount().add(totalAmount) : null) + + .credits(totalCredits.compareTo(BigDecimal.ZERO) > 0 ? totalCredits : null) + .finalCredits(totalCredits.compareTo(BigDecimal.ZERO) > 0 ? user.getAccountCredits().add(totalCredits) : null) + + .total(totalAmount.add(totalCredits)) // 收支合计 + + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.RECHARGE.getCode()) + .remark(TtAccountRecordSource.RECHARGE.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + userBlendErcashMapper.insert(blendErcash); + + // 充值记录 + TtRechargeRecord ttRechargeRecord = TtRechargeRecord.builder().build(); + ttRechargeRecord.setUserId(order.getUserId()); + ttRechargeRecord.setParentId(user.getParentId()); + ttRechargeRecord.setArrivalAmount(totalAmount); + ttRechargeRecord.setAmountActuallyPaid(totalAmount); + ttRechargeRecord.setFinallyPrice(user.getAccountAmount()); + ttRechargeRecord.setOrderId(order.getOrderId()); + ttRechargeRecord.setOutTradeNo(order.getOutTradeNo()); + ttRechargeRecord.setStatus("0"); + ttRechargeRecord.setChannelType("1"); + ttRechargeRecord.setCreateTime(DateUtils.getNowDate()); + ttRechargeRecordMapper.insert(ttRechargeRecord); + + // 更新VIP等级 + userService.updateUserVIPLevel(user.getUserId()); + } + + /** + * 首充赠送 + */ + private void firstChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + // 1.判断是否为首充 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .eq(TtUserBlendErcash::getSource, 1) + .eq(TtUserBlendErcash::getUserId, ttUser.getUserId()); + List ttUserBlendErcashes = userBlendErcashMapper.selectList(wrapper); + if (ttUserBlendErcashes.size() > 0) { + return; + } + + // 2.加钱 + // 充值金额 + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + // 查询赠送比例 + BigDecimal firstChargeAmountRatio = ttFirstRechargeMapper.selectRatioByMinAmount(totalAmount); + if (Objects.isNull(firstChargeAmountRatio)) { + return; + } + // 计算赠送金额 + BigDecimal giftAmount = totalAmount.multiply(firstChargeAmountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 3.记录 + ttUser = userService.getById(ttUser.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.FIRST_CHARGE.getCode()) + .remark(TtAccountRecordSource.FIRST_CHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } + + /** + * 推广等级充值赠送 + */ + @Async + public void promotionLevelChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + // 1.查询用户推广等级对应的赠送比例 + TtPromotionLevel ttPromotionLevel = ttPromotionLevelMapper.selectById(ttUser.getPromotionLevel()); + if (Objects.isNull(ttPromotionLevel)) + return; + + // 2.加钱 + BigDecimal amountRatio = ttPromotionLevel.getRechargeGiftRatio(); + if (Objects.isNull(amountRatio)) + return; + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal giftAmount = totalAmount.multiply(amountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 3.记录 + ttUser = userService.getById(ttUser.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_LEVEL_CHARGE.getCode()) + .remark(TtAccountRecordSource.PROMOTION_LEVEL_CHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } + + /** + * 推广充值返佣 + */ + private void promotionChargeCommission(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + if (Objects.isNull(ttUser.getParentId())) { + return; + } + // 查询用户推广等级信息 + TtPromotionLevel ttPromotionLevel = ttPromotionLevelMapper.selectById(ttUser.getPromotionLevel()); + + // 给上级加钱 + BigDecimal amountRatio = ttPromotionLevel.getCommissions(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal giftAmount = totalAmount.multiply(amountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getParentId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 记录 + ttUser = userService.getById(ttUser.getParentId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_CHARGE_COMMISSION.getCode()) + .remark(TtAccountRecordSource.PROMOTION_CHARGE_COMMISSION.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/util/BaiweiPayUtil.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/util/BaiweiPayUtil.java new file mode 100644 index 0000000..d3fe6b5 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/baiweipay/util/BaiweiPayUtil.java @@ -0,0 +1,76 @@ +package com.ruoyi.thirdparty.baiweipay.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Map; +import java.util.TreeMap; + +public class BaiweiPayUtil { + + private String key; + + public BaiweiPayUtil(String key) { + this.key = key; + } + + /** + * 生成MD5签名 + * + * @param params 参数列表 + * @return 生成的签名 + */ + public String getSign(Map params) { + // 排序参数 + Map sortedParams = new TreeMap<>(params); + sortedParams.remove("sign"); + sortedParams.remove("sign_type"); + + // 构建签名字符串 + StringBuilder signStr = new StringBuilder(); + for (Map.Entry entry : sortedParams.entrySet()) { + if (entry.getValue() != null && !entry.getValue().isEmpty()) { + signStr.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); + } + } + + if (signStr.length() > 0) { + signStr.setLength(signStr.length() - 1); // 移除最后一个& + } + + // 添加商户密钥 + signStr.append(this.key); + + // 计算MD5签名 + return md5(signStr.toString()).toLowerCase(); + } + + /** + * 计算MD5哈希值 + * + * @param input 输入字符串 + * @return MD5哈希值 + */ + private static String md5(String input) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] messageDigest = md.digest(input.getBytes()); + return bytesToHex(messageDigest); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5 algorithm not found", e); + } + } + + /** + * 将字节数组转换为十六进制字符串 + * + * @param bytes 字节数组 + * @return 十六进制字符串 + */ + private static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/NetworkDataLoader/BaseNetworkDataLoader.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/NetworkDataLoader/BaseNetworkDataLoader.java new file mode 100644 index 0000000..5d2a305 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/NetworkDataLoader/BaseNetworkDataLoader.java @@ -0,0 +1,34 @@ +package com.ruoyi.thirdparty.common.NetworkDataLoader; + +import cn.hutool.core.util.HashUtil; +import com.ruoyi.common.core.domain.entity.SysDictData; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Arrays; +import java.util.List; +@Data +public abstract class BaseNetworkDataLoader { + + public String name; + + protected List allEnum; + + // 根据类型和标签获取code + protected String getCodeByNameOrHash(List typeList, String nameOrHash){ + + for (SysDictData type : typeList){ + if (type.getDictLabel().equals(nameOrHash)) return type.getDictValue(); + if (type.getDictType().equals(nameOrHash)) return type.getDictValue(); + } + return null; + } + + protected Long createOrnamentId(String MarketHashName){ + Long id = Long.valueOf(HashUtil.rsHash(MarketHashName)); + if (id < 0) id = Math.abs(id); + return id; + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/NetworkDataLoader/NetworkDataLoader.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/NetworkDataLoader/NetworkDataLoader.java new file mode 100644 index 0000000..d4b1489 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/NetworkDataLoader/NetworkDataLoader.java @@ -0,0 +1,12 @@ +package com.ruoyi.thirdparty.common.NetworkDataLoader; + +import com.ruoyi.domain.entity.TtOrnament; + +import java.util.List; +import java.util.Set; + +public interface NetworkDataLoader { + + Set load(); + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/NetworkDataLoader/ZBTNetworkDataLoader.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/NetworkDataLoader/ZBTNetworkDataLoader.java new file mode 100644 index 0000000..6be3fcf --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/NetworkDataLoader/ZBTNetworkDataLoader.java @@ -0,0 +1,157 @@ +package com.ruoyi.thirdparty.common.NetworkDataLoader; + +import cn.hutool.core.util.HashUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.domain.dto.zbt.OrnamentZBT; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.thirdparty.zbt.param.ProductListParams; +import com.ruoyi.thirdparty.zbt.param.SearchParams; +import com.ruoyi.thirdparty.zbt.result.ResultZbt; +import com.ruoyi.thirdparty.zbt.result.product.AvailableMarket; +import com.ruoyi.thirdparty.zbt.result.product.AvailableMarketList; +import com.ruoyi.thirdparty.zbt.result.product.SearchData; +import com.ruoyi.thirdparty.zbt.service.ZBTService; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +@AllArgsConstructor +// @Data +public class ZBTNetworkDataLoader extends BaseNetworkDataLoader implements NetworkDataLoader{ + + private ZBTService zbtService; + + @Override + public Set load() { + + Set res = new HashSet<>(); + + for (SysDictData type : allEnum){ + // 根据指定的饰品类型加载数据 + if (!type.getDictType().equals("ornaments_type")) continue; + + log.info("正在加载{}类型的饰品资源~",type.getDictLabel()); + + Set data = ZBTLoadOrnamentByType(type); + + res.addAll(data); + } + + return res; + } + + private Set ZBTLoadOrnamentByType(SysDictData type){ + + HashSet result = new HashSet<>(); + + // 分批请求api + int page = 1; + boolean stopFlag = false; + while (true) { + + // 结束标记 + if (stopFlag) break; + + //1 请求平台数据 + SearchParams param = SearchParams.builder() + .page(String.valueOf(page)) + .category(type.getDictLabel()) + .limit("200") + .build(); + ResultZbt response = zbtService.search(param); + + // 解析平台数据 + SearchData data = response.getData(); + if (!response.getSuccess() || ObjectUtil.isEmpty(data)) break; + List list = JSONUtil.toList(JSONUtil.toJsonStr(data.getList()), OrnamentZBT.class); + + if (list.size() < 200) stopFlag = !stopFlag; + + // 转换为通用类型 + Set collect = list.stream().map(item -> { + + // 生成本平台id + Long id = createOrnamentId(item.getMarketHashName()); + + TtOrnament ornament = TtOrnament.builder() + .id(id) + .zbtId(item.getItemId()) + .marketHashName(item.getMarketHashName()) + .name(item.getItemName()) + .shortName(item.getShortName()) + + // 价格信息 + .price(ObjectUtil.isNotEmpty(item.getPriceInfo()) ? item.getPriceInfo().getPrice() : null) + .quantity(ObjectUtil.isNotEmpty(item.getPriceInfo()) ? item.getPriceInfo().getQuantity() : null) + + .imageUrl(item.getImageUrl()) + + .type(type.getDictValue()) + .typeName(item.getTypeName()) + .typeHashName(item.getType()) + + .quality(getCodeByNameOrHash(allEnum,item.getQuality())) + .qualityName(item.getQualityName()) + .qualityHashName(item.getQuality()) + .qualityColor(item.getQualityColor()) + + .rarity(getCodeByNameOrHash(allEnum,item.getRarity())) + .rarityName(item.getRarityName()) + .rarityHashName(item.getRarity()) + .rarityColor(item.getRarityColor()) + + .exterior(getCodeByNameOrHash(allEnum,item.getExterior())) + .exteriorHashName(item.getExterior()) + .exteriorName(item.getExteriorName()) + + .updateTime(new Date()) + .build(); + + // 补充价格信息 + AvailableMarket sellInfo = apiSellInfo(String.valueOf(ornament.getZbtId())); + if (ObjectUtil.isNotEmpty(sellInfo)) { + ornament.setUsePrice(sellInfo.getCnyPrice()); + } + + return ornament; + }).collect(Collectors.toSet()); + + result.addAll(collect); + + // log.info("加载【扎比特】{}条类型为{}的饰品数据。",page*collect.size(),typeHash); + System.out.print("\r加载【扎比特】"+page*collect.size()+"条类型为"+type.getDictLabel()+"的饰品数据。请耐心等待"); + + page++; + } + + return result; + + } + + // 查询价格信息 + private AvailableMarket apiSellInfo(String itemId){ + ProductListParams p = ProductListParams.builder() + .itemId(itemId) + .page("1") + .limit("1") + .language("zh_CN") + .build(); + ResultZbt zbtData = zbtService.productList(p); + if (!zbtData.getSuccess()) return null; + List list = zbtData.getData().getList(); + if (list.isEmpty()) return null; + return list.get(0); + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/config/DeliveryRabbitConsumer.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/config/DeliveryRabbitConsumer.java new file mode 100644 index 0000000..16717c5 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/config/DeliveryRabbitConsumer.java @@ -0,0 +1,35 @@ +package com.ruoyi.thirdparty.common.config; + +import com.rabbitmq.client.Channel; +import com.ruoyi.common.rabbitmq.config.DelayedQueueConfig; +import com.ruoyi.thirdparty.common.service.DeliverGoodsService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +@Slf4j +public class DeliveryRabbitConsumer { + + private final DeliverGoodsService deliverGoodsService; + + public DeliveryRabbitConsumer(DeliverGoodsService deliverGoodsService) { + this.deliverGoodsService = deliverGoodsService; + } + + // 自动提货业务 + @RabbitListener(queues = DelayedQueueConfig.DELIVERY_QUEUE) + public void deliveryReceive(Message message, Channel channel) throws IOException { + Integer userId = Integer.valueOf(new String(message.getBody())); + channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); + try { + deliverGoodsService.autoDelivery(userId); + } catch (Exception e) { + log.error(e.getMessage()); + channel.basicRecover(false); + } + } +} \ No newline at end of file diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/config/NetworkDataLoaderConfig.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/config/NetworkDataLoaderConfig.java new file mode 100644 index 0000000..fa8b85f --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/config/NetworkDataLoaderConfig.java @@ -0,0 +1,51 @@ +package com.ruoyi.thirdparty.common.config; + +// 网络资源加载器配置 + +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.system.mapper.SysDictDataMapper; +import com.ruoyi.thirdparty.common.NetworkDataLoader.NetworkDataLoader; +import com.ruoyi.thirdparty.common.NetworkDataLoader.ZBTNetworkDataLoader; +import com.ruoyi.thirdparty.zbt.service.ZBTService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Configuration +public class NetworkDataLoaderConfig { + + @Autowired + private ZBTService zbtService; + + @Autowired + private SysDictDataMapper dictDataMapper; + + @Bean + public List networkDataLoaderList(){ + + // 加载必要的枚举数据 + List dictTypes = Arrays.asList( + "ornaments_type", "ornaments_type_name", + "ornaments_exterior", "ornaments_exterior_name", + "ornaments_quality", "ornaments_quality_name", + "ornaments_rarity", "ornaments_rarity_name"); + List enumList = dictDataMapper.selectDictDataByTypes(dictTypes); + + ArrayList networkDataLoaders = new ArrayList<>(); + + // zbt加载器 + ZBTNetworkDataLoader zbtNetworkDataLoader = new ZBTNetworkDataLoader(zbtService); + zbtNetworkDataLoader.setAllEnum(enumList); + zbtNetworkDataLoader.setName("扎比特加载器"); + + networkDataLoaders.add(zbtNetworkDataLoader); + + return networkDataLoaders; + + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/ApiCommonController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/ApiCommonController.java new file mode 100644 index 0000000..36cf6ea --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/ApiCommonController.java @@ -0,0 +1,31 @@ +package com.ruoyi.thirdparty.common.controller; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.thirdparty.common.service.ApiCommonService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Api(tags = "第三方公共接口") +@RestController +@RequestMapping("/api/thirdparty/common") +public class ApiCommonController { + + private final ApiCommonService commonService; + + public ApiCommonController(ApiCommonService commonService) { + this.commonService = commonService; + } + + @ApiOperation("生成二维码") + @PostMapping("/skipLinkToQRCode") + public R skipLinkToQRCode(HttpServletResponse response, @RequestParam("contents") String contents) throws IOException { + return commonService.writerPayImage(response, contents); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/ApiRechargeConfigController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/ApiRechargeConfigController.java new file mode 100644 index 0000000..e5b6377 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/ApiRechargeConfigController.java @@ -0,0 +1,84 @@ +package com.ruoyi.thirdparty.common.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.service.TtOrderService; +import com.ruoyi.admin.service.TtRechargeConfigService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.common.constant.PartyType; +import com.ruoyi.domain.common.constant.PayOrderStatus; +import com.ruoyi.domain.dto.recharge.RechargeConfigOrderVo; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.TtRechargeConfig; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Api(tags = "充值配置接口") +@RestController +@RequestMapping("/api/recharge/config") +public class ApiRechargeConfigController extends BaseController { + + @Autowired + private TtRechargeConfigService ttRechargeConfigService; + + @Autowired + private TtOrderService ttOrderService; + + @ApiOperation("充值结果列表") + @GetMapping("/order") + public R> order() { + startPage(); + + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(TtOrder::getStatus, PayOrderStatus.PAY_COMPLE.getCode()); + wrapper.eq(TtOrder::getUserId, getUserId()); + wrapper.eq(TtOrder::getThirdParty, PartyType.INNER_PICTURE_PAY.getCode()); + wrapper.orderByDesc(TtOrder::getCreateTime); + List list = ttOrderService.list(wrapper); + if (CollectionUtils.isEmpty(list)) { + return R.ok(new ArrayList<>()); + } + List vos = new ArrayList<>(); + List goodsIds = new ArrayList<>(); + for (TtOrder ttOrder : list) { + RechargeConfigOrderVo vo = new RechargeConfigOrderVo(); + BeanUtils.copyProperties(ttOrder, vo); + goodsIds.add(ttOrder.getGoodsId()); + vos.add(vo); + } + LambdaQueryWrapper wrapper1 = Wrappers.lambdaQuery(); + wrapper1.in(TtRechargeConfig::getId, goodsIds); + List configs = ttRechargeConfigService.list(wrapper1); + Map configMap = configs.stream().collect(Collectors.toMap(TtRechargeConfig::getId, Function.identity())); + for (RechargeConfigOrderVo vo : vos) { + if (configMap.get(vo.getGoodsId()) != null) { + vo.setPicture(configMap.get(vo.getGoodsId()).getPicture()); + } + } + return R.ok(vos); + } + + + @ApiOperation("充值列表") + @GetMapping("/list") + public R> list() { + startPage(); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(TtRechargeConfig::getStatus, "0"); + List list = ttRechargeConfigService.list(wrapper); + return R.ok(list); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/ApiRechargeController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/ApiRechargeController.java new file mode 100644 index 0000000..7a78bda --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/ApiRechargeController.java @@ -0,0 +1,67 @@ +package com.ruoyi.thirdparty.common.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.service.TtRechargeProdService; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.common.service.ApiRechargeService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Api(tags = "充值") +@RestController +@RequestMapping("/api/recharge") +public class ApiRechargeController extends BaseController { + + private final ISysConfigService sysConfigService; + private final TtUserService userService; + private final ApiRechargeService apiRechargeService; + private final TtRechargeProdService rechargeListService; + + public ApiRechargeController(ISysConfigService sysConfigService, + TtUserService userService, + ApiRechargeService apiRechargeService, + TtRechargeProdService rechargeListService) { + this.sysConfigService = sysConfigService; + this.userService = userService; + this.apiRechargeService = apiRechargeService; + this.rechargeListService = rechargeListService; + } + + @ApiOperation("卡支付") + @UpdateUserCache + @PostMapping("/cardPay") + public R cardPay(String password) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + String msg = apiRechargeService.cardPay(password, ttUser); + return StringUtils.isEmpty(msg) ? R.ok(true, "充值成功!") : R.fail(false, msg); + } + + @ApiOperation("充值列表") + // @UpdateUserCache + @GetMapping("/list") + public R> list() { + startPage(); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(TtRechargeProd::getStatus, "0"); + List list = rechargeListService.list(wrapper); + return R.ok(list); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/ApiSmsController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/ApiSmsController.java new file mode 100644 index 0000000..21be80e --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/ApiSmsController.java @@ -0,0 +1,43 @@ +package com.ruoyi.thirdparty.common.controller; + +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.common.model.ApiSmsBody; +import com.ruoyi.thirdparty.common.service.ApiSmsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "短信") +@RestController +@RequestMapping("/api/sms") +public class ApiSmsController extends BaseController { + + private final ISysConfigService sysConfigService; + private final ApiSmsService apiSmsService; + + public ApiSmsController(ISysConfigService sysConfigService, + ApiSmsService apiSmsService) { + this.sysConfigService = sysConfigService; + this.apiSmsService = apiSmsService; + } + + @ApiOperation("获取短信验证码") + @Anonymous + @PostMapping("/getVerifyCode") + public R getVerifyCode(@RequestBody ApiSmsBody smsBody) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + String msg = apiSmsService.getVerifyCode(smsBody); + return StringUtils.isEmpty(msg) ? R.ok("获取验证码成功,请注意短信通知!") : R.fail(msg); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/DeliverGoodsController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/DeliverGoodsController.java new file mode 100644 index 0000000..0b6d90e --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/controller/DeliverGoodsController.java @@ -0,0 +1,82 @@ +package com.ruoyi.thirdparty.common.controller; + +import com.ruoyi.domain.dto.deliver.TradeBuyParam; +import com.ruoyi.domain.dto.deliver.TradeManualConfirmParam; +import com.ruoyi.domain.vo.AvailableMarketOrnamentVO; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.thirdparty.common.service.DeliverGoodsService; +import com.ruoyi.thirdparty.zbt.param.ProductListParams; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Api(tags = "管理端发货管理") +@RestController +@RequestMapping("/admin/deliverGoods") +public class DeliverGoodsController extends BaseController { + + private final DeliverGoodsService deliverGoodsService; + + public DeliverGoodsController(DeliverGoodsService deliverGoodsService) { + this.deliverGoodsService = deliverGoodsService; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class GetAvailableMarketListParam{ + // private List hashNameList; + private Integer partyType; + private List ornamentsId; + } + + // 获取饰品在各个平台的在售信息 + @ApiOperation("获取饰品在各个平台的在售信息") + @PostMapping("/getAvailableMarketList") + public R getAvailableMarketList(@RequestBody GetAvailableMarketListParam param) { + return deliverGoodsService.getAvailableMarketList(param); + } + + // 根据饰品hashName获取所有平台的在售列表(弃用接口) + @ApiOperation("根据饰品hashName获取所有平台的在售列表(弃用接口)") + @GetMapping("/getAvailableMarketListByHashName/{marketHashName}") + public R> getAvailableMarketListByHashName(@PathVariable("marketHashName") String marketHashName, + ProductListParams productListParams) { + List list = deliverGoodsService.getAvailableMarketListByHashName(marketHashName, productListParams); + if (StringUtils.isNull(list)) return R.fail("获取饰品所有在售列表异常!"); + return R.ok(list); + } + + @ApiOperation("购买发货") + @PostMapping("/tradeBuy") + public R tradeBuy(@RequestBody TradeBuyParam param) { + return deliverGoodsService.tradeBuy(param); + } + + @ApiOperation("手动确认发货") + @PostMapping("/tradeConfirm") + public R tradeConfirm(@RequestBody TradeManualConfirmParam param) { + return deliverGoodsService.tradeConfirm(param); + } + + @ApiOperation("同步状态") + @GetMapping("/synchronousStatus") + public R synchronousStatus(String outTradeNo) { + String msg = deliverGoodsService.synchronousStatus(outTradeNo); + return StringUtils.isEmpty(msg) ? R.ok(true, "同步成功!") : R.fail(false, msg); + } + + @ApiOperation("一键同步状态") + @GetMapping("/syncAllStatus") + public R syncAllStatus() { + int row = deliverGoodsService.syncAllStatus(); + return row > 0 ? R.ok() : R.fail("一键同步失败"); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/mapper/ApiNoticeMapper.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/mapper/ApiNoticeMapper.java new file mode 100644 index 0000000..d639141 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/mapper/ApiNoticeMapper.java @@ -0,0 +1,12 @@ +package com.ruoyi.thirdparty.common.mapper; + +import com.ruoyi.domain.other.TtNotice; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Component; + +@Mapper +@Component("thirdpartyApiNoticeMapper") +public interface ApiNoticeMapper { + + int addNotice(TtNotice ttNotice); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/model/ApiSmsBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/model/ApiSmsBody.java new file mode 100644 index 0000000..0dd5b1d --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/model/ApiSmsBody.java @@ -0,0 +1,11 @@ +package com.ruoyi.thirdparty.common.model; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class ApiSmsBody { + private String phoneNumber; + @ApiModelProperty("1注册 2登录 3更换手机号 4找回密码") + private String type; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/scheduled/ThirdPartyTask.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/scheduled/ThirdPartyTask.java new file mode 100644 index 0000000..8e7d39e --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/scheduled/ThirdPartyTask.java @@ -0,0 +1,73 @@ +package com.ruoyi.thirdparty.common.scheduled; + +import cn.hutool.core.util.ObjectUtil; +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.thirdparty.common.NetworkDataLoader.BaseNetworkDataLoader; +import com.ruoyi.thirdparty.common.NetworkDataLoader.NetworkDataLoader; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Slf4j +@Configuration // 1.主要用于标记配置类,兼备Component的效果。 +@EnableScheduling // 2.开启定时任务 +public class ThirdPartyTask { + + @Autowired + private TtOrnamentService ttOrnamentService; + + @Value("${mkcsgo.startLoadOrnaments}") + private Boolean startLoadOrnaments; + + // 资源加载器集合 + @Autowired + private List networkDataLoaderList; + + // 定时加载发货平台的物品数据 + @Scheduled(cron = "0 50 23 * * ?") + // @Scheduled(cron = "0 6 17 * * ?") + public void networkDataLoad(){ + + log.info("是否加载网络ornaments资源{}", startLoadOrnaments); + if (!startLoadOrnaments) return; + + // 1 加载各个平台数据 + Set ttOrnaments = new HashSet<>(); + for (NetworkDataLoader loader : networkDataLoaderList) { + log.info("【{}】开始加载网络资源~",((BaseNetworkDataLoader)loader).name); + Set load = loader.load(); + ttOrnaments.addAll(load); + } + if (ObjectUtil.isEmpty(ttOrnaments) || ttOrnaments.isEmpty()) { + log.warn("加载饰品网络资源失败。"); + return; + } + + // 2 写db + log.info("db allData ing~~~"); + for (TtOrnament item : ttOrnaments) { + try { + ttOrnamentService.saveOrUpdate(item); + } catch (DuplicateKeyException e) { + // log.info("过滤重复数据 【id】{}【hash】{}save。",item.getId(),item.getMarketHashName()); + } catch (Exception e) { + e.printStackTrace(); + log.info("保存数据异常。"); + break; + } + } + + log.info("加载饰品网络资源成功。"); + + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/ApiCommonService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/ApiCommonService.java new file mode 100644 index 0000000..816005f --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/ApiCommonService.java @@ -0,0 +1,11 @@ +package com.ruoyi.thirdparty.common.service; + +import com.ruoyi.common.core.domain.R; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +public interface ApiCommonService { + + R writerPayImage(HttpServletResponse response, String contents) throws IOException; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/ApiNoticeService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/ApiNoticeService.java new file mode 100644 index 0000000..72a07ea --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/ApiNoticeService.java @@ -0,0 +1,10 @@ +package com.ruoyi.thirdparty.common.service; + +import com.ruoyi.domain.other.TtNotice; + +public interface ApiNoticeService { + + int addNotice(TtNotice ttNotice); + // 发送通知 + void sendNotice(Long userId, String title, String content); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/ApiRechargeService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/ApiRechargeService.java new file mode 100644 index 0000000..e1dae4c --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/ApiRechargeService.java @@ -0,0 +1,8 @@ +package com.ruoyi.thirdparty.common.service; + +import com.ruoyi.domain.entity.sys.TtUser; + +public interface ApiRechargeService { + + String cardPay(String password, TtUser ttUser); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/ApiSmsService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/ApiSmsService.java new file mode 100644 index 0000000..13e9591 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/ApiSmsService.java @@ -0,0 +1,10 @@ +package com.ruoyi.thirdparty.common.service; + +import com.ruoyi.thirdparty.common.model.ApiSmsBody; + +public interface ApiSmsService { + + String getVerifyCode(ApiSmsBody smsBody); + + String validateCaptcha(String code, String verifyKey); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/DeliverGoodsService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/DeliverGoodsService.java new file mode 100644 index 0000000..4bca1ef --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/DeliverGoodsService.java @@ -0,0 +1,30 @@ +package com.ruoyi.thirdparty.common.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.deliver.TradeBuyParam; +import com.ruoyi.domain.dto.deliver.TradeManualConfirmParam; +import com.ruoyi.domain.vo.AvailableMarketOrnamentVO; +import com.ruoyi.thirdparty.common.controller.DeliverGoodsController.GetAvailableMarketListParam; +import com.ruoyi.thirdparty.zbt.param.ProductListParams; + +import java.util.List; + +public interface DeliverGoodsService { + + R getAvailableMarketList(Integer ornamentsId, Integer partyType); + + R tradeBuy(TradeBuyParam param); + + String synchronousStatus(String outTradeNo); + + int syncAllStatus(); + + void autoDelivery(Integer userId); + + List getAvailableMarketListByHashName(String marketHashName, ProductListParams productListParams); + + R getAvailableMarketList(GetAvailableMarketListParam param); + + R tradeConfirm(TradeManualConfirmParam param); + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/RechargeSuccessfulNoticeService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/RechargeSuccessfulNoticeService.java new file mode 100644 index 0000000..576883b --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/RechargeSuccessfulNoticeService.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.common.service; + +import java.math.BigDecimal; + +/** + * 充值成功通知 + */ +public interface RechargeSuccessfulNoticeService { + + /** + * 发送充值成功通知 + */ + void sendRechargeSuccessNotice(String userId, BigDecimal amount); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/ApiCommonServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/ApiCommonServiceImpl.java new file mode 100644 index 0000000..04f7f4f --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/ApiCommonServiceImpl.java @@ -0,0 +1,42 @@ +package com.ruoyi.thirdparty.common.service.impl; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.client.j2se.MatrixToImageWriter; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.thirdparty.common.service.ApiCommonService; +import org.springframework.stereotype.Service; + +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@Service +public class ApiCommonServiceImpl implements ApiCommonService { + + @Override + public R writerPayImage(HttpServletResponse response, String contents) throws IOException { + ServletOutputStream out = response.getOutputStream(); + try { + Map hints = new HashMap<>(); + hints.put(EncodeHintType.CHARACTER_SET,"UTF-8"); + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); + hints.put(EncodeHintType.MARGIN, 0); + BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE,300,300,hints); + MatrixToImageWriter.writeToStream(bitMatrix,"jpg",out); + return R.ok("success"); + }catch (Exception e){ + return R.fail("数据异常"); + }finally { + if(out != null){ + out.flush(); + out.close(); + } + } + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/ApiNoticeServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/ApiNoticeServiceImpl.java new file mode 100644 index 0000000..4117c10 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/ApiNoticeServiceImpl.java @@ -0,0 +1,38 @@ +package com.ruoyi.thirdparty.common.service.impl; + +import com.ruoyi.domain.other.TtNotice; +import com.ruoyi.thirdparty.common.mapper.ApiNoticeMapper; +import com.ruoyi.thirdparty.common.service.ApiNoticeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import java.util.Date; + + +@Slf4j +@Service("thirdpartyApiNoticeServiceImpl") +public class ApiNoticeServiceImpl implements ApiNoticeService { + + @Autowired + @Qualifier("thirdpartyApiNoticeMapper") + private ApiNoticeMapper apiNoticeMapper; + + @Override + public int addNotice(TtNotice ttNotice) { + ttNotice.setCreateTime(new Date()); + return apiNoticeMapper.addNotice(ttNotice); + } + + //用 AsyncManager 线程池异步写入并且发送通知 + @Override + public void sendNotice(Long userId, String title, String content) { + TtNotice notice = new TtNotice(); + notice.setUserId(userId); + notice.setTitle(title); + notice.setContent(content); + notice.setRead("0"); + addNotice(notice); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/ApiRechargeServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/ApiRechargeServiceImpl.java new file mode 100644 index 0000000..d798807 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/ApiRechargeServiceImpl.java @@ -0,0 +1,142 @@ +package com.ruoyi.thirdparty.common.service.impl; + +import cn.hutool.core.util.IdUtil; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.config.RedisConstants; +import com.ruoyi.admin.mapper.TtOrderMapper; +import com.ruoyi.admin.mapper.TtRechargeCardMapper; +import com.ruoyi.admin.mapper.TtRechargeRecordMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.TtBonusService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.redis.config.RedisLock; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.PayOrderStatus; +import com.ruoyi.domain.common.constant.PayType; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtRechargeCard; +import com.ruoyi.domain.other.TtRechargeRecord; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.thirdparty.common.service.ApiRechargeService; +import com.ruoyi.thirdparty.common.service.RechargeSuccessfulNoticeService; +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.math.BigDecimal; +import java.util.Date; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +@Service +@Slf4j +public class ApiRechargeServiceImpl implements ApiRechargeService { + + private final RedisLock redisLock; + private final TtBonusService bonusService; + private final TtUserService userService; + private final TtRechargeCardMapper rechargeCardMapper; + private final TtRechargeRecordMapper rechargeRecordMapper; + + public ApiRechargeServiceImpl(RedisLock redisLock, + TtBonusService bonusService, + TtUserService userService, + TtRechargeCardMapper rechargeCardMapper, + TtRechargeRecordMapper rechargeRecordMapper) { + this.redisLock = redisLock; + this.bonusService = bonusService; + this.userService = userService; + this.rechargeCardMapper = rechargeCardMapper; + this.rechargeRecordMapper = rechargeRecordMapper; + } + + @Autowired + private RechargeSuccessfulNoticeService rechargeSuccessfulNoticeService; + + @Autowired + private TtUserBlendErcashMapper userBlendErcashMapper; + + @Autowired + private TtOrderMapper orderMapper; + + @Override + @Transactional + public String cardPay(String password, TtUser ttUser) { + while (true) { + Boolean cardPayLock = redisLock.tryLock(RedisConstants.CARD_PAY_LOCK + ttUser.getParentId(), 3L, 60L, TimeUnit.SECONDS); + if (cardPayLock) { + try { + TtRechargeCard ttRechargeCard = new LambdaQueryChainWrapper<>(rechargeCardMapper).eq(TtRechargeCard::getPassword, password) + .eq(TtRechargeCard::getStatus, "0").eq(TtRechargeCard::getDelFlag, "0").one(); + if (StringUtils.isNull(ttRechargeCard)) return "卡密已被使用!"; + ttRechargeCard.setStatus("1"); + ttRechargeCard.setUseUserId(ttUser.getUserId()); + ttRechargeCard.setUseTime(DateUtils.getNowDate()); + ttRechargeCard.setUpdateBy(ttUser.getUserName()); + ttRechargeCard.setUpdateTime(DateUtils.getNowDate()); + if (rechargeCardMapper.updateById(ttRechargeCard) > 0) { + + BigDecimal totalAmount = ttRechargeCard.getPrice(); + userService.updateOnlyUserAccount(ttUser.getUserId(), totalAmount, TtAccountRecordSource.RECHARGE); + // 更新VIP等级,目前需要在记录日志之后,要不然总充值不是最新 + userService.updateUserVIPLevel(ttUser.getUserId()); + + TtRechargeRecord ttRechargeRecord = TtRechargeRecord.builder().build(); + ttRechargeRecord.setUserId(ttUser.getUserId()); + ttRechargeRecord.setParentId(ttUser.getParentId()); + ttRechargeRecord.setArrivalAmount(totalAmount); + ttRechargeRecord.setAmountActuallyPaid(totalAmount); + ttRechargeRecord.setFinallyPrice(ttUser.getAccountAmount()); + ttRechargeRecord.setOrderId(password); + ttRechargeRecord.setStatus("0"); + ttRechargeRecord.setChannelType("0"); + ttRechargeRecord.setCreateTime(DateUtils.getNowDate()); + // 实时计算返佣 + if (rechargeRecordMapper.insert(ttRechargeRecord) > 0) { +// AsyncManager.me().run(() -> { +// bonusService.bonus(ttUser.getUserId(), ttRechargeRecord.getId()); +// }); + } + + // 设置商户订单号 + String orderId = String.valueOf(IdUtil.getSnowflake(1, 1).nextId()); + // 创建订单 + TtOrder order = new TtOrder(); + order.setOrderId(orderId); + order.setOutTradeNo(password); + order.setUserId(ttUser.getUserId()); + order.setType(PayType.MK_CARD.getCode()); + order.setGoodsId(ttRechargeCard.getId()); + order.setGoodsPrice(ttRechargeCard.getPrice()); + order.setGoodsNum(1); + order.setTotalAmount(totalAmount); + order.setStatus(PayOrderStatus.PAY_COMPLE.getCode()); + order.setCreateTime(new Date()); + orderMapper.insert(order); + + // 发送充值成功通知 + // rechargeSuccessfulNoticeService.sendRechargeSuccessNotice(ttUser.getUserId().toString(), totalAmount); + + return ""; + } + } catch (Exception e) { + log.error("happen exception is {}", e.getMessage(), e); + return "数据异常!"; + } finally { + redisLock.unlock(RedisConstants.CARD_PAY_LOCK + ttUser.getParentId()); + } + } else { + try { + TimeUnit.MILLISECONDS.sleep(300); + } catch (Exception e) { + log.error("happen exception is {}", e.getMessage(), e); + return "数据异常!"; + } + } + } + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/ApiSmsServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/ApiSmsServiceImpl.java new file mode 100644 index 0000000..16e3b35 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/ApiSmsServiceImpl.java @@ -0,0 +1,112 @@ +package com.ruoyi.thirdparty.common.service.impl; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.RandomUtil; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.thirdparty.common.model.ApiSmsBody; +import com.ruoyi.thirdparty.common.service.ApiSmsService; +import com.ruoyi.thirdparty.note.service.YunXinNoteService; +import com.ruoyi.thirdparty.smsbao.service.SmsbaoService; +import com.ruoyi.thirdparty.winic.service.WinicService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +@Service +@Slf4j +public class ApiSmsServiceImpl implements ApiSmsService { + + private final RedisCache redisCache; + private final TtUserService userService; + private final YunXinNoteService yunXinNoteService; + + public ApiSmsServiceImpl(RedisCache redisCache, + TtUserService userService, + YunXinNoteService yunXinNoteService) { + this.redisCache = redisCache; + this.userService = userService; + this.yunXinNoteService = yunXinNoteService; + } + + @Autowired + private SmsbaoService smsbaoService; + + @Autowired + private WinicService winicService; + + @Value("${ruoyi.name}") + private String systemName; + + @Override + public String getVerifyCode(ApiSmsBody smsBody) { + String phoneNumber = smsBody.getPhoneNumber(); + String type = smsBody.getType(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY; + if (StringUtils.isEmpty(type)) { + return "类型不能为空"; + } + if (StringUtils.isEmpty(phoneNumber)) { + return "手机号不能为空"; + } + if (!Validator.isMobile(phoneNumber)) { + return "手机号格式错误,请检查手机号是否输入正确!"; + } + if ("1".equals(type) && !userService.checkPhoneUnique(TtUser.builder().phoneNumber(phoneNumber).build())) { + return "手机号'" + phoneNumber + "'已被注册!"; + } + if ("2".equals(type) || "3".equals(type) || "4".equals(type)) { + TtUser ttUser = new LambdaQueryChainWrapper<>(userService.getBaseMapper()).eq(TtUser::getPhoneNumber, phoneNumber).one(); + if (StringUtils.isNull(ttUser)) return "该手机号未在本站注册!"; + } + if ("1".equals(type)) { + verifyKey = verifyKey + "ApiRegister_" + phoneNumber; + } else if ("2".equals(type)) { + verifyKey = verifyKey + "ApiLogin_" + phoneNumber; + } else if ("3".equals(type)) { + verifyKey = verifyKey + "UpdatePhoneNumber_" + phoneNumber; + } else if ("4".equals(type)) { + verifyKey = verifyKey + "ApiForgetPassword_" + phoneNumber; + } + String cacheCode = redisCache.getCacheObject(verifyKey); + if (StringUtils.isNotEmpty(cacheCode)) { + return "验证码已发送,请注意手机短信信息!"; + } + String randomCode = RandomUtil.randomNumbers(4); + // 网易云信 + // String code = yunXinNoteService.sendNote(phoneNumber, randomCode); + // if (StringUtils.isEmpty(code)) { + // return "发送短信验证码时出现异常!"; + // } + // 切换为短信宝 + String content = "【" + systemName +"】您的验证码为" + randomCode + ",该验证码5分钟内有效,请勿泄露于他人。"; + String code = smsbaoService.sendSms(phoneNumber, content); + if (!"0".equals(code)) { + return "发送短信验证码时出现异常!"; + } + // 切换为中正云通信 + // String content = "【OG】您的验证码为" + randomCode + ",该验证码5分钟内有效,请勿泄露于他人。"; + // String code = winicService.sendSms(phoneNumber, content); + // if (!"000".equals(code)) { + // return "发送短信验证码时出现异常!"; + // } + redisCache.setCacheObject(verifyKey, randomCode, 5, TimeUnit.MINUTES); + return ""; + } + + @Override + public String validateCaptcha(String code, String verifyKey) { + verifyKey = CacheConstants.CAPTCHA_CODE_KEY + verifyKey; + String captcha = redisCache.getCacheObject(verifyKey); + if (!code.equalsIgnoreCase(captcha)) return "验证码错误"; + redisCache.deleteObject(verifyKey); + return "success"; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/DeliverGoodsServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/DeliverGoodsServiceImpl.java new file mode 100644 index 0000000..bcc7852 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/DeliverGoodsServiceImpl.java @@ -0,0 +1,701 @@ +package com.ruoyi.thirdparty.common.service.impl; + +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.admin.service.TtOrnamentYYService; +import com.ruoyi.admin.service.TtYyOrnamentsService; +import com.ruoyi.common.constant.ThirdParty.Platform; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.DeliveryOrderStatus; +import com.ruoyi.domain.common.constant.PartyType; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.dto.deliver.TradeBuyParam; +import com.ruoyi.domain.dto.deliver.TradeManualConfirmParam; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.entity.delivery.TtDeliveryRecord; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtYYOrnaments; +import com.ruoyi.domain.vo.AvailableMarketOrnamentVO; +import com.ruoyi.domain.vo.OnSaleOrnamentVO; +import com.ruoyi.domain.vo.queryTemplateSaleByCategoryDataVO; +import com.ruoyi.domain.vo.queryTemplateSaleByCategoryVO; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.common.controller.DeliverGoodsController.GetAvailableMarketListParam; +import com.ruoyi.thirdparty.common.service.DeliverGoodsService; +import com.ruoyi.thirdparty.cs340.domain.body.ByTemplateCreateOrder; +import com.ruoyi.thirdparty.cs340.domain.result.ByTemplateCreateOrderResult; +import com.ruoyi.thirdparty.cs340.domain.result.Cs340OrderStatusResult; +import com.ruoyi.thirdparty.cs340.domain.result.Cs340Result; +import com.ruoyi.thirdparty.cs340.service.Cs340Service; +import com.ruoyi.thirdparty.v5item.domain.body.CreateOrderBody; +import com.ruoyi.thirdparty.v5item.domain.result.CreateOrderResult; +import com.ruoyi.thirdparty.v5item.domain.result.QueryOrderStatusResult; +import com.ruoyi.thirdparty.v5item.domain.result.V5ItemResult; +import com.ruoyi.thirdparty.v5item.service.V5ItemService; +import com.ruoyi.thirdparty.yyyouping.service.yyyoupingService; +import com.ruoyi.thirdparty.yyyouping.utils.YYClient; +import com.ruoyi.thirdparty.yyyouping.utils.common.YYResult; +import com.ruoyi.thirdparty.zbt.param.*; +import com.ruoyi.thirdparty.zbt.result.ResultZbt; +import com.ruoyi.thirdparty.zbt.result.buy.OpenBuyResultDTO; +import com.ruoyi.thirdparty.zbt.result.order.OrderBuyDetailDTO; +import com.ruoyi.thirdparty.zbt.result.product.AvailableMarket; +import com.ruoyi.thirdparty.zbt.result.product.AvailableMarketList; +import com.ruoyi.thirdparty.zbt.service.ZBTService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.DeliveryOrderStatus.DELIVERY_BEFORE; +import static com.ruoyi.domain.common.constant.DeliveryOrderStatus.ORDER_CANCEL; + +@Service +@Slf4j +public class DeliverGoodsServiceImpl implements DeliverGoodsService { + + private final ISysConfigService configService; + private final ZBTService zbtService; + private final TtUserMapper userMapper; + private final TtOrnamentMapper ornamentsMapper; + private final TtBoxRecordsMapper boxRecordsMapper; + private final TtDeliveryRecordMapper deliveryRecordMapper; + + //@Autowired + private YYClient yyClient; + + @Autowired + private TtYyOrnamentsService ttYyOrnamentsService; + + @Autowired + private TtOrnamentService ttOrnamentService; + + @Autowired + private TtOrnamentYYService ttOrnamentYYService; + + @Autowired + private TtOrnamentsYYMapper ttOrnamentsYYMapper; + + @Autowired + private yyyoupingService yyyoupingService; + + @Autowired + private V5ItemService v5ItemService; + + @Autowired + private Cs340Service cs340Service; + + public DeliverGoodsServiceImpl(ISysConfigService configService, + ZBTService zbtService, + TtUserMapper userMapper, + TtOrnamentMapper ornamentsMapper, + TtBoxRecordsMapper boxRecordsMapper, + TtDeliveryRecordMapper deliveryRecordMapper) { + this.configService = configService; + this.zbtService = zbtService; + this.userMapper = userMapper; + this.ornamentsMapper = ornamentsMapper; + this.boxRecordsMapper = boxRecordsMapper; + this.deliveryRecordMapper = deliveryRecordMapper; + } + + @Autowired + private RabbitTemplate rabbitTemplate; + + @Override + public R getAvailableMarketList(Integer ornamentsId,Integer partyType) { + + // 从平台获取的数据集 + List resList = null; + if (partyType.equals(PartyType.ZBT.getCode())){ + + // 获取饰品信息 + TtOrnament ornament = new LambdaQueryChainWrapper<>(ornamentsMapper) + .eq(TtOrnament::getId, ornamentsId) + .one(); + // 构建查询参数 + ProductListParams apiParm = ProductListParams.builder() + .itemId(String.valueOf(ornament.getId())) + .build(); + + // 查询接口 + ResultZbt productList = zbtService.productList(apiParm); + if (!productList.getSuccess()) { + log.error("调用ZBT获取某一个饰品的所有在售接口异常,请检查代码是否正确!"); + return null; + } + + // 过滤可用数据 + AvailableMarketList availableMarketList = productList.getData(); + + resList = availableMarketList.getList(); + + }else if (partyType.equals(PartyType.YY_YOU_PING.getCode())){ + + // 获取饰品信息 + TtOrnament ornament = new LambdaQueryChainWrapper<>(ornamentsMapper) + .eq(TtOrnament::getId, ornamentsId) + .one(); + // 构建查询参数 + ProductListParams apiParm = ProductListParams.builder() + .itemId(String.valueOf(ornament.getId())) + .build(); + + // 查询接口 + ResultZbt productList = zbtService.productList(apiParm); + if (!productList.getSuccess()) { + log.error("调用ZBT获取某一个饰品的所有在售接口异常,请检查代码是否正确!"); + return null; + } + + // 过滤可用数据 + AvailableMarketList availableMarketList = productList.getData(); + + resList = availableMarketList.getList(); + } + + if (ObjectUtil.isEmpty(resList)) return R.fail("平台没有该物品在售信息。"); + + // 构建返回结果 + List resultList = new ArrayList<>(); + + // 提取可用数据 + for (AvailableMarket availableMarket : resList) { + OnSaleOrnamentVO data = OnSaleOrnamentVO.builder().build(); + data.setId(availableMarket.getId()); + data.setDelivery(availableMarket.getDelivery()); + data.setItemName(availableMarket.getItemName()); + data.setImageUrl(availableMarket.getImageUrl()); + data.setCnyPrice(availableMarket.getCnyPrice()); + data.setPrice(availableMarket.getPrice()); + resultList.add(data); + } + return R.ok(resultList); + } + + @Override + public R tradeBuy(TradeBuyParam param) { + // 获取提货订单数据 + TtDeliveryRecord ttDeliveryRecord = deliveryRecordMapper.selectById(param.getDeliveryRecordId()); + + // 获取用户数据 + TtUser ttUser = userMapper.selectById(ttDeliveryRecord.getUserId()); + // 初始化数据 + String transactionLink = ttUser.getTransactionLink(); + if (StringUtils.isEmpty(transactionLink)) { + return R.fail("该用户未绑定Steam交易链接!"); + } + String outTradeNo = ttDeliveryRecord.getOutTradeNo(); + + if (param.getPartyType().equals(PartyType.ZBT.getCode())){ + + // 构建购买参数 + NormalBuyParamV2DTO normalBuyParamV2DTO = new NormalBuyParamV2DTO(); + normalBuyParamV2DTO.setOutTradeNo(outTradeNo); + normalBuyParamV2DTO.setProductId(param.getProductId().toString()); + normalBuyParamV2DTO.setTradeUrl(transactionLink); + + // 购买 + ResultZbt tradeBuy = zbtService.tradeBuy(normalBuyParamV2DTO); + if (!tradeBuy.getSuccess()) { + return R.ok(tradeBuy.getErrorMsg()); + } + + // 获取结果数据 + OpenBuyResultDTO data = tradeBuy.getData(); + System.err.println("结果数据:" + data); + // 更新提货订单数据 + ttDeliveryRecord.setBuyPrice(data.getBuyPrice()); + ttDeliveryRecord.setThirdpartyDelivery(data.getDelivery()); + ttDeliveryRecord.setOrderId(data.getOrderId()); + ttDeliveryRecord.setStatus(DELIVERY_BEFORE.getCode()); // 发货订单状态 1待发货 + ttDeliveryRecord.setMessage(DELIVERY_BEFORE.getMsg()); + ttDeliveryRecord.setUpdateBy(SecurityUtils.getUsername()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + if (deliveryRecordMapper.updateById(ttDeliveryRecord) > 0) { + return R.ok(); + } + return R.fail("更新提货订单数据异常!"); + + }else if (param.getPartyType().equals(PartyType.V5ITEM.getCode())){ + LambdaQueryWrapper ttOrnamentsQuery = new LambdaQueryWrapper<>(); + ttOrnamentsQuery.eq(TtOrnament::getId, param.getProductId()); + TtOrnament ttOrnament = ttOrnamentService.getOne(ttOrnamentsQuery); + + CreateOrderBody createOrderBody = new CreateOrderBody(); + createOrderBody.setMarketHashName(ttOrnament.getMarketHashName()); + BigDecimal purchasePrice = param.getPrice(); //.multiply(new BigDecimal("2")); + createOrderBody.setPurchasePrice(purchasePrice); + createOrderBody.setTradeUrl(transactionLink); + createOrderBody.setMerchantOrderNo(outTradeNo); + + V5ItemResult createOrderResult = v5ItemService.createOrder(createOrderBody); + + if (createOrderResult.getCode() != 0) { + return R.fail(createOrderResult.getMsg()); + } + // 更新提货订单数据 + ttDeliveryRecord.setBuyPrice(createOrderResult.getData().getPayAmount()); + ttDeliveryRecord.setThirdpartyDelivery(1); + ttDeliveryRecord.setOrderId(createOrderResult.getData().getOrderNo()); + ttDeliveryRecord.setStatus(DELIVERY_BEFORE.getCode()); // 发货订单状态 1待发货 + ttDeliveryRecord.setMessage(DELIVERY_BEFORE.getMsg()); + ttDeliveryRecord.setUpdateBy(SecurityUtils.getUsername()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + if (deliveryRecordMapper.updateById(ttDeliveryRecord) > 0) { + return R.ok(); + } + return R.ok("正在开发"); + } else if (Objects.equals(param.getPartyType(), PartyType.CS340.getCode())) { + LambdaQueryWrapper ttOrnamentsQuery = new LambdaQueryWrapper<>(); + ttOrnamentsQuery.eq(TtOrnament::getId, param.getProductId()); + TtOrnament ttOrnament = ttOrnamentService.getOne(ttOrnamentsQuery); + BigDecimal purchasePrice = param.getPrice();// .multiply(new BigDecimal("2")); + ByTemplateCreateOrder createOrderBody = new ByTemplateCreateOrder(); + createOrderBody.setPurchasePrice(purchasePrice.toString()); + createOrderBody.setTradeLinks(transactionLink); + createOrderBody.setMerchantOrderNo(outTradeNo); + createOrderBody.setCommodityHashName(ttOrnament.getMarketHashName()); + + Cs340Result createOrderResult = cs340Service.createOrder(createOrderBody); + + if (createOrderResult.getCode() != 0) { + return R.fail(createOrderResult.getMsg()); + } + // 更新提货订单数据 + ttDeliveryRecord.setBuyPrice(createOrderResult.getData().getPayAmount()); + ttDeliveryRecord.setThirdpartyDelivery(1); + ttDeliveryRecord.setOrderId(createOrderResult.getData().getOrderNo()); + ttDeliveryRecord.setStatus(DELIVERY_BEFORE.getCode()); // 发货订单状态 1待发货 + ttDeliveryRecord.setMessage(DELIVERY_BEFORE.getMsg()); + ttDeliveryRecord.setUpdateBy(SecurityUtils.getUsername()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + if (deliveryRecordMapper.updateById(ttDeliveryRecord) > 0) { + return R.ok(); + } + return R.ok("正在开发"); + } else if (param.getPartyType().equals(PartyType.YY_YOU_PING.getCode())){ + return R.ok("暂未开放。"); + } else { + return R.fail("非法的第三方平台代码"); + } + + } + + @Override + @Transactional + public String synchronousStatus(String outTradeNo) { + // 获取提货数据 + TtDeliveryRecord ttDeliveryRecord = new LambdaQueryChainWrapper<>(deliveryRecordMapper) + .eq(TtDeliveryRecord::getOutTradeNo, outTradeNo).one(); + if (10 == ttDeliveryRecord.getStatus() || 11 == ttDeliveryRecord.getStatus()) { + return ""; + } + // 构建查询参数 + Cs340Result orderStatusResult = cs340Service.queryOrderStatus(ttDeliveryRecord.getOrderId()); + if (orderStatusResult == null|| orderStatusResult.getData() == null || orderStatusResult.getCode() != 0 ) { + return "查询购买订单详情失败"; + } + // 获取结果数据 + Integer status = orderStatusResult.getData().getBigStatus(); + + if (Objects.equals(status, 2) || Objects.equals(status, 280)){ + ttDeliveryRecord.setStatus(status); + ttDeliveryRecord.setMessage(orderStatusResult.getData().getStatusMsg()); + ttDeliveryRecord.setUpdateBy(SecurityUtils.getUsername()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + // 将用户提货饰品返回用户背包 + TtBoxRecords ttBoxRecords = new LambdaQueryChainWrapper<>(boxRecordsMapper) + .eq(TtBoxRecords::getId, ttDeliveryRecord.getBoxRecordsId()) + .eq(TtBoxRecords::getHolderUserId, ttDeliveryRecord.getUserId()) + .eq(TtBoxRecords::getOrnamentId, ttDeliveryRecord.getOrnamentId()) + .eq(TtBoxRecords::getStatus, "2") // 状态 2已提取 + .one(); + if (StringUtils.isNull(ttBoxRecords)) { + return "未查询到该用户背包的饰品信息!"; + } + ttBoxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode()); // 状态 0背包 + ttBoxRecords.setUpdateTime(DateUtils.getNowDate()); + boxRecordsMapper.updateById(ttBoxRecords); + if (deliveryRecordMapper.updateById(ttDeliveryRecord) > 0) { + return ""; + } + } + ttDeliveryRecord.setMessage(orderStatusResult.getData().getStatusMsg()); + if (Objects.equals(status, 340)) { + ttDeliveryRecord.setStatus(DeliveryOrderStatus.ORDER_COMPLETE.getCode()); + } else { + ttDeliveryRecord.setStatus(DeliveryOrderStatus.DELIVERY_AFTER.getCode()); + } + ttDeliveryRecord.setUpdateBy(SecurityUtils.getUsername()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + if (deliveryRecordMapper.updateById(ttDeliveryRecord) > 0) return ""; + return "同步提货订单数据异常!"; + } + + /** + * 保留V5历史同步订单状态的代码逻辑,后面再对接V5的话及时打开 + */ + private String synchronousStatusForV5(String outTradeNo) { + // 获取提货数据 + TtDeliveryRecord ttDeliveryRecord = new LambdaQueryChainWrapper<>(deliveryRecordMapper) + .eq(TtDeliveryRecord::getOutTradeNo, outTradeNo).one(); + if (10 == ttDeliveryRecord.getStatus() || 11 == ttDeliveryRecord.getStatus()) { + return ""; + } + // 构建查询参数 + OrderBuyDetailParams orderBuyDetailParams = new OrderBuyDetailParams(); + orderBuyDetailParams.setOutTradeNo(ttDeliveryRecord.getOutTradeNo()); + QueryOrderStatusResult orderStatusResult = v5ItemService.queryOrderStatus(ttDeliveryRecord.getOutTradeNo(), ttDeliveryRecord.getOrderId()); + if (orderStatusResult == null) { + return "查询购买订单详情V2失败"; + } + // 获取结果数据 + Integer status = orderStatusResult.getStatus(); + + if (Objects.equals(status, 4)){ + ttDeliveryRecord.setStatus(status); + ttDeliveryRecord.setMessage(orderStatusResult.getStatusMsg()); + ttDeliveryRecord.setUpdateBy(SecurityUtils.getUsername()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + // 将用户提货饰品返回用户背包 + TtBoxRecords ttBoxRecords = new LambdaQueryChainWrapper<>(boxRecordsMapper) + .eq(TtBoxRecords::getId, ttDeliveryRecord.getBoxRecordsId()) + .eq(TtBoxRecords::getHolderUserId, ttDeliveryRecord.getUserId()) + .eq(TtBoxRecords::getOrnamentId, ttDeliveryRecord.getOrnamentId()) + .eq(TtBoxRecords::getStatus, "2") // 状态 2已提取 + .one(); + if (StringUtils.isNull(ttBoxRecords)) { + return "未查询到该用户背包的饰品信息!"; + } + ttBoxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode()); // 状态 0背包 + ttBoxRecords.setUpdateTime(DateUtils.getNowDate()); + boxRecordsMapper.updateById(ttBoxRecords); + if (deliveryRecordMapper.updateById(ttDeliveryRecord) > 0) { + return ""; + } + } else { + ttDeliveryRecord.setMessage(orderStatusResult.getStatusMsg()); + // 发送发货成功通知 + Map noticeMap = new HashMap<>(); + noticeMap.put("userId", ttDeliveryRecord.getUserId().toString()); + noticeMap.put("title", "发货成功通知"); + noticeMap.put("content", "发货成功,请注意查收。"); + noticeMap.put("createTime", DateUtils.getTime()); + rabbitTemplate.convertAndSend("notice_queue", noticeMap); + } + + ttDeliveryRecord.setStatus(DeliveryOrderStatus.DELIVERY_AFTER.getCode()); + ttDeliveryRecord.setUpdateBy(SecurityUtils.getUsername()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + if (deliveryRecordMapper.updateById(ttDeliveryRecord) > 0) return ""; + return "同步提货订单数据异常!"; + } + + @Override + public int syncAllStatus() { + List records = new LambdaQueryChainWrapper<>(deliveryRecordMapper) + .eq(TtDeliveryRecord::getStatus, 1) + .list(); + for (TtDeliveryRecord record : records) { + String msg = synchronousStatus(record.getOutTradeNo()); + } + return records.size(); + } + + @Override + public void autoDelivery(Integer userId) { + // 获取需要自动发货的数据 + List deliveryRecordList = new LambdaQueryChainWrapper<>(deliveryRecordMapper) + .eq(TtDeliveryRecord::getUserId, userId) + .eq(TtDeliveryRecord::getDelivery, "2") // 网站发货模式(1人工发货 2自动发货 3主播号提取) + .eq(TtDeliveryRecord::getStatus, "0") // 发货订单状态(0发起提货 1待发货 3待收货 10订单完成 11订单取消) + .list(); + if (StringUtils.isNull(deliveryRecordList) || deliveryRecordList.isEmpty()) { + log.info("{}号用户发起提货,无自动发货饰品,请在后台手动发货!", userId); + return; + } + + // 饰品购买价格溢价率 + String buyPricePremiumRateStr = configService.selectConfigByKey("buyPricePremiumRate"); + BigDecimal buyPricePremiumRate = new BigDecimal(buyPricePremiumRateStr); + // ZBT平台币种汇率 + String ZBTParitiesStr = configService.selectConfigByKey("ZBTParities"); + BigDecimal ZBTParities = new BigDecimal(ZBTParitiesStr); + + // 快速购买 + for (TtDeliveryRecord ttDeliveryRecord : deliveryRecordList) { + // 获取用户信息 + TtUser ttUser = userMapper.selectById(ttDeliveryRecord.getUserId()); + // 获取饰品信息 + TtOrnament ttOrnament = ornamentsMapper.selectById(ttDeliveryRecord.getOrnamentId()); + // 计算可接受最高价 + BigDecimal ornamentsPrice = ttDeliveryRecord.getOrnamentsPrice(); // 饰品价格 + BigDecimal price = ornamentsPrice.divide(ZBTParities, 2, RoundingMode.HALF_UP).multiply(buyPricePremiumRate); + // 构建购买参数 + QuickBuyParamV2DTO quickBuyParamV2DTO = new QuickBuyParamV2DTO(); + // TODO: 2024/3/30 ttOrnament.getId() + quickBuyParamV2DTO.setItemId(String.valueOf(ttOrnament.getId())); + quickBuyParamV2DTO.setMaxPrice(price.toString()); // 购买a可以接受的最高价格(T币) + quickBuyParamV2DTO.setOutTradeNo(ttDeliveryRecord.getOutTradeNo()); + quickBuyParamV2DTO.setTradeUrl(ttUser.getTransactionLink()); + ResultZbt tradeQuickBuy = zbtService.tradeQuickBuy(quickBuyParamV2DTO); + if (!tradeQuickBuy.getSuccess()) { + log.error("快速购买接口v2异常,订单号:{}", ttDeliveryRecord.getOutTradeNo()); + continue; + } + // 获取结果数据 + OpenBuyResultDTO data = tradeQuickBuy.getData(); + // 更新提货订单数据 + ttDeliveryRecord.setBuyPrice(data.getBuyPrice()); + ttDeliveryRecord.setThirdpartyDelivery(data.getDelivery()); + ttDeliveryRecord.setOrderId(data.getOrderId()); + ttDeliveryRecord.setStatus(DELIVERY_BEFORE.getCode()); // 发货订单状态 1待发货 + ttDeliveryRecord.setMessage("等待发货"); + ttDeliveryRecord.setUpdateBy("自动发货"); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + // 更新状态 + if (deliveryRecordMapper.updateById(ttDeliveryRecord) > 0) { + log.info("订单'{}'自动发货成功,等待卖家发货中......", ttDeliveryRecord.getOutTradeNo()); + } + } + } + + public List ZBTGetAvailable(String marketHashName, ProductListParams productListParams){ + + LambdaQueryWrapper ttOrnamentsQuery = new LambdaQueryWrapper<>(); + ttOrnamentsQuery.eq(TtOrnament::getMarketHashName, marketHashName); + TtOrnament ornament = ttOrnamentService.getOne(ttOrnamentsQuery); + + // zbt所有数据 + productListParams.setItemId(String.valueOf(ornament.getId())); + ResultZbt availableZbt = zbtService.productList(productListParams); + + if (!availableZbt.getSuccess()){ + log.warn(availableZbt.getErrorMsg()); + return null; + } + + AvailableMarketList data = availableZbt.getData(); + return data.getList().stream().map(item->{ + return AvailableMarketOrnamentVO.builder() + .platform(Platform.ZBT.getCode()) + .itemId(item.getItemId()) + .delivery(item.getDelivery()) + .itemName(item.getItemName()) + .imageUrl(item.getImageUrl()) + .cnyPrice(item.getCnyPrice()) + .price(item.getPrice()) + .build(); + }).collect(Collectors.toList()); + + } + + public List YYGetAvailable(String marketHashName, ProductListParams productListParams){ + + LambdaQueryWrapper ttYYOrnamentsQuery = new LambdaQueryWrapper<>(); + ttYYOrnamentsQuery.eq(TtYYOrnaments::getHashName,marketHashName); + TtYYOrnaments yyOrnament = ttYyOrnamentsService.getOne(ttYYOrnamentsQuery); + + HashMap map = new HashMap<>(); + JSONObject o = new JSONObject(); + o.put("templateId",yyOrnament.getId()); + log.info("obj="+o.toString()); + map.put("requestList", ListUtil.of(o)); + YYResult yyResult = yyyoupingService.yyApiBatchGetOnSaleCommodityInfo(map); + + Object dataJson = yyResult.getData(); + ObjectMapper objectMapper = new ObjectMapper(); + queryTemplateSaleByCategoryDataVO dataVO; + try { + dataVO = objectMapper.readValue(JSON.toJSONString(dataJson), queryTemplateSaleByCategoryDataVO.class); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + List voList = dataVO.getSaleTemplateByCategoryResponseList(); + + List collect = voList.stream().map(item -> { + return AvailableMarketOrnamentVO.builder() + .platform(Platform.YY.getCode()) + .templateId(item.getTemplateId()) + .imageUrl(item.getIconUrl()) + .build(); + }).collect(Collectors.toList()); + + return null; + } + + @Override + public List getAvailableMarketListByHashName(String marketHashName, ProductListParams productListParams) { + + // zbt所有数据 + List ZBTGetAvailable = ZBTGetAvailable(marketHashName, productListParams); + + // yy所有数据 + List YYGetAvailable = YYGetAvailable(marketHashName, productListParams); + + // 合并所有数据 + + return null; + + // // 获取饰品itemId + // TtOrnaments ornaments = new LambdaQueryChainWrapper<>(ornamentsMapper).eq(TtOrnaments::getId, ornamentsId).one(); + // // 构建查询参数 + // productListParams.setItemId(ornaments.getItemId()); + // ResultZbt productList = zbtService.productList(productListParams); // 查询接口 + // if (!productList.getSuccess()) { + // log.error("调用ZBT获取某一个饰品的所有在售接口异常,请检查代码是否正确!"); + // return null; + // } + // AvailableMarketList availableMarketList = productList.getData(); + // // 查询到的数据 + // List list = availableMarketList.getList(); + // // 构建返回结果 + // List resultList = new ArrayList<>(); + // // 提取可用数据 + // for (AvailableMarket availableMarket : list) { + // AvailableMarketOrnamentsListDataVO data = AvailableMarketOrnamentsListDataVO.builder().build(); + // data.setId(availableMarket.getId()); + // data.setDelivery(availableMarket.getDelivery()); + // data.setItemName(availableMarket.getItemName()); + // data.setImageUrl(availableMarket.getImageUrl()); + // data.setCnyPrice(availableMarket.getCnyPrice()); + // data.setPrice(availableMarket.getPrice()); + // resultList.add(data); + // } + // return resultList; + } + + @Override + public R getAvailableMarketList(GetAvailableMarketListParam param) { + + // 返回结果 + List resultList = new ArrayList<>(); + + // 获取饰品信息(这里暂时不支持批量查询) + List list = new LambdaQueryChainWrapper<>(ornamentsMapper) + .in(TtOrnament::getId, param.getOrnamentsId().get(0)) + .list(); + + if (param.getPartyType().equals(PartyType.ZBT.getCode())){ + + for (TtOrnament item: list){ + + if (ObjectUtil.isEmpty(item.getZbtId())) continue; + + // 构建查询参数 + ProductListParams apiParm = ProductListParams.builder() + .itemId(String.valueOf(item.getZbtId())) + .build(); + + // 查询接口 + ResultZbt productList = zbtService.productList(apiParm); + if (!productList.getSuccess()) { + log.error("调用ZBT获取某一个饰品的所有在售接口异常,请检查代码是否正确!"); + return R.fail("调用ZBT获取某一个饰品的所有在售接口异常"); + } + + // 过滤可用数据 + AvailableMarketList availableMarketList = productList.getData(); + + List list1 = availableMarketList.getList(); + + if (ObjectUtil.isEmpty(list1)) { + return R.fail("平台没有该物品在售信息。"); + } + + // 提取可用数据 + for (AvailableMarket availableMarket : list1) { + OnSaleOrnamentVO data = OnSaleOrnamentVO.builder().build(); + data.setPartyType(PartyType.ZBT.getCode()); + data.setId(availableMarket.getId()); + data.setDelivery(availableMarket.getDelivery()); + data.setItemName(availableMarket.getItemName()); + data.setImageUrl(availableMarket.getImageUrl()); + data.setCnyPrice(availableMarket.getCnyPrice()); + data.setPrice(availableMarket.getPrice()); + resultList.add(data); + } + } + + } else if (param.getPartyType().equals(PartyType.YY_YOU_PING.getCode())){ + + // 构建查询参数 + HashMap apiParam = new HashMap<>(); + + ArrayList> ids = new ArrayList<>(); + for (TtOrnament item : list){ + Map i = MapUtil.builder("templateId", String.valueOf(item.getYyyoupingId())).build(); + ids.add(i); + } + + apiParam.put("requestList",ids); + + // 查询接口 + YYResult yyResult = yyyoupingService.yyApiBatchGetOnSaleCommodityInfo(apiParam); + if (!yyResult.isSuccess()) { + log.error("调用ZBT获取某一个饰品的所有在售接口异常,请检查代码是否正确!"); + return R.fail("调用YY获取某一个饰品的所有在售接口异常"); + } + + // 过滤可用数据 + List data = JSONUtil.toList(JSON.toJSONString(yyResult.getData()), String.class); + resultList = data.stream().map(item->{ + cn.hutool.json.JSONObject obj = JSONUtil.parseObj(item); + cn.hutool.json.JSONObject saleTemplateResponse = obj.get("saleTemplateResponse", cn.hutool.json.JSONObject.class); + cn.hutool.json.JSONObject saleCommodityResponse = obj.get("saleCommodityResponse", cn.hutool.json.JSONObject.class); + + return OnSaleOrnamentVO.builder() + .partyType(PartyType.YY_YOU_PING.getCode()) + .id(saleTemplateResponse.get("templateId",String.class)) + .itemName(saleCommodityResponse.get("name",String.class)) + .imageUrl(list.get(0).getImageUrl()) + .price(saleCommodityResponse.get("minSellPrice",BigDecimal.class)) + .build(); + }).collect(Collectors.toList()); + + } + + return R.ok(resultList); + } + + @Override + public R tradeConfirm(TradeManualConfirmParam param) { + // 获取提货订单数据 + TtDeliveryRecord ttDeliveryRecord = deliveryRecordMapper.selectById(param.getDeliveryRecordId()); + + // 更新提货订单数据 + ttDeliveryRecord.setBuyPrice(param.getPrice()); + ttDeliveryRecord.setThirdpartyDelivery(1); + ttDeliveryRecord.setOrderId(param.getOrderNo()); + ttDeliveryRecord.setStatus(param.getStatus()); // 发货订单状态 1待发货 + ttDeliveryRecord.setMessage(param.getMessage()); + ttDeliveryRecord.setUpdateBy(SecurityUtils.getUsername()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + if (deliveryRecordMapper.updateById(ttDeliveryRecord) > 0) { + return R.ok(); + } + return R.fail("更新提货订单数据异常!"); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/RechargeSuccessfulNoticeServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/RechargeSuccessfulNoticeServiceImpl.java new file mode 100644 index 0000000..fd4b416 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/service/impl/RechargeSuccessfulNoticeServiceImpl.java @@ -0,0 +1,55 @@ +package com.ruoyi.thirdparty.common.service.impl; + +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.thirdparty.common.service.ApiNoticeService; +import com.ruoyi.thirdparty.common.service.RechargeSuccessfulNoticeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@Service +public class RechargeSuccessfulNoticeServiceImpl implements RechargeSuccessfulNoticeService { + + /*@Autowired + private RabbitTemplate rabbitTemplate; + + @Override + public void sendRechargeSuccessNotice(String userId, BigDecimal amount) { + try { + Map noticeMap = new HashMap<>(); + noticeMap.put("userId", userId); + noticeMap.put("title", "充值到账通知"); + noticeMap.put("content", "您充值" + amount + "已到账,请注意查收。"); + noticeMap.put("createTime", DateUtils.getTime()); + rabbitTemplate.convertAndSend("notice_queue", noticeMap); + } catch (Exception e) { + log.error("happen exception is {}", e.getMessage(), e); + } + + }*/ + + @Autowired + @Qualifier("thirdpartyApiNoticeServiceImpl") + private ApiNoticeService apiNoticeService; + + @Override + public void sendRechargeSuccessNotice(String userId, BigDecimal amount) { + AsyncManager.me().run(() -> { + try { + String title = "充值到账通知"; + String content = "您充值" + amount + "已到账,请注意查收。"; + apiNoticeService.sendNotice(Long.valueOf(userId), title, content); + } catch (Exception e) { + log.error("发送充值通知失败,userId={}, error={}", userId, e.getMessage(), e); + } + }); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/task/LoadAllOrnaments.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/task/LoadAllOrnaments.java new file mode 100644 index 0000000..8f7f2d3 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/common/task/LoadAllOrnaments.java @@ -0,0 +1,606 @@ +package com.ruoyi.thirdparty.common.task; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.math.MathUtil; +import cn.hutool.core.util.HashUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.ruoyi.admin.service.ConfigService; +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.admin.service.TtOrnamentYYService; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.domain.common.constant.PartyType; +import com.ruoyi.domain.dto.yyyouping.OrnamentYY; +import com.ruoyi.domain.dto.zbt.OrnamentZBT; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.system.mapper.SysDictDataMapper; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.common.NetworkDataLoader.NetworkDataLoader; +import com.ruoyi.thirdparty.yyyouping.service.yyyoupingService; +import com.ruoyi.thirdparty.yyyouping.utils.common.YYResult; +import com.ruoyi.thirdparty.zbt.param.SearchParams; +import com.ruoyi.thirdparty.zbt.result.ResultZbt; +import com.ruoyi.thirdparty.zbt.result.product.SearchData; +import com.ruoyi.thirdparty.zbt.service.ZBTService; +import io.swagger.models.auth.In; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.sql.SQLIntegrityConstraintViolationException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.PartyType.YY_YOU_PING; +import static com.ruoyi.domain.common.constant.PartyType.ZBT; + +@Slf4j +// 项目启动加载所有饰品信息 +//@Component +public class LoadAllOrnaments implements ApplicationRunner { + + // @Value("${yy-youping.ornamentsFilePath}") + // private String YYOrnamentsFilePath; + // + // @Autowired + // private yyyoupingService yyyoupingService; + + @Autowired + private TtOrnamentService ttOrnamentService; + + @Value("${mkcsgo.startLoadOrnaments}") + private Boolean startLoadOrnaments; + + // 资源加载器集合 + @Autowired + private List networkDataLoaderList; + + @Override + public void run(ApplicationArguments args) throws Exception { + + log.info("是否加载网络ornaments资源{}", startLoadOrnaments); + if (!startLoadOrnaments) return; + + // 1 加载各个平台数据 + Set ttOrnaments = new HashSet<>(); + for (NetworkDataLoader loader : networkDataLoaderList) { + Set load = loader.load(); + ttOrnaments.addAll(load); + } + if (ObjectUtil.isEmpty(ttOrnaments) || ttOrnaments.isEmpty()) { + log.warn("加载饰品网络资源失败。"); + return; + } + + // 2 写db + log.info("db allData ing~~~"); + for (TtOrnament item : ttOrnaments) { + try { + ttOrnamentService.saveOrUpdate(item); + } catch (DuplicateKeyException e) { + // log.info("过滤重复数据 【id】{}【hash】{}save。",item.getId(),item.getMarketHashName()); + } catch (Exception e) { + e.printStackTrace(); + log.info("保存数据异常。"); + break; + } + } + + log.info("加载饰品网络资源成功。"); + + } + + // 从平台获取数据 + // private Set loadOrnamentFromNet(PartyType[] partyTypes) { + // + // // 加载必要的枚举数据 + // List dictTypes = Arrays.asList( + // "ornaments_type", "ornaments_type_name", + // "ornaments_exterior", "ornaments_exterior_name", + // "ornaments_quality", "ornaments_quality_name", + // "ornaments_rarity", "ornaments_rarity_name"); + // List enumList = dictDataMapper.selectDictDataByTypes(dictTypes); + // + // Set res = null; + // for (NetworkDataLoader loader:networkDataLoaderList){ + // Set load = loader.load(); + // res.addAll(load); + // } + // + // return res; + // + // //1 加载ZBT平台数据 + // + // //... 加载...平台数据 + // + // //2 合并所有数据 + // + // // //1 从一个平台获取第一组基础数据 + // // Set baseData = loadBaseData(partyTypes[1],enumList); + // // if (ObjectUtil.isEmpty(baseData) || baseData.isEmpty()) return baseData; + // // + // // //2 整合其他平台 + // // return mergeOtherParty(baseData, partyTypes, enumList); + // + // } + // + // public Set mergeOtherParty(Set baseData,PartyType[] partyTypes ,List enumList){ + // + // // Integer f = 0; + // for (int i = 1; i < partyTypes.length; i++) { + // if (partyTypes[i].equals(YY_YOU_PING)) { + // + // YYResult yyResult = yyyoupingService.yyApiTemplateQuery(new HashMap()); + // String fileUrl = (String) yyResult.getData(); + // log.info(fileUrl); + // + // // 下载文件 + // SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss"); + // String format = dateFormat.format(System.currentTimeMillis()); + // String fileName = YYOrnamentsFilePath + "yy_orn_" + format + ".txt"; + // + // long size = HttpUtil.downloadFile(fileUrl, FileUtil.file(fileName)); + // log.info("yy饰品信息下载成功,文件大小: " + size); + // + // // 解析json + // File file = FileUtil.file(fileName); + // JSONArray jsonArray = JSONUtil.readJSONArray(file, StandardCharsets.UTF_8); + // List yyList = jsonArray.toList(OrnamentYY.class); + // + // // 转换为TtOrnament + // Set collect = yyList.stream().map(item -> { + // + // return TtOrnament.builder() + // .id(Long.valueOf(item.getHashName().hashCode())) + // .yyyoupingId(item.getId()) + // .marketHashName(item.getHashName()) + // .name(item.getName()) + // .price(BigDecimal.TEN) //// TODO: 2024/4/1 补充价格信息 + // .usePrice(BigDecimal.TEN) + // .updateTime(item.getUpdateTime()) + // .build(); + // + // }).collect(Collectors.toSet()); + // + // baseData.addAll(collect); + // + // } else if (partyTypes[i].equals(ZBT)) { + // + // // 饰品类型枚举 + // //List typeList = dictDataMapper.selectDictDataByType("ornaments_type"); + // + // for (SysDictData type : enumList) { + // + // if (!type.getDictType().equals("ornaments_type")) continue; + // + // Set ttOrnaments = ZBTLoadOrnamentByType(type.getDictLabel(),type,enumList); + // + // baseData.addAll(ttOrnaments); + // + // log.info("合并其他平台{}条数据。",ttOrnaments.size()); + // + // } + // + // } else { + // log.warn("非法的平台类型代码,获取饰品数据失败。"); + // } + // } + // + // // 过滤zbt没有的数据 + // log.info("过滤数据"); + // Set resData = baseData.stream().filter(item -> { + // return ObjectUtil.isNotEmpty(item.getZbtId()); + // }).collect(Collectors.toSet()); + // + // return resData; + // + // } + // + // // zbt 根据饰品类型获取数据 + // private Set ZBTLoadOrnamentByType(String typeHash,SysDictData type,List allEnum){ + // + // HashSet result = new HashSet<>(); + // + // // 分批请求api + // int page = 1; + // boolean stopFlag = false; + // while (true) { + // + // // 结束标记 + // if (stopFlag) break; + // + // // 请求平台数据 + // SearchParams param = SearchParams.builder() + // .page(String.valueOf(page)) + // .category(typeHash) + // .limit("200") + // .build(); + // ResultZbt response = zbtService.search(param); + // SearchData data = response.getData(); + // if (!response.getSuccess() || ObjectUtil.isEmpty(data)) break; + // + // List list = JSONUtil.toList(JSONUtil.toJsonStr(data.getList()), OrnamentZBT.class); + // + // if (list.size() < 200) stopFlag = !stopFlag; + // + // // 转换为通用类型 + // Set collect = list.stream().map(item -> { + // + // Long id = Long.valueOf(HashUtil.rsHash(item.getMarketHashName())); + // if (id < 0) id = Math.abs(id); + // + // TtOrnament ornament = TtOrnament.builder() + // .id(id) + // .zbtId(item.getItemId()) + // .marketHashName(item.getMarketHashName()) + // .name(item.getItemName()) + // .shortName(item.getShortName()) + // + // .price(ObjectUtil.isNotEmpty(item.getPriceInfo()) ? item.getPriceInfo().getPrice() : null) + // .usePrice(ObjectUtil.isNotEmpty(item.getPriceInfo()) ? item.getPriceInfo().getPrice() : null) + // .quantity(ObjectUtil.isNotEmpty(item.getPriceInfo()) ? item.getPriceInfo().getQuantity() : null) + // + // .imageUrl(item.getImageUrl()) + // + // .type(type.getDictValue()) + // .typeName(item.getTypeName()) + // + // .quality(getCodeByNameOrHash(allEnum,item.getQuality())) + // .qualityName(item.getQualityName()) + // .qualityColor(item.getQualityColor()) + // + // .rarity(getCodeByNameOrHash(allEnum,item.getRarity())) + // .rarityName(item.getRarityName()) + // .rarityColor(item.getRarityColor()) + // + // .exterior(getCodeByNameOrHash(allEnum,item.getExterior())) + // .exteriorHashName(item.getExterior()) + // .exteriorName(item.getExteriorName()) + // + // .updateTime(item.getUpdateTime()) + // .build(); + // + // ornament.setUsePrice(ornament.getPrice()); + // + // return ornament; + // }).collect(Collectors.toSet()); + // + // result.addAll(collect); + // + // // log.info("加载【扎比特】{}条类型为{}的饰品数据。",page*collect.size(),typeHash); + // System.out.print("\r加载【扎比特】"+page*collect.size()+"条类型为"+typeHash+"的饰品数据。"); + // + // page++; + // } + // + // return result; + // + // } + // + // // 从yy平台获取第一组基础数据 + // private Set loadBaseData(PartyType partyType,List allEnum) { + // + // Set baseData = null; + // + // if (partyType.equals(YY_YOU_PING)) { + // baseData = getBaseDataFromYY(allEnum); + // } else if (partyType.equals(ZBT)) { + // + // baseData = getBaseDataFromZBT(allEnum); + // + // } else { + // log.warn("非法的平台类型代码,获取饰品数据失败。"); + // return baseData; + // } + // + // return baseData; + // } + // + // // 从ZBT获取基础数据 + // public Set getBaseDataFromZBT(List allEnum){ + // + // Set baseData = null; + // + // YYResult yyResult = yyyoupingService.yyApiTemplateQuery(new HashMap()); + // if (!yyResult.isSuccess()) { + // log.warn(yyResult.getMsg()); + // return baseData; + // } + // String fileUrl = (String) yyResult.getData(); + // log.info("yyFile=" + fileUrl); + // + // // 下载文件 + // String fileName = YYOrnamentsFilePath + "yyOrn" + System.currentTimeMillis() + ".txt"; + // System.out.println(fileName); + // + // long size = HttpUtil.downloadFile(fileUrl, new File(fileName)); + // log.info("yy饰品信息下载成功,文件大小: " + size); + // + // // 解析json + // File file = FileUtil.file(fileName); + // JSONArray jsonArray = JSONUtil.readJSONArray(file, StandardCharsets.UTF_8); + // List yyList = jsonArray.toList(OrnamentYY.class); + // + // log.info("补充详细信息~~~"); + // // 转换为TtOrnament + // baseData = yyList.stream().map(item -> { + // + // Long id = Long.valueOf(HashUtil.rsHash(item.getHashName())); + // if (id < 0) { + // id = Math.abs(id); + // } + // + // TtOrnament ornament = TtOrnament.builder() + // .id(id) + // .yyyoupingId(item.getId()) + // .marketHashName(item.getHashName()) + // + // .type(getCodeByNameOrHash(allEnum,item.getTypeHashName())) + // .typeName(item.getTypeName()) + // .typeHashName(item.getTypeHashName()) + // + // .name(item.getName()) + // .shortName(item.getName()) + // + // .isPutaway("1") + // .isProprietaryProperty("1") + // .updateTime(item.getUpdateTime()) + // .build(); + // + // return ornament; + // }).collect(Collectors.toSet()); + // + // // 分批补充 补充其他信息 + // List> requestList = baseData.stream().map(item -> { + // return MapUtil.builder("templateId", String.valueOf(item.getYyyoupingId())).build(); + // }).collect(Collectors.toList()); + // + // int page = 0; + // int pageSize = 200; + // Boolean stopFlag = false; + // ArrayList ornamentsList = new ArrayList<>(baseData); + // while (true) { + // + // if (stopFlag) { + // System.out.println("-100% 补充完成✔"); + // break; + // } + // + // BigDecimal p = new BigDecimal(page * pageSize) + // .divide(new BigDecimal(baseData.size()),2,BigDecimal.ROUND_DOWN); + // System.out.print("\r补充"+page * pageSize+"条基础记录"+p.multiply(new BigDecimal(100)).setScale(0)+"%"); + // + // + // List> reqList = null; + // try { + // reqList = requestList.subList(page * pageSize, page * pageSize + pageSize); // 尽量大 + // } catch (Exception e) { + // int lenth = requestList.size(); + // reqList = requestList.subList(page * pageSize, lenth - 1); // 尽量大 + // stopFlag = !stopFlag; + // } + // + // HashMap map = new HashMap<>(); + // map.put("requestList", reqList); + // + // YYResult yyResult1 = yyyoupingService.yyApiBatchGetOnSaleCommodityInfo(map); + // List list = JSONUtil.toList(JSONUtil.toJsonStr(yyResult1.getData()), JSONObject.class); + // + // for (JSONObject obj : list) { + // + // JSONObject saleTemplateResponse = JSONUtil.parseObj(obj.get("saleTemplateResponse")); + // JSONObject saleCommodityResponse = JSONUtil.parseObj(obj.get("saleCommodityResponse")); + // + // String templateId = String.valueOf((Integer) saleTemplateResponse.get("templateId")); + // + // for (int i = 0; i < ornamentsList.size(); i++) { + // + // TtOrnament ornament = ornamentsList.get(i); + // if (templateId.equals(String.valueOf(ornament.getYyyoupingId()))) { + // + // ornament.setPrice(new BigDecimal(saleCommodityResponse.get("minSellPrice").toString())); + // ornament.setUsePrice(new BigDecimal(saleCommodityResponse.get("minSellPrice").toString())); + // + // ornament.setImageUrl(saleTemplateResponse.get("iconUrl").toString()); + // + // Object exteriorName = saleTemplateResponse.get("exteriorName"); + // if (ObjectUtil.isNotNull(exteriorName)){ + // ornament.setExterior(getCodeByNameOrHash(allEnum,exteriorName.toString())); + // ornament.setExteriorName(exteriorName.toString()); + // } + // + // ornament.setQuality(getCodeByNameOrHash(allEnum,saleTemplateResponse.get("qualityName").toString())); + // ornament.setQualityName(saleTemplateResponse.get("qualityName").toString()); + // // if (ObjectUtil.isEmpty(ornament.getQuality()) && ObjectUtil.isNotEmpty(ornament.getQualityName())){ + // // System.out.println(ornament.getQuality()); + // // System.out.println(ornament.getQualityName()); + // // } + // + // ornament.setRarity(getCodeByNameOrHash(allEnum,saleTemplateResponse.get("rarityName").toString())); + // ornament.setRarityName(saleTemplateResponse.get("rarityName").toString()); + // + // ornamentsList.set(i,ornament); + // + // break; + // } + // } + // } + // + // page++; + // + // } + // + // baseData = new HashSet<>(ornamentsList); + // + // return baseData; + // } + // + // // 从yy获取基础数据 + // public Set getBaseDataFromYY(List allEnum){ + // + // Set baseData = null; + // + // YYResult yyResult = yyyoupingService.yyApiTemplateQuery(new HashMap()); + // if (!yyResult.isSuccess()) { + // log.warn(yyResult.getMsg()); + // return baseData; + // } + // String fileUrl = (String) yyResult.getData(); + // log.info("yyFile=" + fileUrl); + // + // // 下载文件 + // String fileName = YYOrnamentsFilePath + "yyOrn" + System.currentTimeMillis() + ".txt"; + // System.out.println(fileName); + // + // long size = HttpUtil.downloadFile(fileUrl, new File(fileName)); + // log.info("yy饰品信息下载成功,文件大小: " + size); + // + // // 解析json + // File file = FileUtil.file(fileName); + // JSONArray jsonArray = JSONUtil.readJSONArray(file, StandardCharsets.UTF_8); + // List yyList = jsonArray.toList(OrnamentYY.class); + // + // log.info("补充详细信息~~~"); + // // 转换为TtOrnament + // baseData = yyList.stream().map(item -> { + // + // Long id = Long.valueOf(HashUtil.rsHash(item.getHashName())); + // if (id < 0) { + // id = Math.abs(id); + // } + // + // TtOrnament ornament = TtOrnament.builder() + // .id(id) + // .yyyoupingId(item.getId()) + // .marketHashName(item.getHashName()) + // + // .type(getCodeByNameOrHash(allEnum,item.getTypeHashName())) + // .typeName(item.getTypeName()) + // .typeHashName(item.getTypeHashName()) + // + // .name(item.getName()) + // .shortName(item.getName()) + // + // .isPutaway("1") + // .isProprietaryProperty("1") + // .updateTime(item.getUpdateTime()) + // .build(); + // + // return ornament; + // }).collect(Collectors.toSet()); + // + // // 分批补充 补充其他信息 + // List> requestList = baseData.stream().map(item -> { + // return MapUtil.builder("templateId", String.valueOf(item.getYyyoupingId())).build(); + // }).collect(Collectors.toList()); + // + // int page = 0; + // int pageSize = 200; + // Boolean stopFlag = false; + // ArrayList ornamentsList = new ArrayList<>(baseData); + // while (true) { + // + // if (stopFlag) { + // System.out.println("-100% 补充完成✔"); + // break; + // } + // + // BigDecimal p = new BigDecimal(page * pageSize) + // .divide(new BigDecimal(baseData.size()),2,BigDecimal.ROUND_DOWN); + // System.out.print("\r补充"+page * pageSize+"条基础记录"+p.multiply(new BigDecimal(100)).setScale(0)+"%"); + // + // + // List> reqList = null; + // try { + // reqList = requestList.subList(page * pageSize, page * pageSize + pageSize); // 尽量大 + // } catch (Exception e) { + // int lenth = requestList.size(); + // reqList = requestList.subList(page * pageSize, lenth - 1); // 尽量大 + // stopFlag = !stopFlag; + // } + // + // HashMap map = new HashMap<>(); + // map.put("requestList", reqList); + // + // YYResult yyResult1 = yyyoupingService.yyApiBatchGetOnSaleCommodityInfo(map); + // List list = JSONUtil.toList(JSONUtil.toJsonStr(yyResult1.getData()), JSONObject.class); + // + // for (JSONObject obj : list) { + // + // JSONObject saleTemplateResponse = JSONUtil.parseObj(obj.get("saleTemplateResponse")); + // JSONObject saleCommodityResponse = JSONUtil.parseObj(obj.get("saleCommodityResponse")); + // + // String templateId = String.valueOf((Integer) saleTemplateResponse.get("templateId")); + // + // for (int i = 0; i < ornamentsList.size(); i++) { + // + // TtOrnament ornament = ornamentsList.get(i); + // if (templateId.equals(String.valueOf(ornament.getYyyoupingId()))) { + // + // ornament.setPrice(new BigDecimal(saleCommodityResponse.get("minSellPrice").toString())); + // ornament.setUsePrice(new BigDecimal(saleCommodityResponse.get("minSellPrice").toString())); + // + // ornament.setImageUrl(saleTemplateResponse.get("iconUrl").toString()); + // + // Object exteriorName = saleTemplateResponse.get("exteriorName"); + // if (ObjectUtil.isNotNull(exteriorName)){ + // ornament.setExterior(getCodeByNameOrHash(allEnum,exteriorName.toString())); + // ornament.setExteriorName(exteriorName.toString()); + // } + // + // ornament.setQuality(getCodeByNameOrHash(allEnum,saleTemplateResponse.get("qualityName").toString())); + // ornament.setQualityName(saleTemplateResponse.get("qualityName").toString()); + // // if (ObjectUtil.isEmpty(ornament.getQuality()) && ObjectUtil.isNotEmpty(ornament.getQualityName())){ + // // System.out.println(ornament.getQuality()); + // // System.out.println(ornament.getQualityName()); + // // } + // + // ornament.setRarity(getCodeByNameOrHash(allEnum,saleTemplateResponse.get("rarityName").toString())); + // ornament.setRarityName(saleTemplateResponse.get("rarityName").toString()); + // + // ornamentsList.set(i,ornament); + // + // break; + // } + // } + // } + // + // page++; + // + // } + // + // baseData = new HashSet<>(ornamentsList); + // + // return baseData; + // } + // + // public static void main(String[] args) { + // System.out.println("StatTrak™".equals("")); + // } + // + // // 根据类型和标签获取code + // private String getCodeByNameOrHash(List typeList,String nameOrHash){ + // + // for (SysDictData type : typeList){ + // if (type.getDictLabel().equals(nameOrHash)) return type.getDictValue(); + // if (type.getDictType().equals(nameOrHash)) return type.getDictValue(); + // } + // return null; + // } + + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/config/Cs340Config.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/config/Cs340Config.java new file mode 100644 index 0000000..a237a5f --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/config/Cs340Config.java @@ -0,0 +1,22 @@ +package com.ruoyi.thirdparty.cs340.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "cs340") +public class Cs340Config { + + private String baseUrl; + + private String appKey; + + private String privateKey; + + private String publicKey; + + private String callbackPublicKey; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/controller/Cs340ItemController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/controller/Cs340ItemController.java new file mode 100644 index 0000000..e5ae556 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/controller/Cs340ItemController.java @@ -0,0 +1,48 @@ +package com.ruoyi.thirdparty.cs340.controller; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.thirdparty.cs340.domain.body.ByTemplateCreateOrder; +import com.ruoyi.thirdparty.cs340.domain.body.Cs340CheckTradeUrlBody; +import com.ruoyi.thirdparty.cs340.domain.body.Cs340OnSaleInfoBody; +import com.ruoyi.thirdparty.cs340.domain.result.Cs340CallBackNotifyInfo; +import com.ruoyi.thirdparty.cs340.service.Cs340Service; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/admin/cs340item") +public class Cs340ItemController extends BaseController { + + @Autowired + private Cs340Service cs340Service; + + @PostMapping("/checkTradeUrl") + public AjaxResult checkTradeUrl(@RequestBody Cs340CheckTradeUrlBody body) { + return success(cs340Service.checkTradeUrl(body.getTradeUrl())); + } + + @GetMapping("/checkMerchantBalance") + public AjaxResult checkMerchantBalance() { + return success(cs340Service.checkMerchantBalance()); + } + + /** + * 查询库存 + */ + @PostMapping("/queryOnSaleInfo") + public AjaxResult queryOnSaleInfo(@Validated @RequestBody Cs340OnSaleInfoBody onSaleInfoBody) { + return success(cs340Service.queryOnSaleInfo(onSaleInfoBody)); + } + + @PostMapping("/createOrder") + public AjaxResult createOrder(@RequestBody ByTemplateCreateOrder createOrderBody) { + return success(cs340Service.createOrder(createOrderBody)); + } + + @PostMapping("/notify") + public AjaxResult notify(@RequestBody Cs340CallBackNotifyInfo notify) { + return cs340Service.notify(notify); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/body/ByTemplateCreateOrder.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/body/ByTemplateCreateOrder.java new file mode 100644 index 0000000..90e278e --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/body/ByTemplateCreateOrder.java @@ -0,0 +1,27 @@ +package com.ruoyi.thirdparty.cs340.domain.body; + +import lombok.Data; + +/** + * merchantOrderNo String 是 商户订单号 小于等于59位的字符串 + * tradeLinks String 是 收货方的Steam交易链接 + * commodityTemplateId String 是 商品模版ID + * 数据来源:查询模板ID templateId和hashName二选一必传递,同时传入以templateId为准,如传入不可为0 + * commodityHashName String 是 模板hashname + * 数据来源:查询模板ID templateId和hashName二选一必传递,同时传入以templateId为准,如传入不可为空 + * purchasePrice String 是 购买最高价 单位:元 + * 不可为0 + * fastShipping Integer 否 极速发货购买模式 0:优先购买极速发货; + * 1:只购买极速发货,该模式purchasePrice参数请使用fastShippingMinSellPrice作为价格,极速发货价格可通过批量查询在售商品价格或者批量查询在售商品详情接口获取,极速发货价格存在并且大于0可使用极速发货。 + */ +@Data +public class ByTemplateCreateOrder { + + private String merchantOrderNo; + + private String tradeLinks; + + private String commodityHashName; + + private String purchasePrice; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/body/Cs340CheckTradeUrlBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/body/Cs340CheckTradeUrlBody.java new file mode 100644 index 0000000..09628f1 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/body/Cs340CheckTradeUrlBody.java @@ -0,0 +1,9 @@ +package com.ruoyi.thirdparty.cs340.domain.body; + +import lombok.Data; + +@Data +public class Cs340CheckTradeUrlBody { + + private String tradeUrl; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/body/Cs340OnSaleInfoBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/body/Cs340OnSaleInfoBody.java new file mode 100644 index 0000000..3e43dca --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/body/Cs340OnSaleInfoBody.java @@ -0,0 +1,12 @@ +package com.ruoyi.thirdparty.cs340.domain.body; + +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Data +public class Cs340OnSaleInfoBody { + + @NotNull(message = "marketHashName不能为空") + private String marketHashName; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/BatchGetOnSaleCommodityInfoDto.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/BatchGetOnSaleCommodityInfoDto.java new file mode 100644 index 0000000..f2de117 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/BatchGetOnSaleCommodityInfoDto.java @@ -0,0 +1,16 @@ +package com.ruoyi.thirdparty.cs340.domain.dto; + +import lombok.Data; + +import java.util.List; + +@Data +public class BatchGetOnSaleCommodityInfoDto { + private String sign; + + private String timestamp; + + private String appKey; + + List requestList; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/ByTemplateCreateOrderDto.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/ByTemplateCreateOrderDto.java new file mode 100644 index 0000000..8bf589d --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/ByTemplateCreateOrderDto.java @@ -0,0 +1,38 @@ +package com.ruoyi.thirdparty.cs340.domain.dto; + +import lombok.Data; + +/** + * merchantOrderNo String 是 商户订单号 小于等于59位的字符串 + * tradeLinks String 是 收货方的Steam交易链接 + * commodityHashName String 是 模板hashname&数据来源:查询模板ID templateId和hashName二选一必传递,同时传入以templateId为准,如传入不可为空 + * purchasePrice String 是 购买最高价 单位:元 不可为0 + * 请求示例 + * { + * "sign":"4LL1O++Btn5JgZ6kHuJAN7dEG4cgLd0goo+Cb4wld0ygpU8lU+73mLxXYtN81ApAhWuw9zn+ENrtc2KcrL6XgHMSG8JC/5NjHCGO/3jDUjAac3YMh5vdQjsqhK1IoxdrOXM9QUyUj2kU9CA4eRey5wBx+BjbzHkSXXt6tOk9pDkeg56wuKR9L3g1BldcwBEacW2fU4pfEW9vpJC9QTCU/FAT0JXMTpYOaeIn+uf9eQRlRNguI1eUoWJAiGQgVQ/TlFlkdVqeE0pHp3Ntod2r+pkb4vCKrMDI6HW28XsM0uMupOfBUL8zRNFEOvLtswJduVo5r5Fd1ysHFcFywHOT0A==", + * "timestamp":"2023-01-17 17:59:00", + * "appKey":"123456", + * "merchantOrderNo": "20230112000", + * "tradeLinks": "https://steamcommunity.com/tradeoffer/new/?partner=123456&token=vP12QeQA", + * "commodityTemplateId": "100696", + * "commodityHashName": "", + * "purchasePrice": "0.08" + * } + */ +@Data +public class ByTemplateCreateOrderDto { + + private String sign; + + private String timestamp; + + private String appKey; + + private String merchantOrderNo; + + private String tradeLinks; + + private String commodityHashName; + + private String purchasePrice; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/CheckMerchantBalanceDto.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/CheckMerchantBalanceDto.java new file mode 100644 index 0000000..37bd1d1 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/CheckMerchantBalanceDto.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.cs340.domain.dto; + +import lombok.Data; + +@Data +public class CheckMerchantBalanceDto { + + private String sign; + + private String timestamp; + + private String appKey; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/CheckTradeUrlDto.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/CheckTradeUrlDto.java new file mode 100644 index 0000000..7904874 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/CheckTradeUrlDto.java @@ -0,0 +1,16 @@ +package com.ruoyi.thirdparty.cs340.domain.dto; + +import lombok.Data; + +@Data +public class CheckTradeUrlDto { + + private String sign; + + private String timestamp; + + private String appKey; + + private String tradeLinks; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/CommodityInfoRequestListDto.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/CommodityInfoRequestListDto.java new file mode 100644 index 0000000..8bb8bd6 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/CommodityInfoRequestListDto.java @@ -0,0 +1,17 @@ +package com.ruoyi.thirdparty.cs340.domain.dto; + +import lombok.Data; + +/** + * requestList Object[] 是 批量请求参数列表 + * 数组数量:1-200 + * templateId Integer 是 模板id + * 来源:查询模板ID templateId和hashName二选一必传递,如传入不可为0 + * templateHashName String 是 模板hashName + * 来源:查询模板ID templateId和hashName二选一必传递,如传入不可为空 + */ +@Data +public class CommodityInfoRequestListDto { + private Long templateId; + private String templateHashName; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/Cs340OrderStatusDto.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/Cs340OrderStatusDto.java new file mode 100644 index 0000000..5c9379e --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/Cs340OrderStatusDto.java @@ -0,0 +1,19 @@ +package com.ruoyi.thirdparty.cs340.domain.dto; + +import lombok.Data; + +/** + * orderNo String 是 订单号 小于等于40位的数字字符串 + * merchantNo String 是 商户订单号 数据来源: + * 指定模板购买 + * 指定商品购买 + * 注:orderNo和merchantNo二选一传递,同时传入以orderNo为准 + * 小于等于59位的字符串 + */ +@Data +public class Cs340OrderStatusDto { + private String sign; + private String timestamp; + private String appKey; + private String orderNo; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/QueryTemplateSaleByCategoryDto.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/QueryTemplateSaleByCategoryDto.java new file mode 100644 index 0000000..41e284e --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/dto/QueryTemplateSaleByCategoryDto.java @@ -0,0 +1,29 @@ +package com.ruoyi.thirdparty.cs340.domain.dto; + +import lombok.Data; + +/** + * typeId Integer 是 武器大类ID + * typeHashName String 是 武器大类hashName 如传入不可为空 + * typeId和typeHashName同时传递时以typeId为准 + * weaponId String 是 武器类型ID + * weaponHashName String 是 武器类型HashName 如传入不可为空 + * weaponId和weaponHashName同时传递时以weaponId为准 + * 武器大类和武器类型同时传递,以武器类型为准。 + * priceRangeMinmun String 否 商品价格区间最小值 单位:元 + * priceRangeMaxmun String 否 商品价格区间最大值 单位:元 + * page String 否 页码 最大50 + * pageSize String 否 每页显示数量 最大值200,若传入参数>200,则按200执行 + */ +@Data +public class QueryTemplateSaleByCategoryDto { + private String sign; + private String timestamp; + private String appKey; + private Integer typeId; + private String typeHashName; + private String weaponId; + private String weaponHashName; + private String page; + private String pageSize; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/BatchGetOnSaleCommodityInfoResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/BatchGetOnSaleCommodityInfoResult.java new file mode 100644 index 0000000..0a07702 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/BatchGetOnSaleCommodityInfoResult.java @@ -0,0 +1,27 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +/** + * data Object[] 返回消息体。 + * saleTemplateResponse Object + * templateId String 模板id。 + * templateHashName String 模板hashName。 + * iconUrl String 模板图片链接。 + * exteriorName String 外观名称。 + * rarityName String 品质。 + * qualityName String 类别。 + * saleCommodityResponse Object + * minSellPrice String 在售最低价(单位:元)。 + * fastShippingMinSellPrice String 极速发货在售最低价(单位:元)。 + * referencePrice String 模板参考价(单位:元)。 + * sellNum String 在售数量。 + */ +@Data +public class BatchGetOnSaleCommodityInfoResult { + + private SaleTemplateResponse saleTemplateResponse; + + private SaleCommodityResponse saleCommodityResponse; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/ByTemplateCreateOrderResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/ByTemplateCreateOrderResult.java new file mode 100644 index 0000000..8c5feec --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/ByTemplateCreateOrderResult.java @@ -0,0 +1,37 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 参数 类型 说明 + * data Object 返回消息体。 + * merchantOrderNo String 商户订单号。 + * orderNo String 订单单号。 + * payAmount number 实际支付金额。 + * orderStatus Integer 交易状态:1,成功;0,失败。 + * 示例: + * { + * "code": 0, + * "msg": "成功", + * "timestamp": 1673943192928, + * "data": { + * "merchantOrderNo": "20230112102", + * "orderNo": "2023011710002994746", + * "payAmount": 0.1, + * "orderStatus": 0 + * } + * } + */ +@Data +public class ByTemplateCreateOrderResult { + + private String merchantOrderNo; + + private String orderNo; + + private BigDecimal payAmount; + + private Integer orderStatus; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340CallBackInfo.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340CallBackInfo.java new file mode 100644 index 0000000..1084dbd --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340CallBackInfo.java @@ -0,0 +1,45 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +/** + * orderNo String 订单号。 + * orderType Integer 预留字段 + * orderSubType Integer 预留字段 + * shippingMode Integer 发货模式:0,卖家直发;1,极速发货 + * tradeOfferId long 报价ID + * tradeOfferLinks String 报价链接 + * buyerUserId Integer 购买用户编号。 + * orderStatus Integer 订单大状态。 + * orderSubStatus Integer 订单小状态。 + * failCode Integer 订单失败原因编号。 + * failReason String 订单失败原因提示信息。 + * merchantOrderNo String 第三方商户单号。 + * notifyType Integer 通知类型(0:创建订单,1:等待发货,2:等待收货,3:购买成功,4:订单取消)。 + * notifyDesc String 通知类型描述(创建订单,等待发货,等待收货,购买成功,订单取消)。 + * + * 通知类型 orderStatus orderSubStatus + * 创建订单 100-创建订单 + * 等待发货 140-交货中 1101 待发送报价 + * 等待收货 1103 待确认报价 + * 购买成功 340-已完成 + * 订单取消 280-已取消 + * 订单购买失败 2-购买失败 + */ +@Data +public class Cs340CallBackInfo { + private String orderNo; + private Integer orderType; + private Integer orderSubType; + private Integer shippingMode; + private Long tradeOfferId; + private String tradeOfferLinks; + private Integer buyerUserId; + private Integer orderStatus; + private Integer orderSubStatus; + private Integer failCode; + private String failReason; + private String merchantOrderNo; + private Integer notifyType; + private String notifyDesc; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340CallBackNotifyInfo.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340CallBackNotifyInfo.java new file mode 100644 index 0000000..40cca12 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340CallBackNotifyInfo.java @@ -0,0 +1,10 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +@Data +public class Cs340CallBackNotifyInfo { + private String messageNo; + private String callBackInfo; + private String sign; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340CallBackResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340CallBackResult.java new file mode 100644 index 0000000..504a5fb --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340CallBackResult.java @@ -0,0 +1,9 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +@Data +public class Cs340CallBackResult { + private String messageNo; + private boolean flag; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340CheckTradeUrlResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340CheckTradeUrlResult.java new file mode 100644 index 0000000..d1c311a --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340CheckTradeUrlResult.java @@ -0,0 +1,13 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +@Data +public class Cs340CheckTradeUrlResult { + + private String steamId; + + private Integer status; + + private String msg; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340MerchantBalanceResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340MerchantBalanceResult.java new file mode 100644 index 0000000..ec20857 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340MerchantBalanceResult.java @@ -0,0 +1,13 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class Cs340MerchantBalanceResult { + + private BigDecimal amount; + + private Integer userId; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340OrderStatusResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340OrderStatusResult.java new file mode 100644 index 0000000..7c2887d --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340OrderStatusResult.java @@ -0,0 +1,43 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +/** + * data object 返回消息体。 + * orderNumber String 订单编号。 + * shippingMode Integer 发货模式:0,卖家直发;1,极速发货 + * tradeOfferId Long 报价ID + * tradeOfferLinks String 报价链接(不是每笔订单都存在) + * statusMsg String 订单状态。 + * bigStatus Integer 订单大状态。 + * bigStatusMsg String 订单大状态提示信息。 + * smallStatus Integer 订单小状态。 + * smallStatusMsg String 订单小状态提示信息。 + * failCode Integer 订单失败原因编号,仅记录失败原因具体以bigStatus状态为准 + * failReason String 订单失败原因提示信息。 + * + * 大状态bigStatus说明 + * 数值 说明 + * 2 失败 + * 100 创建中。 + * 120 支付中。 + * 130 异常挂起。 + * 140 交货中。 + * 360 结算中。 + * 340 已完成。 + * 280 已取消。 + */ +@Data +public class Cs340OrderStatusResult { + private String orderNumber; + private Integer shippingMode; + private Long tradeOfferId; + private String tradeOfferLinks; + private String statusMsg; + private Integer bigStatus; + private String bigStatusMsg; + private Integer smallStatus; + private String smallStatusMsg; + private Integer failCode; + private String failReason; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340Result.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340Result.java new file mode 100644 index 0000000..bcbf603 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/Cs340Result.java @@ -0,0 +1,15 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +@Data +public class Cs340Result { + + private Integer code; + + private String msg; + + private Long timestamp; + + private T data; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/SaleCommodityResponse.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/SaleCommodityResponse.java new file mode 100644 index 0000000..2b43ee8 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/SaleCommodityResponse.java @@ -0,0 +1,18 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +/** + *saleCommodityResponse Object + * minSellPrice String 在售最低价(单位:元)。 + * fastShippingMinSellPrice String 极速发货在售最低价(单位:元)。 + * referencePrice String 模板参考价(单位:元)。 + * sellNum String 在售数量。 + */ +@Data +public class SaleCommodityResponse { + private String minSellPrice; + private String fastShippingMinSellPrice; + private String referencePrice; + private String sellNum; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/SaleTemplateByCategoryResponse.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/SaleTemplateByCategoryResponse.java new file mode 100644 index 0000000..17f5fbc --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/SaleTemplateByCategoryResponse.java @@ -0,0 +1,37 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +/** + * templateId integer 模板id + * templateHashName string 模板hash name + * templateName string 模版名称 + * iconUrl string 模板图片 + * exteriorName string 外观名称 + * rarityName string 品质 + * typeId integer 武器大类 + * typeHashName string 武器大类hashName + * weaponId integer 武器类型ID + * weaponHashName string 武器类型标签hashName + * minSellPrice string 在售最低价(单位元) + * fastShippingMinSellPrice String 极速发货在售最低价(单位:元) + * referencePrice String 模板参考价(单位:元)。 + * sellNum integer 在售数量 + */ +@Data +public class SaleTemplateByCategoryResponse { + private Integer templateId; + private String templateHashName; + private String templateName; + private String iconUrl; + private String exteriorName; + private String rarityName; + private Integer typeId; + private String typeHashName; + private Integer weaponId; + private String weaponHashName; + private String minSellPrice; + private String fastShippingMinSellPrice; + private String referencePrice; + private Integer sellNum; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/SaleTemplateByCategoryResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/SaleTemplateByCategoryResult.java new file mode 100644 index 0000000..f6338b5 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/SaleTemplateByCategoryResult.java @@ -0,0 +1,18 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +import java.util.List; + +/** + * currentPage + * newPageIsHaveContent + * saleTemplateByCategoryResponseList + * templateId + */ +@Data +public class SaleTemplateByCategoryResult { + private Integer currentPage; + private Boolean newPageIsHaveContent; + private List saleTemplateByCategoryResponseList; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/SaleTemplateResponse.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/SaleTemplateResponse.java new file mode 100644 index 0000000..0278505 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/domain/result/SaleTemplateResponse.java @@ -0,0 +1,22 @@ +package com.ruoyi.thirdparty.cs340.domain.result; + +import lombok.Data; + +/** + * * saleTemplateResponse Object + * * templateId String 模板id。 + * * templateHashName String 模板hashName。 + * * iconUrl String 模板图片链接。 + * * exteriorName String 外观名称。 + * * rarityName String 品质。 + * * qualityName String 类别。 + */ +@Data +public class SaleTemplateResponse { + private String templateId; + private String templateHashName; + private String iconUrl; + private String exteriorName; + private String rarityName; + private String qualityName; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/service/Cs340Service.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/service/Cs340Service.java new file mode 100644 index 0000000..0cfb164 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/service/Cs340Service.java @@ -0,0 +1,44 @@ +package com.ruoyi.thirdparty.cs340.service; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.thirdparty.cs340.domain.body.ByTemplateCreateOrder; +import com.ruoyi.thirdparty.cs340.domain.body.Cs340OnSaleInfoBody; +import com.ruoyi.thirdparty.cs340.domain.result.*; + +import java.util.List; + +public interface Cs340Service { + /** + * 验证交易链接 + */ + Cs340CheckTradeUrlResult checkTradeUrl(String tradeUrl); + /** + * 余额查询 + */ + Cs340MerchantBalanceResult checkMerchantBalance(); + /** + * 创建订单 + */ + Cs340Result createOrder(ByTemplateCreateOrder order); + + /** + * 查询订单状态 + * @param orderNo 商家订单号 + */ + Cs340Result queryOrderStatus(String orderNo); + + /** + * 查询在线订单 + */ + BatchGetOnSaleCommodityInfoResult queryOnSaleInfo(Cs340OnSaleInfoBody onSaleInfoBody); + + /** + * 回调通知信息 + */ + AjaxResult notify(Cs340CallBackNotifyInfo notify); + + /** + * 批量获取价格 + */ + List batchGetOnSaleCommodityInfoResult(List templateHashNames); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/service/impl/Cs340ServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/service/impl/Cs340ServiceImpl.java new file mode 100644 index 0000000..afac8bd --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/service/impl/Cs340ServiceImpl.java @@ -0,0 +1,285 @@ +package com.ruoyi.thirdparty.cs340.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ruoyi.admin.mapper.TtDeliveryRecordMapper; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.http.HttpUtils; +import com.ruoyi.domain.common.constant.DeliveryOrderStatus; +import com.ruoyi.domain.entity.delivery.TtDeliveryRecord; +import com.ruoyi.thirdparty.cs340.config.Cs340Config; +import com.ruoyi.thirdparty.cs340.domain.body.ByTemplateCreateOrder; +import com.ruoyi.thirdparty.cs340.domain.body.Cs340OnSaleInfoBody; +import com.ruoyi.thirdparty.cs340.domain.dto.*; +import com.ruoyi.thirdparty.cs340.domain.result.*; +import com.ruoyi.thirdparty.cs340.service.Cs340Service; +import com.ruoyi.thirdparty.cs340.utils.RSAUtils; +import com.ruoyi.thirdparty.yyyouping.utils.common.JacksonUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * https://www.yuque.com/laobi-hrwiq/gzgf3p/qlcuu7b8goar5dg7 + */ +@Slf4j +@Service +public class Cs340ServiceImpl implements Cs340Service { + + // 设置日期转换为字符串格式 + private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + @Autowired + private Cs340Config cs340Config; + + @Autowired + private TtDeliveryRecordMapper deliveryRecordMapper; + + @Override + public Cs340CheckTradeUrlResult checkTradeUrl(String tradeUrl) { + String url = cs340Config.getBaseUrl() + "/v1/api/checkTradeUrl"; + // 构建请求头 + Map headers = new HashMap<>(); + + CheckTradeUrlDto dto = new CheckTradeUrlDto(); + dto.setTimestamp(sdf.format(new Date())); + dto.setAppKey(cs340Config.getAppKey()); + dto.setTradeLinks(tradeUrl); + dto.setSign(generateSign(JSONObject.toJSONString(dto), cs340Config.getPrivateKey())); + String body = JSON.toJSONString(dto); + String result = HttpUtils.sendPostJSONString(url, body, headers); + log.info("body is {}, result is {}", body, result); + // 将JSON转换为对象 + // 定义V5ItemResult的泛型类型 + TypeReference> typeRef = + new TypeReference>() {}; + // 使用FastJson将JSON字符串转换为对象 + Cs340Result result1 = JSONObject.parseObject(result, typeRef); + if (result1.getCode() != 0) { + return null; + } + return result1.getData(); + } + + @Override + public Cs340MerchantBalanceResult checkMerchantBalance() { + String url = cs340Config.getBaseUrl() + "/v1/api/getAssetsInfo"; + // 构建请求头 + Map headers = new HashMap<>(); + + CheckMerchantBalanceDto dto = new CheckMerchantBalanceDto(); + dto.setTimestamp(sdf.format(new Date())); + dto.setAppKey(cs340Config.getAppKey()); + dto.setSign(generateSign(JSONObject.toJSONString(dto), cs340Config.getPrivateKey())); + String body = JSON.toJSONString(dto); + String result = HttpUtils.sendPostJSONString(url, body, headers); + log.info("body is {}, result is {}", body, result); + // 将JSON转换为对象 + // 定义V5ItemResult的泛型类型 + TypeReference> typeRef = + new TypeReference>() {}; + // 使用FastJson将JSON字符串转换为对象 + Cs340Result result1 = JSONObject.parseObject(result, typeRef); + if (result1.getCode() != 0) { + return null; + } + return result1.getData(); + } + + @Override + public Cs340Result createOrder(ByTemplateCreateOrder order) { + String url = cs340Config.getBaseUrl() + "/v1/api/byTemplateCreateOrder"; + // 构建请求头 + Map headers = new HashMap<>(); + + ByTemplateCreateOrderDto dto = new ByTemplateCreateOrderDto(); + BeanUtils.copyProperties(order, dto); + dto.setTimestamp(sdf.format(new Date())); + dto.setAppKey(cs340Config.getAppKey()); + dto.setSign(generateSign(JSONObject.toJSONString(dto), cs340Config.getPrivateKey())); + String body = JSON.toJSONString(dto); + String result = HttpUtils.sendPostJSONString(url, body, headers); + log.info("body is {}, result is {}", body, result); + // 将JSON转换为对象 + // 定义V5ItemResult的泛型类型 + TypeReference> typeRef = + new TypeReference>() {}; + // 使用FastJson将JSON字符串转换为对象 + return JSONObject.parseObject(result, typeRef); + } + + @Override + public Cs340Result queryOrderStatus(String orderNo) { + String url = cs340Config.getBaseUrl() + "/v1/api/orderStatus"; + // 构建请求头 + Map headers = new HashMap<>(); + + Cs340OrderStatusDto dto = new Cs340OrderStatusDto(); + dto.setOrderNo(orderNo); + dto.setTimestamp(sdf.format(new Date())); + dto.setAppKey(cs340Config.getAppKey()); + dto.setSign(generateSign(JSONObject.toJSONString(dto), cs340Config.getPrivateKey())); + String body = JSON.toJSONString(dto); + String result = HttpUtils.sendPostJSONString(url, body, headers); + log.info("body is {}, result is {}", body, result); + // 将JSON转换为对象 + // 定义V5ItemResult的泛型类型 + TypeReference> typeRef = + new TypeReference>() {}; + // 使用FastJson将JSON字符串转换为对象 + return JSONObject.parseObject(result, typeRef); + } + + @Override + public BatchGetOnSaleCommodityInfoResult queryOnSaleInfo(Cs340OnSaleInfoBody onSaleInfoBody) { + List results = batchGetOnSaleCommodityInfoResult(Collections.singletonList(onSaleInfoBody.getMarketHashName())); + if (CollectionUtils.isEmpty(results)) { + return null; + } + return results.get(0); + } + + @Override + public AjaxResult notify(Cs340CallBackNotifyInfo notify) { + log.info("收到订单更改回调通知。入参:{}", notify.toString()); + String callBackInfo = notify.getCallBackInfo(); + String messageNo = notify.getMessageNo(); + String sign = notify.getSign(); + Map params = new HashMap<>(); + params.put("messageNo", messageNo); + params.put("callBackInfo", callBackInfo); + // 第一步:检查参数是否已经排序 + String[] keys = params.keySet().toArray(new String[0]); + Arrays.sort(keys); + // 第二步:把所有参数名和参数值串在一起 + StringBuilder stringBuilder = new StringBuilder(); + for (String key : keys) { + Object value = params.get(key); + if (value != null) { + stringBuilder.append(key).append(value); + } + } + log.info("stringBuilder:{}", stringBuilder); + boolean b = false; + try { + b = RSAUtils.verifyByPublicKey(stringBuilder.toString().getBytes(), cs340Config.getCallbackPublicKey(), sign); + } catch (Exception e) { + log.error("notify happen excepiton is {}", e.getMessage(), e); + } + Cs340CallBackResult result = new Cs340CallBackResult(); + result.setMessageNo(messageNo); + if (!b) { + log.info("b is :{}, cs340Config.getPublicKey() is {}", b, cs340Config.getCallbackPublicKey()); + return AjaxResult.success("verifyByPublicKey fail", result); + } + ObjectMapper objectMapper = new ObjectMapper(); + Cs340CallBackInfo info = objectMapper.convertValue(callBackInfo, Cs340CallBackInfo.class); + // 获取提货订单数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TtDeliveryRecord::getOrderId, info.getOrderNo()); + TtDeliveryRecord ttDeliveryRecord = deliveryRecordMapper.selectOne(queryWrapper); + if (Objects.equals(info.getOrderStatus(), 340)) { + ttDeliveryRecord.setStatus(DeliveryOrderStatus.ORDER_COMPLETE.getCode()); + ttDeliveryRecord.setMessage(DeliveryOrderStatus.ORDER_COMPLETE.getMsg()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + deliveryRecordMapper.updateById(ttDeliveryRecord); + } else if (Objects.equals(info.getOrderStatus(), 280)) { + ttDeliveryRecord.setStatus(DeliveryOrderStatus.ORDER_CANCEL.getCode()); + ttDeliveryRecord.setMessage(DeliveryOrderStatus.ORDER_CANCEL.getMsg()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + deliveryRecordMapper.updateById(ttDeliveryRecord); + } + log.info("验签结果:{}", b); + result.setFlag(true); + return AjaxResult.success(result); + } + + @Override + public List batchGetOnSaleCommodityInfoResult(List templateHashNames) { + if (CollectionUtils.isEmpty(templateHashNames)) { + return Collections.emptyList(); + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + log.error("sleep happen exception"); + } + String url = cs340Config.getBaseUrl() + "/v1/api/batchGetOnSaleCommodityInfo"; + // 构建请求头 + Map headers = new HashMap<>(); + List requestList = new ArrayList<>(); + for (String templateHashName : templateHashNames) { + CommodityInfoRequestListDto dto = new CommodityInfoRequestListDto(); + dto.setTemplateHashName(templateHashName); + requestList.add(dto); + } + + BatchGetOnSaleCommodityInfoDto dto = new BatchGetOnSaleCommodityInfoDto(); + dto.setTimestamp(sdf.format(new Date())); + dto.setAppKey(cs340Config.getAppKey()); + dto.setRequestList(requestList); + dto.setSign(generateSign(JSONObject.toJSONString(dto), cs340Config.getPrivateKey())); + String body = JSON.toJSONString(dto); + String result = HttpUtils.sendPostJSONString(url, body, headers); +// log.info("body is {}, result is {}", body, result); + // 将JSON转换为对象 + // 定义V5ItemResult的泛型类型 + TypeReference>> typeRef = + new TypeReference>>() {}; + // 使用FastJson将JSON字符串转换为对象 + Cs340Result> result1 = JSONObject.parseObject(result, typeRef); + if (result1.getCode() != 0) { + return Collections.emptyList(); + } + if (result1.getData() == null || CollectionUtils.isEmpty(result1.getData())) { + return Collections.emptyList(); + } + return result1.getData(); + } + + /** + * 生成认证sign信息 + */ + private String generateSign(String body, String privateKey) { + Map map = JSONObject.parseObject(body, Map.class); + // 第一步:检查参数是否已经排序 + String[] keys = map.keySet().toArray(new String[0]); + Arrays.sort(keys); + // 第二步:把所有参数名和参数值串在一起 + StringBuilder stringBuilder = new StringBuilder(); + for (String key : keys) { + if (Objects.equals(key, "sign")) { + continue; + } + Object value = map.get(key); + if (value != null) { + stringBuilder.append(key).append(JacksonUtils.writeValueAsString(value)); + } + } + try { + return RSAUtils.signByPrivateKey(stringBuilder.toString().getBytes(), privateKey); + } catch (Exception e) { + log.error("签名失败 is {}", e.getMessage(), e); + throw new RuntimeException(e); + } + } + + public static void main(String[] args) { + String body = "{\"sign\":\"4LL1O++Btn5JgZ6kHuJAN7dEG4cgLd0goo+Cb4wld0ygpU8lU+73mLxXYtN81ApAhWuw9zn+ENrtc2KcrL6XgHMSG8JC/5NjHCGO/3jDUjAac3YMh5vdQjsqhK1IoxdrOXM9QUyUj2kU9CA4eRey5wBx+BjbzHkSXXt6tOk9pDkeg56wuKR9L3g1BldcwBEacW2fU4pfEW9vpJC9QTCU/FAT0JXMTpYOaeIn+uf9eQRlRNguI1eUoWJAiGQgVQ/TlFlkdVqeE0pHp3Ntod2r+pkb4vCKrMDI6HW28XsM0uMupOfBUL8zRNFEOvLtswJduVo5r5Fd1ysHFcFywHOT0A==\",\"timestamp\":\"2023-01-17 16:15:00\",\"appKey\":\"123456\",\"merchantOrderNo\":\"20230112061\",\"tradeLinks\":\"https://steamcommunity.com/tradeoffer/new/?partner=323393236&token=vP12QeQA\",\"commodityId\":\"427620\",\"purchasePrice\":\"0.07\"}"; + ByTemplateCreateOrderDto dtp = JSON.parseObject(body, ByTemplateCreateOrderDto.class); + Cs340ServiceImpl cs340Service = new Cs340ServiceImpl(); + Cs340OnSaleInfoBody cs340OnSaleInfoBody = new Cs340OnSaleInfoBody(); + cs340OnSaleInfoBody.setMarketHashName("Sticker | tabseN | Antwerp 2022"); + BatchGetOnSaleCommodityInfoResult response = cs340Service.queryOnSaleInfo(cs340OnSaleInfoBody); + System.out.println(response != null ? response.toString() : "null"); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/task/Cs340ItemPullTask.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/task/Cs340ItemPullTask.java new file mode 100644 index 0000000..70c8690 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/task/Cs340ItemPullTask.java @@ -0,0 +1,104 @@ +package com.ruoyi.thirdparty.cs340.task; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.cs340.domain.result.BatchGetOnSaleCommodityInfoResult; +import com.ruoyi.thirdparty.cs340.service.Cs340Service; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.*; + +@Slf4j +@Component("Cs340ItemPullTask") +public class Cs340ItemPullTask { + + @Autowired + private TtOrnamentService ornamentsService; + + @Autowired + private Cs340Service cs340Service; + + @Autowired + private ISysConfigService configService; + + public void getCs340OrnamentsData() { + log.info("cs2pifa拉取最新饰品数据任务执行开始..."); + String usePricePremiumRateStr = configService.selectConfigByKey("usePricePremiumRate"); + BigDecimal usePricePremiumRate = new BigDecimal(usePricePremiumRateStr); + + String ZBTParitiesStr = configService.selectConfigByKey("ZBTParities"); + BigDecimal parities = new BigDecimal(ZBTParitiesStr); + + // 查询数据库 + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + List ttOrnamentList = ornamentsService.list(wrapper); + if (CollectionUtils.isEmpty(ttOrnamentList)) { + log.info("ttOrnamentList is empty"); + return; + } + List> splitOrnamentList = ListUtils.partition(ttOrnamentList, 200); + for (List list : splitOrnamentList) { + List templateHashNames = new ArrayList<>(); + Map templateHashMap = new HashMap<>(); + for (TtOrnament ornament : list) { + if (ornament == null || StringUtils.isBlank(ornament.getMarketHashName())) { + continue; + } + templateHashNames.add(ornament.getMarketHashName()); + templateHashMap.put(ornament.getMarketHashName(), ornament); + } + List infos = cs340Service.batchGetOnSaleCommodityInfoResult(templateHashNames); + if (CollectionUtils.isEmpty(infos)) { + continue; + } + Set templateHashNameSet = new HashSet<>(); + List updateList = new ArrayList<>(); + for (BatchGetOnSaleCommodityInfoResult info : infos) { + if (info == null || info.getSaleCommodityResponse() == null || info.getSaleTemplateResponse() == null) { + continue; + } + if (templateHashNameSet.contains(info.getSaleTemplateResponse().getTemplateHashName())) { + log.info("templateHashName same is {}", info.getSaleTemplateResponse().getTemplateHashName()); + } + templateHashNameSet.add(info.getSaleTemplateResponse().getTemplateHashName()); + TtOrnament ttOrnament = templateHashMap.get(info.getSaleTemplateResponse().getTemplateHashName()); + if (ttOrnament == null) { + continue; + } + if (ttOrnament.getPrice() == null || !Objects.equals(ttOrnament.getPrice().toString(), info.getSaleCommodityResponse().getMinSellPrice()) || + ttOrnament.getQuantity() == null || !Objects.equals(ttOrnament.getQuantity().toString(), info.getSaleCommodityResponse().getSellNum())) { + // 加入到更新队列 + BigDecimal price = new BigDecimal(info.getSaleCommodityResponse().getMinSellPrice()); + BigDecimal userPrice = price.multiply(parities).multiply(usePricePremiumRate); + if (userPrice.compareTo(new BigDecimal("99999999.99")) > 0) + continue; + ttOrnament.setPrice(price); + ttOrnament.setQuantity(info.getSaleCommodityResponse().getSellNum() != null ? Integer.parseInt(info.getSaleCommodityResponse().getSellNum()) : 0); + ttOrnament.setUsePrice(userPrice); + ttOrnament.setUpdateTime(DateUtils.getNowDate()); + updateList.add(ttOrnament); + } + } + if (CollectionUtils.isNotEmpty(updateList)) { + batchUpdateOrnaments(updateList); + } + } + + log.info("更新数据完成"); + } + + private void batchUpdateOrnaments(List updateList) { + ornamentsService.updateBatchById(updateList, updateList.size()); + log.info("更新{}条", updateList.size()); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/utils/RSAUtils.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/utils/RSAUtils.java new file mode 100644 index 0000000..b2b9cea --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/cs340/utils/RSAUtils.java @@ -0,0 +1,501 @@ +package com.ruoyi.thirdparty.cs340.utils; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.ArrayUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.*; +import java.security.*; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +/** + * SHA256withRSA + * + * @author zhumiao + * + */ +public class RSAUtils { + + private static final Logger logger = LoggerFactory.getLogger(RSAUtils.class); + + // MAX_DECRYPT_BLOCK应等于密钥长度/8(1byte=8bit),所以当密钥位数为2048时,最大解密长度应为256. +// 128 对应 1024,256对应2048 + private static final int KEYSIZE = 2048; + + // RSA最大加密明文大小 + private static final int MAX_ENCRYPT_BLOCK = 117; + + // RSA最大解密密文大小 + private static final int MAX_DECRYPT_BLOCK = 256; + + // 不仅可以使用DSA算法,同样也可以使用RSA算法做数字签名 + private static final String KEY_ALGORITHM = "RSA"; + private static final String SIGNATURE_ALGORITHM = "SHA256withRSA"; + + + // 默认种子 请根据自己的需要定义 + public static final String DEFAULT_SEED = "xxxxxxxxxxxxxxxxxx"; + + /** + * 目前固定公钥、私钥,有需求再改动 + */ + public static String PUBLIC_KEY_LZ = "***************"; + public static String PRIVATE_KEY_LZ = "*******************************************="; + + public static final String PUBLIC_KEY = "PublicKey"; + public static final String PRIVATE_KEY = "PrivateKey"; + + /** + * + * 生成密钥 + * + * @param seed 种子 + * + * @return 密钥对象 + * @throws Exception + * + */ + public static Map initKey(String seed) throws Exception { + logger.info("生成密钥"); + KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM); + SecureRandom secureRandom = new SecureRandom(); +// 如果指定seed,那么secureRandom结果是一样的,所以生成的公私钥也永远不会变 + secureRandom.setSeed(seed.getBytes()); +// Modulus size must range from 512 to 1024 and be a multiple of 64 + keygen.initialize(KEYSIZE, secureRandom); + KeyPair keys = keygen.genKeyPair(); + PrivateKey privateKey = keys.getPrivate(); + PublicKey publicKey = keys.getPublic(); + Map map = new HashMap<>(2); + map.put(PUBLIC_KEY, publicKey); + map.put(PRIVATE_KEY, privateKey); + return map; + } + + /** + * + * 生成默认密钥 + * + * + * @return 密钥对象 + * @throws Exception + * + */ + + public static Map initKey() throws Exception { + return initKey(DEFAULT_SEED); + } + + /** + * + * 取得私钥 + * + * @param keyMap + * + * @return + * @throws Exception + * + */ + public static String getPrivateKey(Map keyMap) throws Exception { + Key key = (Key) keyMap.get(PRIVATE_KEY); + // return encryptBASE64(key.getEncoded()); // base64加密私钥 + return base64ToStr(key.getEncoded()); // base64加密私钥 + } + + private static String base64ToStr(byte[] encoded) { + return java.util.Base64.getEncoder().encodeToString(encoded); + } + + /** + * + * 取得公钥 + * + * @param keyMap + * + * @return + * @throws Exception + * + */ + public static String getPublicKey(Map keyMap) throws Exception { + Key key = (Key) keyMap.get(PUBLIC_KEY); + // return encryptBASE64(key.getEncoded()); // base64加密公钥 + return base64ToStr(key.getEncoded()); // base64加密公钥 + } + + /** + * + * 用私钥对信息进行数字签名 + * + * @param data 加密数据 + * + * @param privateKey 私钥-base64加密的 + * + * @return + * + * @throws Exception + * + */ + public static String signByPrivateKey(byte[] data, String privateKey) throws Exception { + byte[] keyBytes = decryptBASE64(privateKey); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM); + PrivateKey priKey = factory.generatePrivate(keySpec);// 生成私钥 + // 用私钥对信息进行数字签名 + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initSign(priKey); + signature.update(data); + return encryptBASE64(signature.sign()); + + } + + /** + * + * BASE64Encoder 加密 + * + * @param data 要加密的数据 + * + * @return 加密后的字符串 + * + */ + private static String encryptBASE64(byte[] data) { + // BASE64Encoder encoder = new BASE64Encoder(); + // String encode = encoder.encode(data); + // return encode; + return new String(Base64.encodeBase64(data)); + } + + private static byte[] decryptBASE64(String data) { + // BASE64Decoder 每76个字符换行 + // BASE64Decoder decoder = new BASE64Decoder(); + // byte[] buffer = decoder.decodeBuffer(data); + // return buffer; + // codec 的 Base64 不换行 + return Base64.decodeBase64(data); + } + + // TODO rsa 2 对比是否更优 + public static boolean verifyByPublicKey(byte[] data, String publicKey, String sign) throws Exception { + byte[] keyBytes = decryptBASE64(publicKey); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + PublicKey pubKey = keyFactory.generatePublic(keySpec); + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initVerify(pubKey); + signature.update(data); + return signature.verify(decryptBASE64(sign)); // 验证签名 + } + + /** + * RSA公钥加密 + * + * @param str 加密字符串 + * @param publicKey 公钥 + * @return 密文 + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws NoSuchPaddingException + * @throws InvalidKeyException + * @throws UnsupportedEncodingException + * @throws BadPaddingException + * @throws IllegalBlockSizeException + * @throws Exception 加密过程中的异常信息 + */ + public static String encryptByPublicKey(String str, String publicKey) + throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException { + // base64编码的公钥 + byte[] keyBytes = decryptBASE64(publicKey); + RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(KEY_ALGORITHM) + .generatePublic(new X509EncodedKeySpec(keyBytes)); + // RSA加密 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, pubKey); + logger.info(publicKey); + logger.info("provider: {}", cipher.getProvider().getClass().getName()); + byte[] data = str.getBytes("UTF-8"); + // 加密时超过117字节就报错。为此采用分段加密的办法来加密 + byte[] enBytes = null; + for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) { + // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码 + byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK)); + enBytes = ArrayUtils.addAll(enBytes, doFinal); + } + logger.info(enBytes.length + ""); + String outStr = encryptBASE64(enBytes); + return outStr; + } + + /** + * RSA私钥加密 + * + * @param str 加密字符串 + * @param privateKey 公钥 + * @return 密文 + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws NoSuchPaddingException + * @throws InvalidKeyException + * @throws UnsupportedEncodingException + * @throws BadPaddingException + * @throws IllegalBlockSizeException + * @throws Exception 加密过程中的异常信息 + */ + public static String encryptByPrivateKey(String str, String privateKey) + throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException { + // base64编码的公钥 + byte[] keyBytes = decryptBASE64(privateKey); + RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM) + .generatePrivate(new PKCS8EncodedKeySpec(keyBytes)); + // RSA加密 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, priKey); + + byte[] data = str.getBytes("UTF-8"); + // 加密时超过117字节就报错。为此采用分段加密的办法来加密 + byte[] enBytes = null; + for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) { + // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码 + byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK)); + enBytes = ArrayUtils.addAll(enBytes, doFinal); + } + String outStr = encryptBASE64(enBytes); + return outStr; + } + + /** + * 读取公钥 + * + * @param publicKeyPath + * @return + */ + public static PublicKey readPublic(String publicKeyPath) { + if (publicKeyPath != null) { + try (FileInputStream bais = new FileInputStream(publicKeyPath)) { + CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate) certificatefactory.generateCertificate(bais); + return cert.getPublicKey(); + } catch (CertificateException e) { + logger.error(e.getMessage(), e); + } catch (FileNotFoundException e) { + logger.error(e.getMessage(), e); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + } + return null; + } + + /** + * 读取私钥 + * + * + * @return + */ + public static PrivateKey readPrivate(String privateKeyPath, String privateKeyPwd) { + if (privateKeyPath == null || privateKeyPwd == null) { + return null; + } + try (InputStream stream = new FileInputStream(new File(privateKeyPath));) { + // 获取JKS 服务器私有证书的私钥,取得标准的JKS的 KeyStore实例 + KeyStore store = KeyStore.getInstance("JKS");// JKS,二进制格式,同时包含证书和私钥,一般有密码保护;PKCS12,二进制格式,同时包含证书和私钥,一般有密码保护。 + // jks文件密码,根据实际情况修改 + store.load(stream, privateKeyPwd.toCharArray()); + // 获取jks证书别名 + Enumeration en = store.aliases(); + String pName = null; + while (en.hasMoreElements()) { + String n = (String) en.nextElement(); + if (store.isKeyEntry(n)) { + pName = n; + } + } + // 获取证书的私钥 + PrivateKey key = (PrivateKey) store.getKey(pName, privateKeyPwd.toCharArray()); + return key; + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + return null; + } + + /** + * RSA私钥解密 + * + * @param encryStr 加密字符串 + * @param privateKey 私钥 + * @return 铭文 + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws NoSuchPaddingException + * @throws BadPaddingException + * @throws IllegalBlockSizeException + * @throws InvalidKeyException + * @throws Exception 解密过程中的异常信息 + */ + public static String decryptByPrivateKey(String encryStr, String privateKey) + throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, + BadPaddingException, InvalidKeyException, UnsupportedEncodingException { + // base64编码的私钥 + byte[] decoded = decryptBASE64(privateKey); + RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM) + .generatePrivate(new PKCS8EncodedKeySpec(decoded)); + // RSA解密 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, priKey); + logger.info(privateKey); + logger.info("provider: {}" , cipher.getProvider().getClass().getName()); + // 64位解码加密后的字符串 + byte[] data = decryptBASE64(encryStr); + logger.info(data.length + ""); + // 解密时超过128字节报错。为此采用分段解密的办法来解密 + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < data.length; i += MAX_DECRYPT_BLOCK) { + byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_DECRYPT_BLOCK)); + sb.append(new String(doFinal)); + } + return sb.toString(); + } + + /** + * RSA公钥解密 + * + * @param encryStr 加密字符串 + * + * @return 铭文 + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws NoSuchPaddingException + * @throws BadPaddingException + * @throws IllegalBlockSizeException + * @throws InvalidKeyException + * @throws Exception 解密过程中的异常信息 + */ + public static String decryptByPublicKey(String encryStr, String publicKey) + throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, + BadPaddingException, InvalidKeyException { + // base64编码的私钥 + byte[] decoded = decryptBASE64(publicKey); + RSAPublicKey priKey = (RSAPublicKey) KeyFactory.getInstance(KEY_ALGORITHM) + .generatePublic(new X509EncodedKeySpec(decoded)); + // RSA解密 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, priKey); + + // 64位解码加密后的字符串 + byte[] data = decryptBASE64(encryStr); + // 解密时超过128字节报错。为此采用分段解密的办法来解密 + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < data.length; i += MAX_DECRYPT_BLOCK) { + byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_DECRYPT_BLOCK)); + sb.append(new String(doFinal)); + } + + return sb.toString(); + } + + /** + * 加密 + * @param key + * @param data + * @return + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws NoSuchPaddingException + * @throws IllegalBlockSizeException + * @throws BadPaddingException + * @throws InvalidKeyException + * @throws IOException + */ + public static String testEncrypt(String key,String data) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IOException{ + byte[] decode = Base64.decodeBase64(key); + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decode); + KeyFactory kf = KeyFactory.getInstance(KEY_ALGORITHM); + PrivateKey generatePrivate = kf.generatePrivate(pkcs8EncodedKeySpec); + Cipher ci = Cipher.getInstance(KEY_ALGORITHM); + ci.init(Cipher.ENCRYPT_MODE, generatePrivate); + + byte[] bytes = data.getBytes(); + int inputLen = bytes.length; + int offLen = 0;//偏移量 + int i = 0; + ByteArrayOutputStream bops = new ByteArrayOutputStream(); + while(inputLen - offLen > 0){ + byte [] cache; + if(inputLen - offLen > 117){ + cache = ci.doFinal(bytes, offLen,117); + }else{ + cache = ci.doFinal(bytes, offLen,inputLen - offLen); + } + bops.write(cache); + i++; + offLen = 117 * i; + } + bops.close(); + byte[] encryptedData = bops.toByteArray(); + String encodeToString = Base64.encodeBase64String(encryptedData); + return encodeToString; + } + + + + + /** + * 解密 + * @param key + * @param data + * @return + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + * @throws NoSuchPaddingException + * @throws InvalidKeySpecException + * @throws BadPaddingException + * @throws IllegalBlockSizeException + * @throws IOException + */ + public static String testDecrypt(String key,String data) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException, IOException{ + byte[] decode = Base64.decodeBase64(key); + // PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decode); //java底层 RSA公钥只支持X509EncodedKeySpec这种格式 + X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(decode); + KeyFactory kf = KeyFactory.getInstance(KEY_ALGORITHM); + PublicKey generatePublic = kf.generatePublic(x509EncodedKeySpec); + Cipher ci = Cipher.getInstance(KEY_ALGORITHM); + ci.init(Cipher.DECRYPT_MODE,generatePublic); + + int inputLen = data.getBytes().length; + byte[] bytes = data.getBytes(); + int offLen = 0; + int i = 0; + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + while(inputLen - offLen > 0){ + byte[] cache; + if(inputLen - offLen > 128){ + cache = ci.doFinal(bytes,offLen,128); + }else{ + cache = ci.doFinal(bytes,offLen,inputLen - offLen); + } + byteArrayOutputStream.write(cache); + i++; + offLen = 128 * i; + + } + byteArrayOutputStream.close(); + byte[] byteArray = byteArrayOutputStream.toByteArray(); + return new String(byteArray); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/config/GfhtConfig.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/config/GfhtConfig.java new file mode 100644 index 0000000..fa61ab5 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/config/GfhtConfig.java @@ -0,0 +1,36 @@ +package com.ruoyi.thirdparty.gfht.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "gfht") +public class GfhtConfig { + + /** + * 服务器URL + */ + private String serverUrl; + + /** + * 商户ID + */ + private String pid; + + /** + * 签名 + */ + private String sign; + + /** + * 通知URL + */ + private String notifyUrl; + + /** + * 返回URL + */ + private String returnUrl; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/controller/GfhtController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/controller/GfhtController.java new file mode 100644 index 0000000..ff51904 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/controller/GfhtController.java @@ -0,0 +1,51 @@ +package com.ruoyi.thirdparty.gfht.controller; + +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.UserPermission; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.thirdparty.gfht.service.GfhtService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +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.Map; + +@Api(tags = "国富汇通") +@Slf4j +@RestController +@RequestMapping("api/gfht") +public class GfhtController extends BaseController { + + @Autowired + private TtUserService ttUserService; + + @Autowired + private GfhtService gfhtService; + + @ApiOperation("支付") + @UserPermission + @PostMapping(value = "/pay") + public R pay(@RequestBody @Validated CreateOrderParam param) { + Long userId = SecurityUtils.getUserId(); + TtUser ttUser = ttUserService.getById(userId); + // 是否实名认证(0未认证 1已认证) + /*if ("0".equals(ttUser.getIsRealCheck())) { + return R.fail("未实名认证"); + }*/ + String ip = IpUtils.getIpAddr(); + return gfhtService.pay(param, ttUser, ip); + } + + @GetMapping("/notify") + public String notify(@RequestParam Map params) { + return gfhtService.notify(params); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/domain/GfhtResponse.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/domain/GfhtResponse.java new file mode 100644 index 0000000..196d1a1 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/domain/GfhtResponse.java @@ -0,0 +1,19 @@ +package com.ruoyi.thirdparty.gfht.domain; + +import lombok.Data; + +@Data +public class GfhtResponse { + + private String code; + + private String msg; + + private String tradeNo; + + private String payurl; + + private String qrcode; + + private String urlscheme; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/service/GfhtService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/service/GfhtService.java new file mode 100644 index 0000000..01b4dfb --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/service/GfhtService.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.gfht.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; + +import java.util.Map; + +public interface GfhtService { + + R pay(CreateOrderParam param, TtUser ttUser, String ip); + + String notify(Map params); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/service/impl/GfhtServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/service/impl/GfhtServiceImpl.java new file mode 100644 index 0000000..20419d8 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/service/impl/GfhtServiceImpl.java @@ -0,0 +1,465 @@ +package com.ruoyi.thirdparty.gfht.service.impl; + +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.common.constant.*; +import com.ruoyi.domain.entity.*; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.domain.other.TtRechargeRecord; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.baidu.service.BaiduService; +import com.ruoyi.thirdparty.common.service.RechargeSuccessfulNoticeService; +import com.ruoyi.thirdparty.gfht.config.GfhtConfig; +import com.ruoyi.thirdparty.gfht.domain.GfhtResponse; +import com.ruoyi.thirdparty.gfht.service.GfhtService; +import com.ruoyi.thirdparty.gfht.util.GfhtUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.*; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.*; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Service +public class GfhtServiceImpl implements GfhtService { + + @Autowired + private TtRechargeProdMapper rechargeProdMapper; + + @Autowired + private GfhtConfig gfhtConfig; + + @Autowired + private TtOrderMapper orderMapper; + + @Autowired + private TtUserMapper userMapper; + + @Autowired + private TtRechargeProdMapper rechargeListMapper; + + @Autowired + private TtUserService userService; + + @Autowired + private TtUserBlendErcashMapper userBlendErcashMapper; + + @Autowired + private RechargeSuccessfulNoticeService rechargeSuccessfulNoticeService; + + @Autowired + private TtRechargeRecordMapper ttRechargeRecordMapper; + + @Autowired + private ISysConfigService configService; + + @Autowired + private TtPromotionLevelMapper ttPromotionLevelMapper; + + @Autowired + private TtFirstRechargeMapper ttFirstRechargeMapper; + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private TtRechargeConfigMapper ttRechargeConfigMapper; + + @Autowired + private BaiduService baiduService; + + @Override + public R pay(CreateOrderParam param, TtUser ttUser, String ip) { + TtRechargeProd goods; + if (param.getPayWay() != null && Objects.equals(2, param.getPayWay())) { + // 1.查询商品信息 + TtRechargeConfig config = new LambdaQueryChainWrapper<>(ttRechargeConfigMapper) + .eq(TtRechargeConfig::getId, param.getGoodsId()) + .eq(TtRechargeConfig::getStatus, 0) + .one(); + if (ObjectUtil.isEmpty(config)) { + return R.fail("不存在的价格配置"); + } + if (config.getPrice().compareTo(param.getGoodsPrice()) != 0) { + return R.fail("配置的价格不一致"); + } + goods = new TtRechargeProd(); + BeanUtils.copyProperties(config, goods); + } else { + // 1.查询商品信息 + goods = new LambdaQueryChainWrapper<>(rechargeProdMapper) + .eq(TtRechargeProd::getId, param.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + if (ObjectUtil.isEmpty(goods)) { + return R.fail("不存在的商品"); + } + if (goods.getPrice().compareTo(param.getGoodsPrice()) != 0) { + return R.fail("商品价格不一致"); + } + } + + List orders = new LambdaQueryChainWrapper<>(orderMapper) + .eq(TtOrder::getUserId, ttUser.getUserId()) + .eq(TtOrder::getType, PayType.GFHT_PAY.getCode()) + .eq(TtOrder::getStatus, PayOrderStatus.NO_PAY.getCode()) + .gt(TtOrder::getCreateTime, new Date(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(10))) + .list(); + if (CollectionUtils.isNotEmpty(orders) && orders.size() > 2) { + return R.fail("操作频繁!"); + } + + // 计算总价值 + BigDecimal totalAmount = param.getGoodsPrice().multiply(new BigDecimal(param.getGoodsNum())); + + // 2.构建支付参数 + Map map = new HashMap<>(); + map.put("pid", gfhtConfig.getPid()); + map.put("type", param.getPayType()); + Snowflake snowflake = IdUtil.getSnowflake(1, 1); + String orderId = String.valueOf(snowflake.nextId()); + map.put("out_trade_no", orderId); + map.put("notify_url", gfhtConfig.getNotifyUrl()); + map.put("return_url", gfhtConfig.getReturnUrl()); + map.put("name", goods.getName()); + map.put("money", String.valueOf(totalAmount)); + map.put("clientip", ip); + String key = gfhtConfig.getSign(); + GfhtUtil signUtil = new GfhtUtil(key); + String sign = signUtil.getSign(map); + map.put("sign", sign); + map.put("sign_type", "MD5"); + + // 3.发送请求 + // HttpRequest post = HttpUtil.createPost(gfhtConfig.getServerUrl()); + // post.header("Content-Type","application/x-www-form-urlencoded"); + // post.formStr(map); + // HttpResponse res = post.execute(); + + // 切换请求 + // 获取服务器 URL + String url = gfhtConfig.getServerUrl(); + + // 构建请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + // 构建表单数据 + MultiValueMap formParams = new LinkedMultiValueMap<>(); + for (Map.Entry entry : map.entrySet()) { + formParams.add(entry.getKey(), entry.getValue()); + } + + // 构建请求实体 + HttpEntity> requestEntity = new HttpEntity<>(formParams, headers); + + // 发送 POST 请求 + ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + + // 获取响应 + String result = response.getBody(); + + log.info("result is {}", result); + + // 4.解析响应 + GfhtResponse resBody = JSONUtil.toBean(result, GfhtResponse.class); + if (!resBody.getCode().equals("1")) { + return R.fail(resBody.getMsg()); + } + + // 5.创建订单 + TtOrder order = new TtOrder(); + order.setOrderId(orderId); + order.setThirdParty(PartyType.INNER_PICTURE_PAY.getCode() + ""); + order.setOutTradeNo(resBody.getTradeNo()); + order.setUserId(ttUser.getUserId()); + order.setType(PayType.GFHT_PAY.getCode()); + order.setGoodsId(param.getGoodsId()); + order.setGoodsPrice(param.getGoodsPrice()); + order.setGoodsNum(param.getGoodsNum()); + order.setTotalAmount(totalAmount); + order.setSign(sign); + order.setStatus(PayOrderStatus.NO_PAY.getCode()); + order.setCreateTime(new Date()); + order.setBdVid(param.getBdVid()); + orderMapper.insert(order); + + return R.ok(resBody); + } + + @Override + public String notify(Map params) { + log.info("支付成功通知"); + String result = "failure"; + + // 校验 + String tradeStatus = params.get("trade_status"); + if (!"TRADE_SUCCESS".equals(tradeStatus)) { + log.error("交易失败"); + return result; + } + + // 查询订单信息 + String outTradeNo = params.get("trade_no"); + TtOrder order = new LambdaQueryChainWrapper<>(orderMapper) + .eq(TtOrder::getOutTradeNo, outTradeNo) + .eq(TtOrder::getStatus, PayOrderStatus.NO_PAY.getCode()) + .one(); + // 防止重复通知 + if (!PayOrderStatus.NO_PAY.getCode().equals(order.getStatus())) { + return result; + } + + // 查询用户信息 + TtUser user = new LambdaQueryChainWrapper<>(userMapper) + .eq(TtUser::getUserId, order.getUserId()) + .eq(TtUser::getDelFlag, 0) + .one(); + + // 3.查询商品信息 + TtRechargeProd goods; + boolean innerPicturePay = Objects.equals(PartyType.INNER_PICTURE_PAY.getCode() + "", order.getThirdParty()); + if (innerPicturePay) { + goods = new TtRechargeProd(); + TtRechargeConfig config = new LambdaQueryChainWrapper<>(ttRechargeConfigMapper) + .eq(TtRechargeConfig::getId, order.getGoodsId()) + .eq(TtRechargeConfig::getStatus, 0) + .one(); + BeanUtils.copyProperties(config, goods); + } else { + goods = new LambdaQueryChainWrapper<>(rechargeListMapper) + .eq(TtRechargeProd::getId, order.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + } + + // 4.账户结算 + if (ObjectUtil.isNull(goods.getProductA())) goods.setProductA(BigDecimal.ZERO); + if (ObjectUtil.isNull(goods.getProductC())) goods.setProductC(BigDecimal.ZERO); + payNotifyAccounting(order, user, goods, order.getGoodsNum()); + + try { + // 首充赠送 + firstChargeGiftAmount(user, goods, order.getGoodsNum()); + + // 推广等级充值赠送 + promotionLevelChargeGiftAmount(user, goods, order.getGoodsNum()); + + // 推广充值返佣 + promotionChargeCommission(user, goods, order.getGoodsNum()); + + // 回传百度推广信息 + baiduService.upload(user.getUserId(), 10, order.getBdVid()); + } catch (Exception e) { + log.error("happen exception is {}", e.getMessage(), e); + } + + // 5.更新订单 + boolean update = new LambdaUpdateChainWrapper<>(orderMapper) + .eq(TtOrder::getId, order.getId()) + .set(TtOrder::getStatus, PayOrderStatus.PAY_COMPLE.getCode()) + .set(TtOrder::getUpdateTime, new Date()) + .update(); + if (update) { + result = "success"; + } + + // 6.发送充值通知 + rechargeSuccessfulNoticeService.sendRechargeSuccessNotice(order.getUserId().toString(), order.getGoodsPrice()); + + return result; + } + + // 账户结算 + public void payNotifyAccounting(TtOrder order, TtUser user, TtRechargeProd goods, Integer goodsNumber) { + + // 加钱 + // BigDecimal totalAmount = order.getTotalAmount(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal totalCredits = goods.getProductC().multiply(new BigDecimal(goodsNumber)); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, user.getUserId()) + .setSql("account_amount = account_amount + " + totalAmount.toString() + + ",account_credits = account_credits + " + totalCredits.toString()); + userService.update(userUpdate); + + user = userService.getById(user.getUserId()); + // 综合消费日志 + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(user.getUserId()) + + .amount(totalAmount.compareTo(BigDecimal.ZERO) > 0 ? totalAmount : null) + .finalAmount(totalAmount.compareTo(BigDecimal.ZERO) > 0 ? user.getAccountAmount().add(totalAmount) : null) + + .credits(totalCredits.compareTo(BigDecimal.ZERO) > 0 ? totalCredits : null) + .finalCredits(totalCredits.compareTo(BigDecimal.ZERO) > 0 ? user.getAccountCredits().add(totalCredits) : null) + + .total(totalAmount.add(totalCredits)) // 收支合计 + + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.RECHARGE.getCode()) + .remark(TtAccountRecordSource.RECHARGE.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + userBlendErcashMapper.insert(blendErcash); + + // 充值记录 + TtRechargeRecord ttRechargeRecord = TtRechargeRecord.builder().build(); + ttRechargeRecord.setUserId(order.getUserId()); + ttRechargeRecord.setParentId(user.getParentId()); + ttRechargeRecord.setArrivalAmount(totalAmount); + ttRechargeRecord.setAmountActuallyPaid(totalAmount); + ttRechargeRecord.setFinallyPrice(user.getAccountAmount()); + ttRechargeRecord.setOrderId(order.getOrderId()); + ttRechargeRecord.setOutTradeNo(order.getOutTradeNo()); + ttRechargeRecord.setStatus("0"); + ttRechargeRecord.setChannelType("1"); + ttRechargeRecord.setCreateTime(DateUtils.getNowDate()); + ttRechargeRecordMapper.insert(ttRechargeRecord); + + // 更新VIP等级 + userService.updateUserVIPLevel(user.getUserId()); + } + + /** + * 首充赠送 + */ + private void firstChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + // 1.判断是否为首充 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .eq(TtUserBlendErcash::getSource, 1) + .eq(TtUserBlendErcash::getUserId, ttUser.getUserId()); + List ttUserBlendErcashes = userBlendErcashMapper.selectList(wrapper); + if (ttUserBlendErcashes.size() > 0) { + return; + } + + // 2.加钱 + // 充值金额 + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + // 查询赠送比例 + BigDecimal firstChargeAmountRatio = ttFirstRechargeMapper.selectRatioByMinAmount(totalAmount); + if (Objects.isNull(firstChargeAmountRatio)) { + return; + } + // 计算赠送金额 + BigDecimal giftAmount = totalAmount.multiply(firstChargeAmountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 3.记录 + ttUser = userService.getById(ttUser.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.FIRST_CHARGE.getCode()) + .remark(TtAccountRecordSource.FIRST_CHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } + + /** + * 推广等级充值赠送 + */ + @Async + public void promotionLevelChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + // 1.查询用户推广等级对应的赠送比例 + TtPromotionLevel ttPromotionLevel = ttPromotionLevelMapper.selectById(ttUser.getPromotionLevel()); + if (Objects.isNull(ttPromotionLevel)) + return; + + // 2.加钱 + BigDecimal amountRatio = ttPromotionLevel.getRechargeGiftRatio(); + if (Objects.isNull(amountRatio)) + return; + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal giftAmount = totalAmount.multiply(amountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 3.记录 + ttUser = userService.getById(ttUser.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_LEVEL_CHARGE.getCode()) + .remark(TtAccountRecordSource.PROMOTION_LEVEL_CHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } + + /** + * 推广充值返佣 + */ + private void promotionChargeCommission(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + if (Objects.isNull(ttUser.getParentId())) { + return; + } + // 查询用户推广等级信息 + TtPromotionLevel ttPromotionLevel = ttPromotionLevelMapper.selectById(ttUser.getPromotionLevel()); + + // 给上级加钱 + BigDecimal amountRatio = ttPromotionLevel.getCommissions(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal giftAmount = totalAmount.multiply(amountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getParentId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 记录 + ttUser = userService.getById(ttUser.getParentId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_CHARGE_COMMISSION.getCode()) + .remark(TtAccountRecordSource.PROMOTION_CHARGE_COMMISSION.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/util/GfhtUtil.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/util/GfhtUtil.java new file mode 100644 index 0000000..88ca5ea --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/gfht/util/GfhtUtil.java @@ -0,0 +1,76 @@ +package com.ruoyi.thirdparty.gfht.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Map; +import java.util.TreeMap; + +public class GfhtUtil { + + private String key; + + public GfhtUtil(String key) { + this.key = key; + } + + /** + * 生成MD5签名 + * + * @param params 参数列表 + * @return 生成的签名 + */ + public String getSign(Map params) { + // 排序参数 + Map sortedParams = new TreeMap<>(params); + sortedParams.remove("sign"); + sortedParams.remove("sign_type"); + + // 构建签名字符串 + StringBuilder signStr = new StringBuilder(); + for (Map.Entry entry : sortedParams.entrySet()) { + if (entry.getValue() != null && !entry.getValue().isEmpty()) { + signStr.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); + } + } + + if (signStr.length() > 0) { + signStr.setLength(signStr.length() - 1); // 移除最后一个& + } + + // 添加商户密钥 + signStr.append(this.key); + + // 计算MD5签名 + return md5(signStr.toString()).toLowerCase(); + } + + /** + * 计算MD5哈希值 + * + * @param input 输入字符串 + * @return MD5哈希值 + */ + private static String md5(String input) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] messageDigest = md.digest(input.getBytes()); + return bytesToHex(messageDigest); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5 algorithm not found", e); + } + } + + /** + * 将字节数组转换为十六进制字符串 + * + * @param bytes 字节数组 + * @return 十六进制字符串 + */ + private static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/controller/HiPayController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/controller/HiPayController.java new file mode 100644 index 0000000..d344db6 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/controller/HiPayController.java @@ -0,0 +1,52 @@ +package com.ruoyi.thirdparty.hipay.controller; + +import cn.hutool.json.JSONUtil; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.UserPermission; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.thirdparty.hipay.service.HipayPaymentService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +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.Map; + +@Api(tags = "") +@Slf4j +@RestController +@RequestMapping("/api/hipay") +public class HiPayController { + + @Autowired + private TtUserService ttUserService; + + @Autowired + private HipayPaymentService hipayPaymentService; + + @ApiOperation("支付") + @UserPermission + @PostMapping(value = "/pay") + public R pay(@RequestBody @Validated PayRequest param) { + Long userId = SecurityUtils.getUserId(); + TtUser ttUser = ttUserService.getById(userId); + // 是否实名认证(0未认证 1已认证) + if ("0".equals(ttUser.getIsRealCheck())) { + return R.fail("未实名认证"); + } + String ip = IpUtils.getIpAddr(); + return hipayPaymentService.pay(userId, param); + } + + @PostMapping("/notify") + public R notify(@RequestBody Map params) { + log.info("HiPay notify params: {}", JSONUtil.toJsonStr(params)); + return hipayPaymentService.notifyCallback(params); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/controller/PayRequest.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/controller/PayRequest.java new file mode 100644 index 0000000..db7b0a8 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/controller/PayRequest.java @@ -0,0 +1,22 @@ +package com.ruoyi.thirdparty.hipay.controller; + +import io.swagger.annotations.ApiModelProperty; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class PayRequest { + @NotNull(message = "商品id不能为空。") + private Integer goodsId; + + @NotNull(message = "商品数量不能为空。") + @Min(value = 1, message = "最小值1") + private Integer goodsNum; + + @ApiModelProperty("1:支付宝1 2:支付宝2") + private Integer payType; + + @ApiModelProperty("1:原有支付,2:图片支付") + private Integer payWay = 1; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/controller/PayResponse.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/controller/PayResponse.java new file mode 100644 index 0000000..d76dc18 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/controller/PayResponse.java @@ -0,0 +1,10 @@ +package com.ruoyi.thirdparty.hipay.controller; + +import lombok.Data; + +@Data +public class PayResponse { + private String qrUrl; + private String payUrl; + private String orderId; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/enums/StateEnum.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/enums/StateEnum.java new file mode 100644 index 0000000..de6afd7 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/enums/StateEnum.java @@ -0,0 +1,16 @@ +package com.ruoyi.thirdparty.hipay.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum StateEnum { + CREATE(2, "已创建"), + EXCHANGE(5, "已转币"), + CANCEL(9, "已取消"), + ERROR(99, "错误"), + ; + private final int code; + private final String desc; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/model/NotifyData.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/model/NotifyData.java new file mode 100644 index 0000000..c510027 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/model/NotifyData.java @@ -0,0 +1,26 @@ +package com.ruoyi.thirdparty.hipay.model; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class NotifyData { + + private BigDecimal amount; + private long createtime; + private String id; + private String note; + private String notifyurl; + private String orderid; + private int ordertype; + private BigDecimal recvcharge; + private String recvid; + private String recvnickname; + private String resign; + private String returnurl; + private String sign; + private int state; + private long transtime; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/model/PaymentResponse.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/model/PaymentResponse.java new file mode 100644 index 0000000..d022014 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/model/PaymentResponse.java @@ -0,0 +1,13 @@ +package com.ruoyi.thirdparty.hipay.model; + +import lombok.Data; + +@Data +public class PaymentResponse { + private int code; + private String msg; + private String payUrl; + private String qrcode; + private String tradeNo; +} + diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/model/PaymentResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/model/PaymentResult.java new file mode 100644 index 0000000..fc2a419 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/model/PaymentResult.java @@ -0,0 +1,18 @@ +package com.ruoyi.thirdparty.hipay.model; + +import lombok.Data; + +@Data +public class PaymentResult { + private int code; + private String msg; + private PaymentData data; + + @Data + public static class PaymentData { + private String qrurl; + private String payurl; + private String orderid; + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/service/HipayPaymentService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/service/HipayPaymentService.java new file mode 100644 index 0000000..0d3ae54 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/hipay/service/HipayPaymentService.java @@ -0,0 +1,310 @@ +package com.ruoyi.thirdparty.hipay.service; + +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.TtOrderMapper; +import com.ruoyi.admin.mapper.TtRechargeProdMapper; +import com.ruoyi.admin.mapper.TtRechargeRecordMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.TtBonusService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.MoneyUtil; +import com.ruoyi.domain.common.constant.PayOrderStatus; +import com.ruoyi.domain.common.constant.PayType; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtRechargeRecord; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.thirdparty.common.service.RechargeSuccessfulNoticeService; +import com.ruoyi.thirdparty.hipay.controller.PayRequest; +import com.ruoyi.thirdparty.hipay.controller.PayResponse; +import com.ruoyi.thirdparty.hipay.enums.StateEnum; +import com.ruoyi.thirdparty.hipay.model.NotifyData; +import com.ruoyi.thirdparty.hipay.model.PaymentResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.DigestUtils; +import org.springframework.web.client.RestTemplate; + +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.sql.Timestamp; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@Service +public class HipayPaymentService { + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private TtOrderMapper orderMapper; + + private static final String PAY_URL = "https://xahnob7yqke3.hipay.one/pay/pay/createpay"; + private static final String RECV_ID = "e42835c685f0712b19f27d693ee11f42"; + private static final String API_KEY = "aa1818c04f964043900d21bf38a83768"; + private static final String PAYCHANNELID1 = "610"; // 支付宝1 小额超级支付宝 + private static final String PAYCHANNELID2 = "616"; // 支付宝2 大额超级支付宝 + + @Autowired + private TtRechargeProdMapper rechargeProdMapper; + @Autowired + private TtUserMapper userMapper; + @Autowired + private RechargeSuccessfulNoticeService rechargeSuccessfulNoticeService; + @Autowired + private TtRechargeRecordMapper ttRechargeRecordMapper; + @Autowired + private TtUserService userService; + @Autowired + private TtUserBlendErcashMapper userBlendErcashMapper; + + @Autowired + private TtBonusService bonusService; + + public R pay(Long uid, PayRequest request) { + // 1.查询商品信息 + TtRechargeProd goods = new LambdaQueryChainWrapper<>(rechargeProdMapper) + .eq(TtRechargeProd::getId, request.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + if (ObjectUtil.isEmpty(goods)) { + return R.fail("不存在的商品"); + } + + // 计算总价值 + BigDecimal totalAmount = goods.getPrice().multiply(new BigDecimal(request.getGoodsNum())); + + + Snowflake snowflake = IdUtil.getSnowflake(1, 1); + String orderId = String.valueOf(snowflake.nextId()); + + String amount = MoneyUtil.toStr(totalAmount); + + // MD5签名 + String signText = "pay" + RECV_ID + orderId + amount + API_KEY; + String sign = DigestUtils.md5DigestAsHex(signText.getBytes(StandardCharsets.UTF_8)); + + String paychannelid; + if (request.getPayType() == 2) { + paychannelid = PAYCHANNELID2; + } else { + paychannelid = PAYCHANNELID1; + } + + Map data = new HashMap<>(); + data.put("recvid", RECV_ID); + data.put("orderid", orderId); + data.put("amount", amount); + data.put("sign", sign); + data.put("paychannelid", paychannelid); + data.put("memuid", DigestUtils.md5DigestAsHex((uid + "").getBytes(StandardCharsets.UTF_8))); + data.put("notifyurl", "http://154.12.94.98:8081/api/hipay/notify"); + data.put("returnurl", ""); + data.put("note", "充值" + amount); + + log.info("Hipay request data: {}", JSONUtil.toJsonStr(data)); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + + HttpEntity> entity = new HttpEntity<>(data, headers); + + ResponseEntity response = restTemplate.exchange( + PAY_URL, + HttpMethod.POST, + entity, + String.class + ); + + String result = response.getBody(); + + log.info("Hipay response data: {}", result); + + PaymentResult resBody = JSONUtil.toBean(result, PaymentResult.class); + if (resBody.getCode() != 1) { + return R.fail(resBody.getMsg()); + } + + TtOrder order = new TtOrder(); + order.setOrderId(orderId); + order.setOutTradeNo(resBody.getData().getOrderid()); + order.setUserId(Math.toIntExact(uid)); + order.setType(PayType.HI_PAY.getCode()); + order.setGoodsId(request.getGoodsId()); + order.setGoodsPrice(goods.getPrice()); + order.setGoodsNum(request.getGoodsNum()); + order.setTotalAmount(totalAmount); + order.setSign(sign); + order.setStatus(PayOrderStatus.NO_PAY.getCode()); + order.setCreateTime(new Date()); + orderMapper.insert(order); + + PayResponse resp = new PayResponse(); + resp.setQrUrl(resBody.getData().getQrurl()); + resp.setPayUrl(resBody.getData().getPayurl()); + resp.setOrderId(orderId); + + return R.ok(resp); + } + + + public R notifyCallback(Map params) { + log.info("支付回调通知"); + + String code = params.get("code"); + if (!"1".equals(code)) { + log.error("支付失败,code={}, msg={}", code, params.get("msg")); + return R.fail("支付失败"); + } + NotifyData resBody = JSONUtil.toBean(params.get("data"), NotifyData.class); + // 校验 + int tradeStatus = resBody.getState(); + if (StateEnum.EXCHANGE.getCode() != tradeStatus) { + log.error("交易失败"); + return R.fail("交易失败"); + } + String sign = resBody.getSign(); + String resign = resBody.getResign(); + String d = DigestUtils.md5DigestAsHex((sign + API_KEY).getBytes(StandardCharsets.UTF_8)); + if (!d.equals(resign)) { + return R.fail("签名验证失败"); + } + + // 查询订单信息 + String outTradeNo = resBody.getOrderid(); + TtOrder order = new LambdaQueryChainWrapper<>(orderMapper) + .eq(TtOrder::getOutTradeNo, outTradeNo) + .eq(TtOrder::getStatus, PayOrderStatus.NO_PAY.getCode()) + .one(); + if (order == null) { + return R.fail("订单不存在"); + } + // 防止重复通知 + if (!PayOrderStatus.NO_PAY.getCode().equals(order.getStatus())) { + return R.fail("重复通知"); + } + + // 查询用户信息 + TtUser user = new LambdaQueryChainWrapper<>(userMapper) + .eq(TtUser::getUserId, order.getUserId()) + .eq(TtUser::getDelFlag, 0) + .one(); + + // 3.查询商品信息 + TtRechargeProd goods = new LambdaQueryChainWrapper<>(rechargeProdMapper) + .eq(TtRechargeProd::getId, order.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + + // 4.账户结算 + if (ObjectUtil.isNull(goods.getProductA())) goods.setProductA(BigDecimal.ZERO); + if (ObjectUtil.isNull(goods.getProductC())) goods.setProductC(BigDecimal.ZERO); + payNotifyAccounting(order, user, goods, order.getGoodsNum()); + +// try { +// // 首充赠送 +// firstChargeGiftAmount(user, goods, order.getGoodsNum()); +// +// // 推广等级充值赠送 +// promotionLevelChargeGiftAmount(user, goods, order.getGoodsNum()); +// +// // 推广充值返佣 +// promotionChargeCommission(user, goods, order.getGoodsNum()); +// } catch (Exception e) { +// e.printStackTrace(); +// } + + // 5.更新订单 + new LambdaUpdateChainWrapper<>(orderMapper) + .eq(TtOrder::getId, order.getId()) + .set(TtOrder::getStatus, PayOrderStatus.PAY_COMPLE.getCode()) + .set(TtOrder::getUpdateTime, new Date()) + .update(); + + // 6.发送充值通知 + rechargeSuccessfulNoticeService.sendRechargeSuccessNotice(order.getUserId().toString(), order.getGoodsPrice()); + + return R.ok(); + } + + // 账户结算 + public void payNotifyAccounting(TtOrder order, TtUser user, TtRechargeProd goods, Integer goodsNumber) { + // 加钱 + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + + BigDecimal oldAmount = user.getAccountAmount(); + if (oldAmount == null) { + oldAmount = BigDecimal.ZERO; + } + BigDecimal newAmount = oldAmount.add(totalAmount); + + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, user.getUserId()) + .setSql("account_amount = account_amount + " + totalAmount); + userService.update(userUpdate); + + // 综合消费日志 + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(user.getUserId()) + + .amount(totalAmount) + .finalAmount(newAmount) + + .total(totalAmount) // 收支合计 + + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.RECHARGE.getCode()) + .remark(TtAccountRecordSource.RECHARGE.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + userBlendErcashMapper.insert(blendErcash); + + + // 充值记录 + TtRechargeRecord ttRechargeRecord = TtRechargeRecord.builder().build(); + ttRechargeRecord.setUserId(order.getUserId()); + ttRechargeRecord.setParentId(user.getParentId()); + ttRechargeRecord.setArrivalAmount(totalAmount); + ttRechargeRecord.setAmountActuallyPaid(totalAmount); + ttRechargeRecord.setFinallyPrice(newAmount); + ttRechargeRecord.setOrderId(order.getOrderId()); + ttRechargeRecord.setOutTradeNo(order.getOutTradeNo()); + ttRechargeRecord.setStatus("0"); + ttRechargeRecord.setChannelType("1"); + ttRechargeRecord.setCreateTime(DateUtils.getNowDate()); + ttRechargeRecordMapper.insert(ttRechargeRecord); + // 更新VIP等级 + userService.updateUserVIPLevel(user.getUserId()); + +// AsyncManager.me().run(() -> { +// bonusService.bonus(user.getUserId(), ttRechargeRecord.getId()); +// }); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/config/JiuJiaProperties.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/config/JiuJiaProperties.java new file mode 100644 index 0000000..ad998b0 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/config/JiuJiaProperties.java @@ -0,0 +1,17 @@ +package com.ruoyi.thirdparty.jiujia.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Data +@ConfigurationProperties(prefix = "jiu-jia-pay") +public class JiuJiaProperties { + + private String aliPayUrl; + private String memberId; + private String appKey; + private String apiSecret; + private String apiDomain; + private String callbackUrl; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/controller/JiuJiaPayController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/controller/JiuJiaPayController.java new file mode 100644 index 0000000..3d0090b --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/controller/JiuJiaPayController.java @@ -0,0 +1,56 @@ +package com.ruoyi.thirdparty.jiujia.controller; + +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.jiujia.domain.CallbackBody; +import com.ruoyi.thirdparty.jiujia.service.JiuJiaPayService; +import com.ruoyi.domain.other.CreateOrderParam; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "九嘉支付") +@RestController +@RequestMapping("/api/jiuJiaPay") +public class JiuJiaPayController extends BaseController { + + private final ISysConfigService sysConfigService; + private final TtUserService userService; + private final JiuJiaPayService jiuJiaPayService; + + public JiuJiaPayController(ISysConfigService sysConfigService, + TtUserService userService, + JiuJiaPayService jiuJiaPayService) { + this.sysConfigService = sysConfigService; + this.userService = userService; + this.jiuJiaPayService = jiuJiaPayService; + } + + // TODO: 2024/4/11 要修改!!! + @ApiOperation("回滚") + @Anonymous + @PostMapping("/callback") + public String callback(@RequestBody CallbackBody callbackBody) { + return jiuJiaPayService.callback(callbackBody); + } + + @ApiOperation("九嘉预下单") + @PostMapping("/createPay") + public R createPay(@RequestBody CreateOrderParam createOrderBody) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + String msg = jiuJiaPayService.createPay(createOrderBody, ttUser); + return msg.startsWith("https") ? R.ok(msg) : R.fail(msg); + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CallbackBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CallbackBody.java new file mode 100644 index 0000000..0347866 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CallbackBody.java @@ -0,0 +1,18 @@ +package com.ruoyi.thirdparty.jiujia.domain; + +import lombok.Data; + +@Data +public class CallbackBody { + + private String member_id; + private String total_fee; + private String result_code; + private String trade_no; + private String out_trade_no; + private String time_end; + private String pay_type; + private String ext_sign; + private String sign; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CheckOrderRequestParam.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CheckOrderRequestParam.java new file mode 100644 index 0000000..dd55872 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CheckOrderRequestParam.java @@ -0,0 +1,21 @@ +package com.ruoyi.thirdparty.jiujia.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +@Builder +public class CheckOrderRequestParam { + + @JsonProperty("app_key") + private String appKey; + + @JsonProperty("member_id") + private String memberId; + + @JsonProperty("out_trade_no") + private String outTradeNo; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CheckOrderResponseData.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CheckOrderResponseData.java new file mode 100644 index 0000000..fd33982 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CheckOrderResponseData.java @@ -0,0 +1,12 @@ +package com.ruoyi.thirdparty.jiujia.domain; + +import lombok.Data; + +@Data +public class CheckOrderResponseData { + private String total_amount; + private String out_trade_no; + private String trade_no; + private String pay_time; + private String status; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CheckOrderResponseResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CheckOrderResponseResult.java new file mode 100644 index 0000000..f45bed7 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CheckOrderResponseResult.java @@ -0,0 +1,11 @@ +package com.ruoyi.thirdparty.jiujia.domain; + +import lombok.Data; + +@Data +public class CheckOrderResponseResult { + + private Integer code; + private String msg; + private T data; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CreateOrderRequestParam.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CreateOrderRequestParam.java new file mode 100644 index 0000000..0db81c1 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CreateOrderRequestParam.java @@ -0,0 +1,58 @@ +package com.ruoyi.thirdparty.jiujia.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class CreateOrderRequestParam { + + @JsonProperty("app_key") + private String appKey; + + @JsonProperty("api_domain") + private String apiDomain; + + @JsonProperty("subject") + private String subject; + + @JsonProperty("pay_type") + private Integer payType; + + @JsonProperty("goods_id") + private Integer goodsId; + + @JsonProperty("goods_price") + private BigDecimal goodsPrice; + + @JsonProperty("goods_num") + private Integer goodsNum; + + @JsonProperty("order_id") + private String orderId; + + @JsonProperty("total_amount") + private BigDecimal totalAmount; + + @JsonProperty("member_id") + private String memberId; + + @JsonProperty("callback_url") + private String callbackUrl; + + @JsonProperty("sign") + private String sign; + + @JsonProperty("user_ip") + private String userIp; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CreateOrderResponseResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CreateOrderResponseResult.java new file mode 100644 index 0000000..b3aae2a --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/CreateOrderResponseResult.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.jiujia.domain; + +import lombok.Data; + +@Data +public class CreateOrderResponseResult { + + private Integer code; + private String msg; + private String data; + private String url; + private Integer wait; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/OrderBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/OrderBody.java new file mode 100644 index 0000000..6770e08 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/OrderBody.java @@ -0,0 +1,18 @@ +package com.ruoyi.thirdparty.jiujia.domain; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class OrderBody { + + private BigDecimal totalAmount; + private String orderId; + + public OrderBody(BigDecimal totalAmount, String orderId) { + this.totalAmount = totalAmount; + this.orderId = orderId; + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/VisaVerificationBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/VisaVerificationBody.java new file mode 100644 index 0000000..82946a7 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/domain/VisaVerificationBody.java @@ -0,0 +1,18 @@ +package com.ruoyi.thirdparty.jiujia.domain; + +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +@Builder +public class VisaVerificationBody { + + private String totalFee; + private String resultCode; + private String tradeNo; + private String outTradeNo; + + private String sign; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/service/JiuJiaPayService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/service/JiuJiaPayService.java new file mode 100644 index 0000000..ad336c4 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/service/JiuJiaPayService.java @@ -0,0 +1,21 @@ +package com.ruoyi.thirdparty.jiujia.service; + +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.thirdparty.jiujia.domain.CallbackBody; +import com.ruoyi.domain.other.CreateOrderParam; + +public interface JiuJiaPayService { + + /** + * 创建订单 + * @param createOrderBody 创建支付订单参数数据 + * @param ttUser 用户信息 + */ + String createPay(CreateOrderParam createOrderBody, TtUser ttUser); + + /** + * 支付回调 + * @param callbackBody 回调参数 + */ + String callback(CallbackBody callbackBody); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/service/impl/JiuJiaPayServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/service/impl/JiuJiaPayServiceImpl.java new file mode 100644 index 0000000..b024f14 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/service/impl/JiuJiaPayServiceImpl.java @@ -0,0 +1,349 @@ +package com.ruoyi.thirdparty.jiujia.service.impl; + +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.TtOrderMapper; +import com.ruoyi.admin.mapper.TtRechargeProdMapper; +import com.ruoyi.admin.mapper.TtRechargeRecordMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.TtBonusService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.domain.common.constant.PartyType; +import com.ruoyi.domain.common.constant.PayOrderStatus; +import com.ruoyi.domain.common.constant.PayType; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.common.service.RechargeSuccessfulNoticeService; +import com.ruoyi.thirdparty.jiujia.config.JiuJiaProperties; +import com.ruoyi.thirdparty.jiujia.domain.CallbackBody; +import com.ruoyi.thirdparty.jiujia.domain.CheckOrderRequestParam; +import com.ruoyi.thirdparty.jiujia.domain.CheckOrderResponseData; +import com.ruoyi.thirdparty.jiujia.domain.CheckOrderResponseResult; +import com.ruoyi.thirdparty.jiujia.domain.CreateOrderRequestParam; +import com.ruoyi.thirdparty.jiujia.domain.CreateOrderResponseResult; +import com.ruoyi.thirdparty.jiujia.domain.OrderBody; +import com.ruoyi.thirdparty.jiujia.domain.VisaVerificationBody; +import com.ruoyi.thirdparty.jiujia.service.JiuJiaPayService; +import com.ruoyi.thirdparty.jiujia.util.JiuJiaUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Date; +import java.util.concurrent.Executor; + +@Service +@EnableConfigurationProperties(value = JiuJiaProperties.class) +@Slf4j +public class JiuJiaPayServiceImpl implements JiuJiaPayService { + + private final TtBonusService bonusService; + private final JiuJiaProperties jiuJiaProperties; + private final Executor customThreadPoolExecutor; + private final TtRechargeProdMapper rechargeListMapper; + private final TtOrderMapper orderMapper; + private final TtUserService userService; + private final TtRechargeRecordMapper rechargeRecordMapper; + + public JiuJiaPayServiceImpl(TtBonusService bonusService, + JiuJiaProperties jiuJiaProperties, + Executor customThreadPoolExecutor, + TtRechargeProdMapper rechargeListMapper, + TtOrderMapper orderMapper, + TtUserService userService, + TtRechargeRecordMapper rechargeRecordMapper) { + + this.bonusService = bonusService; + this.jiuJiaProperties = jiuJiaProperties; + this.customThreadPoolExecutor = customThreadPoolExecutor; + this.rechargeListMapper = rechargeListMapper; + this.orderMapper = orderMapper; + this.userService = userService; + this.rechargeRecordMapper = rechargeRecordMapper; + } + + @Autowired + private RechargeSuccessfulNoticeService rechargeSuccessfulNoticeService; + + @Autowired + private TtUserBlendErcashMapper userBlendErcashMapper; + + @Autowired + private ISysConfigService configService; + + @Override + public String createPay(CreateOrderParam createOrderBody, TtUser ttUser) { + + Integer goodsId = createOrderBody.getGoodsId(); + BigDecimal goodsPrice = createOrderBody.getGoodsPrice().setScale(2, RoundingMode.HALF_UP); + Integer goodsNum = createOrderBody.getGoodsNum(); + BigDecimal totalAmount = goodsPrice.multiply(new BigDecimal(goodsNum)).setScale(2, RoundingMode.HALF_UP); + + // 本站充值产品 + TtRechargeProd ttRechargeProd = rechargeListMapper.selectById(goodsId); + if (goodsPrice.compareTo(ttRechargeProd.getPrice()) != 0) return "客户端传参产品价格不一致异常!"; + if (ttRechargeProd.getPrice().multiply(new BigDecimal(goodsNum)).compareTo(totalAmount) != 0) + return "客户端传参产品价格不一致异常!"; + + // 是否有购买相同商品并未超时的订单 + TtOrder order = new LambdaQueryChainWrapper<>(orderMapper) + .eq(TtOrder::getUserId, ttUser.getUserId()) + .eq(TtOrder::getThirdParty, PartyType.JIU_JIA_ZFB.getCode()) + .eq(TtOrder::getType, PayType.JIU_JIA_ZFB.getCode()) + .eq(TtOrder::getGoodsId, goodsId) + .eq(TtOrder::getGoodsPrice, goodsPrice) + .eq(TtOrder::getGoodsNum, goodsNum) + .eq(TtOrder::getTotalAmount, totalAmount) + .eq(TtOrder::getStatus, PayOrderStatus.NO_PAY.getCode()) + .one(); + + if (ObjectUtil.isNotNull(order)) { + + // 已存在相同订单 + Date orderCreateTime = order.getCreateTime(); + DateTime timeout = DateUtil.offset(orderCreateTime, DateField.MINUTE, 5); + if (DateUtils.getNowDate().compareTo(DateUtil.date(timeout)) < 0) { + // 订单未超时直接返回支付url + return order.getPayUrl(); + } else { + // 超时 + // order.setStatus(PayOrderStatus.CANCEL.getCode()); + // order.setUpdateTime(DateUtils.getNowDate()); + // orderMapper.updateById(order); + new LambdaUpdateChainWrapper<>(orderMapper) + .eq(TtOrder::getId, order.getId()) + .eq(TtOrder::getOrderId, order.getOrderId()) + .set(TtOrder::getStatus, PayOrderStatus.CANCEL.getCode()) + .set(TtOrder::getUpdateTime, DateUtils.getNowDate()) + .update(); + // if (orderMapper.updateById(order) > 0) { + // log.info("订单超时已关闭!"); + // }else { + // return "订单超时关闭失败,请联系管理员!"; + // } + } + + } + + // 初始化预下单请求参数 + String orderId = IdUtils.fastSimpleUUID().toUpperCase().substring(0, 20); + OrderBody orderBody = new OrderBody(totalAmount, orderId); + String sign = JiuJiaUtils.createSign(jiuJiaProperties, orderBody); + + // CreateOrderRequestParam requestParam = CreateOrderRequestParam.builder().build(); + CreateOrderRequestParam apiParam = new CreateOrderRequestParam(); + apiParam.setAppKey(jiuJiaProperties.getAppKey()); + apiParam.setApiDomain(jiuJiaProperties.getApiDomain()); + apiParam.setSubject(JiuJiaUtils.createSubject() + "*" + goodsNum); + apiParam.setPayType(Integer.valueOf(PayType.JIU_JIA_ZFB.getCode())); + apiParam.setGoodsId(goodsId); + apiParam.setGoodsPrice(goodsPrice); + apiParam.setGoodsNum(goodsNum); + apiParam.setOrderId(orderId); + apiParam.setTotalAmount(totalAmount); + apiParam.setMemberId(jiuJiaProperties.getMemberId()); + apiParam.setCallbackUrl(jiuJiaProperties.getCallbackUrl()); + apiParam.setSign(sign); + apiParam.setUserIp(IpUtils.getIpAddr()); + String jsonParam = JSONObject.toJSONString(apiParam); + + // 请求api + String result = HttpUtils.sendPostJSONString(jiuJiaProperties.getAliPayUrl() + "/create_pay", jsonParam); + + CreateOrderResponseResult responseResult = JSONObject.parseObject(result, CreateOrderResponseResult.class); + if (responseResult.getCode() > 0) return responseResult.getMsg(); + if (responseResult.getCode() < 0) return "创建订单异常,请联系管理员!"; + + // 构建新订单 + TtOrder ttOrder = new TtOrder(); + ttOrder.setUserId(ttUser.getUserId()); + ttOrder.setThirdParty(String.valueOf(PartyType.JIU_JIA_ZFB.getCode())); + ttOrder.setType(PayType.JIU_JIA_ZFB.getCode()); + ttOrder.setGoodsId(goodsId); + ttOrder.setGoodsPrice(goodsPrice); + ttOrder.setGoodsNum(goodsNum); + ttOrder.setTotalAmount(totalAmount); + ttOrder.setOrderId(orderId); + ttOrder.setSign(sign); + ttOrder.setStatus(PayOrderStatus.NO_PAY.getCode()); + ttOrder.setOutTradeNo(responseResult.getData()); + ttOrder.setPayUrl(responseResult.getUrl()); + ttOrder.setCreateTime(DateUtils.getNowDate()); + + int isSuccess = orderMapper.insert(ttOrder); + if (isSuccess <= 0) { + return "创建订单异常,请联系管理员!"; + } + + // 发送充值成功通知 + rechargeSuccessfulNoticeService.sendRechargeSuccessNotice(ttUser.getUserId().toString(), order.getGoodsPrice()); + + return responseResult.getUrl(); + } + + @Override + public String callback(CallbackBody callbackBody) { + + String resultCode = callbackBody.getResult_code(); + if (!"success".equals(resultCode)) { + log.error("九嘉回调出现异常,请联系客服!"); + return "fail"; + } + + String outTradeNo = callbackBody.getOut_trade_no(); + String orderId = callbackBody.getTrade_no(); + // 查询创建的订单 + TtOrder ttOrder = new LambdaQueryChainWrapper<>(orderMapper) + .eq(TtOrder::getOutTradeNo, outTradeNo) + .eq(TtOrder::getOrderId, orderId) + .eq(TtOrder::getStatus, PayOrderStatus.NO_PAY.getCode()) + .one(); + + if (StringUtils.isNull(ttOrder)) { + log.error("不存在的订单。"); + return "fail"; + } + + // 查询用户 + TtUser user = new LambdaQueryChainWrapper<>(userService.getBaseMapper()) + .eq(TtUser::getUserId, ttOrder.getUserId()) + .eq(TtUser::getDelFlag, "0") + .one(); + if (ObjectUtil.isNull(user)) { + log.error("不存在的订单支付用户。"); + return "fail"; + } + + // 验签 + String sign = callbackBody.getSign(); + VisaVerificationBody vvBody = VisaVerificationBody.builder().build(); + vvBody.setTotalFee(String.valueOf(ttOrder.getTotalAmount())); + vvBody.setResultCode(resultCode); + vvBody.setTradeNo(ttOrder.getOrderId()); + vvBody.setOutTradeNo(ttOrder.getOutTradeNo()); + vvBody.setSign(sign); + if (!JiuJiaUtils.visaVerification(jiuJiaProperties, vvBody)) { + log.warn("九嘉支付回调,验签失败。"); + return "fail"; + } + + // 查询支付情况 + CheckOrderResponseData check = checkPay(outTradeNo); + if (ObjectUtil.isNull(check)) { + log.error("九嘉支付回调,订单支付情况复查无结果!"); + return "fail"; + } + + // 状态:TRADE_FINISHED、TRADE_SUCCESS、TRADE_CLOSED、WAIT_BUYER_PAY + String status = check.getStatus(); + if (!status.equals("TRADE_SUCCESS") && !status.equals("TRADE_FINISHED")) { + ttOrder.setStatus(PayOrderStatus.PAY_FAIL.getCode()); + ttOrder.setUpdateTime(DateUtils.getNowDate()); + orderMapper.updateById(ttOrder); + return "fail"; + } + + rechargeSuccess(ttOrder, user); + + return resultCode; + } + + private CheckOrderResponseData checkPay(String outTradeNo) { + + // 查询订单 + CheckOrderRequestParam requestParam = CheckOrderRequestParam.builder() + .appKey(jiuJiaProperties.getAppKey()) + .memberId(jiuJiaProperties.getMemberId()) + .outTradeNo(outTradeNo) + .build(); + String jsonParam = JSONObject.toJSONString(requestParam); + + String result = HttpUtils.sendPostJSONString(jiuJiaProperties.getAliPayUrl() + "/check_pay", jsonParam); + + CheckOrderResponseResult responseResult = + JSONObject.parseObject(result, new TypeReference>() { + }); + + return responseResult.getCode() == 0 ? responseResult.getData() : null; + } + + private void rechargeSuccess(TtOrder ttOrder, TtUser user) { + + //String status = ttOrder.getStatus(); + ttOrder.setStatus(PayOrderStatus.PAY_YET.getCode()); + ttOrder.setUpdateTime(DateUtils.getNowDate()); + orderMapper.updateById(ttOrder); + + + // TODO: 2024/4/12 这张表弃用 + // TtRechargeRecord rechargeRecord = new LambdaQueryChainWrapper<>(rechargeRecordMapper) + // .eq(TtRechargeRecord::getOrderId, ttOrder.getOrderId()) + // .eq(TtRechargeRecord::getOutTradeNo, ttOrder.getOutTradeNo()) + // .one(); + // if (StringUtils.isNotNull(rechargeRecord)) return; + + // 本站产品 + TtRechargeProd goods = rechargeListMapper.selectById(ttOrder.getGoodsId()); + + // 更新用户账户 + BigDecimal prodA = goods.getProductA().multiply(new BigDecimal(ttOrder.getGoodsNum())); + BigDecimal prodC = goods.getProductC().multiply(new BigDecimal(ttOrder.getGoodsNum())); + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper + .eq(TtUser::getUserId, user.getUserId()) + .setSql("account_amount = account_amount + " + prodA.toString() + + ",account_credits = account_credits + " + prodC.toString()); + userService.update(wrapper); + + user = userService.getById(user.getUserId()); + // 更新充值记录 + userService.insertUserAmountRecords( + user.getUserId(), + TtAccountRecordType.INPUT, + TtAccountRecordSource.RECHARGE, + prodA, + user.getAccountAmount()); + userService.insertUserCreditsRecords( + user.getUserId(), + TtAccountRecordType.INPUT, + TtAccountRecordSource.RECHARGE, + prodC, + user.getAccountCredits()); + // TtRechargeRecord ttRechargeRecord = TtRechargeRecord.builder().build(); + // ttRechargeRecord.setUserId(ttOrder.getUserId()); + // ttRechargeRecord.setParentId(user.getParentId()); + // ttRechargeRecord.setArrivalAmount(totalAmount); + // ttRechargeRecord.setAmountActuallyPaid(totalAmount); + // ttRechargeRecord.setFinallyPrice(user.getAccountAmount()); + // ttRechargeRecord.setOrderId(ttOrder.getOrderId()); + // ttRechargeRecord.setOutTradeNo(ttOrder.getOutTradeNo()); + // ttRechargeRecord.setStatus("0"); + // ttRechargeRecord.setChannelType("1"); + // ttRechargeRecord.setCreateTime(DateUtils.getNowDate()); + // if (rechargeRecordMapper.insert(ttRechargeRecord) > 0) { + // + // // todo 奖励津贴? + // // bonusService.bonus(user.getUserId(), ttRechargeRecord.getId()); + // } + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/util/JiuJiaUtils.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/util/JiuJiaUtils.java new file mode 100644 index 0000000..a838ef0 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/jiujia/util/JiuJiaUtils.java @@ -0,0 +1,48 @@ +package com.ruoyi.thirdparty.jiujia.util; + +import com.ruoyi.common.utils.sign.Md5Utils; +import com.ruoyi.thirdparty.jiujia.config.JiuJiaProperties; +import com.ruoyi.thirdparty.jiujia.domain.OrderBody; +import com.ruoyi.thirdparty.jiujia.domain.VisaVerificationBody; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class JiuJiaUtils { + + public static String createSign(JiuJiaProperties jiuJiaProperties, OrderBody orderBody) { + String param = "member_id=" + jiuJiaProperties.getMemberId() + "&app_key=" + jiuJiaProperties.getAppKey() + + "&api_domain=" + jiuJiaProperties.getApiDomain() + "&total_amount=" + orderBody.getTotalAmount() + + "&callback_url=" + jiuJiaProperties.getCallbackUrl() + "&order_id=" + orderBody.getOrderId() + + "&key=" + jiuJiaProperties.getApiSecret(); + return Md5Utils.hash(param); + } + + public static boolean visaVerification(JiuJiaProperties jiuJiaProperties, VisaVerificationBody visaVerificationBody) { + String param = "member_id=" + jiuJiaProperties.getMemberId() + "&total_fee=" + visaVerificationBody.getTotalFee() + + "&result_code=" + visaVerificationBody.getResultCode() + "&trade_no=" + visaVerificationBody.getTradeNo() + + "&out_trade_no=" + visaVerificationBody.getOutTradeNo() + "&key=" + jiuJiaProperties.getApiSecret(); + String hash = Md5Utils.hash(param); + return visaVerificationBody.getSign().equals(hash); + } + + public static String createSubject() { + List list = new ArrayList<>(); + list.add("水晶箱子"); + list.add("木箱子"); + list.add("手工箱子"); + list.add("坚毅的箱子"); + list.add("金箱子"); + list.add("木钥匙"); + list.add("水晶钥匙"); + list.add("铁箱子"); + list.add("手工钥匙"); + list.add("金钥匙"); + list.add("纸箱子"); + list.add("万能钥匙"); + list.add("生锈的钥匙"); + int randomIndex = new Random().nextInt(list.size()); + return list.get(randomIndex); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/config/YunXinProperties.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/config/YunXinProperties.java new file mode 100644 index 0000000..5923bdc --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/config/YunXinProperties.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.note.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Data +@ConfigurationProperties(prefix = "yun-xin") +public class YunXinProperties { + + private String appKey; + private String appSecret; + private String templateId; + private String serverUrl; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/domain/YunXinNoteResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/domain/YunXinNoteResult.java new file mode 100644 index 0000000..1724464 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/domain/YunXinNoteResult.java @@ -0,0 +1,10 @@ +package com.ruoyi.thirdparty.note.domain; + +import lombok.Data; + +@Data +public class YunXinNoteResult { + private Integer code; + private String msg; + private String obj; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/service/YunXinNoteService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/service/YunXinNoteService.java new file mode 100644 index 0000000..affb929 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/service/YunXinNoteService.java @@ -0,0 +1,6 @@ +package com.ruoyi.thirdparty.note.service; + +public interface YunXinNoteService { + + String sendNote(String mobile, String nonce); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/service/impl/YunXinNoteServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/service/impl/YunXinNoteServiceImpl.java new file mode 100644 index 0000000..d6520d8 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/service/impl/YunXinNoteServiceImpl.java @@ -0,0 +1,62 @@ +package com.ruoyi.thirdparty.note.service.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.thirdparty.note.config.YunXinProperties; +import com.ruoyi.thirdparty.note.domain.YunXinNoteResult; +import com.ruoyi.thirdparty.note.service.YunXinNoteService; +import com.ruoyi.thirdparty.note.util.CheckSumBuilder; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Service +@EnableConfigurationProperties(value = YunXinProperties.class) +@Slf4j +public class YunXinNoteServiceImpl implements YunXinNoteService { + + private final YunXinProperties yunXinProperties; + + public YunXinNoteServiceImpl(YunXinProperties yunXinProperties) { + this.yunXinProperties = yunXinProperties; + } + + @Override + public String sendNote(String mobile, String nonce) { + CloseableHttpClient httpClient = HttpClients.createDefault(); + HttpPost httpPost = new HttpPost(yunXinProperties.getServerUrl() + "/sendcode.action"); + String curTime = String.valueOf((new Date()).getTime() / 1000L); + try { + String checkSum = CheckSumBuilder.getCheckSum(yunXinProperties.getAppSecret(), nonce, curTime); + httpPost.addHeader("AppKey", yunXinProperties.getAppKey()); + httpPost.addHeader("Nonce", nonce); + httpPost.addHeader("CurTime", curTime); + httpPost.addHeader("CheckSum", checkSum); + httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); + List nvps = new ArrayList(); + nvps.add(new BasicNameValuePair("templateid", yunXinProperties.getTemplateId())); + nvps.add(new BasicNameValuePair("mobile", mobile)); + nvps.add(new BasicNameValuePair("authCode", nonce)); + httpPost.setEntity(new UrlEncodedFormEntity(nvps, "utf-8")); + HttpResponse response = httpClient.execute(httpPost); + String responseEntity = EntityUtils.toString(response.getEntity()); + YunXinNoteResult result = JSONObject.parseObject(responseEntity, YunXinNoteResult.class); + if (result.getCode() == 200) return result.getObj(); + } catch (IOException e) { + return ""; + } + return ""; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/util/CheckSumBuilder.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/util/CheckSumBuilder.java new file mode 100644 index 0000000..f8d76dd --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/note/util/CheckSumBuilder.java @@ -0,0 +1,41 @@ +package com.ruoyi.thirdparty.note.util; + +import java.security.MessageDigest; + +public class CheckSumBuilder { + + private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + public static String getCheckSum(String appSecret, String nonce, String curTime) { + return encode("sha1", appSecret + nonce + curTime); + } + + public static String getMD5(String requestBody) { + return encode("md5", requestBody); + } + + private static String encode(String algorithm, String value) { + if (value == null) { + return null; + } + try { + MessageDigest messageDigest + = MessageDigest.getInstance(algorithm); + messageDigest.update(value.getBytes()); + return getFormattedText(messageDigest.digest()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static String getFormattedText(byte[] bytes) { + int len = bytes.length; + StringBuilder buf = new StringBuilder(len * 2); + for (int j = 0; j < len; j++) { + buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]); + buf.append(HEX_DIGITS[bytes[j] & 0x0f]); + } + return buf.toString(); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/config/SmsbaoConfig.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/config/SmsbaoConfig.java new file mode 100644 index 0000000..70a30f3 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/config/SmsbaoConfig.java @@ -0,0 +1,17 @@ +package com.ruoyi.thirdparty.smsbao.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "smsbao") +public class SmsbaoConfig { + + private String httpUrl; + + private String username; + + private String password; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/controller/SmsbaoController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/controller/SmsbaoController.java new file mode 100644 index 0000000..2a7ab41 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/controller/SmsbaoController.java @@ -0,0 +1,22 @@ +package com.ruoyi.thirdparty.smsbao.controller; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.thirdparty.smsbao.service.SmsbaoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/admin/smsbao") +public class SmsbaoController extends BaseController { + + @Autowired + private SmsbaoService smsbaoService; + + @GetMapping("/test") + public AjaxResult test(String mobile, String content) { + return success(smsbaoService.sendSms(mobile, content)); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/service/SmsbaoService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/service/SmsbaoService.java new file mode 100644 index 0000000..62727e1 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/service/SmsbaoService.java @@ -0,0 +1,6 @@ +package com.ruoyi.thirdparty.smsbao.service; + +public interface SmsbaoService { + + public String sendSms(String mobile, String content); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/service/impl/SmsbaoServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/service/impl/SmsbaoServiceImpl.java new file mode 100644 index 0000000..00f18ab --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/service/impl/SmsbaoServiceImpl.java @@ -0,0 +1,25 @@ +package com.ruoyi.thirdparty.smsbao.service.impl; + +import com.ruoyi.thirdparty.smsbao.config.SmsbaoConfig; +import com.ruoyi.thirdparty.smsbao.service.SmsbaoService; +import com.ruoyi.thirdparty.smsbao.util.SmsbaoUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class SmsbaoServiceImpl implements SmsbaoService { + + @Autowired + private SmsbaoConfig smsbaoConfig; + + @Override + public String sendSms(String mobile, String content) { + StringBuffer httpArg = new StringBuffer(); + httpArg.append("u=").append(smsbaoConfig.getUsername()).append("&"); + httpArg.append("p=").append(SmsbaoUtil.md5(smsbaoConfig.getPassword())).append("&"); + httpArg.append("m=").append(mobile).append("&"); + httpArg.append("c=").append(SmsbaoUtil.encodeUrlString(content, "UTF-8")); + + return SmsbaoUtil.request(smsbaoConfig.getHttpUrl(), httpArg.toString()); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/util/SmsbaoUtil.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/util/SmsbaoUtil.java new file mode 100644 index 0000000..b64345b --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/smsbao/util/SmsbaoUtil.java @@ -0,0 +1,76 @@ +package com.ruoyi.thirdparty.smsbao.util; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class SmsbaoUtil { + + public static String md5(String plainText) { + StringBuffer buf = null; + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(plainText.getBytes()); + byte b[] = md.digest(); + int i; + buf = new StringBuffer(""); + for (int offset = 0; offset < b.length; offset++) { + i = b[offset]; + if (i < 0) + i += 256; + if (i < 16) + buf.append("0"); + buf.append(Integer.toHexString(i)); + } + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return buf.toString(); + } + + public static String encodeUrlString(String str, String charset) { + String strret = null; + if (str == null) + return str; + try { + strret = java.net.URLEncoder.encode(str, charset); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return strret; + } + + public static String request(String httpUrl, String httpArg) { + BufferedReader reader = null; + String result = null; + StringBuffer sbf = new StringBuffer(); + httpUrl = httpUrl + "?" + httpArg; + + try { + URL url = new URL(httpUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.connect(); + InputStream is = connection.getInputStream(); + reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); + String strRead = reader.readLine(); + if (strRead != null) { + sbf.append(strRead); + while ((strRead = reader.readLine()) != null) { + sbf.append("\n"); + sbf.append(strRead); + } + } + reader.close(); + result = sbf.toString(); + } catch (Exception e) { + e.printStackTrace(); + } + return result; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/config/V5ItemConfig.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/config/V5ItemConfig.java new file mode 100644 index 0000000..299ffe3 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/config/V5ItemConfig.java @@ -0,0 +1,21 @@ +package com.ruoyi.thirdparty.v5item.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "v5item") +public class V5ItemConfig { + + private String baseUrl; + + private String merchantKey; + + private String account; + + private String password; + + private String publicKey; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/controller/V5ItemController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/controller/V5ItemController.java new file mode 100644 index 0000000..090a7a1 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/controller/V5ItemController.java @@ -0,0 +1,67 @@ +package com.ruoyi.thirdparty.v5item.controller; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.thirdparty.v5item.domain.body.CheckTradeUrlBody; +import com.ruoyi.thirdparty.v5item.domain.body.CreateOrderBody; +import com.ruoyi.thirdparty.v5item.domain.body.GetItemListBody; +import com.ruoyi.thirdparty.v5item.domain.body.QueryOnSaleInfoBody; +import com.ruoyi.thirdparty.v5item.domain.dto.GetItemListDTO; +import com.ruoyi.thirdparty.v5item.domain.result.QueryMerchantInfoResult; +import com.ruoyi.thirdparty.v5item.domain.result.V5CallBackNotifyInfo; +import com.ruoyi.thirdparty.v5item.service.V5ItemService; +import com.ruoyi.thirdparty.zbt.service.ZBTService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/admin/v5item") +public class V5ItemController extends BaseController { + + @Autowired + private V5ItemService v5ItemService; + + @GetMapping("/test") + public AjaxResult test() { + return success(); + } + + @GetMapping("/queryMerchantInfo") + public AjaxResult queryMerchantInfo() { + return success(v5ItemService.queryMerchantInfo()); + } + + @GetMapping("/checkMerchantBalance") + public AjaxResult checkMerchantBalance() { + return success(v5ItemService.checkMerchantBalance()); + } + + @PostMapping("/getItemList") + public AjaxResult getItemList(@RequestBody GetItemListDTO getItemListDTO) { + return success(v5ItemService.getItemList(getItemListDTO)); + } + + @PostMapping("/checkTradeUrl") + public AjaxResult checkTradeUrl(@RequestBody CheckTradeUrlBody checkTradeUrlBody) { + return success(v5ItemService.checkTradeUrl(checkTradeUrlBody.getTradeUrl())); + } + + /** + * 查询库存 + */ + @PostMapping("/queryOnSaleInfo") + public AjaxResult queryOnSaleInfo(@Validated @RequestBody QueryOnSaleInfoBody queryOnSaleInfoBody) { + return success(v5ItemService.queryOnSaleInfo(queryOnSaleInfoBody)); + } + + @PostMapping("/createOrder") + public AjaxResult createOrder(@RequestBody CreateOrderBody createOrderBody) { + return success(v5ItemService.createOrder(createOrderBody)); + } + + @PostMapping("/v5Notify") + public String v5Notify(@RequestBody V5CallBackNotifyInfo v5CallBackNotifyInfo) { + return v5ItemService.v5Notify(v5CallBackNotifyInfo); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/CheckMerchantBalanceBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/CheckMerchantBalanceBody.java new file mode 100644 index 0000000..b4a4997 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/CheckMerchantBalanceBody.java @@ -0,0 +1,9 @@ +package com.ruoyi.thirdparty.v5item.domain.body; + +import lombok.Data; + +@Data +public class CheckMerchantBalanceBody { + + private String merchantKey; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/CheckTradeUrlBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/CheckTradeUrlBody.java new file mode 100644 index 0000000..5c2a30f --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/CheckTradeUrlBody.java @@ -0,0 +1,11 @@ +package com.ruoyi.thirdparty.v5item.domain.body; + +import lombok.Data; + +@Data +public class CheckTradeUrlBody { + + private String merchantKey; + + private String tradeUrl; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/CreateOrderBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/CreateOrderBody.java new file mode 100644 index 0000000..80300b4 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/CreateOrderBody.java @@ -0,0 +1,17 @@ +package com.ruoyi.thirdparty.v5item.domain.body; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class CreateOrderBody { + + private String marketHashName; + + private BigDecimal purchasePrice; + + private String tradeUrl; + + private String merchantOrderNo; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/GetItemListBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/GetItemListBody.java new file mode 100644 index 0000000..1f33ae9 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/GetItemListBody.java @@ -0,0 +1,18 @@ +package com.ruoyi.thirdparty.v5item.domain.body; + +import lombok.Data; + +@Data +public class GetItemListBody { + + private String merchantKey; + + /** 页码 */ + private Integer pageIndex; + + /** 每页条数 */ + private Integer pageNum; + + /** 饰品类型 */ + private String itemType; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/QueryMerchantInfoBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/QueryMerchantInfoBody.java new file mode 100644 index 0000000..2dcba07 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/QueryMerchantInfoBody.java @@ -0,0 +1,11 @@ +package com.ruoyi.thirdparty.v5item.domain.body; + +import lombok.Data; + +@Data +public class QueryMerchantInfoBody { + + private String account; + + private String password; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/QueryOnSaleInfoBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/QueryOnSaleInfoBody.java new file mode 100644 index 0000000..b7ed87b --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/QueryOnSaleInfoBody.java @@ -0,0 +1,12 @@ +package com.ruoyi.thirdparty.v5item.domain.body; + +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Data +public class QueryOnSaleInfoBody { + + @NotNull(message = "marketHashName不能为空") + private String marketHashName; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/QueryOrderStatusBody.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/QueryOrderStatusBody.java new file mode 100644 index 0000000..17da6a4 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/body/QueryOrderStatusBody.java @@ -0,0 +1,10 @@ +package com.ruoyi.thirdparty.v5item.domain.body; + +import lombok.Data; + +@Data +public class QueryOrderStatusBody { + private String merchantKey; + private String merchantOrderNo; + private String orderNo; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/dto/CreateOrderDTO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/dto/CreateOrderDTO.java new file mode 100644 index 0000000..739f525 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/dto/CreateOrderDTO.java @@ -0,0 +1,21 @@ +package com.ruoyi.thirdparty.v5item.domain.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class CreateOrderDTO { + + private String marketHashName; + + private BigDecimal purchasePrice; + + private String tradeUrl; + + private String merchantOrderNo; + + private String merchantKey; + + private Integer buyType; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/dto/GetItemListDTO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/dto/GetItemListDTO.java new file mode 100644 index 0000000..ba73e22 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/dto/GetItemListDTO.java @@ -0,0 +1,16 @@ +package com.ruoyi.thirdparty.v5item.domain.dto; + +import lombok.Data; + +@Data +public class GetItemListDTO { + + /** 页码 */ + private Integer pageIndex; + + /** 每页条数 */ + private Integer pageNum; + + /** 饰品类型 */ + private String itemType; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/dto/ReceiverInfoDTO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/dto/ReceiverInfoDTO.java new file mode 100644 index 0000000..1522c52 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/dto/ReceiverInfoDTO.java @@ -0,0 +1,16 @@ +package com.ruoyi.thirdparty.v5item.domain.dto; + +import lombok.Data; + +/** + * 收货人信息 + */ +@Data +public class ReceiverInfoDTO { + + private String headPicture; + + private String steamCreateDate; + + private String nickname; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/CheckMerchantBalanceResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/CheckMerchantBalanceResult.java new file mode 100644 index 0000000..8b5ba26 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/CheckMerchantBalanceResult.java @@ -0,0 +1,15 @@ +package com.ruoyi.thirdparty.v5item.domain.result; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class CheckMerchantBalanceResult { + + private BigDecimal balance; + + private String merchantName; + + private String account; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/CheckTradeUrlResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/CheckTradeUrlResult.java new file mode 100644 index 0000000..2adced0 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/CheckTradeUrlResult.java @@ -0,0 +1,17 @@ +package com.ruoyi.thirdparty.v5item.domain.result; + +import lombok.Data; + +@Data +public class CheckTradeUrlResult { + + private String steamId; + + private String steamCreateDate; + + private Integer status; + + private String statusMsg; + + private String steamName; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/CreateOrderResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/CreateOrderResult.java new file mode 100644 index 0000000..15d2301 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/CreateOrderResult.java @@ -0,0 +1,18 @@ +package com.ruoyi.thirdparty.v5item.domain.result; + +import com.ruoyi.thirdparty.v5item.domain.dto.ReceiverInfoDTO; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class CreateOrderResult { + + private BigDecimal payAmount; + + private String merchantOrderNo; + + private String orderNo; + + private ReceiverInfoDTO receiverInfoDTO; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/GetItemListResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/GetItemListResult.java new file mode 100644 index 0000000..c0118b4 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/GetItemListResult.java @@ -0,0 +1,37 @@ +package com.ruoyi.thirdparty.v5item.domain.result; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class GetItemListResult { + + private String marketHashName; + + private String itemName; + + private String shortItemName; + + private String itemType; + + private String itemTypeName; + + private String itemRarity; + + private String itemRarityName; + + private String itemExterior; + + private String itemExteriorName; + + private String itemQuality; + + private String itemQualityName; + + private Integer onSaleStock; + + private BigDecimal minSellPrice; + + private String itemIcon; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/QueryMerchantInfoResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/QueryMerchantInfoResult.java new file mode 100644 index 0000000..02a345d --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/QueryMerchantInfoResult.java @@ -0,0 +1,11 @@ +package com.ruoyi.thirdparty.v5item.domain.result; + +import lombok.Data; + +@Data +public class QueryMerchantInfoResult { + + private String tradeToken; + + private String account; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/QueryOnSaleInfoResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/QueryOnSaleInfoResult.java new file mode 100644 index 0000000..78c87e2 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/QueryOnSaleInfoResult.java @@ -0,0 +1,43 @@ +package com.ruoyi.thirdparty.v5item.domain.result; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class QueryOnSaleInfoResult { + + private String marketHashName; + + private String itemName; + + private String shortItemName; + + private String itemType; + + private String itemTypeName; + + private String itemRarity; + + private String itemRarityName; + + private String itemExterior; + + private String itemExteriorName; + + private String itemQuality; + + private String itemQualityName; + + private Integer onSaleStock; + + private BigDecimal minSellPrice; + + private String itemIcon; + + private BigDecimal selfPrice; + + private BigDecimal playerPrice; + + private BigDecimal consultPrice; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/QueryOrderStatusResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/QueryOrderStatusResult.java new file mode 100644 index 0000000..c52830b --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/QueryOrderStatusResult.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.v5item.domain.result; + +import lombok.Data; + +@Data +public class QueryOrderStatusResult { + private String merchantOrderNo; + private String orderNo; + private Integer orderAmount; + private Integer status; + private String statusMsg; + private Integer deliverStatus; + private String deliverMsg; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/V5CallBackInfo.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/V5CallBackInfo.java new file mode 100644 index 0000000..7c12513 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/V5CallBackInfo.java @@ -0,0 +1,15 @@ +package com.ruoyi.thirdparty.v5item.domain.result; + +import lombok.Data; + +@Data +public class V5CallBackInfo { + private String orderNo; + private String merchantNo; + private Integer userId; + private Integer orderStatus; + private String orderStatusDesc; + private Integer deliverStatus; + private String deliverStatusDesc; + private String failReason; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/V5CallBackNotifyInfo.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/V5CallBackNotifyInfo.java new file mode 100644 index 0000000..45dc67b --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/V5CallBackNotifyInfo.java @@ -0,0 +1,10 @@ +package com.ruoyi.thirdparty.v5item.domain.result; + +import lombok.Data; + +@Data +public class V5CallBackNotifyInfo { + private String notifyMsgNo; + private String callBackInfo; + private String sign; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/V5ItemResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/V5ItemResult.java new file mode 100644 index 0000000..5e82dba --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/V5ItemResult.java @@ -0,0 +1,13 @@ +package com.ruoyi.thirdparty.v5item.domain.result; + +import lombok.Data; + +@Data +public class V5ItemResult { + + private Integer code; + + private String msg; + + private T data; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/V5ItemResultContainTotal.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/V5ItemResultContainTotal.java new file mode 100644 index 0000000..f5a36a7 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/domain/result/V5ItemResultContainTotal.java @@ -0,0 +1,15 @@ +package com.ruoyi.thirdparty.v5item.domain.result; + +import lombok.Data; + +@Data +public class V5ItemResultContainTotal { + + private Integer code; + + private String msg; + + private T data; + + private Integer totalCount; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/service/V5ItemService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/service/V5ItemService.java new file mode 100644 index 0000000..d12930c --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/service/V5ItemService.java @@ -0,0 +1,28 @@ +package com.ruoyi.thirdparty.v5item.service; + +import com.ruoyi.thirdparty.v5item.domain.body.CreateOrderBody; +import com.ruoyi.thirdparty.v5item.domain.body.GetItemListBody; +import com.ruoyi.thirdparty.v5item.domain.body.QueryOnSaleInfoBody; +import com.ruoyi.thirdparty.v5item.domain.dto.GetItemListDTO; +import com.ruoyi.thirdparty.v5item.domain.result.*; + +import java.util.List; + +public interface V5ItemService { + + public V5ItemResult queryMerchantInfo(); + + public V5ItemResult checkMerchantBalance(); + + public V5ItemResultContainTotal> getItemList(GetItemListDTO getItemListDTO); + + public V5ItemResult checkTradeUrl(String tradeUrl); + + public QueryOnSaleInfoResult queryOnSaleInfo(QueryOnSaleInfoBody queryOnSaleInfoBody); + + public V5ItemResult createOrder(CreateOrderBody createOrderBody); + + String v5Notify(V5CallBackNotifyInfo v5CallBackNotifyInfo); + + QueryOrderStatusResult queryOrderStatus(String merchantOrderNo, String orderNo); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/service/impl/V5ItemServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/service/impl/V5ItemServiceImpl.java new file mode 100644 index 0000000..de91eb0 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/service/impl/V5ItemServiceImpl.java @@ -0,0 +1,362 @@ +package com.ruoyi.thirdparty.v5item.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ruoyi.admin.mapper.TtDeliveryRecordMapper; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.http.HttpUtils; +import com.ruoyi.domain.common.constant.DeliveryOrderStatus; +import com.ruoyi.domain.entity.delivery.TtDeliveryRecord; +import com.ruoyi.thirdparty.v5item.config.V5ItemConfig; +import com.ruoyi.thirdparty.v5item.domain.body.*; +import com.ruoyi.thirdparty.v5item.domain.dto.CreateOrderDTO; +import com.ruoyi.thirdparty.v5item.domain.dto.GetItemListDTO; +import com.ruoyi.thirdparty.v5item.domain.result.*; +import com.ruoyi.thirdparty.v5item.service.V5ItemService; +import com.ruoyi.thirdparty.yyyouping.utils.common.RSAUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +@Service +public class V5ItemServiceImpl implements V5ItemService { + + private static final Logger log = LoggerFactory.getLogger(V5ItemServiceImpl.class); + + private static final String V5ITEM_TOKEN_KEY = "v5item_token"; + + @Autowired + private V5ItemConfig v5ItemConfig; + + @Autowired + private RedisCache redisCache; + + @Autowired + private TtDeliveryRecordMapper deliveryRecordMapper; + + @Override + public V5ItemResult queryMerchantInfo() { + String url = v5ItemConfig.getBaseUrl() + "/open/api/queryMerchantInfo"; + QueryMerchantInfoBody queryMerchantInfoBody = new QueryMerchantInfoBody(); + queryMerchantInfoBody.setAccount(v5ItemConfig.getAccount()); + queryMerchantInfoBody.setPassword(v5ItemConfig.getPassword()); + + String result = HttpUtils.sendPostJSONString(url, JSON.toJSONString(queryMerchantInfoBody)); + + // 定义V5ItemResult的泛型类型 + TypeReference> typeRef = + new TypeReference>() {}; + // 使用FastJson将JSON字符串转换为V5ItemResult对象 + V5ItemResult v5ItemResult = JSONObject.parseObject(result, typeRef); + if (v5ItemResult.getCode() != 0) + throw new RuntimeException("查询商户信息失败"); + + // 将v5itemToken存入redis + String token = v5ItemResult.getData().getTradeToken(); + redisCache.setCacheObject(V5ITEM_TOKEN_KEY, token, 10, TimeUnit.MINUTES); + + return v5ItemResult; + } + + @Override + public V5ItemResult checkMerchantBalance() { + String url = v5ItemConfig.getBaseUrl() + "/open/api/checkMerchantBalance"; + + // 查询v5itemToken + String v5itemToken = redisCache.getCacheObject(V5ITEM_TOKEN_KEY); + // 处理redis中不存在v5itemToken的情况 + if (Objects.isNull(v5itemToken)) { + queryMerchantInfo(); + v5itemToken = redisCache.getCacheObject(V5ITEM_TOKEN_KEY); + } + + // 构建请求头 + Map headers = new HashMap<>(); + headers.put("Authorization", v5itemToken); + + CheckMerchantBalanceBody checkMerchantBalanceBody = new CheckMerchantBalanceBody(); + checkMerchantBalanceBody.setMerchantKey(v5ItemConfig.getMerchantKey()); + String result = HttpUtils.sendPostJSONString(url, JSON.toJSONString(checkMerchantBalanceBody), headers); + + // 定义V5ItemResult的泛型类型 + TypeReference> typeRef = + new TypeReference>() {}; + // 使用FastJson将JSON字符串转换为V5ItemResult对象 + V5ItemResult v5ItemResult = JSONObject.parseObject(result, typeRef); + + // 处理token过期情况 + if (v5ItemResult.getCode() == 401) { + redisCache.deleteObject(V5ITEM_TOKEN_KEY); + return checkMerchantBalance(); + } + + return v5ItemResult; + } + + @Override + public V5ItemResultContainTotal> getItemList(GetItemListDTO getItemListDTO) { + String url = v5ItemConfig.getBaseUrl() + "/open/api/getItemList"; + + // 查询v5itemToken + String v5itemToken = redisCache.getCacheObject(V5ITEM_TOKEN_KEY); + // 处理redis中不存在v5itemToken的情况 + if (Objects.isNull(v5itemToken)) { + queryMerchantInfo(); + v5itemToken = redisCache.getCacheObject(V5ITEM_TOKEN_KEY); + } + + // 构建请求头 + Map headers = new HashMap<>(); + headers.put("Authorization", v5itemToken); + + GetItemListBody getItemListBody = new GetItemListBody(); + getItemListBody.setMerchantKey(v5ItemConfig.getMerchantKey()); + BeanUtils.copyProperties(getItemListDTO, getItemListBody); + String result = HttpUtils.sendPostJSONString(url, JSON.toJSONString(getItemListBody), headers); + + // 定义V5ItemResult的泛型类型 + TypeReference>> typeRef = + new TypeReference>>() {}; + // 使用FastJson将JSON字符串转换为V5ItemResult对象 + V5ItemResultContainTotal> v5ItemResult = JSONObject.parseObject(result, typeRef); + + // 处理token过期情况 + if (v5ItemResult.getCode() == 401) { + redisCache.deleteObject(V5ITEM_TOKEN_KEY); + return getItemList(getItemListDTO); + } + + return v5ItemResult; + } + + @Override + public V5ItemResult checkTradeUrl(String tradeUrl) { + String url = v5ItemConfig.getBaseUrl() + "/open/api/checkTradeUrl"; + + // 查询v5itemToken + String v5itemToken = redisCache.getCacheObject(V5ITEM_TOKEN_KEY); + // 处理redis中不存在v5itemToken的情况 + if (Objects.isNull(v5itemToken)) { + queryMerchantInfo(); + v5itemToken = redisCache.getCacheObject(V5ITEM_TOKEN_KEY); + } + + // 构建请求头 + Map headers = new HashMap<>(); + headers.put("Authorization", v5itemToken); + + CheckTradeUrlBody checkTradeUrlBody = new CheckTradeUrlBody(); + checkTradeUrlBody.setMerchantKey(v5ItemConfig.getMerchantKey()); + checkTradeUrlBody.setTradeUrl(tradeUrl); + String result = HttpUtils.sendPostJSONString(url, JSON.toJSONString(checkTradeUrlBody), headers); + + // 定义V5ItemResult的泛型类型 + TypeReference> typeRef = + new TypeReference>() {}; + // 使用FastJson将JSON字符串转换为V5ItemResult对象 + V5ItemResult v5ItemResult = JSONObject.parseObject(result, typeRef); + + // 处理token过期情况 + if (v5ItemResult.getCode() == 401) { + redisCache.deleteObject(V5ITEM_TOKEN_KEY); + return checkTradeUrl(tradeUrl); + } + + return v5ItemResult; + } + + @Override + public QueryOnSaleInfoResult queryOnSaleInfo(QueryOnSaleInfoBody queryOnSaleInfoBody) { + String url = v5ItemConfig.getBaseUrl() + "/open/api/queryOnSaleInfo"; + + // 查询v5itemToken + String v5itemToken = redisCache.getCacheObject(V5ITEM_TOKEN_KEY); + // 处理redis中不存在v5itemToken的情况 + if (Objects.isNull(v5itemToken)) { + queryMerchantInfo(); + v5itemToken = redisCache.getCacheObject(V5ITEM_TOKEN_KEY); + } + + // 构建请求头 + Map headers = new HashMap<>(); + headers.put("Authorization", v5itemToken); + + List marketHashNameList = new ArrayList<>(); + marketHashNameList.add(queryOnSaleInfoBody.getMarketHashName()); + Map bodyMap = new HashMap<>(); + bodyMap.put("templateHashNameList", marketHashNameList); + bodyMap.put("merchantKey", v5ItemConfig.getMerchantKey()); + String result = HttpUtils.sendPostJSONString(url, JSON.toJSONString(bodyMap), headers); + + // 将JSON转换为对象 + // 定义V5ItemResult的泛型类型 + TypeReference>> typeRef = + new TypeReference>>() {}; + // 使用FastJson将JSON字符串转换为V5ItemResult对象 + V5ItemResult> v5ItemResult = JSONObject.parseObject(result, typeRef); + + // 处理token过期情况 + if (v5ItemResult.getCode() == 401) { + redisCache.deleteObject(V5ITEM_TOKEN_KEY); + return queryOnSaleInfo(queryOnSaleInfoBody); + } + + if (!Objects.isNull(v5ItemResult.getData()) && v5ItemResult.getData().size() > 0) { + return v5ItemResult.getData().get(0); + } + return null; + } + + @Override + public V5ItemResult createOrder(CreateOrderBody createOrderBody) { + String url = v5ItemConfig.getBaseUrl() + "/open/api/createOrderByMarketHashName"; + + // 查询v5itemToken + String v5itemToken = redisCache.getCacheObject(V5ITEM_TOKEN_KEY); + // 处理redis中不存在v5itemToken的情况 + if (Objects.isNull(v5itemToken)) { + queryMerchantInfo(); + v5itemToken = redisCache.getCacheObject(V5ITEM_TOKEN_KEY); + } + + // 构建请求头 + Map headers = new HashMap<>(); + headers.put("Authorization", v5itemToken); + + CreateOrderDTO createOrderDTO = new CreateOrderDTO(); + BeanUtils.copyProperties(createOrderBody, createOrderDTO); + createOrderDTO.setMerchantKey(v5ItemConfig.getMerchantKey()); + createOrderDTO.setBuyType(0); + String result = HttpUtils.sendPostJSONString(url, JSON.toJSONString(createOrderDTO), headers); + + // 将JSON转换为对象 + // 定义V5ItemResult的泛型类型 + TypeReference> typeRef = + new TypeReference>() {}; + // 使用FastJson将JSON字符串转换为V5ItemResult对象 + V5ItemResult v5ItemResult = JSONObject.parseObject(result, typeRef); + + // 处理token过期情况 + if (v5ItemResult.getCode() == 401) { + redisCache.deleteObject(V5ITEM_TOKEN_KEY); + return createOrder(createOrderBody); + } + + return v5ItemResult; + } + + @Override + public String v5Notify(V5CallBackNotifyInfo v5CallBackNotifyInfo) { + log.info("收到v5订单更改回调通知。入参:{}", v5CallBackNotifyInfo.toString()); + String callBackInfo = v5CallBackNotifyInfo.getCallBackInfo(); + String notifyMsgNo = v5CallBackNotifyInfo.getNotifyMsgNo(); + String sign = v5CallBackNotifyInfo.getSign(); + Map params = new HashMap<>(); + params.put("notifyMsgNo", notifyMsgNo); + params.put("callBackInfo", callBackInfo); + // 第一步:检查参数是否已经排序 + String[] keys = params.keySet().toArray(new String[0]); + Arrays.sort(keys); + // 第二步:把所有参数名和参数值串在一起 + StringBuilder stringBuilder = new StringBuilder(); + for (String key : keys) { + Object value = params.get(key); + if (value != null) { + stringBuilder.append(key).append(value); + } + } + log.info("stringBuilder:{}",stringBuilder); + boolean b = false; + try { + b = RSAUtils.verifyByPublicKey(stringBuilder.toString().getBytes(), v5ItemConfig.getPublicKey(), sign); + } catch (Exception e) { + log.error("v5Notify happen excepiton is {}", e.getMessage(), e); + } + if (!b) { + log.info("b is :{}, v5ItemConfig.getMerchantKey() is {}", b, v5ItemConfig.getPublicKey()); + return "failed"; + } + ObjectMapper objectMapper = new ObjectMapper(); + V5CallBackInfo info = objectMapper.convertValue(callBackInfo, V5CallBackInfo.class); + // 获取提货订单数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TtDeliveryRecord::getOrderId, info.getOrderNo()); + TtDeliveryRecord ttDeliveryRecord = deliveryRecordMapper.selectOne(queryWrapper); + if (Objects.equals(info.getOrderStatus(), 2)) { + ttDeliveryRecord.setStatus(DeliveryOrderStatus.DELIVERY_AFTER.getCode()); + ttDeliveryRecord.setMessage(DeliveryOrderStatus.DELIVERY_AFTER.getMsg()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + deliveryRecordMapper.updateById(ttDeliveryRecord); + } else if (Objects.equals(info.getOrderStatus(), 3)) { + ttDeliveryRecord.setStatus(DeliveryOrderStatus.ORDER_COMPLETE.getCode()); + ttDeliveryRecord.setMessage(DeliveryOrderStatus.ORDER_COMPLETE.getMsg()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + deliveryRecordMapper.updateById(ttDeliveryRecord); + } else if (Objects.equals(info.getOrderStatus(), 4)) { + ttDeliveryRecord.setStatus(DeliveryOrderStatus.ORDER_CANCEL.getCode()); + ttDeliveryRecord.setMessage(DeliveryOrderStatus.ORDER_CANCEL.getMsg()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + deliveryRecordMapper.updateById(ttDeliveryRecord); + } + log.info("验签结果:{}", b); + return"success"; + } + + @Override + public QueryOrderStatusResult queryOrderStatus(String merchantOrderNo, String orderNo) { + String url = v5ItemConfig.getBaseUrl() + "/open/api/queryOrderStatus"; + QueryOrderStatusBody body = new QueryOrderStatusBody(); + body.setMerchantKey(v5ItemConfig.getMerchantKey()); + body.setMerchantOrderNo(merchantOrderNo); + body.setOrderNo(orderNo); + // 查询v5itemToken + String v5itemToken = redisCache.getCacheObject(V5ITEM_TOKEN_KEY); + // 处理redis中不存在v5itemToken的情况 + if (Objects.isNull(v5itemToken)) { + queryMerchantInfo(); + v5itemToken = redisCache.getCacheObject(V5ITEM_TOKEN_KEY); + } + + // 构建请求头 + Map headers = new HashMap<>(); + headers.put("Authorization", v5itemToken); + + String postBody = JSON.toJSONString(body); + log.info("query order status body is {}", postBody); + String result = HttpUtils.sendPostJSONString(url, postBody, headers); + log.info("query order status result is {}", result); + if (StringUtils.isBlank(result)) { + return null; + } + // 将JSON转换为对象 + // 定义V5ItemResult的泛型类型 + TypeReference typeRef = + new TypeReference() {}; + // 使用FastJson将JSON字符串转换为V5ItemResult对象 + return JSONObject.parseObject(result, typeRef); + } + + private String postMethod(Map param){ + StringBuilder params = new StringBuilder(); + for (Map.Entry entry : param.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + params.append(key); + params.append("="); + params.append(value); + params.append("&"); + } + params.trimToSize(); + return params.substring(0, params.length() - 1); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/task/V5ItemPullTask.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/task/V5ItemPullTask.java new file mode 100644 index 0000000..b10f4a4 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/v5item/task/V5ItemPullTask.java @@ -0,0 +1,146 @@ +package com.ruoyi.thirdparty.v5item.task; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.v5item.domain.dto.GetItemListDTO; +import com.ruoyi.thirdparty.v5item.domain.result.GetItemListResult; +import com.ruoyi.thirdparty.v5item.domain.result.V5ItemResultContainTotal; +import com.ruoyi.thirdparty.v5item.service.V5ItemService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@Component("V5ItemPullTask") +public class V5ItemPullTask { + + @Autowired + private TtOrnamentService ornamentsService; + + @Autowired + private V5ItemService v5ItemService; + + @Autowired + private ISysConfigService configService; + + // @Scheduled(cron = "0 0 */3 * * ?") + public void getOrnamentsData() { + log.info("更新数据开始"); + String usePricePremiumRateStr = configService.selectConfigByKey("usePricePremiumRate"); + BigDecimal usePricePremiumRate = new BigDecimal(usePricePremiumRateStr); + + String ZBTParitiesStr = configService.selectConfigByKey("ZBTParities"); + BigDecimal parities = new BigDecimal(ZBTParitiesStr); + + // 查询数据库 + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + // wrapper.eq(TtOrnament::getIsProprietaryProperty, "1"); + List ttOrnamentList = ornamentsService.list(wrapper); + int i = 1; + while (true) { + List updateList = new ArrayList<>(); + // 获取初次外部数据 + GetItemListDTO getItemListDTO = new GetItemListDTO(); + getItemListDTO.setPageIndex(i); + getItemListDTO.setPageNum(500); + V5ItemResultContainTotal> v5ItemResult = v5ItemService.getItemList(getItemListDTO); + List getItemListResult = v5ItemResult.getData(); + if (CollectionUtils.isEmpty(getItemListResult)) { + break; + } + i++; + // 比对更新数据 + for (GetItemListResult itemListResult : getItemListResult) { + for (TtOrnament ttOrnament : ttOrnamentList) { + if (itemListResult.getMarketHashName().equals(ttOrnament.getMarketHashName())) { + if (!ttOrnament.getPrice().equals(itemListResult.getMinSellPrice()) || + !ttOrnament.getQuantity().equals(itemListResult.getOnSaleStock())) { + // 加入到更新队列 + BigDecimal price = itemListResult.getMinSellPrice(); + BigDecimal userPrice = price.multiply(parities).multiply(usePricePremiumRate); + if (userPrice.compareTo(new BigDecimal("99999999.99")) > 0) + continue; + ttOrnament.setPrice(price); + ttOrnament.setQuantity(itemListResult.getOnSaleStock()); + ttOrnament.setUsePrice(userPrice); + ttOrnament.setUpdateTime(DateUtils.getNowDate()); + updateList.add(ttOrnament); + } + } + } + } + if (CollectionUtils.isNotEmpty(updateList)) { + batchUpdateOrnaments(updateList); + } + } + + /*// 获取剩余外部数据 + Integer total = v5ItemResult.getTotalCount(); + int pageSize = 500; + int pageNumCount = (total % pageSize == 0) ? total / pageSize : total / pageSize + 1; + for (int i = 2; i <= pageNumCount; i++) { + getItemListDTO.setPageIndex(i); + if (i == pageNumCount) + getItemListDTO.setPageNum(total % pageSize); + v5ItemResult = v5ItemService.getItemList(getItemListDTO); + getItemListResult = v5ItemResult.getData(); + // 比对更新数据 + updateList = new ArrayList<>(); // 每请求一批数据清空更新列表 + for (GetItemListResult itemListResult : getItemListResult) { + for (TtOrnament ttOrnament : ttOrnamentList) { + if (itemListResult.getMarketHashName().equals(ttOrnament.getMarketHashName())) { + if (!ttOrnament.getPrice().equals(itemListResult.getMinSellPrice()) || + !ttOrnament.getQuantity().equals(itemListResult.getOnSaleStock())) { + // 加入到更新队列 + BigDecimal price = itemListResult.getMinSellPrice(); + BigDecimal userPrice = price.multiply(parities).multiply(usePricePremiumRate); + if (userPrice.compareTo(new BigDecimal("99999999.99")) > 0) + continue; + ttOrnament.setPrice(price); + ttOrnament.setQuantity(itemListResult.getOnSaleStock()); + ttOrnament.setUsePrice(userPrice); + ttOrnament.setUpdateTime(DateUtils.getNowDate()); + updateList.add(ttOrnament); + } + } + } + } + batchUpdateOrnaments(updateList); + }*/ + + log.info("更新数据完成"); + } + + private void batchInsertOrnaments() { + } + + private void batchUpdateOrnaments(List updateList) { + ornamentsService.updateBatchById(updateList, updateList.size()); + log.info("更新{}条", updateList.size()); + } + + private void batchSoldOutOrnaments() { + } + + private Map objectToMap(List list) { + Map map = new HashMap<>(); + for (SysDictData sysDictData : list) { + map.put(sysDictData.getDictLabel(), sysDictData.getDictValue()); + } + return map; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/config/TianXinProperties.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/config/TianXinProperties.java new file mode 100644 index 0000000..7b476ad --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/config/TianXinProperties.java @@ -0,0 +1,18 @@ +package com.ruoyi.thirdparty.wechat.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Data +@ConfigurationProperties(prefix = "tianxin") +public class TianXinProperties { + + private String version; // 版本号 + private String customerid; // 商户编号 + private String paytype; // 支付编号 + private String notifyurl; // 异步通知URL + private String returnurl; // 同步通知URL + private String access_type; // 接入方式 + private String userkey; // 商户Key + private String apiurl; // 请求地址 +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/controller/TianXinController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/controller/TianXinController.java new file mode 100644 index 0000000..21036a3 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/controller/TianXinController.java @@ -0,0 +1,74 @@ +package com.ruoyi.thirdparty.wechat.controller; + +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.UserPermission; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.thirdparty.wechat.domain.PayOrderParam; +import com.ruoyi.thirdparty.wechat.service.ApiTokenService; +import com.ruoyi.thirdparty.wechat.service.TianXinService; +import com.ruoyi.thirdparty.wechat.utils.GetIpUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.servlet.http.HttpServletRequest; + +import static com.ruoyi.common.utils.SecurityUtils.getUserId; + +@Api(tags = "田心支付") +@RestController +@RequestMapping("/api/tianxinOrder") +public class TianXinController { + + private final ApiTokenService tokenService; + private final TianXinService tianXinService; + private final TtUserService ttUserService; + + public TianXinController(ApiTokenService tokenService, + TianXinService tianXinService, + TtUserService ttUserService) { + this.tokenService = tokenService; + this.tianXinService = tianXinService; + this.ttUserService = ttUserService; + } + + @ApiOperation("预下单") + @UserPermission + @PostMapping("/createOrder") + public AjaxResult createOrder(@RequestBody PayOrderParam param, HttpServletRequest request) { + + // 判断是否开启田心支付 + // if (!("true".equals(sysConfigService.selectConfigByKey("tianxinpay")))) { + // return AjaxResult.error("当前系统没有开启田心支付!"); + // } + + return AjaxResult.success("尚未开放。"); + + // TtUser ttUser = ttUserService.getById(getUserId()); + // // 是否实名认证0未认证,1已认证' + // if (ttUser.getIsRealCheck().equals("0")) { + // return AjaxResult.error("请实名认证后再进行充值!"); + // } + // + // String ip = GetIpUtil.getIpAddress(request); + // return tianXinService.createOrder(param, ttUser, ip); + } + + @ApiOperation("查询订单状态") + @UserPermission + @PostMapping(value = "/queryOrderStatus") + public AjaxResult queryOrderStatus(String sdorderno) { + return tianXinService.queryOrderStatus(sdorderno); + } + + @ApiOperation("回调") + @RequestMapping(value = "/callBack") + public String returnCallBack(HttpServletRequest request) { + return tianXinService.callBack(request); + } + +} \ No newline at end of file diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/ApiLoginUser.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/ApiLoginUser.java new file mode 100644 index 0000000..d29d75c --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/ApiLoginUser.java @@ -0,0 +1,237 @@ +package com.ruoyi.thirdparty.wechat.domain; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.ruoyi.domain.entity.sys.TtUser; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author ruoyi + */ +public class ApiLoginUser implements UserDetails { + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 用户信息 + */ + private TtUser user; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getDeptId() { + return deptId; + } + + public void setDeptId(Long deptId) { + this.deptId = deptId; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public ApiLoginUser() { + } + + public ApiLoginUser(TtUser user, Set permissions) { + this.user = user; + this.permissions = permissions; + } + + public ApiLoginUser(Long userId, Long deptId, TtUser user, Set permissions) { + this.userId = userId; + this.deptId = deptId; + this.user = user; + this.permissions = permissions; + } + + @JSONField(serialize = false) + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public String getUsername() { + return user.getUserName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonExpired() { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonLocked() { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isEnabled() { + return true; + } + + public Long getLoginTime() { + return loginTime; + } + + public void setLoginTime(Long loginTime) { + this.loginTime = loginTime; + } + + public String getIpaddr() { + return ipaddr; + } + + public void setIpaddr(String ipaddr) { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) { + this.loginLocation = loginLocation; + } + + public String getBrowser() { + return browser; + } + + public void setBrowser(String browser) { + this.browser = browser; + } + + public String getOs() { + return os; + } + + public void setOs(String os) { + this.os = os; + } + + public Long getExpireTime() { + return expireTime; + } + + public void setExpireTime(Long expireTime) { + this.expireTime = expireTime; + } + + public Set getPermissions() { + return permissions; + } + + public void setPermissions(Set permissions) { + this.permissions = permissions; + } + + public TtUser getUser() { + return user; + } + + public void setUser(TtUser user) { + this.user = user; + this.userId = Long.parseLong(user.getUserId() + ""); + + } + + @Override + public Collection getAuthorities() { + return Collections.emptyList(); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/PayOrderParam.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/PayOrderParam.java new file mode 100644 index 0000000..614ab71 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/PayOrderParam.java @@ -0,0 +1,33 @@ +package com.ruoyi.thirdparty.wechat.domain; + +import java.math.BigDecimal; + +public class PayOrderParam { + private Integer coinItemId;//商户商品id + private Integer coinItemNum; //商户商品数量 + private BigDecimal coinItemAmount; //商户商品价格 + + public Integer getCoinItemId() { + return coinItemId; + } + + public void setCoinItemId(Integer coinItemId) { + this.coinItemId = coinItemId; + } + + public Integer getCoinItemNum() { + return coinItemNum; + } + + public void setCoinItemNum(Integer coinItemNum) { + this.coinItemNum = coinItemNum; + } + + public BigDecimal getCoinItemAmount() { + return coinItemAmount; + } + + public void setCoinItemAmount(BigDecimal coinItemAmount) { + this.coinItemAmount = coinItemAmount; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/PtLoginUser.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/PtLoginUser.java new file mode 100644 index 0000000..ba64cd9 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/PtLoginUser.java @@ -0,0 +1,277 @@ +package com.ruoyi.thirdparty.wechat.domain; + +import com.alibaba.fastjson2.annotation.JSONField; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author ruoyi + */ +public class PtLoginUser implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 平台用户信息 + */ + private PtUserVO user; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + public PtLoginUser() + { + } + + + public PtLoginUser(PtUserVO user, Set permissions) + { + this.user = user; + this.permissions = permissions; + } + + public PtLoginUser(Long userId, Long deptId, PtUserVO user, Set permissions) + { + this.userId = userId; + this.deptId = deptId; + this.user = user; + this.permissions = permissions; + } + + public PtLoginUser(Long userId, Long deptId, PtUserVO user) + { + this.userId = userId; + this.deptId = deptId; + this.user = user; + } + + @JSONField(serialize = false) + @Override + public String getPassword() + { + return user.getPassword(); + } + + @Override + public String getUsername() + { + return user.getName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isEnabled() + { + return true; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + public PtUserVO getUser() + { + return user; + } + + public void setUser(PtUserVO user) + { + this.user = user; + this.userId = user.getId(); + + } + + @Override + public Collection getAuthorities() + { + return Collections.emptyList(); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/PtUserVO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/PtUserVO.java new file mode 100644 index 0000000..d04278d --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/PtUserVO.java @@ -0,0 +1,576 @@ +package com.ruoyi.thirdparty.wechat.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 平台用户信息管理对象 tt_user + * + * @author junhai + * @date 2023-08-12 + */ +public class PtUserVO extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** $column.columnComment */ + private Long id; + + /** steam id */ + @Excel(name = "steam id") + private Long steamid; + + /** 用户名称 */ + @Excel(name = "用户名称") + private String name; + + /** 用户头像 */ + @Excel(name = "用户头像") + private String portrait; + + /** 手机号 */ + @Excel(name = "手机号") + private String phone; + + /** 登录密码 */ + @Excel(name = "登录密码") + private String password; + + /** 登录密码错误次数(最多四次) */ + @Excel(name = "登录密码错误次数(最多四次)") + private Integer passwordError; + + /** 邮箱账号 */ + @Excel(name = "邮箱账号") + private String email; + + /** 用户余额 */ + @Excel(name = "用户余额") + private BigDecimal bean; + + /** 商城余额 */ + @Excel(name = "商城余额") + private BigDecimal storeBean; + + /** 上级id */ + @Excel(name = "上级id") + private Long superiorId; + + /** 邀请码 */ + @Excel(name = "邀请码") + private String invitationCode; + + /** 交易链接 */ + @Excel(name = "交易链接") + private String transactionLink; + + /** 用户类型 1普通用户 2主播 3推广 4官方机器人 */ + @Excel(name = "用户类型 1普通用户 2主播 3推广 4官方机器人") + private Integer type; + + /** 推广等级 */ + @Excel(name = "推广等级") + private Long spreadGrade; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private BigDecimal spreadAmount; + + /** 爆率等级 */ + @Excel(name = "爆率等级") + private Long winningGrade; + + /** 当前爆率是否增加概率 */ + @Excel(name = "当前爆率是否增加概率") + private Integer winningAdd; + + /** 上次爆率等级结束时间 */ + @Excel(name = "上次爆率等级结束时间") + private Long winningTime; + + /** 充值是否可更改爆率等级 1是 0否 */ + @Excel(name = "充值是否可更改爆率等级 1是 0否") + private Integer winningStatus; + + /** 加入时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "加入时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date time; + + /** 最后登录时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date endTime; + + /** vip到期时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "vip到期时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date vipTime; + + /** vip服务 1独特的优质会员标识 2私人资料 3免费赠品 4优质会员箱子 */ + @Excel(name = "vip服务 1独特的优质会员标识 2私人资料 3免费赠品 4优质会员箱子") + private String vipService; + + /** ip地址 */ + @Excel(name = "ip地址") + private String ip; + + /** 登录状态 1正常 0禁止 */ + @Excel(name = "登录状态 1正常 0禁止") + private Integer status; + + /** 充值提货状态 1正常 0禁止 */ + @Excel(name = "充值提货状态 1正常 0禁止") + private Integer status2; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private Integer rewardNewUser; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private Long lotteryTimes; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private BigDecimal shipmentPrice; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private BigDecimal rechargeHour; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private BigDecimal recharge12hour; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private BigDecimal recharge24hour; + + /** 最后登录IP */ + @Excel(name = "最后登录IP") + private String loginIp; + + /** 最后登录时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date loginDate; + + /** 删除标志(0代表存在 2代表删除) */ + private Long delFlag; + + /** 真实姓名 */ + @Excel(name = "真实姓名") + private String realName; + + /** 身份证号码 */ + @Excel(name = "身份证号码") + private String idNumber; + + /** 是否实名认证0未认证,1已认证 */ + @Excel(name = "是否实名认证0未认证,1已认证") + private Long isRealCheck; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setSteamid(Long steamid) + { + this.steamid = steamid; + } + + public Long getSteamid() + { + return steamid; + } + public void setName(String name) + { + this.name = name; + } + + public String getName() + { + return name; + } + public void setPortrait(String portrait) + { + this.portrait = portrait; + } + + public String getPortrait() + { + return portrait; + } + public void setPhone(String phone) + { + this.phone = phone; + } + + public String getPhone() + { + return phone; + } + public void setPassword(String password) + { + this.password = password; + } + + public String getPassword() + { + return password; + } + public void setPasswordError(Integer passwordError) + { + this.passwordError = passwordError; + } + + public Integer getPasswordError() + { + return passwordError; + } + public void setEmail(String email) + { + this.email = email; + } + + public String getEmail() + { + return email; + } + public void setBean(BigDecimal bean) + { + this.bean = bean; + } + + public BigDecimal getBean() + { + return bean; + } + public void setStoreBean(BigDecimal storeBean) + { + this.storeBean = storeBean; + } + + public BigDecimal getStoreBean() + { + return storeBean; + } + public void setSuperiorId(Long superiorId) + { + this.superiorId = superiorId; + } + + public Long getSuperiorId() + { + return superiorId; + } + public void setInvitationCode(String invitationCode) + { + this.invitationCode = invitationCode; + } + + public String getInvitationCode() + { + return invitationCode; + } + public void setTransactionLink(String transactionLink) + { + this.transactionLink = transactionLink; + } + + public String getTransactionLink() + { + return transactionLink; + } + public void setType(Integer type) + { + this.type = type; + } + + public Integer getType() + { + return type; + } + public void setSpreadGrade(Long spreadGrade) + { + this.spreadGrade = spreadGrade; + } + + public Long getSpreadGrade() + { + return spreadGrade; + } + public void setSpreadAmount(BigDecimal spreadAmount) + { + this.spreadAmount = spreadAmount; + } + + public BigDecimal getSpreadAmount() + { + return spreadAmount; + } + public void setWinningGrade(Long winningGrade) + { + this.winningGrade = winningGrade; + } + + public Long getWinningGrade() + { + return winningGrade; + } + public void setWinningAdd(Integer winningAdd) + { + this.winningAdd = winningAdd; + } + + public Integer getWinningAdd() + { + return winningAdd; + } + public void setWinningTime(Long winningTime) + { + this.winningTime = winningTime; + } + + public Long getWinningTime() + { + return winningTime; + } + public void setWinningStatus(Integer winningStatus) + { + this.winningStatus = winningStatus; + } + + public Integer getWinningStatus() + { + return winningStatus; + } + public void setTime(Date time) + { + this.time = time; + } + + public Date getTime() + { + return time; + } + public void setEndTime(Date endTime) + { + this.endTime = endTime; + } + + public Date getEndTime() + { + return endTime; + } + public void setVipTime(Date vipTime) + { + this.vipTime = vipTime; + } + + public Date getVipTime() + { + return vipTime; + } + public void setVipService(String vipService) + { + this.vipService = vipService; + } + + public String getVipService() + { + return vipService; + } + public void setIp(String ip) + { + this.ip = ip; + } + + public String getIp() + { + return ip; + } + public void setStatus(Integer status) + { + this.status = status; + } + + public Integer getStatus() + { + return status; + } + public void setStatus2(Integer status2) + { + this.status2 = status2; + } + + public Integer getStatus2() + { + return status2; + } + public void setRewardNewUser(Integer rewardNewUser) + { + this.rewardNewUser = rewardNewUser; + } + + public Integer getRewardNewUser() + { + return rewardNewUser; + } + public void setLotteryTimes(Long lotteryTimes) + { + this.lotteryTimes = lotteryTimes; + } + + public Long getLotteryTimes() + { + return lotteryTimes; + } + public void setShipmentPrice(BigDecimal shipmentPrice) + { + this.shipmentPrice = shipmentPrice; + } + + public BigDecimal getShipmentPrice() + { + return shipmentPrice; + } + public void setRechargeHour(BigDecimal rechargeHour) + { + this.rechargeHour = rechargeHour; + } + + public BigDecimal getRechargeHour() + { + return rechargeHour; + } + public void setRecharge12hour(BigDecimal recharge12hour) + { + this.recharge12hour = recharge12hour; + } + + public BigDecimal getRecharge12hour() + { + return recharge12hour; + } + public void setRecharge24hour(BigDecimal recharge24hour) + { + this.recharge24hour = recharge24hour; + } + + public BigDecimal getRecharge24hour() + { + return recharge24hour; + } + public void setLoginIp(String loginIp) + { + this.loginIp = loginIp; + } + + public String getLoginIp() + { + return loginIp; + } + public void setLoginDate(Date loginDate) + { + this.loginDate = loginDate; + } + + public Date getLoginDate() + { + return loginDate; + } + public void setDelFlag(Long delFlag) + { + this.delFlag = delFlag; + } + + public Long getDelFlag() + { + return delFlag; + } + public void setRealName(String realName) + { + this.realName = realName; + } + + public String getRealName() + { + return realName; + } + public void setIdNumber(String idNumber) + { + this.idNumber = idNumber; + } + + public String getIdNumber() + { + return idNumber; + } + public void setIsRealCheck(Long isRealCheck) + { + this.isRealCheck = isRealCheck; + } + + public Long getIsRealCheck() + { + return isRealCheck; + } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("steamid", getSteamid()) + .append("name", getName()) + .append("portrait", getPortrait()) + .append("phone", getPhone()) + .append("password", getPassword()) + .append("passwordError", getPasswordError()) + .append("email", getEmail()) + .append("bean", getBean()) + .append("storeBean", getStoreBean()) + .append("superiorId", getSuperiorId()) + .append("invitationCode", getInvitationCode()) + .append("transactionLink", getTransactionLink()) + .append("type", getType()) + .append("spreadGrade", getSpreadGrade()) + .append("spreadAmount", getSpreadAmount()) + .append("winningGrade", getWinningGrade()) + .append("winningAdd", getWinningAdd()) + .append("winningTime", getWinningTime()) + .append("winningStatus", getWinningStatus()) + .append("time", getTime()) + .append("endTime", getEndTime()) + .append("vipTime", getVipTime()) + .append("vipService", getVipService()) + .append("ip", getIp()) + .append("status", getStatus()) + .append("status2", getStatus2()) + .append("rewardNewUser", getRewardNewUser()) + .append("lotteryTimes", getLotteryTimes()) + .append("shipmentPrice", getShipmentPrice()) + .append("rechargeHour", getRechargeHour()) + .append("recharge12hour", getRecharge12hour()) + .append("recharge24hour", getRecharge24hour()) + .append("loginIp", getLoginIp()) + .append("loginDate", getLoginDate()) + .append("delFlag", getDelFlag()) + .append("realName", getRealName()) + .append("idNumber", getIdNumber()) + .append("isRealCheck", getIsRealCheck()) + .toString(); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/TtCoinFirstChargeBonus.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/TtCoinFirstChargeBonus.java new file mode 100644 index 0000000..ec6cd49 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/TtCoinFirstChargeBonus.java @@ -0,0 +1,29 @@ +package com.ruoyi.thirdparty.wechat.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class TtCoinFirstChargeBonus implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + private Long cid; // 充值商品ID + private BigDecimal keyCoin; // 充值金额 + private BigDecimal rewards; // 奖励价值 + private Long oid; // 代金券饰品ID + private Integer num; // 奖励数量(默认5) + private Integer status; // 开启状态(默认1) 1开启 0关闭 + private String name; + private String img; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/TtCoinFirstChargeBonusRecord.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/TtCoinFirstChargeBonusRecord.java new file mode 100644 index 0000000..ae4c883 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/TtCoinFirstChargeBonusRecord.java @@ -0,0 +1,30 @@ +package com.ruoyi.thirdparty.wechat.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class TtCoinFirstChargeBonusRecord implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + private Long uid; // 用户ID + private Long fcbid; // 首充奖励表ID + private BigDecimal rewards; // 奖励价值 + private Integer status; // 冻结状态(默认1) 0解冻 1冻结 2销毁 + private Date createTime; // 创建时间 + private Date latestUseTime; // 最晚使用时间 + private Date useTime; // 使用时间 + private String remark; // 备注 +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/TtCoinFirstChargeBonusRecordParam.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/TtCoinFirstChargeBonusRecordParam.java new file mode 100644 index 0000000..08ec8f1 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/domain/TtCoinFirstChargeBonusRecordParam.java @@ -0,0 +1,18 @@ +package com.ruoyi.thirdparty.wechat.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class TtCoinFirstChargeBonusRecordParam { + private Long id; + private Long uid; + private Integer status; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TianXinOrder.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TianXinOrder.java new file mode 100644 index 0000000..34926ed --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TianXinOrder.java @@ -0,0 +1,36 @@ +package com.ruoyi.thirdparty.wechat.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@Builder +public class TianXinOrder { + private String orderId; // 系统订单号(唯一) + private String payType; // 支付方式:3支付宝H5,4支付宝App + private String goodsId; // 商户商品ID + private BigDecimal goodsPrice; // 商户商品价格 + private Integer goodsNum; // 商户商品数量 + private BigDecimal totalAmount; // 付款总价 + private String userIp; // 用户IP + private Integer payStatus; // 支付状态: 0待支付,1已支付,2,取消支付 + private Date createTime; // 创建时间 + private Date updateTime; // 状态更新时间 + private Long userId; // 订单发起用户ID + private String userName; // 订单发起用户名 + private String remark; // 备注 + private String callBackOrderId; // 回传单号 + private String callBackMsg; // 回传信息 + private String callBackStatus; // 回传状态 + private String sign; // sign + private String subject; // 商品名 +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtCoinRechargeParam.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtCoinRechargeParam.java new file mode 100644 index 0000000..d53aaad --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtCoinRechargeParam.java @@ -0,0 +1,34 @@ +package com.ruoyi.thirdparty.wechat.entity; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class TtCoinRechargeParam { + + /** 金币 */ + private BigDecimal coin; + + private BigDecimal minCoin; + + private BigDecimal maxCoin; + + /** 订单状态:0未付款,1已付款,2已取消 */ + private String payStatus; + + /** 用户ID */ + private Long uid; + private Long rid; + + /** 用户名 */ + private String uname; + + /** 订单号 */ + private String orderNo; + + + /** 订单号 */ + private String beginTime; + private String endTime; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtCoinRechargeRecord.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtCoinRechargeRecord.java new file mode 100644 index 0000000..881bbf6 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtCoinRechargeRecord.java @@ -0,0 +1,139 @@ +package com.ruoyi.thirdparty.wechat.entity; + +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import java.math.BigDecimal; + +/** + * 充值记录对象 tt_coin_recharge_record + * + * @author ruoyi + * @date 2023-07-06 + */ +public class TtCoinRechargeRecord extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + private Long id; + + /** 金币 */ + @Excel(name = "金币") + private BigDecimal coin; + + /** 订单状态:0未付款,1已付款,2已取消 */ + @Excel(name = "订单状态",dictType = "alipay_status") + private String payStatus; + + /** 用户ID */ + @Excel(name = "用户ID") + private Long uid; + + /** 用户名 */ + @Excel(name = "用户名") + private String uname; + + /** 订单号 */ + @Excel(name = "订单号") + private String orderNo; + + /** 支付宝订单号 */ + @Excel(name = "支付宝订单号") + private String callbackNo; + + /** 回调信息 */ + @Excel(name = "回调信息") + private String callbackMsg; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setCoin(BigDecimal coin) + { + this.coin = coin; + } + + public BigDecimal getCoin() + { + return coin; + } + public void setPayStatus(String payStatus) + { + this.payStatus = payStatus; + } + + public String getPayStatus() + { + return payStatus; + } + public void setUid(Long uid) + { + this.uid = uid; + } + + public Long getUid() + { + return uid; + } + public void setUname(String uname) + { + this.uname = uname; + } + + public String getUname() + { + return uname; + } + public void setOrderNo(String orderNo) + { + this.orderNo = orderNo; + } + + public String getOrderNo() + { + return orderNo; + } + public void setCallbackNo(String callbackNo) + { + this.callbackNo = callbackNo; + } + + public String getCallbackNo() + { + return callbackNo; + } + public void setCallbackMsg(String callbackMsg) + { + this.callbackMsg = callbackMsg; + } + + public String getCallbackMsg() + { + return callbackMsg; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("coin", getCoin()) + .append("payStatus", getPayStatus()) + .append("uid", getUid()) + .append("uname", getUname()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .append("orderNo", getOrderNo()) + .append("callbackNo", getCallbackNo()) + .append("callbackMsg", getCallbackMsg()) + .toString(); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtCoinRecord.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtCoinRecord.java new file mode 100644 index 0000000..6ecde1c --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtCoinRecord.java @@ -0,0 +1,156 @@ +package com.ruoyi.thirdparty.wechat.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 金币变动记录对象 tt_coin_record + * + * @author ruoyi + * @date 2023-07-06 + */ +public class TtCoinRecord extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** */ + private Long id; + + /** 所属用户 */ + @Excel(name = "所属用户") + private Long uid; + + /** 用户名 */ + @Excel(name = "用户名") + private String uname; + + /** 变动类型 */ + @Excel(name = "变动类型",dictType = "change_type") + private String type; + + + /** 变动类型 */ + @Excel(name = "操作场景",dictType = "operation_type") + private String operType; + + /** 奖励类型 */ + @Excel(name = "奖励类型",dictType = "reward_type") + private String rewardType; + + /** 变动金额 */ + @Excel(name = "变动金额") + private BigDecimal money; + + /** 返佣时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date time; + + /** 状态 */ + @Excel(name = "状态") + private Long status; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setUid(Long uid) + { + this.uid = uid; + } + + public Long getUid() + { + return uid; + } + public void setUname(String uname) + { + this.uname = uname; + } + + public String getUname() + { + return uname; + } + public void setType(String type) + { + this.type = type; + } + + public String getType() + { + return type; + } + public void setRewardType(String rewardType) + { + this.rewardType = rewardType; + } + + public String getRewardType() + { + return rewardType; + } + public void setMoney(BigDecimal money) + { + this.money = money; + } + + public BigDecimal getMoney() + { + return money; + } + public void setTime(Date time) + { + this.time = time; + } + + public Date getTime() + { + return time; + } + public void setStatus(Long status) + { + this.status = status; + } + + public Long getStatus() + { + return status; + } + + public String getOperType() { + return operType; + } + + public void setOperType(String operType) { + this.operType = operType; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("uid", getUid()) + .append("uname", getUname()) + .append("type", getType()) + .append("oper_type", getOperType()) + .append("rewardType", getRewardType()) + .append("money", getMoney()) + .append("time", getTime()) + .append("status", getStatus()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtCoinRecordParam.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtCoinRecordParam.java new file mode 100644 index 0000000..b19a333 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtCoinRecordParam.java @@ -0,0 +1,103 @@ +package com.ruoyi.thirdparty.wechat.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 充值卡密对象 tt_coin_key + * + * @author ruoyi + * @date 2023-07-06 + */ +public class TtCoinRecordParam extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + private Long id; + + /** 所属用户 */ + @Excel(name = "所属用户") + private Long uid; + + + /** 使用状态 */ + @Excel(name = "用户名") + private String uname; + + /** 使用状态 */ + private String changeType; + + /** 使用状态 */ + private String rewardType; + + /** 使用时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "使用时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date beginTime; + + + /** 使用时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "使用时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getUid() { + return uid; + } + + public void setUid(Long uid) { + this.uid = uid; + } + + public String getUname() { + return uname; + } + + public void setUname(String uname) { + this.uname = uname; + } + + public String getChangeType() { + return changeType; + } + + public void setChangeType(String changeType) { + this.changeType = changeType; + } + + public String getRewardType() { + return rewardType; + } + + public void setRewardType(String rewardType) { + this.rewardType = rewardType; + } + + public Date getBeginTime() { + return beginTime; + } + + public void setBeginTime(Date beginTime) { + this.beginTime = beginTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtUserLsjl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtUserLsjl.java new file mode 100644 index 0000000..8dc987d --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtUserLsjl.java @@ -0,0 +1,167 @@ +package com.ruoyi.thirdparty.wechat.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 流水记录对象 tt_user_lsjl + * + * @author junhai + * @date 2023-08-19 + */ +@Data +public class TtUserLsjl +{ + private static final long serialVersionUID = 1L; + + /** 流水id */ + private Long id; + + /** 用户id */ + @Excel(name = "用户id") + private Long ttUserId; + + /** 上上级用户id **/ + private Long ttSsUserId; + + /** 业务类型:充值和消费 */ + @Excel(name = "业务类型:充值和消费") + private String type; + + /** v币变动前 */ + @Excel(name = "v币变动前") + private BigDecimal vCoinBefore; + + /** v币变动后 */ + @Excel(name = "v币变动后") + private BigDecimal vCoinAfter; + + /** v币变动金额 */ + @Excel(name = "v币变动金额") + private BigDecimal vCoinChange; + + /** 钻石变动前的金额 */ + @Excel(name = "钻石变动前的金额") + private BigDecimal dCoinBefore; + + /** 钻石变动金额 */ + @Excel(name = "钻石变动金额") + private BigDecimal dCoinChange; + + /** 钻石变动后的金额 */ + @Excel(name = "钻石变动后的金额") + private BigDecimal dCoinAfter; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setTtUserId(Long ttUserId) + { + this.ttUserId = ttUserId; + } + + public Long getTtUserId() + { + return ttUserId; + } + public void setType(String type) + { + this.type = type; + } + + public String getType() + { + return type; + } + public void setvCoinBefore(BigDecimal vCoinBefore) + { + this.vCoinBefore = vCoinBefore; + } + + public BigDecimal getvCoinBefore() + { + return vCoinBefore; + } + public void setvCoinAfter(BigDecimal vCoinAfter) + { + this.vCoinAfter = vCoinAfter; + } + + public BigDecimal getvCoinAfter() + { + return vCoinAfter; + } + public void setvCoinChange(BigDecimal vCoinChange) + { + this.vCoinChange = vCoinChange; + } + + public BigDecimal getvCoinChange() + { + return vCoinChange; + } + public void setdCoinBefore(BigDecimal dCoinBefore) + { + this.dCoinBefore = dCoinBefore; + } + + public BigDecimal getdCoinBefore() + { + return dCoinBefore; + } + public void setdCoinChange(BigDecimal dCoinChange) + { + this.dCoinChange = dCoinChange; + } + + public BigDecimal getdCoinChange() + { + return dCoinChange; + } + public void setdCoinAfter(BigDecimal dCoinAfter) + { + this.dCoinAfter = dCoinAfter; + } + + public BigDecimal getdCoinAfter() + { + return dCoinAfter; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("ttUserId", getTtUserId()) + .append("type", getType()) + .append("vCoinBefore", getvCoinBefore()) + .append("vCoinAfter", getvCoinAfter()) + .append("vCoinChange", getvCoinChange()) + .append("dCoinBefore", getdCoinBefore()) + .append("dCoinChange", getdCoinChange()) + .append("dCoinAfter", getdCoinAfter()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtUserLsjlParam.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtUserLsjlParam.java new file mode 100644 index 0000000..6e8e7ef --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/entity/TtUserLsjlParam.java @@ -0,0 +1,63 @@ +package com.ruoyi.thirdparty.wechat.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import lombok.Data; + +import java.util.Date; + +@Data +public class TtUserLsjlParam { + + /** 流水id */ + private Long id; + + /** 用户id */ + @Excel(name = "用户id") + private Long ttUserId; + + /** 上上级用户id **/ + private Long ttSsUserId; + + /** 类型 **/ + private String type; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String toString() { + return "TtUserLsjlParam{" + + "id=" + id + + ", type='" + type + '\'' + + ", createTime=" + createTime + + '}'; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/mapper/TianXinMapper.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/mapper/TianXinMapper.java new file mode 100644 index 0000000..6671fa8 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/mapper/TianXinMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.thirdparty.wechat.mapper; + +import com.ruoyi.thirdparty.wechat.entity.TianXinOrder; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TianXinMapper { + void insertTianXinOrder(TianXinOrder tianXinOrder); + + TianXinOrder selectTianXinOrderByOrderId(String sdorderno); + + void updateTianXinOrder(TianXinOrder tianXinOrder); + + void deleteDataTianXinOrder(); + + void deleteDataTtCoinRechargeRecord(); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/mapper/TtCoinRechargeRecordMapper.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/mapper/TtCoinRechargeRecordMapper.java new file mode 100644 index 0000000..742e0da --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/mapper/TtCoinRechargeRecordMapper.java @@ -0,0 +1,38 @@ +package com.ruoyi.thirdparty.wechat.mapper; + +import com.ruoyi.thirdparty.wechat.entity.TtCoinRechargeRecord; + +/** + * 充值记录Mapper接口 + * + * @author ruoyi + * @date 2023-07-06 + */ +public interface TtCoinRechargeRecordMapper +{ + + /** + * 新增充值记录 + * + * @param ttCoinRechargeRecord 充值记录 + * @return 结果 + */ + public int insertTtCoinRechargeRecord(TtCoinRechargeRecord ttCoinRechargeRecord); + + /** + * 修改充值记录 + * + * @param ttCoinRechargeRecord 充值记录 + * @return 结果 + */ + public int updateTtCoinRechargeRecord(TtCoinRechargeRecord ttCoinRechargeRecord); + + /** + * 根据订单号查询数据 + * + * @param orderNo + * @return + */ + TtCoinRechargeRecord selectTtCoinRechargeRecordByOrder(String orderNo); + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/mapper/TtCoinRecordMapper.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/mapper/TtCoinRecordMapper.java new file mode 100644 index 0000000..5599e5d --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/mapper/TtCoinRecordMapper.java @@ -0,0 +1,23 @@ +package com.ruoyi.thirdparty.wechat.mapper; + +import com.ruoyi.thirdparty.wechat.entity.TtCoinRecord; + +/** + * 金币变动记录Mapper接口 + * + * @author ruoyi + * @date 2023-07-06 + */ +public interface TtCoinRecordMapper +{ + + /** + * 新增金币变动记录 + * + * @param ttCoinRecord 金币变动记录 + * @return 结果 + */ + public int insertTtCoinRecord(TtCoinRecord ttCoinRecord); + + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/mapper/TtUserLsjlMapper.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/mapper/TtUserLsjlMapper.java new file mode 100644 index 0000000..5723539 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/mapper/TtUserLsjlMapper.java @@ -0,0 +1,22 @@ +package com.ruoyi.thirdparty.wechat.mapper; + +import com.ruoyi.thirdparty.wechat.entity.TtUserLsjl; + +/** + * 流水记录Mapper接口 + * + * @author junhai + * @date 2023-08-19 + */ +public interface TtUserLsjlMapper +{ + + /** + * 新增流水记录 + * + * @param ttUserLsjl 流水记录 + * @return 结果 + */ + public int insertTtUserLsjl(TtUserLsjl ttUserLsjl); + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/ApiTokenService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/ApiTokenService.java new file mode 100644 index 0000000..cad2fe6 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/ApiTokenService.java @@ -0,0 +1,258 @@ +package com.ruoyi.thirdparty.wechat.service; + +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.thirdparty.wechat.domain.ApiLoginUser; +import com.ruoyi.thirdparty.wechat.domain.PtLoginUser; +import eu.bitwalker.useragentutils.UserAgent; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * token验证处理 + * + * @author ruoyi + */ +@Component +public class ApiTokenService +{ + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + // 令牌秘钥 + @Value("${token.secret}") + private String secret; + + // 令牌有效期(默认30分钟) + @Value("${token.expireTime}") + private int expireTime; + + protected static final long MILLIS_SECOND = 1000; + + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; + + @Autowired + private RedisCache redisCache; + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public PtLoginUser getLoginUser(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = getToken(request); + if (StringUtils.isNotEmpty(token)) + { + try + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + PtLoginUser user = redisCache.getCacheObject(userKey); + return user; + } + catch (Exception e) + { + } + } + return null; + } + + + + /** + * @author 作者:Jason E-mail: Jason_Lee_lizhen@outlook.com + * @copyright All Rights Reserved @version v1.0 + * @version 创建时间:2023年6月13日16:41:51 + * @description方法说明 : + * @arg 入参类型:*****;出参类型:**** + */ + public ApiLoginUser getLoginUser(String token) + { + // 获取请求携带的令牌 + if (StringUtils.isNotEmpty(token)) + { + try + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + ApiLoginUser user = redisCache.getCacheObject(userKey); + return user; + } + catch (Exception e) + { + } + } + return null; + } + + /** + * 设置用户身份信息 + */ + public void setLoginUser(PtLoginUser loginUser) + { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) + { + refreshToken(loginUser); + } + } + + /** + * 删除用户身份信息 + */ + public void delLoginUser(String token) + { + if (StringUtils.isNotEmpty(token)) + { + String userKey = getTokenKey(token); + redisCache.deleteObject(userKey); + } + } + + /** + * 创建令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + public String createToken(PtLoginUser loginUser) + { + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + setUserAgent(loginUser); + refreshToken(loginUser); + + Map claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_KEY, token); + return createToken(claims); + } + + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param loginUser + * @return 令牌 + */ + public void verifyToken(PtLoginUser loginUser) + { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) + { + refreshToken(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken(PtLoginUser loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + public void setUserAgent(PtLoginUser loginUser) + { + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = IpUtils.getIpAddr(); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + loginUser.setBrowser(userAgent.getBrowser().getName()); + loginUser.setOs(userAgent.getOperatingSystem().getName()); + } + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createToken(Map claims) + { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + private Claims parseToken(String token) + { + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token) + .getBody(); + } + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public String getUsernameFromToken(String token) + { + Claims claims = parseToken(token); + return claims.getSubject(); + } + + /** + * 获取请求token + * + * @param request + * @return token + */ + private String getToken(HttpServletRequest request) + { + String token = request.getHeader(header); + if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) + { + token = token.replace(Constants.TOKEN_PREFIX, ""); + } + return token; + } + + private String getTokenKey(String uuid) + { + return CacheConstants.LOGIN_TOKEN_KEY + uuid; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/ITtCoinRechargeRecordService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/ITtCoinRechargeRecordService.java new file mode 100644 index 0000000..e0bdadd --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/ITtCoinRechargeRecordService.java @@ -0,0 +1,38 @@ +package com.ruoyi.thirdparty.wechat.service; + +import com.ruoyi.thirdparty.wechat.entity.TtCoinRechargeRecord; + +/** + * 充值记录Service接口 + * + * @author ruoyi + * @date 2023-07-06 + */ +public interface ITtCoinRechargeRecordService { + + /** + * 新增充值记录 + * + * @param ttCoinRechargeRecord 充值记录 + * @return 结果 + */ + public int insertTtCoinRechargeRecord(TtCoinRechargeRecord ttCoinRechargeRecord); + + /** + * 修改充值记录 + * + * @param ttCoinRechargeRecord 充值记录 + * @return 结果 + */ + public int updateTtCoinRechargeRecord(TtCoinRechargeRecord ttCoinRechargeRecord); + + + /** + * 根据订单号查询 + * + * @param tradeNo + * @return + */ + TtCoinRechargeRecord selectTtCoinRechargeRecordByOrder(String tradeNo); + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/ITtCoinRecordService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/ITtCoinRecordService.java new file mode 100644 index 0000000..3c895f0 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/ITtCoinRecordService.java @@ -0,0 +1,21 @@ +package com.ruoyi.thirdparty.wechat.service; + +import com.ruoyi.thirdparty.wechat.entity.TtCoinRecord; + +/** + * 金币变动记录Service接口 + * + * @author ruoyi + * @date 2023-07-06 + */ +public interface ITtCoinRecordService +{ + /** + * 新增金币变动记录 + * + * @param ttCoinRecord 金币变动记录 + * @return 结果 + */ + public int insertTtCoinRecord(TtCoinRecord ttCoinRecord); + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/ITtUserLsjlService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/ITtUserLsjlService.java new file mode 100644 index 0000000..306b4f6 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/ITtUserLsjlService.java @@ -0,0 +1,21 @@ +package com.ruoyi.thirdparty.wechat.service; + +import com.ruoyi.thirdparty.wechat.entity.TtUserLsjl; + +/** + * 流水记录Service接口 + * + * @author junhai + * @date 2023-08-19 + */ +public interface ITtUserLsjlService +{ + + /** + * 新增流水记录 + * + * @param ttUserLsjl 流水记录 + * @return 结果 + */ + public int insertTtUserLsjl(TtUserLsjl ttUserLsjl); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/TianXinService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/TianXinService.java new file mode 100644 index 0000000..f998fba --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/TianXinService.java @@ -0,0 +1,15 @@ +package com.ruoyi.thirdparty.wechat.service; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.thirdparty.wechat.domain.PayOrderParam; + +import jakarta.servlet.http.HttpServletRequest; + +public interface TianXinService { + AjaxResult createOrder(PayOrderParam param, TtUser user, String ip); + + String callBack(HttpServletRequest request); + + AjaxResult queryOrderStatus(String sdorderno); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/impl/TianXinServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/impl/TianXinServiceImpl.java new file mode 100644 index 0000000..13f5d55 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/impl/TianXinServiceImpl.java @@ -0,0 +1,265 @@ +package com.ruoyi.thirdparty.wechat.service.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.admin.mapper.TtOrderMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.TtRechargeProdService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.thirdparty.wechat.config.TianXinProperties; +import com.ruoyi.thirdparty.wechat.domain.PayOrderParam; +import com.ruoyi.thirdparty.wechat.entity.TianXinOrder; +import com.ruoyi.thirdparty.wechat.entity.TtCoinRechargeRecord; +import com.ruoyi.thirdparty.wechat.entity.TtCoinRecord; +import com.ruoyi.thirdparty.wechat.entity.TtUserLsjl; +import com.ruoyi.thirdparty.wechat.mapper.TianXinMapper; +import com.ruoyi.thirdparty.wechat.service.ITtCoinRechargeRecordService; +import com.ruoyi.thirdparty.wechat.service.ITtCoinRecordService; +import com.ruoyi.thirdparty.wechat.service.ITtUserLsjlService; +import com.ruoyi.thirdparty.wechat.service.TianXinService; +import com.ruoyi.thirdparty.wechat.utils.DemoUtils; +import com.ruoyi.thirdparty.wechat.utils.GenerateUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import jakarta.servlet.http.HttpServletRequest; +import java.math.BigDecimal; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +@Service +@EnableConfigurationProperties(value = TianXinProperties.class) +@Slf4j +public class TianXinServiceImpl implements TianXinService { + + private final TianXinProperties tianXinProperties; + private final TtRechargeProdService coinItemService; + private final TtUserMapper userMapper; + private final TianXinMapper tianXinMapper; + private final ITtCoinRechargeRecordService ttCoinRechargeRecordService; + private final ITtCoinRecordService recordService; + private final ITtUserLsjlService userLsjlService; + private final TtOrderMapper orderMapper; + + public TianXinServiceImpl(TianXinProperties tianXinProperties, + TtRechargeProdService coinItemService, + TtUserMapper userMapper, + TianXinMapper tianXinMapper, + ITtCoinRechargeRecordService ttCoinRechargeRecordService, + ITtCoinRecordService recordService, + ITtUserLsjlService userLsjlService, + TtOrderMapper orderMapper) { + this.tianXinProperties = tianXinProperties; + this.coinItemService = coinItemService; + this.userMapper = userMapper; + this.tianXinMapper = tianXinMapper; + this.ttCoinRechargeRecordService = ttCoinRechargeRecordService; + this.recordService = recordService; + this.userLsjlService = userLsjlService; + this.orderMapper = orderMapper; + } + + @Override + public AjaxResult createOrder(PayOrderParam param, TtUser ttUser, String ip) { + + long itemId = param.getCoinItemId(); + Integer itemNum = param.getCoinItemNum(); + BigDecimal payAmount = param.getCoinItemAmount(); + TtRechargeProd item = coinItemService.getById(itemId); + + BigDecimal realPayAmout = item.getPrice().multiply(new BigDecimal(itemNum)); + if (realPayAmout.compareTo(payAmount) != 0) { + return AjaxResult.error("支付金额不正确!"); + } + + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add("version", tianXinProperties.getVersion()); // 版本号 + map.add("customerid", tianXinProperties.getCustomerid()); // 商户编号 + map.add("sdorderno", GenerateUtils.getGuid("TX")); //商户请求的唯一标识,推荐为uuid,必填 IdUtil hutool工具 + map.add("total_fee", realPayAmout); // 订单金额 + map.add("paytype", tianXinProperties.getPaytype()); // 支付编号 + map.add("bankcode", ""); // 银行编号 + map.add("notifyurl", tianXinProperties.getNotifyurl()); // 异步通知URL + map.add("clientip", ip); // 用户ip + map.add("returnurl", tianXinProperties.getReturnurl()); // 同步通知URL + map.add("remark", "会员充值"); // 订单备注 + map.add("access_type", tianXinProperties.getAccess_type()); // 接入方式 + String signStr = DemoUtils.sortMapByValues(map.toSingleValueMap()); + String sign = DemoUtils.encryptToMD5(signStr + "&" + tianXinProperties.getUserkey()); + map.add("sign", sign); + map.add("is_jump", false); + + Map resMap = DemoUtils.postFormData(map, tianXinProperties.getApiurl() + "/apisubmit"); + + if (tianXinProperties.getAccess_type().equals("API")) { + if (resMap.get("status").equals("OK")) { + // tianxin_order表添加数据 +// TianXinOrder tianXinOrder = TianXinOrder.builder().build(); +// tianXinOrder.setOrderId(resMap.get("sdorderno")); // 系统订单号(唯一) +// tianXinOrder.setPayType("5"); // 支付方式:5微信扫码 +// tianXinOrder.setGoodsId(String.valueOf(itemId)); // 商户商品ID +// tianXinOrder.setGoodsPrice(payAmount); // 商户商品价格 +// tianXinOrder.setGoodsNum(itemNum); // 商户商品数量 +// tianXinOrder.setTotalAmount(realPayAmout); // 付款总价 +// tianXinOrder.setUserIp(ip); // 用户IP +// tianXinOrder.setPayStatus(0); // 支付状态: 0待支付,1已支付,2,取消支付 +// tianXinOrder.setCreateTime(DateUtils.getNowDate()); // 创建时间 +// tianXinOrder.setUserId(Long.valueOf(ttUser.getUserId())); // 订单发起用户ID +// tianXinOrder.setUserName(ttUser.getUserName()); // 订单发起用户名 +// tianXinOrder.setRemark("待付款!"); // 备注 +// tianXinOrder.setCallBackOrderId(resMap.get("orderid")); // 回传单号 +// tianXinMapper.insertTianXinOrder(tianXinOrder); + + TtOrder ttOrder = TtOrder.builder().build(); + ttOrder.setUserId(ttUser.getUserId()); + ttOrder.setThirdParty("0"); + ttOrder.setType("2"); + ttOrder.setGoodsId((int)itemId); + ttOrder.setGoodsPrice(payAmount); + ttOrder.setGoodsNum(itemNum); + ttOrder.setTotalAmount(realPayAmout); + ttOrder.setOrderId(resMap.get("sdorderno")); + ttOrder.setSign(sign); + ttOrder.setStatus("0"); + ttOrder.setOutTradeNo(resMap.get("orderid")); + ttOrder.setPayUrl(resMap.get("url")); + ttOrder.setCreateTime(DateUtils.getNowDate()); + int isSuccess = orderMapper.insert(ttOrder); + + //充值记录的新增 +// TtCoinRechargeRecord coinRechargeRecord = new TtCoinRechargeRecord(); +// coinRechargeRecord.setCoin(param.getCoinItemAmount()); +// coinRechargeRecord.setPayStatus("0"); // 订单状态:0未付款,1已付款,2已取消' +// coinRechargeRecord.setUid(Long.valueOf(ttUser.getUserId())); +// coinRechargeRecord.setUname(ttUser.getUserName()); +// coinRechargeRecord.setCreateTime(DateUtils.getNowDate()); +// coinRechargeRecord.setOrderNo(tianXinOrder.getOrderId()); +// ttCoinRechargeRecordService.insertTtCoinRechargeRecord(coinRechargeRecord); + + return AjaxResult.success("订单创建成功!", resMap); + } else { + return AjaxResult.error("订单创建失败!"); + } + } + return AjaxResult.error("接入方式选择错误,请联系管理员!"); + } + + + @Override + public String callBack(HttpServletRequest request) { + String sign_str = "customerid=" + request.getParameter("customerid") + + "&status=" + request.getParameter("status") + + "&sdpayno=" + request.getParameter("sdpayno") + + "&sdorderno=" + request.getParameter("sdorderno") + + "&total_fee=" + request.getParameter("total_fee") + + "&realmoney=" + request.getParameter("realmoney") + + "&paytype=" + request.getParameter("paytype") + + "&" + tianXinProperties.getUserkey(); + String sign = DemoUtils.encryptToMD5(sign_str); + + // 订单ID + String sdorderno = request.getParameter("sdorderno"); +// String sdorderno = "20231111160000000002TX"; + + // 获取tianxin订单数据 + TianXinOrder tianXinOrder = tianXinMapper.selectTianXinOrderByOrderId(sdorderno); + // 获取充值记录 + TtCoinRechargeRecord ttCoinRechargeRecord = ttCoinRechargeRecordService.selectTtCoinRechargeRecordByOrder(sdorderno); + + if ("1".equals(request.getParameter("status")) && sign.equals(request.getParameter("sign"))) { + Enumeration parameterNames = request.getParameterNames(); + Map parameterMap = new HashMap<>(); + while (parameterNames.hasMoreElements()) { + String paraName = parameterNames.nextElement(); + parameterMap.put(paraName, request.getParameter(paraName)); + } + + try { + tianXinOrder.setPayStatus(1); + tianXinOrder.setCallBackMsg("支付成功!"); // 回传信息 + tianXinOrder.setCallBackStatus(request.getParameter("status")); // 回传状态 1:成功,其他失败 + tianXinOrder.setSign(request.getParameter("sign")); // sign + tianXinOrder.setSubject("FireSkins平台" + request.getParameter("realmoney") + "充值!"); // 商品名 + tianXinOrder.setUpdateTime(DateUtils.getNowDate()); + tianXinOrder.setRemark("付款成功!"); // 备注 + tianXinMapper.updateTianXinOrder(tianXinOrder); + + ttCoinRechargeRecord.setPayStatus("1"); + ttCoinRechargeRecord.setCallbackNo(request.getParameter("sdpayno")); + ttCoinRechargeRecord.setUpdateTime(DateUtils.getNowDate()); + ttCoinRechargeRecord.setCallbackMsg(JSONObject.toJSONString(parameterMap)); + ttCoinRechargeRecordService.updateTtCoinRechargeRecord(ttCoinRechargeRecord); + + //记录金币数 + TtCoinRecord record = new TtCoinRecord(); + record.setMoney(tianXinOrder.getTotalAmount()); + record.setOperType("3"); + record.setType("0"); + record.setUid(tianXinOrder.getUserId()); + record.setUname(tianXinOrder.getUserName()); + record.setCreateTime(new Date()); + record.setStatus(1l); + recordService.insertTtCoinRecord(record); + + //记录流水的变更 + TtUser ttUser = userMapper.selectTtUserById(tianXinOrder.getUserId()); + TtUserLsjl userLsjl = new TtUserLsjl(); +// userLsjl.setTtSsUserId(ttUser.getSuperiorId()); + userLsjl.setType("充值"); +// userLsjl.setvCoinBefore(ttUser.getBean()); + userLsjl.setvCoinChange(record.getMoney()); +// userLsjl.setvCoinAfter(ttUser.getBean().add(record.getMoney())); + //设置变动积分情况 +// userLsjl.setdCoinBefore(ttUser.getStoreBean()); +// userLsjl.setdCoinAfter(ttUser.getStoreBean()); + userLsjl.setdCoinChange(BigDecimal.valueOf(0)); + userLsjl.setCreateTime(DateUtils.getNowDate()); + userLsjlService.insertTtUserLsjl(userLsjl); + + userMapper.updateTtUserCoin(tianXinOrder.getUserId(), record.getMoney()); + + // 删除10分钟之前的无用数据 + tianXinMapper.deleteDataTianXinOrder(); + tianXinMapper.deleteDataTtCoinRechargeRecord(); + return "success"; + } catch (Exception e) { + return "error"; + } + + } else { + try { + tianXinOrder.setPayStatus(2); + tianXinOrder.setUpdateTime(new Date()); + tianXinMapper.updateTianXinOrder(tianXinOrder); + ttCoinRechargeRecord.setPayStatus("2"); + ttCoinRechargeRecord.setUpdateTime(DateUtils.getNowDate()); + ttCoinRechargeRecordService.updateTtCoinRechargeRecord(ttCoinRechargeRecord); + } catch (Exception e) { + return "error"; + } + return "error"; + } + } + + @Override + public AjaxResult queryOrderStatus(String sdorderno) { + MultiValueMap map = new LinkedMultiValueMap<>(); + String reqtime = String.valueOf(System.currentTimeMillis() / 1000); + String signStr = "customerid=" + tianXinProperties.getCustomerid() + "&sdorderno=" + sdorderno + "&reqtime=" + reqtime; + String sign = DemoUtils.encryptToMD5(signStr + "&" + tianXinProperties.getUserkey()); + map.add("customerid", tianXinProperties.getCustomerid()); + map.add("sdorderno", sdorderno); + map.add("reqtime", reqtime); + map.add("sign", sign); + Map resMap = DemoUtils.postFormData(map, tianXinProperties.getApiurl() + "/apiorderquery"); + return AjaxResult.success(resMap); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/impl/TtCoinRechargeRecordServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/impl/TtCoinRechargeRecordServiceImpl.java new file mode 100644 index 0000000..291ee19 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/impl/TtCoinRechargeRecordServiceImpl.java @@ -0,0 +1,57 @@ +package com.ruoyi.thirdparty.wechat.service.impl; + +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.thirdparty.wechat.entity.TtCoinRechargeRecord; +import com.ruoyi.thirdparty.wechat.mapper.TtCoinRechargeRecordMapper; +import com.ruoyi.thirdparty.wechat.service.ITtCoinRechargeRecordService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 充值记录Service业务层处理 + * + * @author ruoyi + * @date 2023-07-06 + */ +@Service +public class TtCoinRechargeRecordServiceImpl implements ITtCoinRechargeRecordService +{ + @Autowired + private TtCoinRechargeRecordMapper ttCoinRechargeRecordMapper; + + + + /** + * 新增充值记录 + * + * @param ttCoinRechargeRecord 充值记录 + * @return 结果 + */ + @Override + public int insertTtCoinRechargeRecord(TtCoinRechargeRecord ttCoinRechargeRecord) + { + ttCoinRechargeRecord.setCreateTime(DateUtils.getNowDate()); + return ttCoinRechargeRecordMapper.insertTtCoinRechargeRecord(ttCoinRechargeRecord); + } + + /** + * 修改充值记录 + * + * @param ttCoinRechargeRecord 充值记录 + * @return 结果 + */ + @Override + public int updateTtCoinRechargeRecord(TtCoinRechargeRecord ttCoinRechargeRecord) + { + ttCoinRechargeRecord.setUpdateTime(DateUtils.getNowDate()); + return ttCoinRechargeRecordMapper.updateTtCoinRechargeRecord(ttCoinRechargeRecord); + } + + + @Override + public TtCoinRechargeRecord selectTtCoinRechargeRecordByOrder(String tradeNo) { + return ttCoinRechargeRecordMapper.selectTtCoinRechargeRecordByOrder(tradeNo); + } + + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/impl/TtCoinRecordServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/impl/TtCoinRecordServiceImpl.java new file mode 100644 index 0000000..a9dde42 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/impl/TtCoinRecordServiceImpl.java @@ -0,0 +1,36 @@ +package com.ruoyi.thirdparty.wechat.service.impl; + +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.thirdparty.wechat.entity.TtCoinRecord; +import com.ruoyi.thirdparty.wechat.mapper.TtCoinRecordMapper; +import com.ruoyi.thirdparty.wechat.service.ITtCoinRecordService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 金币变动记录Service业务层处理 + * + * @author ruoyi + * @date 2023-07-06 + */ +@Service +public class TtCoinRecordServiceImpl implements ITtCoinRecordService +{ + @Autowired + private TtCoinRecordMapper ttCoinRecordMapper; + + /** + * 新增金币变动记录 + * + * @param ttCoinRecord 金币变动记录 + * @return 结果 + */ + @Override + public int insertTtCoinRecord(TtCoinRecord ttCoinRecord) + { + ttCoinRecord.setCreateTime(DateUtils.getNowDate()); + return ttCoinRecordMapper.insertTtCoinRecord(ttCoinRecord); + } + + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/impl/TtUserLsjlServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/impl/TtUserLsjlServiceImpl.java new file mode 100644 index 0000000..5681033 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/service/impl/TtUserLsjlServiceImpl.java @@ -0,0 +1,35 @@ +package com.ruoyi.thirdparty.wechat.service.impl; + +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.thirdparty.wechat.entity.TtUserLsjl; +import com.ruoyi.thirdparty.wechat.mapper.TtUserLsjlMapper; +import com.ruoyi.thirdparty.wechat.service.ITtUserLsjlService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 流水记录Service业务层处理 + * + * @author junhai + * @date 2023-08-19 + */ +@Service +public class TtUserLsjlServiceImpl implements ITtUserLsjlService +{ + @Autowired + private TtUserLsjlMapper ttUserLsjlMapper; + + /** + * 新增流水记录 + * + * @param ttUserLsjl 流水记录 + * @return 结果 + */ + @Override + public int insertTtUserLsjl(TtUserLsjl ttUserLsjl) + { + ttUserLsjl.setCreateTime(DateUtils.getNowDate()); + return ttUserLsjlMapper.insertTtUserLsjl(ttUserLsjl); + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/utils/DateUtils.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/utils/DateUtils.java new file mode 100644 index 0000000..d3b67b4 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/utils/DateUtils.java @@ -0,0 +1,305 @@ +package com.ruoyi.thirdparty.wechat.utils; + +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +public class DateUtils { + + /** + * 获取当前时间 + * "yyyy-MM-dd" + * @return + */ + public static String queryCurrentDate(){ + Date d = new Date(); + System.out.println(d); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return sdf.format(d); + } + + /** + * 获取当前时间 + * "yyyy-MM-dd" + * @return + */ + public static String queryCurrentDay(){ + Date d = new Date(); + System.out.println(d); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + return sdf.format(d); + } + + public static String queryCurrentHour(){ + Date d = new Date(); + System.out.println(d); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return sdf.format(d); + } + + public static String queryCurrentHourToEnd(){ + Date d = new Date(); + Date dend = new Date(d.getTime()+300000); + System.out.println(d); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String eString=sdf.format(dend); + + return eString; + } + + public static String longToString(long time){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + String date = sdf.format(new Date(time*1000)); + return date; + } + + public static String longToStringMonth(long time){ + SimpleDateFormat sdf = new SimpleDateFormat("MM-dd"); + String date = sdf.format(new Date(time)); + return date; + } + + public static String longToStringQuan(long time){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String date = sdf.format(new Date(time*1000)); + return date; + } + + public static String longToStringAll(long time){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String date = sdf.format(new Date(time)); + return date; + } + + public static String dateStrclean(String dateStr){ + int index = dateStr.indexOf("."); + dateStr = dateStr.substring(0, index); + return dateStr; + } + + public static String dateToStringShort(Date time){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + String date = sdf.format(time); + return date; + } + + public static String dateToStringLong(Date time){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String date = sdf.format(time); + return date; + } + + public static String dateToStringShortFormate(Date time){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); + String date = sdf.format(time); + return date; + } + + public static String dateToDes(Date time){ + SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHH"); + String date = sdf.format(time); + return date; + } + + public static int dateTimestamp(){ + return Integer.parseInt((new Date().getTime() + "").substring( + 0, 10)); + } + + public static Long stringToLong(String time){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date date = null; + try { + // Fri Feb 24 00:00:00 CST 2012 + date = sdf.parse(time); + } catch (Exception e) { + e.printStackTrace(); + } + // 2012-02-24 + + return date.getTime(); + } + + public static Date stringToDateLong(String time){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date date = null; + try { + // Fri Feb 24 00:00:00 CST 2012 + date = sdf.parse(time); + } catch (Exception e) { + e.printStackTrace(); + } + // 2012-02-24 + + return date; + } + + public static Timestamp getTimestamp(){ + + try { + // Fri Feb 24 00:00:00 CST 2012 + Timestamp date = Timestamp.valueOf(dateToStringLong(new Date())); + return date; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + // 2012-02-24 + + + } + + public static long geTimestamp(){ + + try { + // Fri Feb 24 00:00:00 CST 2012 + long date = System.currentTimeMillis(); + return date; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + // 2012-02-24 + + + } + + public static Date stringToDateShort(String time){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + Date date = null; + try { + // Fri Feb 24 00:00:00 CST 2012 + date = sdf.parse(time); + } catch (Exception e) { + e.printStackTrace(); + } + // 2012-02-24 + + return date; + } + + public static Date StringToDate(String time){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + Date date = null; + try { + // Fri Feb 24 00:00:00 CST 2012 + date = sdf.parse(time); + } catch (Exception e) { + e.printStackTrace(); + } + // 2012-02-24 + + return date; + } + + public static String getWeekOfDate(Date dt) { + String[] weekDays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"}; + Calendar cal = Calendar.getInstance(); + cal.setTime(dt); + + int w = cal.get(Calendar.DAY_OF_WEEK) - 1; + if (w < 0) + w = 0; + + return weekDays[w]; + } + + public static int getWeekIndexOfDate(Date dt) { + Calendar cal = Calendar.getInstance(); + cal.setTime(dt); + + int w = cal.get(Calendar.DAY_OF_WEEK) - 1; + if (w < 0) + w = 0; + + return w; + } + + public static int getMonthOfSystem(){ + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis( System.currentTimeMillis()); + int month = cal.get(Calendar.MONTH) + 1; + return month; + } + + public static int getYearOfSystem(){ + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis( System.currentTimeMillis()); + int year = cal.get(Calendar.YEAR); + return year; + } + + public static int getHourOfSystem(){ + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis( System.currentTimeMillis()); + int hour = cal.get(Calendar.HOUR_OF_DAY); + return hour; + } + + public static int getDayOfDate(Date date){ + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(date.getTime()); + int day = cal.get(Calendar.DAY_OF_MONTH); + return day; + } + + public static String getDayJiaByDay(int day){ + SimpleDateFormat dft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date beginDate = new Date(); + Calendar date = Calendar.getInstance(); + date.setTime(beginDate); + date.set(Calendar.DATE, date.get(Calendar.DATE) + day); + String endDate = dft.format(date.getTime()); + return endDate; + } + + public static String getDayJiaByDay(Date date1 ,int day){ + SimpleDateFormat dft = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date beginDate = date1; + Calendar date = Calendar.getInstance(); + date.setTime(beginDate); + date.set(Calendar.DATE, date.get(Calendar.DATE) + day); + String endDate = dft.format(date.getTime()); + return endDate; + } + + + public static String getDayShortJiaByDay(Date date1 ,int day){ + SimpleDateFormat dft = new SimpleDateFormat("yyyy-MM-dd"); + Date beginDate = date1; + Calendar date = Calendar.getInstance(); + date.setTime(beginDate); + date.set(Calendar.DATE, date.get(Calendar.DATE) + day); + String endDate = dft.format(date.getTime()); + return endDate; + } + + public static boolean belongCalendar(Date time, Date from, Date to) { + Calendar date = Calendar.getInstance(); + date.setTime(time); + + Calendar after = Calendar.getInstance(); + after.setTime(from); + + Calendar before = Calendar.getInstance(); + before.setTime(to); + + if (date.after(after) && date.before(before)) { + return true; + } else { + return false; + } + } + +// public static void main(String[] args) throws Exception { +// +// Date now = DateUtils.stringToDateShort("2017-10-18"); +// Date from = DateUtils.stringToDateShort("2017-10-18"); +// Date to = DateUtils.stringToDateShort("2017-10-18"); +// +// System.out.println(DateUtils.belongCalendar(now, from, to)); +// +// } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/utils/DemoUtils.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/utils/DemoUtils.java new file mode 100644 index 0000000..f940aef --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/utils/DemoUtils.java @@ -0,0 +1,104 @@ +package com.ruoyi.thirdparty.wechat.utils; + +import com.alibaba.fastjson2.JSON; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.*; + +public class DemoUtils { + public static String getRandom620(Integer length) { + String result = ""; + Random rand = new Random(); + int n = 20; + if (null != length && length > 0) { + n = length; + } + int randInt = 0; + for (int i = 0; i < n; i++) { + randInt = rand.nextInt(10); + + result += randInt; + } + return result; + } + + public static String sortMapByValues(Map map){ + String[] sortedKeys = map.keySet().toArray(new String[]{}); + Arrays.sort(sortedKeys);// 排序请求参数 + StringBuilder s2 = new StringBuilder(); + for (String key : sortedKeys) { + //if str(v).strip() is not None and k != 'paytype' and k != 'remark' and k != 'bankcode' and k != 'sign' and k != 'userkey' and k != 'apiurl': + if(!Objects.equals(key, "paytype") && !Objects.equals(key, "remark") && !Objects.equals(key, "bankcode") && !Objects.equals(key, "sign")){ + s2.append(key).append("=").append(map.get(key)).append("&"); + } + } + s2.deleteCharAt(s2.length() - 1); + return String.valueOf(s2); + } + + /** + * RestTemplate发送POST请求之formData形式 + * @return + */ + public static Map postFormData(MultiValueMap map, String url){ + RestTemplate restTemplate = new RestTemplate(); + HttpHeaders headers = new HttpHeaders(); + //头部类型 + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + //构造实体对象 + HttpEntity> param = new HttpEntity<>(map, headers); + //发起请求,服务地址,请求参数,返回消息体的数据类型 + ResponseEntity response = restTemplate.postForEntity(url, param, String.class); + //body + String body = response.getBody(); + if(body ==null){ + String location = Objects.requireNonNull(response.getHeaders().getLocation()).toASCIIString(); + Map m = new HashMap<>(); + m.put("url",location); + return m; + }else{ + Map result = JSON.parseObject(body, Map.class); + System.out.println("2"+result); + return result; + } + } + + /** + * MD5加密之方法一 + * @explain 借助apache工具类DigestUtils实现 + * @param str + * 待加密字符串 + * @return 16进制加密字符串 + */ + public static String encryptToMD5(String str) { + return DigestUtils.md5Hex(str); + } + + + + // 把json格式的字符串写到文件 + public static boolean writeFile(String filePath, String sets) { + FileWriter fw; + try { + fw = new FileWriter(filePath); + PrintWriter out = new PrintWriter(fw); + out.write(sets); + out.println(); + fw.close(); + out.close(); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/utils/GenerateUtils.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/utils/GenerateUtils.java new file mode 100644 index 0000000..73cfc47 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/utils/GenerateUtils.java @@ -0,0 +1,228 @@ +package com.ruoyi.thirdparty.wechat.utils; + +import java.text.SimpleDateFormat; +import java.util.*; + +public class GenerateUtils { + public static String getOrderIdByUUId() { + + SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmm"); + String first = df.format(new Date()); + System.out.println(first); + int hashCodeV = UUID.randomUUID().toString().hashCode(); + if (hashCodeV < 0) {//有可能是负数 + hashCodeV = -hashCodeV; + } + // 0 代表前面补充0 + // 4 代表长度为4 + // d 代表参数为正数型 + return first + String.format("%015d", hashCodeV); + } + + /** + * @author 作者:Jason E-mail: Jason_Lee_lizhen@outlook.com + * @copyright All Rights Reserved @version v1.0 + * @version 创建时间:2019年7月1日 上午9:56:16 + * @description方法说明 :获取UUID + * @arg 入参类型:*****;出参类型:String + */ + public static String generateUUid() { + String uuid = UUID.randomUUID().toString().replaceAll("-", ""); + + + return uuid; + } + + /** + * @author 作者:Jason E-mail: Jason_Lee_lizhen@outlook.com + * @copyright All Rights Reserved @version v1.0 + * @version 创建时间:2019年7月1日 上午9:58:40 + * @description方法说明 :生成商户号 + * @arg 入参类型:*****;出参类型:**** + */ + public static String generateStoreId() { + +// 注册年月日+CHAMBROAD+四位随机数 + //加上三位随机数 + String dateString = DateUtils.queryCurrentDay().replaceAll("-", ""); + Random random = new Random(); + int end4 = random.nextInt(9999); + String storeId = "store-" + dateString + "-LEE-" + end4; + + + return storeId; + } + + /** + * @author 作者:Jason E-mail: Jason_Lee_lizhen@outlook.com + * @copyright All Rights Reserved @version v1.0 + * @version 创建时间:2019年8月9日 上午10:34:08 + * @description方法说明 : 生成用户名 + * @arg 入参类型:*****;出参类型:**** + */ + public static String getStringRandom(int length) { + + String val = ""; + Random random = new Random(); + + //参数length,表示生成几位随机数 + for (int i = 0; i < length; i++) { + + String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num"; + //输出字母还是数字 + if ("char".equalsIgnoreCase(charOrNum)) { + //输出是大写字母还是小写字母 + int temp = random.nextInt(2) % 2 == 0 ? 65 : 97; + val += (char) (random.nextInt(26) + temp); + } else if ("num".equalsIgnoreCase(charOrNum)) { + val += String.valueOf(random.nextInt(10)); + } + } + return val; + } + + public static int Guid = 1; + + public static String guidDate = "2021070900"; + + public static int guidNew() { + SimpleDateFormat simpleDateFormatDay = new SimpleDateFormat("yyyyMMddHH"); + String outTradeDate = simpleDateFormatDay.format(new Date()); + if (!outTradeDate.equals(GenerateUtils.guidDate)) { + GenerateUtils.guidDate = outTradeDate; + GenerateUtils.Guid = 1; + return 1; + } + return GenerateUtils.Guid; + + + } + + /** + * @author 作者:Jason E-mail: Jason_Lee_lizhen@outlook.com + * @copyright All Rights Reserved @version v1.0 + * @version 创建时间:2021年7月12日 下午2:48:15 + * @description方法说明 : + * @arg 入参类型:*****workId String 分布式系统中传入机器码;出参类型:****String 生成的以yyyyMMddHH开头的有序单号 + */ + + public static String getGuid(String workId) { + + GenerateUtils.Guid += 1; + + int ran = 0; + + ran = GenerateUtils.guidNew(); + String guidIntStr = addZeroForNum(ran + "", 10); + + + return GenerateUtils.guidDate + guidIntStr + workId; + } + + public static String addZeroForNum(String str, int strLength) { + int strLen = str.length(); + if (strLen < strLength) { + while (strLen < strLength) { + StringBuffer sb = new StringBuffer(); + sb.append("0").append(str);//左补0 + str = sb.toString(); + strLen = str.length(); + } + } + return str; + } + + + public static List GuidList = new ArrayList(); + public static int GuidIndex = 0; + + + /** + * @author 作者:Jason E-mail: Jason_Lee_lizhen@outlook.com + * @copyright All Rights Reserved @version v1.0 + * @version 创建时间:2021年7月28日 上午10:54:24 + * @description方法说明 :获取最新下标 + * @arg 入参类型:*****;出参类型:**** + */ + public static int getGuidIndex() { + + SimpleDateFormat simpleDateFormatDay = new SimpleDateFormat("yyyyMMddHH"); + String outTradeDate = simpleDateFormatDay.format(new Date()); + if (!outTradeDate.equals(GenerateUtils.guidDate)) { + GenerateUtils.guidDate = outTradeDate; + Collections.shuffle(GuidList); + GenerateUtils.GuidIndex = 0; + } + return GenerateUtils.GuidIndex; + + } + + /** + * @author 作者:Jason E-mail: Jason_Lee_lizhen@outlook.com + * @copyright All Rights Reserved @version v1.0 + * @version 创建时间:2021年7月28日 上午10:54:50 + * @description方法说明 :获取无序单号 + * @arg 入参类型:*****;出参类型:****String 生成的以yyyyMMddHH开头的无序单号 + */ + public static String getGuidRand(String workId) { + + GenerateUtils.GuidIndex += 1; + + long now = System.currentTimeMillis(); + String info = now + ""; + int ran = 0; + + ran = GenerateUtils.getGuidIndex(); + String guidIntStr = GuidList.get(ran); + + + return GenerateUtils.guidDate + guidIntStr + workId; + } + + +// public static void main (String args[]) { +//// for(int i=0;i<999999;i++) { +//// GuidList.add(addZeroForNum(i+"",6)); +//// System.out.println(GuidList.get(i)); +//// } +// for(int i=0;i<999999;i++) { +// System.out.println(getGuidRand("")); +// try { +// Thread.sleep(5 * 1000); +// } catch (InterruptedException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// } +// +// +// } + + // 生成6位随机验证码 + public static String generateCode() { + Random randObj = new Random(); + return Integer.toString(100000 + randObj.nextInt(900000)); + } + + // 生成4位随机验证码 + public static String generateCode4() { + Random randObj = new Random(); + return Integer.toString(1000 + randObj.nextInt(9000)); + } + + + public static void main(String args[]) { + for (int i = 0; i < 100000000; i++) { + System.out.println(getGuid("")); + try { + Thread.sleep(5 * 1000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + } + + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/utils/GetIpUtil.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/utils/GetIpUtil.java new file mode 100644 index 0000000..18acbfc --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/wechat/utils/GetIpUtil.java @@ -0,0 +1,47 @@ +package com.ruoyi.thirdparty.wechat.utils; + +import com.ruoyi.common.utils.StringUtils; + +import jakarta.servlet.http.HttpServletRequest; + +public class GetIpUtil { + public static String getIpAddress(HttpServletRequest request) { + // 首先, 获取 X-Forwarded-For 中的 IP 地址,它在 HTTP 扩展协议中能表示真实的客户端 IP + String ipAddress = request.getHeader("X-Forwarded-For"); + if (StringUtils.isNotBlank(ipAddress) && !"unknown".equalsIgnoreCase(ipAddress)) { + // 多次反向代理后会有多个 ip 值,第一个 ip 才是真实 ip, 例: X-Forwarded-For: client, proxy1, proxy2,proxy… + int index = ipAddress.indexOf(","); + if (index != -1) { + return ipAddress.substring(0, index); + } + + return ipAddress; + } + + // 如果 X-Forwarded-For 获取不到, 就去获取 X-Real-IP + ipAddress = request.getHeader("X-Real-IP"); + if (StringUtils.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) { + // 如果 X-Real-IP 获取不到, 就去获取 Proxy-Client-IP + ipAddress = request.getHeader("Proxy-Client-IP"); + } + if (StringUtils.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) { + // 如果 Proxy-Client-IP 获取不到, 就去获取 WL-Proxy-Client-IP + ipAddress = request.getHeader("WL-Proxy-Client-IP"); + } + if (StringUtils.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) { + // 如果 WL-Proxy-Client-IP 获取不到, 就去获取 HTTP_CLIENT_IP + ipAddress = request.getHeader("HTTP_CLIENT_IP"); + } + if (StringUtils.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) { + // 如果 HTTP_CLIENT_IP 获取不到, 就去获取 HTTP_X_FORWARDED_FOR + ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (StringUtils.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) { + // 都获取不到, 最后才通过 request.getRemoteAddr() 获取IP + ipAddress = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ipAddress) ? "127.0.0.1" : ipAddress; + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/config/WinicConfig.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/config/WinicConfig.java new file mode 100644 index 0000000..a9a7f73 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/config/WinicConfig.java @@ -0,0 +1,17 @@ +package com.ruoyi.thirdparty.winic.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "winic") +public class WinicConfig { + + private String httpUrl; + + private String username; + + private String password; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/controller/WinicController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/controller/WinicController.java new file mode 100644 index 0000000..6a3c160 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/controller/WinicController.java @@ -0,0 +1,22 @@ +package com.ruoyi.thirdparty.winic.controller; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.thirdparty.winic.service.WinicService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/admin/winic") +public class WinicController extends BaseController { + + @Autowired + private WinicService winicService; + + @GetMapping("/test") + public AjaxResult test(String mobile, String content) { + return success(winicService.sendSms(mobile, content)); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/service/WinicService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/service/WinicService.java new file mode 100644 index 0000000..95c2783 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/service/WinicService.java @@ -0,0 +1,6 @@ +package com.ruoyi.thirdparty.winic.service; + +public interface WinicService { + + public String sendSms(String mobile, String content); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/service/impl/WinicServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/service/impl/WinicServiceImpl.java new file mode 100644 index 0000000..a30045d --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/service/impl/WinicServiceImpl.java @@ -0,0 +1,26 @@ +package com.ruoyi.thirdparty.winic.service.impl; + +import com.ruoyi.thirdparty.winic.config.WinicConfig; +import com.ruoyi.thirdparty.winic.service.WinicService; +import com.ruoyi.thirdparty.winic.util.WinicUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class WinicServiceImpl implements WinicService { + + @Autowired + private WinicConfig winicConfig; + + @Override + public String sendSms(String mobile, String content) { + StringBuffer httpArg = new StringBuffer(); + httpArg.append("id=").append(winicConfig.getUsername()).append("&"); + httpArg.append("pwd=").append(winicConfig.getPassword()).append("&"); + httpArg.append("to=").append(mobile).append("&"); + httpArg.append("content=").append(WinicUtil.encodeUrlString(content, "GB2312")); + + String result = WinicUtil.request(winicConfig.getHttpUrl(), httpArg.toString()); + return result.split("/", 2)[0]; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/util/WinicUtil.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/util/WinicUtil.java new file mode 100644 index 0000000..02c1fa4 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/winic/util/WinicUtil.java @@ -0,0 +1,54 @@ +package com.ruoyi.thirdparty.winic.util; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class WinicUtil { + + public static String encodeUrlString(String str, String charset) { + String strret = null; + if (str == null) + return str; + try { + strret = java.net.URLEncoder.encode(str, charset); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return strret; + } + + public static String request(String httpUrl, String httpArg) { + BufferedReader reader = null; + String result = null; + StringBuffer sbf = new StringBuffer(); + httpUrl = httpUrl + "?" + httpArg; + + try { + URL url = new URL(httpUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.connect(); + InputStream is = connection.getInputStream(); + reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); + String strRead = reader.readLine(); + if (strRead != null) { + sbf.append(strRead); + while ((strRead = reader.readLine()) != null) { + sbf.append("\n"); + sbf.append(strRead); + } + } + reader.close(); + result = sbf.toString(); + } catch (Exception e) { + e.printStackTrace(); + } + return result; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/config/XinghuoPayConfig.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/config/XinghuoPayConfig.java new file mode 100644 index 0000000..96c035b --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/config/XinghuoPayConfig.java @@ -0,0 +1,36 @@ +package com.ruoyi.thirdparty.xinghuopay.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "xinghuopay") +public class XinghuoPayConfig { + + /** + * 服务器URL + */ + private String serverUrl; + + /** + * 商户ID + */ + private String pid; + + /** + * 签名 + */ + private String sign; + + /** + * 通知URL + */ + private String notifyUrl; + + /** + * 返回URL + */ + private String returnUrl; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/controller/XinghuoPayController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/controller/XinghuoPayController.java new file mode 100644 index 0000000..37b4646 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/controller/XinghuoPayController.java @@ -0,0 +1,51 @@ +package com.ruoyi.thirdparty.xinghuopay.controller; + +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.UserPermission; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.thirdparty.xinghuopay.service.XinghuoPayService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +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.Map; + +@Api(tags = "XinghuoPay") +@Slf4j +@RestController +@RequestMapping("api/xinghuopay") +public class XinghuoPayController extends BaseController { + + @Autowired + private TtUserService ttUserService; + + @Autowired + private XinghuoPayService xinghuoService; + + @ApiOperation("支付") + @UserPermission + @PostMapping(value = "/pay") + public R pay(@RequestBody @Validated CreateOrderParam param) { + Long userId = SecurityUtils.getUserId(); + TtUser ttUser = ttUserService.getById(userId); + // 是否实名认证(0未认证 1已认证) + if ("0".equals(ttUser.getIsRealCheck())) { + return R.fail("未实名认证"); + } + String ip = IpUtils.getIpAddr(); + return xinghuoService.pay(param, ttUser, ip); + } + + @GetMapping("/notify") + public String notify(@RequestParam Map params) { + return xinghuoService.notify(params); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/domain/XinghuoPayResponse.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/domain/XinghuoPayResponse.java new file mode 100644 index 0000000..4257e5c --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/domain/XinghuoPayResponse.java @@ -0,0 +1,19 @@ +package com.ruoyi.thirdparty.xinghuopay.domain; + +import lombok.Data; + +@Data +public class XinghuoPayResponse { + + private String code; + + private String msg; + + private String tradeNo; + + private String payurl; + + private String qrcode; + + private String urlscheme; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/service/XinghuoPayService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/service/XinghuoPayService.java new file mode 100644 index 0000000..1cde8cb --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/service/XinghuoPayService.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.xinghuopay.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; + +import java.util.Map; + +public interface XinghuoPayService { + + R pay(CreateOrderParam param, TtUser ttUser, String ip); + + String notify(Map params); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/service/impl/XinghuoPayServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/service/impl/XinghuoPayServiceImpl.java new file mode 100644 index 0000000..f05f0ae --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/service/impl/XinghuoPayServiceImpl.java @@ -0,0 +1,387 @@ +package com.ruoyi.thirdparty.xinghuopay.service.impl; + +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.common.constant.PayOrderStatus; +import com.ruoyi.domain.common.constant.PayType; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.TtPromotionLevel; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.domain.other.TtRechargeRecord; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.common.service.RechargeSuccessfulNoticeService; +import com.ruoyi.thirdparty.xinghuopay.config.XinghuoPayConfig; +import com.ruoyi.thirdparty.xinghuopay.domain.XinghuoPayResponse; +import com.ruoyi.thirdparty.xinghuopay.service.XinghuoPayService; +import com.ruoyi.thirdparty.xinghuopay.util.XinghuoPayUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.*; + +@Slf4j +@Service +public class XinghuoPayServiceImpl implements XinghuoPayService { + + @Autowired + private TtRechargeProdMapper rechargeProdMapper; + + @Autowired + private XinghuoPayConfig xinghuoPayConfig; + + @Autowired + private TtOrderMapper orderMapper; + + @Autowired + private TtUserMapper userMapper; + + @Autowired + private TtRechargeProdMapper rechargeListMapper; + + @Autowired + private TtUserService userService; + + @Autowired + private TtUserBlendErcashMapper userBlendErcashMapper; + + @Autowired + private RechargeSuccessfulNoticeService rechargeSuccessfulNoticeService; + + @Autowired + private TtRechargeRecordMapper ttRechargeRecordMapper; + + @Autowired + private ISysConfigService configService; + + @Autowired + private TtPromotionLevelMapper ttPromotionLevelMapper; + + @Autowired + private TtFirstRechargeMapper ttFirstRechargeMapper; + + @Override + public R pay(CreateOrderParam param, TtUser ttUser, String ip) { + // 1.查询商品信息 + TtRechargeProd goods = new LambdaQueryChainWrapper<>(rechargeProdMapper) + .eq(TtRechargeProd::getId, param.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + if (ObjectUtil.isEmpty(goods)) { + return R.fail("不存在的商品"); + } + if (goods.getPrice().compareTo(param.getGoodsPrice()) != 0) { + return R.fail("商品价格不一致"); + } + + // 计算总价值 + BigDecimal totalAmount = param.getGoodsPrice().multiply(new BigDecimal(param.getGoodsNum())); + + // 2.构建支付参数 + Map map = new HashMap<>(); + map.put("pid", xinghuoPayConfig.getPid()); + map.put("type", "alipay"); + Snowflake snowflake = IdUtil.getSnowflake(1, 1); + String orderId = String.valueOf(snowflake.nextId()); + map.put("out_trade_no", orderId); + map.put("notify_url", xinghuoPayConfig.getNotifyUrl()); + map.put("return_url", xinghuoPayConfig.getReturnUrl()); + map.put("name", goods.getName()); + map.put("money", String.valueOf(totalAmount)); + map.put("clientip", ip); + String key = xinghuoPayConfig.getSign(); + XinghuoPayUtil signUtil = new XinghuoPayUtil(key); + String sign = signUtil.getSign(map); + map.put("sign", sign); + map.put("sign_type", "MD5"); + + // 3.发送请求 + HttpRequest post = HttpUtil.createPost(xinghuoPayConfig.getServerUrl()); + post.header("Content-Type","application/x-www-form-urlencoded"); + post.formStr(map); + HttpResponse res = post.execute(); + + // 4.解析响应 + XinghuoPayResponse resBody = JSONUtil.toBean(res.body(), XinghuoPayResponse.class); + if (!resBody.getCode().equals("1")) { + return R.fail(resBody.getMsg()); + } + + // 5.创建订单 + TtOrder order = new TtOrder(); + order.setOrderId(orderId); + order.setOutTradeNo(resBody.getTradeNo()); + order.setUserId(ttUser.getUserId()); + order.setType(PayType.XIN_HUO_ZFB.getCode()); + order.setGoodsId(param.getGoodsId()); + order.setGoodsPrice(param.getGoodsPrice()); + order.setGoodsNum(param.getGoodsNum()); + order.setTotalAmount(totalAmount); + order.setSign(sign); + order.setStatus(PayOrderStatus.NO_PAY.getCode()); + order.setCreateTime(new Date()); + orderMapper.insert(order); + + return R.ok(resBody); + } + + @Override + public String notify(Map params) { + log.info("支付成功通知"); + String result = "failure"; + + // 校验 + String tradeStatus = params.get("trade_status"); + if (!"TRADE_SUCCESS".equals(tradeStatus)) { + log.error("交易失败"); + return result; + } + + // 1.查询订单信息 + String outTradeNo = params.get("trade_no"); + TtOrder order = new LambdaQueryChainWrapper<>(orderMapper) + .eq(TtOrder::getOutTradeNo, outTradeNo) + .eq(TtOrder::getStatus, PayOrderStatus.NO_PAY.getCode()) + .one(); + // 防止重复通知 + if (!PayOrderStatus.NO_PAY.getCode().equals(order.getStatus())) { + return result; + } + + // 2.查询用户信息 + TtUser user = new LambdaQueryChainWrapper<>(userMapper) + .eq(TtUser::getUserId, order.getUserId()) + .eq(TtUser::getDelFlag, 0) + .one(); + + // 3.查询商品信息 + TtRechargeProd goods = new LambdaQueryChainWrapper<>(rechargeListMapper) + .eq(TtRechargeProd::getId, order.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + + // 4.账户结算 + if (ObjectUtil.isNull(goods.getProductA())) goods.setProductA(BigDecimal.ZERO); + if (ObjectUtil.isNull(goods.getProductC())) goods.setProductC(BigDecimal.ZERO); + payNotifyAccounting(order, user, goods, order.getGoodsNum()); + + try { + // 首充赠送 + firstChargeGiftAmount(user, goods, order.getGoodsNum()); + + // 推广等级充值赠送 + promotionLevelChargeGiftAmount(user, goods, order.getGoodsNum()); + + // 推广充值返佣 + promotionChargeCommission(user, goods, order.getGoodsNum()); + } catch (Exception e) { + e.printStackTrace(); + } + + // 5.更新订单 + boolean update = new LambdaUpdateChainWrapper<>(orderMapper) + .eq(TtOrder::getId, order.getId()) + .set(TtOrder::getStatus, PayOrderStatus.PAY_COMPLE.getCode()) + .set(TtOrder::getUpdateTime, new Date()) + .update(); + if (update) { + result = "success"; + } + + // 6.发送充值通知 + rechargeSuccessfulNoticeService.sendRechargeSuccessNotice(order.getUserId().toString(), order.getGoodsPrice()); + + return result; + } + + // 账户结算 + public void payNotifyAccounting(TtOrder order, TtUser user, TtRechargeProd goods, Integer goodsNumber) { + + // 加钱 + // BigDecimal totalAmount = order.getTotalAmount(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal totalCredits = goods.getProductC().multiply(new BigDecimal(goodsNumber)); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, user.getUserId()) + .setSql("account_amount = account_amount + " + totalAmount.toString() + + ",account_credits = account_credits + " + totalCredits.toString()); + userService.update(userUpdate); + + user = userService.getById(user.getUserId()); + // 综合消费日志 + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(user.getUserId()) + + .amount(totalAmount.compareTo(BigDecimal.ZERO) > 0 ? totalAmount : null) + .finalAmount(totalAmount.compareTo(BigDecimal.ZERO) > 0 ? user.getAccountAmount().add(totalAmount) : null) + + .credits(totalCredits.compareTo(BigDecimal.ZERO) > 0 ? totalCredits : null) + .finalCredits(totalCredits.compareTo(BigDecimal.ZERO) > 0 ? user.getAccountCredits().add(totalCredits) : null) + + .total(totalAmount.add(totalCredits)) // 收支合计 + + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.RECHARGE.getCode()) + .remark(TtAccountRecordSource.RECHARGE.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + userBlendErcashMapper.insert(blendErcash); + + // 充值记录 + TtRechargeRecord ttRechargeRecord = TtRechargeRecord.builder().build(); + ttRechargeRecord.setUserId(order.getUserId()); + ttRechargeRecord.setParentId(user.getParentId()); + ttRechargeRecord.setArrivalAmount(totalAmount); + ttRechargeRecord.setAmountActuallyPaid(totalAmount); + ttRechargeRecord.setFinallyPrice(user.getAccountAmount()); + ttRechargeRecord.setOrderId(order.getOrderId()); + ttRechargeRecord.setOutTradeNo(order.getOutTradeNo()); + ttRechargeRecord.setStatus("0"); + ttRechargeRecord.setChannelType("1"); + ttRechargeRecord.setCreateTime(DateUtils.getNowDate()); + ttRechargeRecordMapper.insert(ttRechargeRecord); + + // 更新VIP等级 + userService.updateUserVIPLevel(user.getUserId()); + } + + /** + * 首充赠送 + */ + private void firstChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + // 1.判断是否为首充 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .eq(TtUserBlendErcash::getSource, 1) + .eq(TtUserBlendErcash::getUserId, ttUser.getUserId()); + List ttUserBlendErcashes = userBlendErcashMapper.selectList(wrapper); + if (ttUserBlendErcashes.size() > 0) { + return; + } + + // 2.加钱 + // 充值金额 + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + // 查询赠送比例 + BigDecimal firstChargeAmountRatio = ttFirstRechargeMapper.selectRatioByMinAmount(totalAmount); + if (Objects.isNull(firstChargeAmountRatio)) { + return; + } + // 计算赠送金额 + BigDecimal giftAmount = totalAmount.multiply(firstChargeAmountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 3.记录 + ttUser = userService.getById(ttUser.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.FIRST_CHARGE.getCode()) + .remark(TtAccountRecordSource.FIRST_CHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } + + /** + * 推广等级充值赠送 + */ + private void promotionLevelChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + // 1.查询用户推广等级对应的赠送比例 + TtPromotionLevel ttPromotionLevel = ttPromotionLevelMapper.selectById(ttUser.getPromotionLevel()); + if (Objects.isNull(ttPromotionLevel)) + return; + + // 2.加钱 + BigDecimal amountRatio = ttPromotionLevel.getRechargeGiftRatio(); + if (Objects.isNull(amountRatio)) + return; + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal giftAmount = totalAmount.multiply(amountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 3.记录 + ttUser = userService.getById(ttUser.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_LEVEL_CHARGE.getCode()) + .remark(TtAccountRecordSource.PROMOTION_LEVEL_CHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } + + /** + * 推广充值返佣 + */ + private void promotionChargeCommission(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + if (Objects.isNull(ttUser.getParentId())) { + return; + } + // 查询用户推广等级信息 + TtPromotionLevel ttPromotionLevel = ttPromotionLevelMapper.selectById(ttUser.getPromotionLevel()); + + // 给上级加钱 + BigDecimal amountRatio = ttPromotionLevel.getCommissions(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal giftAmount = totalAmount.multiply(amountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getParentId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 记录 + ttUser = userService.getById(ttUser.getParentId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_CHARGE_COMMISSION.getCode()) + .remark(TtAccountRecordSource.PROMOTION_CHARGE_COMMISSION.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/util/XinghuoPayUtil.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/util/XinghuoPayUtil.java new file mode 100644 index 0000000..89ab5ae --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/xinghuopay/util/XinghuoPayUtil.java @@ -0,0 +1,76 @@ +package com.ruoyi.thirdparty.xinghuopay.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Map; +import java.util.TreeMap; + +public class XinghuoPayUtil { + + private String key; + + public XinghuoPayUtil(String key) { + this.key = key; + } + + /** + * 生成MD5签名 + * + * @param params 参数列表 + * @return 生成的签名 + */ + public String getSign(Map params) { + // 排序参数 + Map sortedParams = new TreeMap<>(params); + sortedParams.remove("sign"); + sortedParams.remove("sign_type"); + + // 构建签名字符串 + StringBuilder signStr = new StringBuilder(); + for (Map.Entry entry : sortedParams.entrySet()) { + if (entry.getValue() != null && !entry.getValue().isEmpty()) { + signStr.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); + } + } + + if (signStr.length() > 0) { + signStr.setLength(signStr.length() - 1); // 移除最后一个& + } + + // 添加商户密钥 + signStr.append(this.key); + + // 计算MD5签名 + return md5(signStr.toString()).toLowerCase(); + } + + /** + * 计算MD5哈希值 + * + * @param input 输入字符串 + * @return MD5哈希值 + */ + private static String md5(String input) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] messageDigest = md.digest(input.getBytes()); + return bytesToHex(messageDigest); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5 algorithm not found", e); + } + } + + /** + * 将字节数组转换为十六进制字符串 + * + * @param bytes 字节数组 + * @return 十六进制字符串 + */ + private static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/config/YYConfig.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/config/YYConfig.java new file mode 100644 index 0000000..ad0a1ef --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/config/YYConfig.java @@ -0,0 +1,70 @@ +package com.ruoyi.thirdparty.yyyouping.config; + +import com.ruoyi.thirdparty.yyyouping.utils.YYClient; +import com.ruoyi.thirdparty.yyyouping.utils.common.RSAUtils; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "yy-youping") +public class YYConfig { + + private String appKey; + private String publicKey; + private String privateKey; + + public static String BaseUrl = "https://gw-openapi.youpin898.com"; + /** + * 用户相关接口 + */ + public static final String ApiGetAssetsInfo = "/open/v1/api/getAssetsInfo"; + public static final String ApiCheckTradeUrl = "/open/v1/api/checkTradeUrl"; + public static final String ApiDetailDataQueryAplly = "/open/v1/api/detailDataQueryAplly"; + public static final String ApiDetailDataQueryResult = "/open/v1/api/detailDataQueryResult"; + + /** + * 市场查询接口 + */ + public static final String ApiTemplateQuery = "/open/v1/api/templateQuery"; + public static final String ApiGoodsQuery = "/open/v1/api/goodsQuery"; + public static final String ApiBatchGetOnSaleCommodityInfo = "/open/v1/api/batchGetOnSaleCommodityInfo"; + public static final String ApiQueryTemplateSaleByCategory = "/open/v1/api/queryTemplateSaleByCategory"; + public static final String ApiQueryViewChart = "/open/v1/api/queryViewChart"; + + /** + * 购买接口 + */ + public static final String ApiByTemplateCreateOrder = "/open/v1/api/byTemplateCreateOrder"; + public static final String ApiByGoodsIdCreateOrder = "/open/v1/api/byGoodsIdCreateOrder"; + public static final String ApiByTemplateAsyncCreateOrder = "/open/v1/api/byTemplateAsyncCreateOrder"; + public static final String ApiByGoodsIdAsyncCreateOrder = "/open/v1/api/byGoodsIdAsyncCreateOrder"; + + /** + * 买家订单接口 + */ + public static final String ApiOrderCancel = "/open/v1/api/orderCancel"; + public static final String ApiOrderStatus = "/open/v1/api/orderStatus"; + public static final String ApiOrderInfo = "/open/v1/api/orderInfo"; + + /** + * 出售接口 + */ + public static final String ApiGetUserSteamInventoryData = "/open/v1/api/getUserSteamInventoryData"; + public static final String ApiGetUserOnSaleCommodityData = "/open/v1/api/getUserOnSaleCommodityData"; + + /** + * 卖家订单接口 + */ + public static final String ApiSellerQueryOrderList = "/open/v1/api/sellerQueryOrderList"; + public static final String ApiSellerOrderStatus = "/open/v1/api/sellerOrderStatus"; + public static final String ApiSellerOrderDetail = "/open/v1/api/sellerOrderDetail"; + + @Bean + public RSAUtils yyClient(){ + return new RSAUtils(appKey,publicKey,privateKey); + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/controller/yyyoupingController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/controller/yyyoupingController.java new file mode 100644 index 0000000..7df3339 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/controller/yyyoupingController.java @@ -0,0 +1,117 @@ +package com.ruoyi.thirdparty.yyyouping.controller; + + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.ruoyi.admin.service.TtOrnamentYYService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.TtOrnamentsYY; +import com.ruoyi.thirdparty.yyyouping.service.yyyoupingService; +import com.ruoyi.thirdparty.yyyouping.utils.YYClient; +import com.ruoyi.thirdparty.yyyouping.utils.common.YYResult; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +@Api(tags = "YYYouPing") +@RestController +@RequestMapping("api/yyyouping") +public class yyyoupingController { + + @Autowired + private yyyoupingService yyyoupingService; + + @Data + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static class Param { + private Integer templateId; + private String templateHashName; + } + + @Autowired + private TtOrnamentYYService ttOrnamentYYService; + + // 加载平台的饰品数据 + @ApiOperation("加载平台的饰品数据") + @GetMapping("loadData") + public R loadData(){ + // 读json + File file = new File("C:\\Users\\Administrator\\Desktop\\response.json"); + JSONArray array = JSONUtil.readJSONArray(file, StandardCharsets.UTF_8); + List list = JSONUtil.toList(array, TtOrnamentsYY.class); + // 写db + ttOrnamentYYService.saveBatch(list); + return R.ok(); + } + + // 通过此接口可获取悠悠有品所有商品对应的模板ID的下载链接。 + @ApiOperation("查询模板ID下载链接") + @GetMapping("yyApiTemplateQuery") + public R yyApiTemplateQuery(){ + YYResult yyResult = yyyoupingService.yyApiTemplateQuery(new HashMap<>()); + if (!yyResult.getCode().equals(0)) return R.fail(yyResult); + return R.ok(yyResult); + } + + @ApiOperation("查询商品列表") + @PostMapping("yyApiGoodsQuery") + public R yyApiGoodsQuery(@RequestBody Map map){ + yyyoupingService.yyApiGoodsQuery(map); + return R.ok(); + } + + @ApiOperation("余额查询") + @PostMapping("yyApiGetAssetsInfo") + public R yyApiGetAssetsInfo(){ + YYResult yyResult = yyyoupingService.yyApiGetAssetsInfo(); + if (!yyResult.getCode().equals(0)) return R.fail(yyResult); + return R.ok(yyResult); + } + + @ApiOperation("批量查询模板饰品的在售列表") + @PostMapping("/yyApiBatchGetOnSaleCommodityInfo") + public R balance(@RequestBody List ids) { + ArrayList list = new ArrayList<>(); + HashMap param = new HashMap<>(); + for (String id:ids){ + HashMap map = new HashMap<>(); + map.put("templateId",id); + list.add(map); + } + param.put("requestList",list); + YYResult yyResult = yyyoupingService.yyApiBatchGetOnSaleCommodityInfo(param); + return R.ok(yyResult); + } + + @ApiOperation("按类别查询模板销售") + @GetMapping("/yyApiQueryTemplateSaleByCategory") + public R yyApiQueryTemplateSaleByCategory() throws Exception { + HashMap map = new HashMap<>(); + + // map.put("typeHashName","FAMAS | Commemoration (Factory New)"); + map.put("weaponHashName","FAMAS | Commemoration (Factory New)"); + map.put("page",1); + map.put("size",10); + + // return yyClient.yyApiQueryTemplateSaleByCategory(map); + + return R.ok(); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/service/Impl/yyyoupingServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/service/Impl/yyyoupingServiceImpl.java new file mode 100644 index 0000000..644ec62 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/service/Impl/yyyoupingServiceImpl.java @@ -0,0 +1,176 @@ +package com.ruoyi.thirdparty.yyyouping.service.Impl; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson2.JSON; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ruoyi.thirdparty.yyyouping.config.YYConfig; +import com.ruoyi.thirdparty.yyyouping.service.yyyoupingService; +import com.ruoyi.thirdparty.yyyouping.utils.common.RSAUtils; +import com.ruoyi.thirdparty.yyyouping.utils.common.YYResult; +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.PostMapping; + +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.Map; + +import static com.ruoyi.thirdparty.yyyouping.config.YYConfig.*; + + +@Slf4j +@Service +public class yyyoupingServiceImpl implements yyyoupingService { + + @Autowired + private YYConfig yyConfig; + + @Autowired + private RSAUtils rsaUtils; + + // 添加基本请求参数 + private Map createParam(Map param) { + try { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + param.put("appKey", yyConfig.getAppKey()); + param.put("timestamp", dateFormat.format(new Timestamp(System.currentTimeMillis()))); + param.put("sign", rsaUtils.getSign(param)); + }catch (Exception e){ + log.warn("参数构造异常"); + } + return param; + } + + + public YYResult yyApiBatchGetOnSaleCommodityInfo(Map param) { + + Map p = createParam(param); + HttpRequest post = HttpUtil.createPost(BaseUrl + ApiBatchGetOnSaleCommodityInfo); + post.body(JSONUtil.toJsonStr(p)); + HttpResponse res = post.execute(); + String body = res.body(); + + return JSONUtil.toBean(body, YYResult.class); + + } + + // 通过此接口可获取悠悠有品所有商品对应的模板ID的下载链接。 + public YYResult yyApiTemplateQuery(Map param) { + + try { + + Map p = createParam(param); + + HttpRequest post = HttpUtil.createPost(BaseUrl + ApiTemplateQuery); + post.body(JSON.toJSONString(p), "json"); + String body = post.execute().body(); + + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(body, YYResult.class); + + } catch (Exception e) { + log.warn("yyApiTemplateQuery"); + return YYResult.builder().code(005).msg("yyApiTemplateQuery").build(); + } + } + + + public YYResult yyApiQueryTemplateSaleByCategory(Map param) { + + Map p = createParam(param); + String res = HttpUtil.post(BaseUrl + ApiQueryTemplateSaleByCategory, p); + ObjectMapper objectMapper = new ObjectMapper(); + + try { + return objectMapper.readValue(res, YYResult.class); + }catch (Exception e){ + log.warn("响应结果解析异常"); + return YYResult.builder().code(005).msg("响应结果解析异常").build(); + } + } + + public YYResult yyApiGetAssetsInfo() { + + try { + + Map p = createParam(new HashMap()); + + HttpRequest post = HttpUtil.createPost(BaseUrl + ApiGetAssetsInfo); + post.body(JSON.toJSONString(p),"json"); + String body = post.execute().body(); + + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(body, YYResult.class); + + } catch (Exception e) { + log.warn("yyApiGetAssetsInfo"); + return YYResult.builder().code(005).msg("yyApiGetAssetsInfo").build(); + } + } + + public YYResult yyApiGoodsQuery(Map param) { + + try { + + Map p = createParam(param); + String res = HttpUtil.post(BaseUrl + ApiGoodsQuery, p); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(res, YYResult.class); + + } catch (Exception e) { + log.warn("参数构造异常"); + return YYResult.builder().code(005).msg("参数构造异常").build(); + } + + } + + public YYResult yyApiByGoodsIdCreateOrder(Map param) { + try { + + Map p = createParam(param); + String res = HttpUtil.post(BaseUrl + ApiByGoodsIdCreateOrder, p); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(res, YYResult.class); + + } catch (Exception e) { + log.warn("参数构造异常"); + return YYResult.builder().code(005).msg("参数构造异常").build(); + } + } + + public YYResult yyApiOrderInfo(Map param) { + try { + + Map p = createParam(param); + String res = HttpUtil.post(BaseUrl + ApiOrderInfo, p); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(res, YYResult.class); + + } catch (Exception e) { + log.warn("参数构造异常"); + return YYResult.builder().code(005).msg("参数构造异常").build(); + } + } + + public YYResult yyApiGetUserSteamInventoryData(Map param) { + try { + + Map p = createParam(param); + String res = HttpUtil.post(BaseUrl + ApiGetUserSteamInventoryData, p); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(res, YYResult.class); + + } catch (Exception e) { + log.warn("参数构造异常"); + return YYResult.builder().code(005).msg("参数构造异常").build(); + } + } + + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/service/yyyoupingService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/service/yyyoupingService.java new file mode 100644 index 0000000..f9cc36f --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/service/yyyoupingService.java @@ -0,0 +1,23 @@ +package com.ruoyi.thirdparty.yyyouping.service; + +import com.ruoyi.thirdparty.yyyouping.config.YYConfig; +import com.ruoyi.thirdparty.yyyouping.utils.common.YYResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Service; + +import java.util.Map; + +public interface yyyoupingService { + + YYResult yyApiBatchGetOnSaleCommodityInfo(Map param); + YYResult yyApiTemplateQuery(Map param); + YYResult yyApiQueryTemplateSaleByCategory(Map param); + YYResult yyApiGetAssetsInfo(); + YYResult yyApiGoodsQuery(Map param); + YYResult yyApiByGoodsIdCreateOrder(Map param); + YYResult yyApiOrderInfo(Map param); + YYResult yyApiGetUserSteamInventoryData(Map param); + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/YYClient.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/YYClient.java new file mode 100644 index 0000000..5157347 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/YYClient.java @@ -0,0 +1,256 @@ +package com.ruoyi.thirdparty.yyyouping.utils; + +import com.alibaba.fastjson2.JSON; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ruoyi.thirdparty.yyyouping.utils.common.RSAUtils; +import com.ruoyi.thirdparty.yyyouping.utils.common.YYResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Map; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + + +@Slf4j +public class YYClient { + + private RSAUtils rsaUtils; + + private static String BaseUrl = "https://gw-openapi.youpin898.com"; + + /** + * 用户相关接口 + */ + private static String ApiGetAssetsInfo = "/open/v1/api/getAssetsInfo"; + private static String ApiCheckTradeUrl = "/open/v1/api/checkTradeUrl"; + private static String ApiDetailDataQueryAplly = "/open/v1/api/detailDataQueryAplly"; + private static String ApiDetailDataQueryResult = "/open/v1/api/detailDataQueryResult"; + + /** + * 市场查询接口 + */ + private static String ApiTemplateQuery = "/open/v1/api/templateQuery"; + private static String ApiGoodsQuery = "/open/v1/api/goodsQuery"; + private static String ApiBatchGetOnSaleCommodityInfo = "/open/v1/api/batchGetOnSaleCommodityInfo"; + private static String ApiQueryTemplateSaleByCategory = "/open/v1/api/queryTemplateSaleByCategory"; + private static String ApiQueryViewChart = "/open/v1/api/queryViewChart"; + + /** + * 购买接口 + */ + private static String ApiByTemplateCreateOrder = "/open/v1/api/byTemplateCreateOrder"; + private static String ApiByGoodsIdCreateOrder = "/open/v1/api/byGoodsIdCreateOrder"; + private static String ApiByTemplateAsyncCreateOrder = "/open/v1/api/byTemplateAsyncCreateOrder"; + private static String ApiByGoodsIdAsyncCreateOrder = "/open/v1/api/byGoodsIdAsyncCreateOrder"; + + /** + * 买家订单接口 + */ + private static String ApiOrderCancel = "/open/v1/api/orderCancel"; + private static String ApiOrderStatus = "/open/v1/api/orderStatus"; + private static String ApiOrderInfo = "/open/v1/api/orderInfo"; + + /** + * 出售接口 + */ + private static String ApiGetUserSteamInventoryData = "/open/v1/api/getUserSteamInventoryData"; + private static String ApiGetUserOnSaleCommodityData = "/open/v1/api/getUserOnSaleCommodityData"; + + /** + * 卖家订单接口 + */ + private static String ApiSellerQueryOrderList = "/open/v1/api/sellerQueryOrderList"; + private static String ApiSellerOrderStatus = "/open/v1/api/sellerOrderStatus"; + private static String ApiSellerOrderDetail = "/open/v1/api/sellerOrderDetail"; + + + public YYClient(String appKey,String publicKey,String privateKey){ + this.rsaUtils = new RSAUtils(appKey,publicKey,privateKey); + } + + public String yyApiTemplateQuery(Map param) { + Map p = null; + try { + p = createParam(param); + }catch (Exception e){ + log.warn("YYClient 参数构造异常"); + return ""; + } + + return doPostToJson(BaseUrl+ApiTemplateQuery, JSON.toJSONString(p)); + } + + //ApiBatchGetOnSaleCommodityInfo + public YYResult yyApiBatchGetOnSaleCommodityInfo(Map param) { + + try { + Map p = createParam(param); + + String s = doPostToJson(BaseUrl + ApiBatchGetOnSaleCommodityInfo, JSON.toJSONString(p)); + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(s, YYResult.class); + + }catch (Exception e){ + log.warn("YYClient 参数构造异常"); + return YYResult.builder().code(005).msg("YYClient 参数构造异常").build(); + } + } + + public String yyApiQueryTemplateSaleByCategory(Map param) { + Map p = null; + try { + p = createParam(param); + }catch (Exception e){ + log.warn("YYClient 参数构造异常"); + return ""; + } + + return doPostToJson(BaseUrl+ApiQueryTemplateSaleByCategory, JSON.toJSONString(p)); + } + + public String yyApiGetAssetsInfo(Map param) throws Exception { + Map p = createParam(param); + return doPostToJson(BaseUrl+ApiGetAssetsInfo, JSON.toJSONString(p)); + } + + public String yyApiGoodsQuery(Map param) { + + Map p = null; + try { + p = createParam(param); + }catch (Exception e){ + log.warn("参数构建异常"); + return ""; + } + + return doPostToJson(BaseUrl+ApiGoodsQuery,JSON.toJSONString(p)); + } + + public String yyApiByGoodsIdCreateOrder(Map param) throws Exception { + Map p = createParam(param); + return doPostToJson(BaseUrl+ApiByGoodsIdCreateOrder,JSON.toJSONString(p)); + } + + public String yyApiOrderInfo(Map param) throws Exception { + Map p = createParam(param); + return doPostToJson(BaseUrl+ApiOrderInfo,JSON.toJSONString(p)); + } + + public String yyApiGetUserSteamInventoryData(Map param) throws Exception { + Map p = createParam(param); + return doPostToJson(BaseUrl+ApiGetUserSteamInventoryData,JSON.toJSONString(p)); + } + + private Map createParam(Map param) throws Exception { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + param.put("appKey",rsaUtils.getAppKey()); + param.put("timestamp",dateFormat.format(new Timestamp(System.currentTimeMillis()))); + param.put("sign",rsaUtils.getSign(param)); + return param; + } + + // 基础post请求 + public static String doPostToJson(String urlPath, String Json) { + + String result = ""; + BufferedReader reader = null; + HttpURLConnection conn = null; + try { + trustAllHosts(); + URL url = new URL(urlPath); + if (url.getProtocol().toLowerCase().equals("https")) { + HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection(); + httpsConn.setHostnameVerifier(DO_NOT_VERIFY); + conn = httpsConn; + } else { + conn = (HttpURLConnection) url.openConnection(); + } + + conn.setRequestMethod("POST"); + conn.setDoOutput(true); + conn.setDoInput(true); + conn.setUseCaches(false); + conn.setRequestProperty("Connection", "Keep-Alive"); + conn.setRequestProperty("Charset", "UTF-8"); + conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + // conn.setRequestProperty("accept","*/*"); + conn.setRequestProperty("accept", "application/json"); + if (Json != null) { + byte[] writebytes = Json.getBytes(); + conn.setRequestProperty("Content-Length", String.valueOf(writebytes.length)); + OutputStream outwritestream = conn.getOutputStream(); + outwritestream.write(Json.getBytes()); + outwritestream.flush(); + outwritestream.close(); + } + + if (conn.getResponseCode() == 200) { + reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8")); + result = reader.readLine(); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return result; + } + + final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { + public boolean verify(String arg0, SSLSession arg1) { + return true; + } + }; + + public static void trustAllHosts() { + TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + + public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { + + } + + public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { + + } + } + + }; + + try { + SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, trustAllCerts, new SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } catch (Exception e) { + e.printStackTrace(); + } + + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/BaseException.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/BaseException.java new file mode 100644 index 0000000..1c91149 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/BaseException.java @@ -0,0 +1,150 @@ +package com.ruoyi.thirdparty.yyyouping.utils.common; + +public class BaseException extends RuntimeException { + private Status status; + private String message; + private Throwable e; + + public BaseException(Status status) { + super(status.getMsg()); + this.status = status; + this.message = status.getMsg(); + } + + public BaseException(Status status, String message) { + super(message); + this.status = status; + this.message = message; + } + + public BaseException(Status status, Throwable cause) { + super(status.getMsg(), cause); + this.status = status; + this.e = cause; + this.message = status.getMsg(); + } + + public BaseException(Status status, String message, Throwable cause) { + super(message, cause); + this.status = status; + this.message = message; + this.e = cause; + } + + public BaseException() { + } + + public static BaseException of(Status status) { + return new BaseException(status); + } + + public static BaseException of(Status status, String message) { + return new BaseException(status, message); + } + + public static BaseException of(Status status, Throwable cause) { + return new BaseException(status, cause); + } + + public static BaseException of(Status status, String message, Throwable cause) { + return new BaseException(status, message, cause); + } + + public static BaseException of(Status status, Throwable cause, String msg) { + return new BaseException(status, msg, cause); + } + + public Status getStatus() { + return this.status; + } + + public String getMessage() { + return this.message; + } + + public Throwable getE() { + return this.e; + } + + public void setStatus(Status status) { + this.status = status; + } + + public void setMessage(String message) { + this.message = message; + } + + public void setE(Throwable e) { + this.e = e; + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (!(o instanceof BaseException)) { + return false; + } else { + BaseException other = (BaseException)o; + if (!other.canEqual(this)) { + return false; + } else { + label47: { + Object this$status = this.getStatus(); + Object other$status = other.getStatus(); + if (this$status == null) { + if (other$status == null) { + break label47; + } + } else if (this$status.equals(other$status)) { + break label47; + } + + return false; + } + + Object this$message = this.getMessage(); + Object other$message = other.getMessage(); + if (this$message == null) { + if (other$message != null) { + return false; + } + } else if (!this$message.equals(other$message)) { + return false; + } + + Object this$e = this.getE(); + Object other$e = other.getE(); + if (this$e == null) { + if (other$e != null) { + return false; + } + } else if (!this$e.equals(other$e)) { + return false; + } + + return true; + } + } + } + + protected boolean canEqual(Object other) { + return other instanceof BaseException; + } + + public int hashCode() { + boolean PRIME = true; + int result = 1; + Object $status = this.getStatus(); + result = result * 59 + ($status == null ? 43 : $status.hashCode()); + Object $message = this.getMessage(); + result = result * 59 + ($message == null ? 43 : $message.hashCode()); + Object $e = this.getE(); + result = result * 59 + ($e == null ? 43 : $e.hashCode()); + return result; + } + + public String toString() { + Status var10000 = this.getStatus(); + return "BaseException(status=" + var10000 + ", message=" + this.getMessage() + ", e=" + this.getE() + ")"; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/JacksonUtils.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/JacksonUtils.java new file mode 100644 index 0000000..954f0ee --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/JacksonUtils.java @@ -0,0 +1,545 @@ +package com.ruoyi.thirdparty.yyyouping.utils.common; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.core.type.ResolvedType; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.MappingIterator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.net.URL; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Date; + +import org.springframework.stereotype.Component; + +@Component +public class JacksonUtils { + private static ObjectMapper objectMapper = null; + + public JacksonUtils() { + } + + private static ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(Include.NON_NULL); + objectMapper.configure(SerializationFeature.INDENT_OUTPUT, Boolean.FALSE); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addSerializer(LocalDateTime.class, new JsonSerializer() { + public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeNumber(value.toInstant(ZoneOffset.of("+8")).toEpochMilli()); + } + }); + javaTimeModule.addDeserializer(LocalDateTime.class, new JsonDeserializer() { + public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { + return (new Date(Long.parseLong(p.getValueAsString()))).toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime(); + } + }); + objectMapper.registerModule(javaTimeModule); + return objectMapper; + } + + public static ObjectMapper getObjectMapper() { + return objectMapper; + } + + public static T readValue(JsonParser p, Class valueType) { + try { + return objectMapper.readValue(p, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(JsonParser p, TypeReference valueTypeRef) { + try { + return objectMapper.readValue(p, valueTypeRef); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(JsonParser p, ResolvedType valueType) { + try { + return objectMapper.readValue(p, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(JsonParser p, JavaType valueType) { + try { + return objectMapper.readValue(p, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readTree(JsonParser p) { + try { + return objectMapper.readTree(p); + } catch (Exception var2) { + var2.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var2); + } + } + + public static MappingIterator readValues(JsonParser p, ResolvedType valueType) { + try { + return objectMapper.readValues(p, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static MappingIterator readValues(JsonParser p, JavaType valueType) { + try { + return objectMapper.readValues(p, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static MappingIterator readValues(JsonParser p, Class valueType) { + try { + return objectMapper.readValues(p, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static MappingIterator readValues(JsonParser p, TypeReference valueTypeRef) { + try { + return objectMapper.readValues(p, valueTypeRef); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static JsonNode readTree(InputStream in) { + try { + return objectMapper.readTree(in); + } catch (Exception var2) { + var2.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var2); + } + } + + public static JsonNode readTree(Reader r) { + try { + return objectMapper.readTree(r); + } catch (Exception var2) { + var2.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var2); + } + } + + public static JsonNode readTree(String content) { + try { + return objectMapper.readTree(content); + } catch (Exception var2) { + var2.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var2); + } + } + + public static JsonNode readTree(byte[] content) { + try { + return objectMapper.readTree(content); + } catch (Exception var2) { + var2.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var2); + } + } + + public static JsonNode readTree(File file) { + try { + return objectMapper.readTree(file); + } catch (Exception var2) { + var2.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var2); + } + } + + public static JsonNode readTree(URL source) { + try { + return objectMapper.readTree(source); + } catch (Exception var2) { + var2.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var2); + } + } + + public static void writeValue(JsonGenerator g, Object value) { + try { + objectMapper.writeValue(g, value); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static void writeTree(JsonGenerator g, TreeNode rootNode) { + try { + objectMapper.writeTree(g, rootNode); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static void writeTree(JsonGenerator g, JsonNode rootNode) { + try { + objectMapper.writeTree(g, rootNode); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T treeToValue(TreeNode n, Class valueType) { + try { + return objectMapper.treeToValue(n, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T valueToTree(Object fromValue) { + try { + return objectMapper.valueToTree(fromValue); + } catch (Exception var2) { + var2.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var2); + } + } + + public static T readValue(File src, Class valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(File src, TypeReference valueTypeRef) { + try { + return objectMapper.readValue(src, valueTypeRef); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(File src, JavaType valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(URL src, Class valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(URL src, TypeReference valueTypeRef) { + try { + return objectMapper.readValue(src, valueTypeRef); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(URL src, JavaType valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(String content, Class valueType) { + try { + return objectMapper.readValue(content, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(String content, TypeReference valueTypeRef) { + try { + return objectMapper.readValue(content, valueTypeRef); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(String content, JavaType valueType) { + try { + return objectMapper.readValue(content, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(Reader src, Class valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(Reader src, TypeReference valueTypeRef) { + try { + return objectMapper.readValue(src, valueTypeRef); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(Reader src, JavaType valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(InputStream src, Class valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(InputStream src, TypeReference valueTypeRef) { + try { + return objectMapper.readValue(src, valueTypeRef); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(InputStream src, JavaType valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(byte[] src, Class valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(byte[] src, int offset, int len, Class valueType) { + try { + return objectMapper.readValue(src, offset, len, valueType); + } catch (Exception var5) { + var5.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var5); + } + } + + public static T readValue(byte[] src, TypeReference valueTypeRef) { + try { + return objectMapper.readValue(src, valueTypeRef); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(byte[] src, int offset, int len, TypeReference valueTypeRef) { + try { + return objectMapper.readValue(src, offset, len, valueTypeRef); + } catch (Exception var5) { + var5.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var5); + } + } + + public static T readValue(byte[] src, JavaType valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(byte[] src, int offset, int len, JavaType valueType) { + try { + return objectMapper.readValue(src, offset, len, valueType); + } catch (Exception var5) { + var5.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var5); + } + } + + public static T readValue(DataInput src, Class valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T readValue(DataInput src, JavaType valueType) { + try { + return objectMapper.readValue(src, valueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static void writeValue(File resultFile, Object value) { + try { + objectMapper.writeValue(resultFile, value); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static void writeValue(OutputStream out, Object value) { + try { + objectMapper.writeValue(out, value); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static void writeValue(DataOutput out, Object value) { + try { + objectMapper.writeValue(out, value); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static void writeValue(Writer w, Object value) { + try { + objectMapper.writeValue(w, value); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static String writeValueAsString(Object value) { + if (null == value) { + return null; + } else { + try { + return objectMapper.writeValueAsString(value); + } catch (Exception var2) { + var2.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var2); + } + } + } + + public static byte[] writeValueAsBytes(Object value) { + try { + return objectMapper.writeValueAsBytes(value); + } catch (Exception var2) { + var2.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var2); + } + } + + public static T convertValue(Object fromValue, Class toValueType) { + try { + return objectMapper.convertValue(fromValue, toValueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T convertValue(Object fromValue, TypeReference toValueTypeRef) { + try { + return objectMapper.convertValue(fromValue, toValueTypeRef); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + public static T convertValue(Object fromValue, JavaType toValueType) { + try { + return objectMapper.convertValue(fromValue, toValueType); + } catch (Exception var3) { + var3.printStackTrace(); + throw BaseException.of(Status.JACKSON_EXCEPTION, var3); + } + } + + static { + objectMapper = objectMapper(); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/RSAUtils.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/RSAUtils.java new file mode 100644 index 0000000..b145a04 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/RSAUtils.java @@ -0,0 +1,549 @@ +package com.ruoyi.thirdparty.yyyouping.utils.common; + +import cn.hutool.core.util.ObjectUtil; +import lombok.Data; +import org.slf4j.Logger; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.ArrayUtils; +import org.slf4j.LoggerFactory; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +/** + * SHA256withRSA + * + * @author zhumiao + * + */ +@Data +public class RSAUtils { + + private static final Logger logger = LoggerFactory.getLogger(RSAUtils.class); + + // MAX_DECRYPT_BLOCK应等于密钥长度/8(1byte=8bit),所以当密钥位数为2048时,最大解密长度应为256. + // 128 对应 1024,256对应2048 + private static final int KEYSIZE = 2048; + + // RSA最大加密明文大小 + private static final int MAX_ENCRYPT_BLOCK = 117; + + // RSA最大解密密文大小 + private static final int MAX_DECRYPT_BLOCK = 256; + + // 不仅可以使用DSA算法,同样也可以使用RSA算法做数字签名 + private static final String KEY_ALGORITHM = "RSA"; + private static final String SIGNATURE_ALGORITHM = "SHA256withRSA"; + + + // 默认种子 请根据自己的需要定义 + public static final String DEFAULT_SEED = "xxxxxxxxxxxxxxxxxx"; + + /** + * 目前固定公钥、私钥,有需求再改动 + */ + public String publicKey; + + public String privateKey; + public String appKey; + + public static final String PUBLIC_KEY = "PublicKey"; + public static final String PRIVATE_KEY = "PrivateKey"; + + public RSAUtils(String appKey,String publicKey,String privateKey){ + this.appKey = appKey; + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + /** + * + * 生成密钥 + * + * @param seed 种子 + * + * @return 密钥对象 + * @throws Exception + * + */ + public static Map initKey(String seed) throws Exception { + logger.debug("生成密钥"); + KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM); + SecureRandom secureRandom = new SecureRandom(); + // 如果指定seed,那么secureRandom结果是一样的,所以生成的公私钥也永远不会变 + secureRandom.setSeed(seed.getBytes()); + // Modulus size must range from 512 to 1024 and be a multiple of 64 + keygen.initialize(KEYSIZE, secureRandom); + KeyPair keys = keygen.genKeyPair(); + PrivateKey privateKey = keys.getPrivate(); + PublicKey publicKey = keys.getPublic(); + Map map = new HashMap<>(2); + map.put(PUBLIC_KEY, publicKey); + map.put(PRIVATE_KEY, privateKey); + return map; + } + + /** + * + * 生成默认密钥 + * + * + * @return 密钥对象 + * @throws Exception + * + */ + + public static Map initKey() throws Exception { + return initKey(DEFAULT_SEED); + } + + /** + * + * 取得私钥 + * + * @param keyMap + * + * @return + * @throws Exception + * + */ + public static String getPrivateKey(Map keyMap) throws Exception { + Key key = (Key) keyMap.get(PRIVATE_KEY); + // return encryptBASE64(key.getEncoded()); // base64加密私钥 + return base64ToStr(key.getEncoded()); // base64加密私钥 + } + + private static String base64ToStr(byte[] encoded) { + return java.util.Base64.getEncoder().encodeToString(encoded); + } + + /** + * + * 取得公钥 + * + * @param keyMap + * + * @return + * @throws Exception + * + */ + public static String getPublicKey(Map keyMap) throws Exception { + Key key = (Key) keyMap.get(PUBLIC_KEY); + // return encryptBASE64(key.getEncoded()); // base64加密公钥 + return base64ToStr(key.getEncoded()); // base64加密公钥 + } + + /** + * + * 用私钥对信息进行数字签名 + * + * @param data 加密数据 + * + * @param privateKey 私钥-base64加密的 + * + * @return + * + * @throws Exception + * + */ + public static String signByPrivateKey(byte[] data, String privateKey) throws Exception { + byte[] keyBytes = decryptBASE64(privateKey); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM); + PrivateKey priKey = factory.generatePrivate(keySpec);// 生成私钥 + // 用私钥对信息进行数字签名 + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initSign(priKey); + signature.update(data); + return encryptBASE64(signature.sign()); + } + + /** + * 获取签名 + */ + public String getSign(Map params) throws Exception { + // 第一步:检查参数是否已经排序 + String[] keys = params.keySet().toArray(new String[0]); + Arrays.sort(keys); + // 第二步:把所有参数名和参数值串在一起 + StringBuilder stringBuilder = new StringBuilder(); + for (String key : keys) { + Object value = params.get(key); + // if (!StringUtils.isEmpty(value)) { + if (!ObjectUtil.isEmpty(value)) { + stringBuilder + .append(key) + .append(JacksonUtils.writeValueAsString(value)); + } + } + //采用私钥签名 + return signByPrivateKey(stringBuilder.toString().getBytes(), privateKey); + } + + /** + * + * BASE64Encoder 加密 + * + * @param data 要加密的数据 + * + * @return 加密后的字符串 + * + */ + private static String encryptBASE64(byte[] data) { + // BASE64Encoder encoder = new BASE64Encoder(); + // String encode = encoder.encode(data); + // return encode; + return new String(Base64.encodeBase64(data)); + } + + private static byte[] decryptBASE64(String data) { + // BASE64Decoder 每76个字符换行 + // BASE64Decoder decoder = new BASE64Decoder(); + // byte[] buffer = decoder.decodeBuffer(data); + // return buffer; + // codec 的 Base64 不换行 + return Base64.decodeBase64(data); + } + + // TODO rsa 2 对比是否更优 + public static boolean verifyByPublicKey(byte[] data, String publicKey, String sign) throws Exception { + byte[] keyBytes = decryptBASE64(publicKey); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + PublicKey pubKey = keyFactory.generatePublic(keySpec); + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initVerify(pubKey); + signature.update(data); + return signature.verify(decryptBASE64(sign)); // 验证签名 + } + + /** + * RSA公钥加密 + * + * @param str 加密字符串 + * @param publicKey 公钥 + * @return 密文 + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws NoSuchPaddingException + * @throws InvalidKeyException + * @throws UnsupportedEncodingException + * @throws BadPaddingException + * @throws IllegalBlockSizeException + * @throws Exception 加密过程中的异常信息 + */ + public static String encryptByPublicKey(String str, String publicKey) + throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException { + // base64编码的公钥 + byte[] keyBytes = decryptBASE64(publicKey); + RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(KEY_ALGORITHM) + .generatePublic(new X509EncodedKeySpec(keyBytes)); + // RSA加密 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, pubKey); + logger.debug(publicKey); + logger.debug("provider: {}", cipher.getProvider().getClass().getName()); + byte[] data = str.getBytes("UTF-8"); + // 加密时超过117字节就报错。为此采用分段加密的办法来加密 + byte[] enBytes = null; + for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) { + // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码 + byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK)); + enBytes = ArrayUtils.addAll(enBytes, doFinal); + } + logger.debug(enBytes.length + ""); + String outStr = encryptBASE64(enBytes); + return outStr; + } + + /** + * RSA私钥加密 + * + * @param str 加密字符串 + * @param privateKey 公钥 + * @return 密文 + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws NoSuchPaddingException + * @throws InvalidKeyException + * @throws UnsupportedEncodingException + * @throws BadPaddingException + * @throws IllegalBlockSizeException + * @throws Exception 加密过程中的异常信息 + */ + public static String encryptByPrivateKey(String str, String privateKey) + throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException { + // base64编码的公钥 + byte[] keyBytes = decryptBASE64(privateKey); + RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM) + .generatePrivate(new PKCS8EncodedKeySpec(keyBytes)); + // RSA加密 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, priKey); + + byte[] data = str.getBytes("UTF-8"); + // 加密时超过117字节就报错。为此采用分段加密的办法来加密 + byte[] enBytes = null; + for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) { + // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码 + byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK)); + enBytes = ArrayUtils.addAll(enBytes, doFinal); + } + String outStr = encryptBASE64(enBytes); + return outStr; + } + + /** + * 读取公钥 + * + * @param publicKeyPath + * @return + */ + public static PublicKey readPublic(String publicKeyPath) { + if (publicKeyPath != null) { + try (FileInputStream bais = new FileInputStream(publicKeyPath)) { + CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate) certificatefactory.generateCertificate(bais); + return cert.getPublicKey(); + } catch (CertificateException e) { + logger.error(e.getMessage(), e); + } catch (FileNotFoundException e) { + logger.error(e.getMessage(), e); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + } + return null; + } + + /** + * 读取私钥 + * + * @param + * @return + */ + public static PrivateKey readPrivate(String privateKeyPath, String privateKeyPwd) { + if (privateKeyPath == null || privateKeyPwd == null) { + return null; + } + try (InputStream stream = new FileInputStream(new File(privateKeyPath));) { + // 获取JKS 服务器私有证书的私钥,取得标准的JKS的 KeyStore实例 + KeyStore store = KeyStore.getInstance("JKS");// JKS,二进制格式,同时包含证书和私钥,一般有密码保护;PKCS12,二进制格式,同时包含证书和私钥,一般有密码保护。 + // jks文件密码,根据实际情况修改 + store.load(stream, privateKeyPwd.toCharArray()); + // 获取jks证书别名 + Enumeration en = store.aliases(); + String pName = null; + while (en.hasMoreElements()) { + String n = (String) en.nextElement(); + if (store.isKeyEntry(n)) { + pName = n; + } + } + // 获取证书的私钥 + PrivateKey key = (PrivateKey) store.getKey(pName, privateKeyPwd.toCharArray()); + return key; + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + return null; + } + + /** + * RSA私钥解密 + * + * @param encryStr 加密字符串 + * @param privateKey 私钥 + * @return 铭文 + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws NoSuchPaddingException + * @throws BadPaddingException + * @throws IllegalBlockSizeException + * @throws InvalidKeyException + * @throws Exception 解密过程中的异常信息 + */ + public static String decryptByPrivateKey(String encryStr, String privateKey) + throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, + BadPaddingException, InvalidKeyException, UnsupportedEncodingException { + // base64编码的私钥 + byte[] decoded = decryptBASE64(privateKey); + RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM) + .generatePrivate(new PKCS8EncodedKeySpec(decoded)); + // RSA解密 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, priKey); + logger.debug(privateKey); + logger.debug("provider: {}" , cipher.getProvider().getClass().getName()); + // 64位解码加密后的字符串 + byte[] data = decryptBASE64(encryStr); + logger.debug(data.length + ""); + // 解密时超过128字节报错。为此采用分段解密的办法来解密 + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < data.length; i += MAX_DECRYPT_BLOCK) { + byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_DECRYPT_BLOCK)); + sb.append(new String(doFinal)); + } + return sb.toString(); + } + + /** + * RSA公钥解密 + * + * @param encryStr 加密字符串 + * @param + * @return 铭文 + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws NoSuchPaddingException + * @throws BadPaddingException + * @throws IllegalBlockSizeException + * @throws InvalidKeyException + * @throws Exception 解密过程中的异常信息 + */ + public static String decryptByPublicKey(String encryStr, String publicKey) + throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, + BadPaddingException, InvalidKeyException { + // base64编码的私钥 + byte[] decoded = decryptBASE64(publicKey); + RSAPublicKey priKey = (RSAPublicKey) KeyFactory.getInstance(KEY_ALGORITHM) + .generatePublic(new X509EncodedKeySpec(decoded)); + // RSA解密 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, priKey); + + // 64位解码加密后的字符串 + byte[] data = decryptBASE64(encryStr); + // 解密时超过128字节报错。为此采用分段解密的办法来解密 + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < data.length; i += MAX_DECRYPT_BLOCK) { + byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_DECRYPT_BLOCK)); + sb.append(new String(doFinal)); + } + + return sb.toString(); + } + + /** + * 加密 + * @param key + * @param data + * @return + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws NoSuchPaddingException + * @throws IllegalBlockSizeException + * @throws BadPaddingException + * @throws InvalidKeyException + * @throws IOException + */ + public static String testEncrypt(String key,String data) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IOException{ + byte[] decode = Base64.decodeBase64(key); + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decode); + KeyFactory kf = KeyFactory.getInstance(KEY_ALGORITHM); + PrivateKey generatePrivate = kf.generatePrivate(pkcs8EncodedKeySpec); + Cipher ci = Cipher.getInstance(KEY_ALGORITHM); + ci.init(Cipher.ENCRYPT_MODE, generatePrivate); + + byte[] bytes = data.getBytes(); + int inputLen = bytes.length; + int offLen = 0;//偏移量 + int i = 0; + ByteArrayOutputStream bops = new ByteArrayOutputStream(); + while(inputLen - offLen > 0){ + byte [] cache; + if(inputLen - offLen > 117){ + cache = ci.doFinal(bytes, offLen,117); + }else{ + cache = ci.doFinal(bytes, offLen,inputLen - offLen); + } + bops.write(cache); + i++; + offLen = 117 * i; + } + bops.close(); + byte[] encryptedData = bops.toByteArray(); + String encodeToString = Base64.encodeBase64String(encryptedData); + return encodeToString; + } + + + + + /** + * 解密 + * @param key + * @param data + * @return + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + * @throws NoSuchPaddingException + * @throws InvalidKeySpecException + * @throws BadPaddingException + * @throws IllegalBlockSizeException + * @throws IOException + */ + public static String testDecrypt(String key,String data) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException, IOException{ + byte[] decode = Base64.decodeBase64(key); + // PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decode); //java底层 RSA公钥只支持X509EncodedKeySpec这种格式 + X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(decode); + KeyFactory kf = KeyFactory.getInstance(KEY_ALGORITHM); + PublicKey generatePublic = kf.generatePublic(x509EncodedKeySpec); + Cipher ci = Cipher.getInstance(KEY_ALGORITHM); + ci.init(Cipher.DECRYPT_MODE,generatePublic); + + int inputLen = data.getBytes().length; + byte[] bytes = data.getBytes(); + int offLen = 0; + int i = 0; + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + while(inputLen - offLen > 0){ + byte[] cache; + if(inputLen - offLen > 128){ + cache = ci.doFinal(bytes,offLen,128); + }else{ + cache = ci.doFinal(bytes,offLen,inputLen - offLen); + } + byteArrayOutputStream.write(cache); + i++; + offLen = 128 * i; + + } + byteArrayOutputStream.close(); + byte[] byteArray = byteArrayOutputStream.toByteArray(); + return new String(byteArray); + } +} \ No newline at end of file diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/Status.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/Status.java new file mode 100644 index 0000000..82e5972 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/Status.java @@ -0,0 +1,79 @@ +package com.ruoyi.thirdparty.yyyouping.utils.common; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.stereotype.Component; + + +public enum Status { + JACKSON_EXCEPTION(5104, "Jackson异常"), + UNKNOWN(100, "Code异常"); + + private final Integer code; + private final String msg; + private static final int SUCCESS = 2000; + public static final int SUCCESS_ZERO = 0; + private static final int WEIGHT = 80000; + private static final int DONET_WEIGHT = 100000; + private static final String DONET_STATUS_PREFIX = "DONET_"; + private static MessageSource messageSource; + + private Status(Integer code, String msg) { + this.code = code; + this.msg = msg; + } + + public String getMsg() { + String msg = null; + + try { + msg = messageSource.getMessage("status." + this.code, (Object[])null, LocaleContextHolder.getLocale()); + } catch (Exception var3) { + msg = this.msg; + } + + return msg; + } + + public Integer getCode() { + if (this.code == 2000) { + return 0; + } else { + return this.name().startsWith("DONET_") ? Math.subtractExact(this.code, 100000) : Math.addExact(this.code, 80000); + } + } + + public Integer getOriginCode() { + return this.code; + } + + public static Status getByCode(Integer code) { + if (null == code) { + return UNKNOWN; + } else { + Status[] var1 = values(); + int var2 = var1.length; + + for(int var3 = 0; var3 < var2; ++var3) { + Status status = var1[var3]; + if (status.getCode().equals(code)) { + return status; + } + } + + return UNKNOWN; + } + } + + @Component + public static class Builder { + public Builder() { + } + + @Autowired + public void setMessageResource(MessageSource messageSource) { + Status.messageSource = messageSource; + } + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/YYResult.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/YYResult.java new file mode 100644 index 0000000..f069d03 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/yyyouping/utils/common/YYResult.java @@ -0,0 +1,26 @@ +package com.ruoyi.thirdparty.yyyouping.utils.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class YYResult { + + private Integer code; + private String msg; + private Long timestamp; + private Object data; + + public boolean isSuccess(){ + if (this.code.equals(0)){ + return true; + } + return false; + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/config/ZBTProperties.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/config/ZBTProperties.java new file mode 100644 index 0000000..5e6969a --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/config/ZBTProperties.java @@ -0,0 +1,43 @@ +package com.ruoyi.thirdparty.zbt.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Data +@ConfigurationProperties(prefix = "zbt") +public class ZBTProperties { + private String appKey; + private String language; + private String appId; + private String baseUrl; + private String callbackUrl; + private String userBalance = "/open/user/v1/t-coin/balance"; + private String userCreate = "/open/user/steam-check/create"; + private String userSteamInfo = "/open/user/steam-info"; + private String developmentSave = "/open/development/callback-url/save"; + private String developmentInfo = "/open/development/info"; + private String userSteamAccounts = "/open/user/steam-accounts"; + private String productSearch = "/open/product/v2/search"; + private String productInfo = "/open/product/price/info"; + private String productFilters = "/open/product/v1/filters"; + private String productList = "/open/product/v1/sell/list"; + private String product = "/open/product/v1/sell/product"; + private String productV1Info = "/open/product/v1/info"; + private String tradeBuy = "/open/trade/v2/buy"; + private String tradeQuickBuy = "/open/trade/v2/quick-buy"; + private String orderBuyerList = "/open/order/buyer/list"; + private String orderSellerList = "/open/order/seller/list"; + private String orderBuyDetail = "/open/order/v2/buy/detail"; + private String orderSellerDetail = "/open/order/v2/seller/detail"; + private String orderBuyerCancel = "/open/order/buyer-cancel"; + private String orderSellerCancel = "/open/order/seller-cancel"; + private String offerStatus = "/open/offer/v1/status"; + private String offerSendAssets = "/open/offer/v1/send-assets"; + private String v1Inventory = "/open/v1/inventory/{steamId}/{appId}/"; + private String v1Bind = "/open/v1/seller/steam/bind/"; + private String v1Items = "/open/v1/sale/items"; + private String v1Deliver = "/open/v1/sale/deliver"; + private String v1SellList = "/open/v1/sell-list"; + private String v1ModifyPrice = "/open/v1/modify-price"; + private String v1OffSale = "/open/v1/off-sale"; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/controller/ZBTController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/controller/ZBTController.java new file mode 100644 index 0000000..5b3ee02 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/controller/ZBTController.java @@ -0,0 +1,49 @@ +package com.ruoyi.thirdparty.zbt.controller; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.thirdparty.zbt.config.ZBTProperties; +import com.ruoyi.thirdparty.zbt.result.ResultZbt; +import com.ruoyi.thirdparty.zbt.result.user.BusinessData; +import com.ruoyi.thirdparty.zbt.result.user.UserAccountModel; +import com.ruoyi.thirdparty.zbt.service.ZBTService; +import com.ruoyi.thirdparty.zbt.util.ZBTUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@Api(tags = "扎比特") +@RestController +@RequestMapping +public class ZBTController { + + private final ZBTService zbtService; + + public ZBTController(ZBTService zbtService) { + this.zbtService = zbtService; + } + + @ApiOperation("管理端查询用户余额") + @GetMapping("/admin/zbt/balance") + public R balance() { + ResultZbt balance = zbtService.balance(); + if (balance.getSuccess()) return R.ok(balance.getData()); + return R.fail("用户余额查询失败"); + } + + @ApiOperation("管理端获取开发者信息") + @GetMapping("/admin/zbt/developmentInfo") + public R developmentInfo() { + ResultZbt developmentInfo = zbtService.developmentInfo(); + if (developmentInfo.getSuccess()) return R.ok(developmentInfo.getData()); + return R.fail("获取用户开发者账号相关信息失败"); + } + + @PostMapping("/admin/zbt/callback") + public String callback(@RequestBody Map payload) { + return zbtService.callback(payload); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/NormalBuyParamV2DTO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/NormalBuyParamV2DTO.java new file mode 100644 index 0000000..9878de0 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/NormalBuyParamV2DTO.java @@ -0,0 +1,17 @@ +package com.ruoyi.thirdparty.zbt.param; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class NormalBuyParamV2DTO { + + private BigDecimal buyPrice; + private BigDecimal maxPrice; + private String outTradeNo; + + // 平台的物品在售记录id + private String productId; + private String tradeUrl; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/OrderBuyDetailParams.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/OrderBuyDetailParams.java new file mode 100644 index 0000000..a607477 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/OrderBuyDetailParams.java @@ -0,0 +1,12 @@ +package com.ruoyi.thirdparty.zbt.param; + +import lombok.Data; + +@Data +public class OrderBuyDetailParams { + + private String appKey; + private String language; + private String orderId; + private String outTradeNo; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/ProductListParams.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/ProductListParams.java new file mode 100644 index 0000000..8626a83 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/ProductListParams.java @@ -0,0 +1,42 @@ +package com.ruoyi.thirdparty.zbt.param; + +import com.ruoyi.common.annotation.Excel; +import lombok.Builder; +import lombok.Data; +// zbt平台接口查询参数 +@Data +@Builder +public class ProductListParams { + + // // 第三方平台 + // private Integer partyType; + // + // // 饰品在该平台上的id + // private String id; + + private String itemId; + private String appKey; + private String language; + private String containSticker; + private String delivery; + private String endWear; + private String gemEnName; + private String gemId; + private String gemIds; + private String levelIds; + private String levelName; + private String limit; + private String orderBy; + private String page; + private String slotStickerIds; + private String sort; + private String startWear; + private String stickerCapsule; + private String stickerIds; + private String styleId; + private String hasFraudwarning; + private String standardPrice; + private String withHistory; + @Excel(name = "饰品唯一名称英文") + private String marketHashName; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/QuickBuyParamV2DTO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/QuickBuyParamV2DTO.java new file mode 100644 index 0000000..83a9495 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/QuickBuyParamV2DTO.java @@ -0,0 +1,16 @@ +package com.ruoyi.thirdparty.zbt.param; + +import lombok.Data; + +@Data +public class QuickBuyParamV2DTO { + + private String appId; + private String delivery; + private String itemId; + private String lowPrice; + private String marketHashName; + private String maxPrice; + private String outTradeNo; + private String tradeUrl; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/SaveCallbackUrlDTO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/SaveCallbackUrlDTO.java new file mode 100644 index 0000000..d912853 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/SaveCallbackUrlDTO.java @@ -0,0 +1,8 @@ +package com.ruoyi.thirdparty.zbt.param; + +import lombok.Data; + +@Data +public class SaveCallbackUrlDTO { + private String callbackUrl; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/SearchParams.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/SearchParams.java new file mode 100644 index 0000000..9ef561a --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/SearchParams.java @@ -0,0 +1,34 @@ +package com.ruoyi.thirdparty.zbt.param; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SearchParams { + private String appId; + private String appKey; + private String category; + private String exterior; + private String hero; + private List itemIds; + private String itemSet; + private String keyword; + private String language; + private String limit; + private String maxPrice; + private String minPrice; + private String orderBy; + private String page; + private String quality; + private String rarity; + private String slot; + private String type; + private String weapon; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/UserSteamInfoParams.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/UserSteamInfoParams.java new file mode 100644 index 0000000..23ef36b --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/param/UserSteamInfoParams.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.zbt.param; + +import lombok.Data; + +@Data +public class UserSteamInfoParams { + + private String appId; + private String type; + private String appKey; + private String language; + private String steamId; + private String tradeUrl; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/ResultZbt.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/ResultZbt.java new file mode 100644 index 0000000..36b0ad5 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/ResultZbt.java @@ -0,0 +1,16 @@ +package com.ruoyi.thirdparty.zbt.result; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ResultZbt { + private T data; + private Integer errorCode; + private Object errorData; + private String errorMsg; + private Boolean success; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/buy/OpenBuyResultDTO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/buy/OpenBuyResultDTO.java new file mode 100644 index 0000000..c0a1405 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/buy/OpenBuyResultDTO.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.zbt.result.buy; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class OpenBuyResultDTO { + + private BigDecimal buyPrice; + private Integer delivery; + private String offerId; + private String orderId; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/order/OfferInfoDTO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/order/OfferInfoDTO.java new file mode 100644 index 0000000..c1527ed --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/order/OfferInfoDTO.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.zbt.result.order; + +import lombok.Data; + +@Data +public class OfferInfoDTO { + + private String appId; + private String imageUrl; + private String itemId; + private String marketHashName; + private String name; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/order/OpenItemInfo.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/order/OpenItemInfo.java new file mode 100644 index 0000000..3aaa286 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/order/OpenItemInfo.java @@ -0,0 +1,10 @@ +package com.ruoyi.thirdparty.zbt.result.order; + +import lombok.Data; + +@Data +public class OpenItemInfo { + + private String tradeOfferId; + private String transferId; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/order/OrderBuyDetailDTO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/order/OrderBuyDetailDTO.java new file mode 100644 index 0000000..47c324f --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/order/OrderBuyDetailDTO.java @@ -0,0 +1,27 @@ +package com.ruoyi.thirdparty.zbt.result.order; + +import com.ruoyi.domain.common.constant.DeliveryOrderStatus; +import com.ruoyi.thirdparty.zbt.result.product.AssetInfo; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class OrderBuyDetailDTO { + + private AssetInfo assetInfo; + private String createTime; + private String deliverType; + private String failedDesc; + private OfferInfoDTO offerInfoDTO; + private OpenItemInfo openItemInfo; + private String orderId; + private BigDecimal price; + private String productId; + private String receiveSteamId; + + //订单状态 + private DeliveryOrderStatus status; + private String statusName; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/AssetInfo.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/AssetInfo.java new file mode 100644 index 0000000..f3f3c6f --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/AssetInfo.java @@ -0,0 +1,33 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.Data; + +import java.util.List; + +@Data +public class AssetInfo { + + private String assetId; + private String classId; + private String classInfoId; + private String ext; + private String fadeColor; + private String fraudwarning; + private List gems; + private String gradient; + private String inspectImageUrl; + + private String instanceId; + + private String lastStyle; + private String levelColor; + private String levelName; + private Integer paintIndex; + private Integer paintSeed; + private List stickers; + private String styleId; + private String styleProgress; + private List styles; + private String wear; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/AvailableMarket.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/AvailableMarket.java new file mode 100644 index 0000000..0233e25 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/AvailableMarket.java @@ -0,0 +1,40 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class AvailableMarket { + + private Integer acceptBargain; + private Integer appId; + private String appName; + private AssetInfo assetInfo; + private String classInfoId; + private BigDecimal cnyPrice; + private Integer compensateType; + private Integer delivery; + private String description; + private String id; + private String imageUrl; + private String inspect3dUrl; + private Integer inspect3dViewable; + private String inspectImageUrl; + private String inspectUrl; + private Integer inspectViewable; + private Integer inspectable; + + private Integer isCollection; + + private String itemId; + private ItemInfo itemInfo; + private String itemName; + private String marketHashName; + private BigDecimal price; + private SellerUserInfoVO sellerInfo; + private BigDecimal sellerPrice; + private Integer systemTime; + + private String token; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/AvailableMarketList.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/AvailableMarketList.java new file mode 100644 index 0000000..552617d --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/AvailableMarketList.java @@ -0,0 +1,17 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.Data; + +import java.util.List; + +@Data +public class AvailableMarketList { + + private Integer limit; + private List list; + private String offsetToken; + private Integer page; + private Integer pages; + private String scrollId; + private Long total; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/DeliveryStatsInfoDTO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/DeliveryStatsInfoDTO.java new file mode 100644 index 0000000..c418872 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/DeliveryStatsInfoDTO.java @@ -0,0 +1,10 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.Data; + +@Data +public class DeliveryStatsInfoDTO { + + private DeliveryStatsItem day15; + private DeliveryStatsItem day7; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/DeliveryStatsItem.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/DeliveryStatsItem.java new file mode 100644 index 0000000..a88a99a --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/DeliveryStatsItem.java @@ -0,0 +1,10 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.Data; + +@Data +public class DeliveryStatsItem { + private String deliveryAvgTime; + private Integer deliveryNoneNum; + private String deliverySuccessRate; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/FilterKeyList.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/FilterKeyList.java new file mode 100644 index 0000000..fde20b6 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/FilterKeyList.java @@ -0,0 +1,19 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FilterKeyList { + + private String alias; + private String color; + private String image; + private String key; + private String name; + private String searchKey; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/FilterList.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/FilterList.java new file mode 100644 index 0000000..966ef0e --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/FilterList.java @@ -0,0 +1,20 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FilterList { + + private String alias; + private String key; + private List list; + private String name; + private String searchKey; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/Gems.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/Gems.java new file mode 100644 index 0000000..230607e --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/Gems.java @@ -0,0 +1,16 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.Data; + +@Data +public class Gems { + + private String border; + private String gemEnType; + private String gemType; + private String id; + private String image; + private String name; + private String type; + private String value; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/ItemAssetClassInfoStyleDTO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/ItemAssetClassInfoStyleDTO.java new file mode 100644 index 0000000..f218136 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/ItemAssetClassInfoStyleDTO.java @@ -0,0 +1,13 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.Data; + +@Data +public class ItemAssetClassInfoStyleDTO { + + private String color; + private String locked; + private String styleEnName; + private String styleId; + private String styleName; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/ItemAssetStickerDTO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/ItemAssetStickerDTO.java new file mode 100644 index 0000000..a210765 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/ItemAssetStickerDTO.java @@ -0,0 +1,19 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ItemAssetStickerDTO { + + private String id; + private String image; + private String itemId; + private String name; + private BigDecimal price; + private String slot; + private String stickerId; + private String type; + private String wear; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/ItemInfo.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/ItemInfo.java new file mode 100644 index 0000000..7d6dbab --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/ItemInfo.java @@ -0,0 +1,31 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.Data; + +@Data +public class ItemInfo { + + private String category; + private String categoryName; + private String exterior; + private String exteriorColor; + private String exteriorName; + private String hero; + private String heroAvatar; + private String heroName; + private String itemSet; + private String itemSetName; + private String quality; + private String qualityColor; + private String qualityName; + private String rarity; + private String rarityColor; + private String rarityName; + private String slot; + private String slotName; + private String type; + private String typeName; + private String weapon; + private String weaponName; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/PriceInfo.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/PriceInfo.java new file mode 100644 index 0000000..a6d8ffa --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/PriceInfo.java @@ -0,0 +1,18 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class PriceInfo { + + private BigDecimal autoDeliverPrice; + private Integer autoDeliverQuantity; + private BigDecimal manualDeliverPrice; + private Integer manualQuantity; + private BigDecimal price; + private Integer quantity; + private Integer userId; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/ProductFilters.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/ProductFilters.java new file mode 100644 index 0000000..6d74928 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/ProductFilters.java @@ -0,0 +1,20 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ProductFilters { + + private Integer appId; + private String appName; + private String icon; + private String iconLarge; + private List list; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/SearchData.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/SearchData.java new file mode 100644 index 0000000..6c9177a --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/SearchData.java @@ -0,0 +1,18 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import com.ruoyi.domain.dto.zbt.OrnamentZBT; +import lombok.Data; + +import java.util.Set; + +@Data +public class SearchData { + private Integer limit; + private Set list; + private String offsetToken; + private Integer page; + private Integer pages; + private String scrollId; + private Integer total; +} + diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/SearchList.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/SearchList.java new file mode 100644 index 0000000..7bcc6c7 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/SearchList.java @@ -0,0 +1,26 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.Data; + +import java.util.List; + +@Data +public class SearchList { + private Integer appId; + private String exterior; + private String exteriorName; + private String imageUrl; + private String itemId; + private String itemName; + private String marketHashName; + private List priceInfo; + private String quality; + private String qualityColor; + private String qualityName; + private String rarity; + private String rarityColor; + private String rarityName; + private String shortName; + private String type; + private String typeName; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/SellerUserInfoVO.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/SellerUserInfoVO.java new file mode 100644 index 0000000..66713b6 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/product/SellerUserInfoVO.java @@ -0,0 +1,16 @@ +package com.ruoyi.thirdparty.zbt.result.product; + +import lombok.Data; + +@Data +public class SellerUserInfoVO { + + private String avatar; + private DeliveryStatsInfoDTO deliveryInfo; + private String lastActive; + private String nickname; + private Integer platformId; + private String thirdUserId; + private String userId; + private String verified; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/BusinessData.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/BusinessData.java new file mode 100644 index 0000000..f9aa01b --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/BusinessData.java @@ -0,0 +1,27 @@ +package com.ruoyi.thirdparty.zbt.result.user; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class BusinessData { + + private String appKey; + private String appSecret; + private String applyNote; + private String area; + private BigDecimal balance; + private BigDecimal balanceLack; + private String callbackUrl; + private String email; + private String grantIpList; + private String rejectReason; + private String remark; + private String status; + private Integer successRate24; + private String telephone; + private Long total24Access; + private Long totalAccess; + private String weixin; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/CheckStatusInfo.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/CheckStatusInfo.java new file mode 100644 index 0000000..3375880 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/CheckStatusInfo.java @@ -0,0 +1,10 @@ +package com.ruoyi.thirdparty.zbt.result.user; + +import lombok.Data; + +@Data +public class CheckStatusInfo { + + private String statusKey; + private Object statusValue; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/GenericCheckSteamOutPut.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/GenericCheckSteamOutPut.java new file mode 100644 index 0000000..6107072 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/GenericCheckSteamOutPut.java @@ -0,0 +1,14 @@ +package com.ruoyi.thirdparty.zbt.result.user; + +import lombok.Data; + +import java.util.List; + +@Data +public class GenericCheckSteamOutPut { + + private Integer appId; + private Integer checkStatus; + private List statusList; + private UserSteamInfo steamInfo; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/UserAccountModel.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/UserAccountModel.java new file mode 100644 index 0000000..a393cc3 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/UserAccountModel.java @@ -0,0 +1,11 @@ +package com.ruoyi.thirdparty.zbt.result.user; + +import lombok.Data; + +@Data +public class UserAccountModel { + + private String data; + private String name; + private String userId; +} \ No newline at end of file diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/UserSteamInfo.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/UserSteamInfo.java new file mode 100644 index 0000000..5a90f28 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/result/user/UserSteamInfo.java @@ -0,0 +1,12 @@ +package com.ruoyi.thirdparty.zbt.result.user; + +import lombok.Data; + +@Data +public class UserSteamInfo { + + private String avatar; + private String nickName; + private String steamId; + private String tradeToken; +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/service/ZBTService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/service/ZBTService.java new file mode 100644 index 0000000..ca64108 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/service/ZBTService.java @@ -0,0 +1,39 @@ +package com.ruoyi.thirdparty.zbt.service; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.thirdparty.zbt.param.*; +import com.ruoyi.thirdparty.zbt.result.ResultZbt; +import com.ruoyi.thirdparty.zbt.result.buy.OpenBuyResultDTO; +import com.ruoyi.thirdparty.zbt.result.order.OrderBuyDetailDTO; +import com.ruoyi.thirdparty.zbt.result.product.AvailableMarketList; +import com.ruoyi.thirdparty.zbt.result.product.ProductFilters; +import com.ruoyi.thirdparty.zbt.result.product.SearchData; +import com.ruoyi.thirdparty.zbt.result.user.BusinessData; +import com.ruoyi.thirdparty.zbt.result.user.GenericCheckSteamOutPut; +import com.ruoyi.thirdparty.zbt.result.user.UserAccountModel; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.Map; + +public interface ZBTService { + + ResultZbt balance(); + + ResultZbt userSteamInfo(UserSteamInfoParams userSteamInfoParams); + + ResultZbt developmentInfo(); + + ResultZbt search(SearchParams params); + + ResultZbt productFilters(); + + ResultZbt productList(ProductListParams productListParams); + + ResultZbt tradeBuy(NormalBuyParamV2DTO normalBuyParamV2DTO); + + ResultZbt tradeQuickBuy(QuickBuyParamV2DTO quickBuyParamV2DTO); + + ResultZbt orderBuyDetail(OrderBuyDetailParams orderBuyDetailParams); + + String callback(Map payload); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/service/impl/ZBTServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/service/impl/ZBTServiceImpl.java new file mode 100644 index 0000000..06b82db --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/service/impl/ZBTServiceImpl.java @@ -0,0 +1,220 @@ +package com.ruoyi.thirdparty.zbt.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.ruoyi.admin.mapper.TtDeliveryRecordMapper; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpUtils; +import com.ruoyi.domain.common.constant.DeliveryOrderStatus; +import com.ruoyi.domain.entity.delivery.TtDeliveryRecord; +import com.ruoyi.thirdparty.zbt.config.ZBTProperties; +import com.ruoyi.thirdparty.zbt.param.*; +import com.ruoyi.thirdparty.zbt.result.ResultZbt; +import com.ruoyi.thirdparty.zbt.result.buy.OpenBuyResultDTO; +import com.ruoyi.thirdparty.zbt.result.order.OrderBuyDetailDTO; +import com.ruoyi.thirdparty.zbt.result.product.AvailableMarketList; +import com.ruoyi.thirdparty.zbt.result.product.ProductFilters; +import com.ruoyi.thirdparty.zbt.result.product.SearchData; +import com.ruoyi.thirdparty.zbt.result.user.BusinessData; +import com.ruoyi.thirdparty.zbt.result.user.GenericCheckSteamOutPut; +import com.ruoyi.thirdparty.zbt.result.user.UserAccountModel; +import com.ruoyi.thirdparty.zbt.service.ZBTService; +import com.ruoyi.thirdparty.zbt.util.ZBTUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.stereotype.Service; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@Service +@Slf4j +@EnableConfigurationProperties(value = ZBTProperties.class) +public class ZBTServiceImpl implements ZBTService { + + private final ZBTProperties zbtProperties; + + public ZBTServiceImpl(ZBTProperties zbtProperties) { + this.zbtProperties = zbtProperties; + } + + @Autowired + private TtDeliveryRecordMapper deliveryRecordMapper; + + @Override + public ResultZbt balance() { + String result = getMethod(zbtProperties.getUserBalance(), null); + if (StringUtils.isBlank(result)) return null; + return JSONObject.parseObject(result, new TypeReference>() { + }); + } + + @Override + public ResultZbt userSteamInfo(UserSteamInfoParams params) { + try { + params.setTradeUrl(URLEncoder.encode(params.getTradeUrl(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + return null; + } + Map paramMap = ZBTUtils.objectToMap(params); + String result = getMethod(zbtProperties.getUserSteamInfo(), paramMap); + if (StringUtils.isBlank(result)) return null; + return JSONObject.parseObject(result, new TypeReference>() { + }); + } + + @Override + public ResultZbt developmentInfo() { + String result = getMethod(zbtProperties.getDevelopmentInfo(), null); + if (StringUtils.isBlank(result)) return null; + return JSONObject.parseObject(result, new TypeReference>() { + }); + } + + @Override + public ResultZbt search(SearchParams params) { + Map paramMap = ZBTUtils.objectToMap(params); + String result = getMethod(zbtProperties.getProductSearch(), paramMap); + if (StringUtils.isBlank(result)) return null; + return JSONObject.parseObject(result, new TypeReference>() { + }); + } + + @Override + public ResultZbt productFilters() { + String result = getMethod(zbtProperties.getProductFilters(), null); + if (StringUtils.isBlank(result)) return null; + return JSONObject.parseObject(result, new TypeReference>() { + }); + } + + @Override + public ResultZbt productList(ProductListParams params) { + Map paramMap = ZBTUtils.objectToMap(params); + String result = getMethod(zbtProperties.getProductList(), paramMap); + if (StringUtils.isBlank(result)) { + return null; + } + return JSONObject.parseObject(result, new TypeReference>() { + }); + } + + @Override + public ResultZbt tradeBuy(NormalBuyParamV2DTO normalBuyParamV2DTO) { + // 设置回调地址 + SaveCallbackUrlDTO saveCallbackUrlDTO = new SaveCallbackUrlDTO(); + saveCallbackUrlDTO.setCallbackUrl(zbtProperties.getCallbackUrl()); + postMethod(zbtProperties.getDevelopmentSave(), JSON.toJSONString(saveCallbackUrlDTO)); + + String result = postMethod(zbtProperties.getTradeBuy(), JSON.toJSONString(normalBuyParamV2DTO)); + System.err.println(result); + if (StringUtils.isBlank(result)) { + return null; + } + return JSONObject.parseObject(result, new TypeReference>() { + }); + } + + @Override + public ResultZbt tradeQuickBuy(QuickBuyParamV2DTO quickBuyParamV2DTO) { + String result = postMethod(zbtProperties.getTradeQuickBuy(), JSON.toJSONString(quickBuyParamV2DTO)); + if (StringUtils.isBlank(result)) return null; + return JSONObject.parseObject(result, new TypeReference>() { + }); + } + + @Override + public ResultZbt orderBuyDetail(OrderBuyDetailParams params) { + Map paramMap = ZBTUtils.objectToMap(params); + String result = getMethod(zbtProperties.getOrderBuyDetail(), paramMap); + if (StringUtils.isBlank(result)) return null; + return JSONObject.parseObject(result, new TypeReference>() { + }); + } + + @Override + public String callback(Map payload) { + ResultZbt businessDataResultZbt = developmentInfo(); + String appSecret = businessDataResultZbt.getData().getAppSecret(); + + if (!ZBTUtils.verifySign(payload, appSecret)) { + log.error("扎比特回调通知验签未通过"); + return ""; + } + + Integer status = (Integer) payload.get("status"); + String outTradeNo = (String) payload.get("outTradeNo"); + // 获取提货订单数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TtDeliveryRecord::getOutTradeNo, outTradeNo); + TtDeliveryRecord ttDeliveryRecord = deliveryRecordMapper.selectOne(queryWrapper); + if (status == 1) { + ttDeliveryRecord.setStatus(DeliveryOrderStatus.DELIVERY_BEFORE.getCode()); + ttDeliveryRecord.setMessage(DeliveryOrderStatus.DELIVERY_BEFORE.getMsg()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + deliveryRecordMapper.updateById(ttDeliveryRecord); + } else if (status == 3) { + ttDeliveryRecord.setStatus(DeliveryOrderStatus.DELIVERY_AFTER.getCode()); + ttDeliveryRecord.setMessage(DeliveryOrderStatus.DELIVERY_AFTER.getMsg()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + deliveryRecordMapper.updateById(ttDeliveryRecord); + } else if (status == 10) { + ttDeliveryRecord.setStatus(DeliveryOrderStatus.ORDER_COMPLETE.getCode()); + ttDeliveryRecord.setMessage(DeliveryOrderStatus.ORDER_COMPLETE.getMsg()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + deliveryRecordMapper.updateById(ttDeliveryRecord); + return "success"; + } else if (status == 11) { + ttDeliveryRecord.setStatus(DeliveryOrderStatus.ORDER_CANCEL.getCode()); + ttDeliveryRecord.setMessage(DeliveryOrderStatus.ORDER_CANCEL.getMsg()); + ttDeliveryRecord.setUpdateTime(DateUtils.getNowDate()); + deliveryRecordMapper.updateById(ttDeliveryRecord); + } + + return ""; + } + + private String postMethod(String url, String JSONString) { + Map param = new HashMap<>(); + param.put("app-key", zbtProperties.getAppKey()); + param.put("language", zbtProperties.getLanguage()); + String substring = GeneratedStr(param); + url = zbtProperties.getBaseUrl() + url + "?" + substring; + return HttpUtils.sendPostJSONString(url, JSONString); + } + + private String getMethod(String url, Map param) { + + if (param == null) { + param = new HashMap<>(); + } + param.put("appId", zbtProperties.getAppId()); + param.put("app-key", zbtProperties.getAppKey()); + param.put("language", zbtProperties.getLanguage()); + String substring = GeneratedStr(param); + return HttpUtils.sendGet(zbtProperties.getBaseUrl() + url, substring); + } + + private String GeneratedStr(Map param){ + StringBuilder params = new StringBuilder(); + for (Map.Entry entry : param.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + params.append(key); + params.append("="); + params.append(value); + params.append("&"); + } + params.trimToSize(); + return params.substring(0, params.length() - 1); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/task/OrnamentsPullTask.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/task/OrnamentsPullTask.java new file mode 100644 index 0000000..71afadb --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/task/OrnamentsPullTask.java @@ -0,0 +1,253 @@ +package com.ruoyi.thirdparty.zbt.task; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.domain.dto.zbt.OrnamentZBT; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.mapper.SysDictDataMapper; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.zbt.param.SearchParams; +import com.ruoyi.thirdparty.zbt.result.ResultZbt; +import com.ruoyi.thirdparty.zbt.result.product.SearchData; +import com.ruoyi.thirdparty.zbt.result.product.SearchList; +import com.ruoyi.thirdparty.zbt.service.ZBTService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Component("OrnamentsPullTask") +public class OrnamentsPullTask { + + private Set removeDuplicates = new HashSet<>(); + private final ZBTService zbtService; + private final SysDictDataMapper dictDataMapper; + private final ISysConfigService configService; + private final TtOrnamentService ornamentsService; + + public OrnamentsPullTask(ZBTService zbtService, + SysDictDataMapper dictDataMapper, + ISysConfigService configService, + TtOrnamentService ornamentsService) { + this.zbtService = zbtService; + this.dictDataMapper = dictDataMapper; + this.configService = configService; + this.ornamentsService = ornamentsService; + } + + public void getOrnamentsData() { + log.info("拉取最新饰品数据任务执行开始..."); + long startTime = System.currentTimeMillis(); + List itemIdList = ornamentsService.selectOrnamentsMarketHashNameList(); + if (!itemIdList.isEmpty()) removeDuplicates = new HashSet<>(itemIdList); + log.info("数据库总条数:" + removeDuplicates.size()); + List type = dictDataMapper.selectDictDataByType("ornaments_type"); + + if (ObjectUtils.isEmpty(type)) return; + Map typeMap = objectToMap(type); + + Set tempRemoveDuplicates = new HashSet<>(); + List ZBTOrnamentsDataList = new ArrayList<>(); + + for (Map.Entry entry : typeMap.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + SearchParams searchParams = new SearchParams(); + searchParams.setLimit("200"); + searchParams.setPage("1"); + searchParams.setCategory(key); + ResultZbt searchResult = zbtService.search(searchParams); + if (!searchResult.getSuccess()) return; + List list = searchResult.getData().getList().stream() + .filter(searchList -> !tempRemoveDuplicates.contains(searchList.getMarketHashName())) + .peek(searchList -> tempRemoveDuplicates.add(searchList.getMarketHashName())) + .collect(Collectors.toList()); + ZBTOrnamentsDataList.addAll(list); + List ZBTOrnamentsDataListByType = searchResult.getData().getList().stream() + .filter(searchList -> !removeDuplicates.contains(searchList.getMarketHashName())) + .peek(searchList -> removeDuplicates.add(searchList.getMarketHashName())) + .collect(Collectors.toList()); + Integer total = searchResult.getData().getTotal(); + int pageSize = 200; + int pageNumCount = (total % pageSize == 0) ? total / pageSize : total / pageSize + 1; + for (int i = 2; i <= pageNumCount; i++) { + searchParams.setPage(String.valueOf(i)); + if (i == pageNumCount) searchParams.setLimit(String.valueOf(total % pageSize)); + searchResult = zbtService.search(searchParams); + if (!searchResult.getSuccess()) return; + list = searchResult.getData().getList().stream().filter(searchList -> !tempRemoveDuplicates.contains(searchList.getMarketHashName())) + .peek(searchList -> tempRemoveDuplicates.add(searchList.getMarketHashName())).collect(Collectors.toList()); + ZBTOrnamentsDataList.addAll(list); + List typeList = searchResult.getData().getList().stream().filter(searchList -> !removeDuplicates.contains(searchList.getMarketHashName())) + .peek(searchList -> removeDuplicates.add(searchList.getMarketHashName())).collect(Collectors.toList()); + ZBTOrnamentsDataListByType.addAll(typeList); + } + batchInsertOrnaments(ZBTOrnamentsDataListByType, value); + } + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + // wrapper.eq(TtOrnament::getIsProprietaryProperty, "1"); + Map ornamentsMap = + ornamentsService.list(wrapper).stream().collect(Collectors.toMap(TtOrnament::getMarketHashName, ornaments -> ornaments)); + log.info("扎比特数据条数:" + ZBTOrnamentsDataList.size()); + batchUpdateOrnamentsPrice(ZBTOrnamentsDataList, ornamentsMap); + batchSoldOutOrnaments(ZBTOrnamentsDataList, ornamentsMap); + // 获取方法结束时间 + long endTime = System.currentTimeMillis(); + // 计算方法执行时间 + long duration = endTime - startTime; + log.info("方法执行时间(秒):" + duration / 1000.0); + log.info("拉取最新饰品数据任务执行结束..."); + } + + private void batchSoldOutOrnaments(List ZBTOrnamentsDataList, Map ornamentsMap) { + List ornamentsList = ornamentsMap.entrySet().stream() + .filter(entry -> ZBTOrnamentsDataList.stream().noneMatch(searchList -> searchList.getMarketHashName().equals(entry.getKey()))) + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + ornamentsList = ornamentsList.stream().peek(ttOrnaments -> { + ttOrnaments.setUsePrice(BigDecimal.ZERO); + ttOrnaments.setPrice(BigDecimal.ZERO); + ttOrnaments.setUpdateTime(DateUtils.getNowDate()); + ttOrnaments.setRemark("ZBT市场已下架"); + ttOrnaments.setIsPutaway("1"); + }).collect(Collectors.toList()); + log.info("下架条数:" + ornamentsList.size()); + boolean isSuccess = ornamentsService.updateBatchById(ornamentsList, 1); + if (isSuccess) { + log.info("批量下架饰品成功!"); + } else { + log.info("批量下架饰品失败!"); + } + } + + private void batchUpdateOrnamentsPrice(List list, Map ornamentsMap) { + String usePricePremiumRateStr = configService.selectConfigByKey("usePricePremiumRate"); + BigDecimal usePricePremiumRate = new BigDecimal(usePricePremiumRateStr); + String ZBTParitiesStr = configService.selectConfigByKey("ZBTParities"); + BigDecimal ZBTParities = new BigDecimal(ZBTParitiesStr); + List updateList = list.stream().distinct().filter(ornamentZBT -> { + OrnamentZBT.PriceInfo priceInfo = ornamentZBT.getPriceInfo(); + TtOrnament ornament = ornamentsMap.get(ornamentZBT.getMarketHashName()); + BigDecimal priceData; + if (!Objects.isNull(ornament)) { + priceData = ornament.getPrice(); + } else { + priceData = BigDecimal.ZERO; + } + BigDecimal price; + if (!Objects.isNull(priceInfo)) { + price = priceInfo.getPrice(); + } else { + price = BigDecimal.ZERO; + } + Integer quantityData; + if (!Objects.isNull(ornament)) { + quantityData = ornament.getQuantity(); + } else { + quantityData = 0; + } + Integer quantity; + if (!Objects.isNull(priceInfo)) { + quantity = priceInfo.getQuantity(); + } else { + quantity = 0; + } + return (priceData == null || quantityData == null) || + (priceInfo == null) || + (ornament == null) || + (priceData.compareTo(price) != 0 || !Objects.equals(quantityData, quantity)); + }).collect(Collectors.toList()); + List ttOrnamentList = new ArrayList<>(); + updateList.forEach(searchList -> { + String marketHashName = searchList.getMarketHashName(); + OrnamentZBT.PriceInfo priceInfo = searchList.getPriceInfo(); + BigDecimal price = StringUtils.isNull(priceInfo) ? BigDecimal.ZERO : priceInfo.getPrice(); + Integer quantity = StringUtils.isNull(priceInfo) ? 0 : priceInfo.getQuantity(); + TtOrnament ttOrnament = ornamentsMap.get(marketHashName); + if (!Objects.isNull(ttOrnament)) { + ttOrnament.setUsePrice(price.multiply(ZBTParities).multiply(usePricePremiumRate)); + ttOrnament.setPrice(price); + ttOrnament.setQuantity(quantity); + ttOrnament.setUpdateTime(DateUtils.getNowDate()); + ttOrnamentList.add(ttOrnament); + } + }); + log.info("更新条数:" + ttOrnamentList.size()); + boolean isSuccess = ornamentsService.updateBatchById(ttOrnamentList, 1); + if (isSuccess) { + log.info("批量更新饰品价格成功!"); + } else { + log.info("批量更新饰品价格失败!"); + } + } + + // 批量插入饰品 + private void batchInsertOrnaments(List ZBTOrnamentsDataListByType, String ornamentsTypeValue) { + List rarity = dictDataMapper.selectDictDataByType("ornaments_rarity"); + if (ObjectUtils.isEmpty(rarity)) return; + List exterior = dictDataMapper.selectDictDataByType("ornaments_exterior"); + if (ObjectUtils.isEmpty(exterior)) return; + List quality = dictDataMapper.selectDictDataByType("ornaments_quality"); + if (ObjectUtils.isEmpty(quality)) return; + String usePricePremiumRateStr = configService.selectConfigByKey("usePricePremiumRate"); + BigDecimal usePricePremiumRate = new BigDecimal(usePricePremiumRateStr); + String ZBTParitiesStr = configService.selectConfigByKey("ZBTParities"); + BigDecimal ZBTParities = new BigDecimal(ZBTParitiesStr); + Map rarityMap = objectToMap(rarity); + Map exteriorMap = objectToMap(exterior); + Map qualityMap = objectToMap(quality); + List ornamentsList = new ArrayList<>(); + ZBTOrnamentsDataListByType.forEach(searchList -> { + OrnamentZBT.PriceInfo priceInfo = searchList.getPriceInfo(); + BigDecimal price = StringUtils.isNull(priceInfo) ? BigDecimal.ZERO : priceInfo.getPrice(); + Integer quantity = StringUtils.isNull(priceInfo) ? 0 : priceInfo.getQuantity(); + String rarityValue = rarityMap.get(searchList.getRarity()); + String exteriorValue = exteriorMap.get(searchList.getExterior()); + String qualityValue = qualityMap.get(searchList.getQuality()); + TtOrnament ornaments = TtOrnament.builder().build(); + ornaments.setName(searchList.getItemName()); + ornaments.setUsePrice(price.multiply(ZBTParities).multiply(usePricePremiumRate)); + ornaments.setImageUrl(searchList.getImageUrl()); + ornaments.setMarketHashName(searchList.getMarketHashName()); + ornaments.setId(Long.valueOf(searchList.getItemId())); + ornaments.setPrice(price); + ornaments.setQuantity(quantity); + ornaments.setShortName(searchList.getShortName()); + ornaments.setType(ornamentsTypeValue); + ornaments.setTypeName(searchList.getTypeName()); + ornaments.setQuality(qualityValue); + ornaments.setQualityName(searchList.getQualityName()); + ornaments.setQualityColor(searchList.getQualityColor()); + ornaments.setRarity(rarityValue); + ornaments.setRarityName(searchList.getRarityName()); + ornaments.setRarityColor(searchList.getRarityColor()); + ornaments.setExterior(exteriorValue); + ornaments.setExteriorName(searchList.getExteriorName()); + ornaments.setCreateTime(DateUtils.getNowDate()); + ornamentsList.add(ornaments); + }); + log.info("新增条数:" + ornamentsList.size()); + boolean isSuccess = ornamentsService.saveBatch(ornamentsList, 1); + if (isSuccess) { + log.info("批量插入饰品成功!"); + } else { + log.info("批量插入饰品失败!"); + } + } + + private Map objectToMap(List list) { + Map map = new HashMap<>(); + for (SysDictData sysDictData : list) { + map.put(sysDictData.getDictLabel(), sysDictData.getDictValue()); + } + return map; + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/util/MD5Utils.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/util/MD5Utils.java new file mode 100644 index 0000000..0499088 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/util/MD5Utils.java @@ -0,0 +1,21 @@ +package com.ruoyi.thirdparty.zbt.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class MD5Utils { + + public static String md5(String input) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] messageDigest = md.digest(input.getBytes()); + StringBuilder sb = new StringBuilder(); + for (byte b : messageDigest) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5 algorithm not found", e); + } + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/util/ZBTUtils.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/util/ZBTUtils.java new file mode 100644 index 0000000..c80d2e0 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zbt/util/ZBTUtils.java @@ -0,0 +1,75 @@ +package com.ruoyi.thirdparty.zbt.util; + +import com.ruoyi.common.utils.sign.Md5Utils; +import com.ruoyi.thirdparty.zbt.config.ZBTProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +public class ZBTUtils { + + private static final Logger log = LoggerFactory.getLogger(ZBTUtils.class); + + public static Map objectToMap(T obj) { + Map map = new HashMap<>(); + if (obj == null) { + return map; + } + + try { + Class clazz = obj.getClass(); + for (Field field : clazz.getDeclaredFields()) { + field.setAccessible(true); + String value = (String) field.get(obj); + if (value != null) { + map.put(field.getName(), value); + } + } + } catch (Exception e) { + log.error("对象转Map时出现异常", e); + } + return map; + } + + /** + * 验签 + */ + public static boolean verifySign(Map requestBody, String appSecret) { + // 提取sign字段 + String receivedSign = (String) requestBody.get("sign"); + + // 删除sign字段 + requestBody.remove("sign"); + + // 生成待签名字符串 + StringBuilder sb = new StringBuilder(); + TreeMap sortedParams = new TreeMap<>(); + requestBody.forEach((key, value) -> { + String stringValue = value == null ? "null" : value.toString(); + try { + String decodedValue = URLDecoder.decode(stringValue, "UTF-8"); + sortedParams.put(key, decodedValue); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("URL decoding error", e); + } + }); + + sortedParams.forEach((key, value) -> sb.append(key).append("=").append(value).append("&")); + + // 添加appSecret + sb.append("sign=").append(appSecret); + + // 计算签名 + String calculatedSign = MD5Utils.md5(sb.toString()).toUpperCase(); + + // 比较签名 + return calculatedSign.equals(receivedSign); + } + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/config/ZYConfig.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/config/ZYConfig.java new file mode 100644 index 0000000..00e9208 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/config/ZYConfig.java @@ -0,0 +1,30 @@ +package com.ruoyi.thirdparty.zyZFB.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "zy-zfb") +public class ZYConfig { + + private String merKey; + private String gateway; + private String tranType; + private String merchantId; + private String notifyBaseUrl; + + // 下单接口 + public static final String ApiAddTrans = "/ctp/view/server/aotori/addTrans"; + + // 查询订单接口 + public static final String ApiQueryTrans = "/ctp/view/server/aotori/queryTrans"; + + // 代付接口 + public static final String ApiPropayTrans = "/ctp/view/server/aotori/propayTrans"; + + // 查询余额接口 + public static final String ApiQueryBalancePHP = "/ctp/view/server/aotori/querybalance.php"; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/controller/ZyController.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/controller/ZyController.java new file mode 100644 index 0000000..830e8af --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/controller/ZyController.java @@ -0,0 +1,119 @@ +package com.ruoyi.thirdparty.zyZFB.controller; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.thirdparty.zyZFB.config.ZYConfig; +import com.ruoyi.thirdparty.zyZFB.service.zyService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Map; + +@Api(tags = "中云支付宝") +@Slf4j +@RestController +@RequestMapping("api/zyZFB") +public class ZyController extends BaseController { + + @Autowired + private ZYConfig zyConfig; + + @Autowired + private zyService zyService; + + @Autowired + private TtUserService userService; + + // 下单 + @ApiOperation("预下单接口") + @PostMapping("/ApiAddTrans") + @UpdateUserCache + public R ApiAddTrans(HttpServletRequest request, CreateOrderParam param) { + R checkLogin = checkLogin(); + if (!checkLogin.getCode().equals(200)) return checkLogin; + Integer userId = ((Long) checkLogin.getData()).intValue(); + + TtUser user = userService.getById(userId); + + return zyService.ApiAddTrans(param, user, request); + } + + @AllArgsConstructor + @NoArgsConstructor + @Data + public static class PayNotifyData { + // 支付方式 + private String tranType; + private String respCode; + private String respDesc; + // 交易流水号 + private String serverRspNo; + private String amt; + private String merReqNo; + private String msgExt; + private String tranTime; + private String sign; + } + + // 支付回调 + @ApiOperation("支付回调") + @PostMapping("/addTransNotify") + public String addTransNotify(PayNotifyData data) { + log.info("支付回调 {}", JSONUtil.toJsonStr(data)); + return zyService.payNotify(data); + } + + // 查询订单接口 + @ApiOperation("查询订单") + @PostMapping("/ApiQueryTrans") + public String ApiQueryTrans(HttpServletRequest request, Map param) { + return "success"; + } + + // 代付接口 + @ApiOperation("代付") + @PostMapping("/ApiPropayTrans") + public String ApiPropayTrans(HttpServletRequest request, Map param) { + return "success"; + } + + // 查询余额接口 + @ApiOperation("查询余额") + @PostMapping("/ApiQueryBalancePHP") + public String ApiQueryBalancePHP(HttpServletRequest request, Map param) { + return "success"; + } + + public R checkLogin() { + Long userId; + try { + userId = getUserId(); + if (ObjectUtil.isEmpty(userId)) return R.fail(401,"登录过期,请重新登录。"); + return R.ok(userId); + } catch (Exception e) { + return R.fail("登录过期,请重新登录。"); + } + } + + // 支付回调 + // @PostMapping("/propayTransNotify") + // public String propayTransNotify(Map map) { + // log.info("代付回调 {}", JSONUtil.toJsonStr(map)); + // return "success"; + // } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/service/Impl/zyServiceImpl.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/service/Impl/zyServiceImpl.java new file mode 100644 index 0000000..f9e18d0 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/service/Impl/zyServiceImpl.java @@ -0,0 +1,425 @@ +package com.ruoyi.thirdparty.zyZFB.service.Impl; + +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.TtFirstRechargeMapper; +import com.ruoyi.admin.mapper.TtOrderMapper; +import com.ruoyi.admin.mapper.TtPromotionLevelMapper; +import com.ruoyi.admin.mapper.TtRechargeProdMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.common.constant.PayOrderStatus; +import com.ruoyi.domain.common.constant.PayType; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.TtPromotionLevel; +import com.ruoyi.domain.entity.TtRechargeProd; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.common.service.RechargeSuccessfulNoticeService; +import com.ruoyi.thirdparty.zyZFB.config.ZYConfig; +import com.ruoyi.thirdparty.zyZFB.controller.ZyController.PayNotifyData; +import com.ruoyi.thirdparty.zyZFB.service.zyService; +import com.ruoyi.thirdparty.zyZFB.utils.ZYHTTPRes; +import com.ruoyi.thirdparty.zyZFB.utils.signUtil; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Executor; + +@Slf4j +@Service +public class zyServiceImpl implements zyService { + + @Autowired + private ZYConfig zyConfig; + + @Autowired + private TtOrderMapper orderMapper; + + @Autowired + private Executor customThreadPoolExecutor; + + @Autowired + private TtUserService userService; + + @Autowired + private TtUserMapper userMapper; + + @Autowired + private TtRechargeProdMapper rechargeListMapper; + + @Autowired + private TtUserBlendErcashMapper userBlendErcashMapper; + + @Autowired + private RechargeSuccessfulNoticeService rechargeSuccessfulNoticeService; + + @Autowired + private ISysConfigService configService; + + @Autowired + private TtPromotionLevelMapper ttPromotionLevelMapper; + + @Autowired + private TtFirstRechargeMapper ttFirstRechargeMapper; + + @Override + public R ApiAddTrans(@Validated CreateOrderParam param, TtUser user, HttpServletRequest request) { + + // 构建完整参数 + Map map = buildBaseParam(new HashMap<>(), request); + + Snowflake snowflake = IdUtil.getSnowflake(1, 1); + String merReqNo = String.valueOf(snowflake.nextId()); + map.put("merReqNo", merReqNo); + map.put("notifyUrl", zyConfig.getNotifyBaseUrl() + "/api/zyZFB/addTransNotify"); + map.put("returnUrl", zyConfig.getNotifyBaseUrl()); + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); + String tranDateTime = dateFormat.format(new Date()); + map.put("tranDateTime", tranDateTime); + + BigDecimal totalAmount = param.getGoodsPrice() + .multiply(new BigDecimal(param.getGoodsNum())) + .multiply(new BigDecimal("100")) + .setScale(0, BigDecimal.ROUND_HALF_EVEN); + map.put("amt", totalAmount.toString()); + map.put("goodsName", param.getGoodsId().toString()); + + String sign = signUtil.getSign(map, zyConfig.getMerKey()); + map.put("sign", sign); + + // 发送请求 + HttpRequest post = HttpUtil.createPost(zyConfig.getGateway() + ZYConfig.ApiAddTrans); + // post.header("Content-Type","application/x-www-form-urlencoded"); + // post.body(param, "application/x-www-form-urlencoded"); + post.body(JSONUtil.toJsonStr(map), "json"); + HttpResponse res = post.execute(); + + // 解析响应 + ZYHTTPRes resBody = JSONUtil.toBean(res.body(), ZYHTTPRes.class); + if (!resBody.getRespCode().equals("0000")) { + return R.fail(resBody); + } + + TtOrder order = new TtOrder(); + order.setUserId(user.getUserId()); + // ttOrder.setThirdParty("0"); + order.setType(PayType.ZY_ZFB.getCode()); + order.setGoodsId(param.getGoodsId()); + order.setGoodsPrice(param.getGoodsPrice()); + order.setGoodsNum(param.getGoodsNum()); + order.setTotalAmount(totalAmount); + order.setOrderId(map.get("merReqNo")); + order.setSign(sign); + order.setStatus(PayOrderStatus.NO_PAY.getCode()); + order.setOutTradeNo(resBody.getMerReqNo()); + order.setPayUrl(resBody.getPayUrl()); + order.setCreateTime(new Date()); + + // System.out.println(order.getPayUrl().length()); + + orderMapper.insert(order); + + // 发送充值成功通知 + rechargeSuccessfulNoticeService.sendRechargeSuccessNotice(user.getUserId().toString(), order.getGoodsPrice()); + + return R.ok(order); + } + + @Override + public String payNotify(PayNotifyData data) { + + // System.out.println("中云 支付回调" + JSONUtil.toJsonStr(data)); + + TtOrder order = null; + // TODO: 2024/4/12 最好再查询一下第三方平台的订单信息 + + try { + + // 查询订单信息 + order = new LambdaQueryChainWrapper<>(orderMapper) + .eq(TtOrder::getOrderId, data.getMerReqNo()) + .eq(TtOrder::getStatus, PayOrderStatus.NO_PAY.getCode()) + .one(); + + if (ObjectUtil.isEmpty(order)) { + log.warn("支付回调异常,不存在的有效订单。"); + return "fail"; + } + + // 查询用户信息 + TtUser user = new LambdaQueryChainWrapper<>(userMapper) + .eq(TtUser::getUserId, order.getUserId()) + .eq(TtUser::getDelFlag, 0) + .one(); + if (ObjectUtil.isEmpty(user)) { + log.warn("支付回调异常,不存在的有效用户。"); + return "fail"; + } + + if (order.getStatus().equals(PayOrderStatus.PAY_COMPLE.getCode()) || order.getStatus().equals(PayOrderStatus.PAY_YET.getCode())) { + log.warn("重复的回调!该订单已完成。"); + return "success"; + } + + // 查询商品信息 + TtRechargeProd goods = new LambdaQueryChainWrapper<>(rechargeListMapper) + .eq(TtRechargeProd::getId, order.getGoodsId()) + .eq(TtRechargeProd::getStatus, 0) + .one(); + if (ObjectUtil.isEmpty(goods)) { + log.warn("支付回调异常,不存在的商品。"); + return "fail"; + } + + // 账户结算 + if (ObjectUtil.isNull(goods.getProductA())) goods.setProductA(BigDecimal.ZERO); + if (ObjectUtil.isNull(goods.getProductC())) goods.setProductC(BigDecimal.ZERO); + payNotifyAccounting(order, user, goods, order.getGoodsNum()); + + // 首充赠送 + firstChargeGiftAmount(user, goods, order.getGoodsNum()); + + // 推广等级充值赠送 + promotionLevelChargeGiftAmount(user, goods, order.getGoodsNum()); + + // 推广充值返佣 + promotionChargeCommission(user, goods, order.getGoodsNum()); + + // 更新订单 + new LambdaUpdateChainWrapper<>(orderMapper) + .eq(TtOrder::getId, order.getId()) + .set(TtOrder::getStatus, PayOrderStatus.PAY_COMPLE.getCode()) + .set(TtOrder::getUpdateTime, new Date()) + .update(); + + return "success"; + + } catch (Exception e) { + + e.printStackTrace(); + + log.warn("zy zfb 回调异常"); + + if (ObjectUtil.isNotNull(order)) { + new LambdaUpdateChainWrapper<>(orderMapper) + .eq(TtOrder::getId, order.getId()) + .set(TtOrder::getStatus, PayOrderStatus.CALL_BACK_ERRO.getCode()) + .set(TtOrder::getUpdateTime, new Date()) + .update(); + } + + return "fail"; + + } + + } + + @Override + public R ApiQueryTrans(Map param, TtUser user) { + return null; + } + + @Override + public R ApiPropayTrans(Map param, TtUser user) { + return null; + } + + @Override + public R ApiQueryBalancePHP(Map param, TtUser user) { + return null; + } + + // 构建基本请求信息 + private Map buildBaseParam(Map map, HttpServletRequest request) { + map.put("merchantId", zyConfig.getMerchantId()); + map.put("tranType", zyConfig.getTranType()); + map.put("msgExt", "success"); + // map.put("tranType", "2004"); + // map.put("notifyUrl",zyConfig.getNotifyBaseUrl()+"api/zyZFB/addTransNotify"); + if (ObjectUtil.isNotEmpty(request)) { + map.put("creatIp", request.getRemoteAddr()); + // map.put("creatIp", "110.228.15.148"); + } + return map; + } + + public void payNotifyAccounting(TtOrder order, TtUser user, TtRechargeProd goods, Integer goodsNumber) { + + // 加钱 + // BigDecimal totalAmount = order.getTotalAmount(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal totalCredits = goods.getProductC().multiply(new BigDecimal(goodsNumber)); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, user.getUserId()) + .setSql("account_amount = account_amount + " + totalAmount.toString() + + ",account_credits = account_credits + " + totalCredits.toString()); + userService.update(userUpdate); + + user = userService.getById(user.getUserId()); + // 综合消费日志 + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(user.getUserId()) + + .amount(totalAmount.compareTo(BigDecimal.ZERO) > 0 ? totalAmount : null) + .finalAmount(totalAmount.compareTo(BigDecimal.ZERO) > 0 ? user.getAccountAmount().add(totalAmount) : null) + + .credits(totalCredits.compareTo(BigDecimal.ZERO) > 0 ? totalCredits : null) + .finalCredits(totalCredits.compareTo(BigDecimal.ZERO) > 0 ? user.getAccountCredits().add(totalCredits) : null) + + .total(totalAmount.add(totalCredits)) // 收支合计 + + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.RECHARGE.getCode()) + .remark(TtAccountRecordSource.RECHARGE.getMsg()) + + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + + int insert = userBlendErcashMapper.insert(blendErcash); + + } + + /** + * 首充赠送 + */ + private void firstChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + // 1.判断是否为首充 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .eq(TtUserBlendErcash::getSource, 1) + .eq(TtUserBlendErcash::getUserId, ttUser.getUserId()); + List ttUserBlendErcashes = userBlendErcashMapper.selectList(wrapper); + if (ttUserBlendErcashes.size() > 0) { + return; + } + + // 2.加钱 + // 充值金额 + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + // 查询赠送比例 + BigDecimal firstChargeAmountRatio = ttFirstRechargeMapper.selectRatioByMinAmount(totalAmount); + if (Objects.isNull(firstChargeAmountRatio)) { + return; + } + // 计算赠送金额 + BigDecimal giftAmount = totalAmount.multiply(firstChargeAmountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 3.记录 + ttUser = userService.getById(ttUser.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.FIRST_CHARGE.getCode()) + .remark(TtAccountRecordSource.FIRST_CHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } + + /** + * 推广等级充值赠送 + */ + private void promotionLevelChargeGiftAmount(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + // 1.查询用户推广等级对应的赠送比例 + TtPromotionLevel ttPromotionLevel = ttPromotionLevelMapper.selectById(ttUser.getPromotionLevel()); + + // 2.加钱 + BigDecimal amountRatio = ttPromotionLevel.getRechargeGiftRatio(); + if (Objects.isNull(amountRatio)) + return; + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal giftAmount = totalAmount.multiply(amountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getUserId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 3.记录 + ttUser = userService.getById(ttUser.getUserId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_LEVEL_CHARGE.getCode()) + .remark(TtAccountRecordSource.PROMOTION_LEVEL_CHARGE.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } + + /** + * 推广充值返佣 + */ + private void promotionChargeCommission(TtUser ttUser, TtRechargeProd goods, Integer goodsNumber) { + if (Objects.isNull(ttUser.getParentId())) { + return; + } + // 查询用户推广等级信息 + TtPromotionLevel ttPromotionLevel = ttPromotionLevelMapper.selectById(ttUser.getPromotionLevel()); + + // 给上级加钱 + BigDecimal amountRatio = ttPromotionLevel.getCommissions(); + BigDecimal totalAmount = goods.getProductA().multiply(new BigDecimal(goodsNumber)); + BigDecimal giftAmount = totalAmount.multiply(amountRatio); + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, ttUser.getParentId()) + .setSql("account_amount = account_amount + " + giftAmount.toString()); + userService.update(userUpdate); + + // 记录 + ttUser = userService.getById(ttUser.getParentId()); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(ttUser.getUserId()) + .amount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? giftAmount : null) + .finalAmount(giftAmount.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(giftAmount) : null) + .total(giftAmount) + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.PROMOTION_CHARGE_COMMISSION.getCode()) + .remark(TtAccountRecordSource.PROMOTION_CHARGE_COMMISSION.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .build(); + userBlendErcashMapper.insert(blendErcash); + } +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/service/zyService.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/service/zyService.java new file mode 100644 index 0000000..9a13c2e --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/service/zyService.java @@ -0,0 +1,19 @@ +package com.ruoyi.thirdparty.zyZFB.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.other.CreateOrderParam; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.thirdparty.zyZFB.controller.ZyController; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Map; + +public interface zyService { + + R ApiAddTrans(CreateOrderParam param, TtUser user, HttpServletRequest request); + R ApiQueryTrans(Map param, TtUser user); + R ApiPropayTrans(Map param, TtUser user); + R ApiQueryBalancePHP(Map param, TtUser user); + + String payNotify(ZyController.PayNotifyData data); +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/utils/ZYHTTPRes.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/utils/ZYHTTPRes.java new file mode 100644 index 0000000..b6dce5b --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/utils/ZYHTTPRes.java @@ -0,0 +1,47 @@ +package com.ruoyi.thirdparty.zyZFB.utils; + +// zy返回值 + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class ZYHTTPRes { + + public enum resCode{ + + success("0000"), + DOING("0001"); + + private String code; + + resCode(String code){ + this.code = code; + } + public String getCode(){ + return this.code; + } + } + + private String respCode; + //返回码说明 + private String respDesc; + //返回码 + private String payCode; + private String payDesc; + private String amt; + private String tranTime; + private String tranType; + //扩展信息 + private String msgExt; + private String payUrl; + // 交易流水号 + private String merReqNo; + //服务端响应流水号 + private String serverRspNo; + private String sign; + +} diff --git a/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/utils/signUtil.java b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/utils/signUtil.java new file mode 100644 index 0000000..3175a35 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/java/com/ruoyi/thirdparty/zyZFB/utils/signUtil.java @@ -0,0 +1,46 @@ +package com.ruoyi.thirdparty.zyZFB.utils; + +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; + +import java.security.MessageDigest; +import java.util.*; + +public class signUtil { + + public static String getSign(Map map, String key) { + + // 排序 + List> entryList = new ArrayList<>(map.entrySet()); + Collections.sort(entryList, new Comparator>() { + @Override + public int compare(Map.Entry o1, Map.Entry o2) { + return o1.getKey().compareTo(o2.getKey()); + } + }); + + String str = ""; + for (int i = 0; i < entryList.size(); i++) { + if (i == entryList.size()-1) { + str = str + entryList.get(i).getKey() + "=" + entryList.get(i).getValue(); + continue; + } + str = str + entryList.get(i).getKey() + "=" + entryList.get(i).getValue() + "&"; + } + str = str + key; + + // MD5加密 + String sign = ""; + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + // 转换为MD5码 + byte[] digest = md5.digest(str.getBytes("utf-8")); + sign = ByteUtils.toHexString(digest); + } catch (Exception e) { + System.out.println("md5加密 warn"); + } + + return sign; + + } + +} diff --git a/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/ApiNoticeMapper.xml b/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/ApiNoticeMapper.xml new file mode 100644 index 0000000..7255d2b --- /dev/null +++ b/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/ApiNoticeMapper.xml @@ -0,0 +1,23 @@ + + + + + insert into tt_notice + + user_id, + title, + content, + `read`, + create_time, + + + #{userId}, + #{title}, + #{content}, + #{read}, + #{createTime}, + + + diff --git a/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/TianXinMapper.xml b/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/TianXinMapper.xml new file mode 100644 index 0000000..f42a283 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/TianXinMapper.xml @@ -0,0 +1,114 @@ + + + + + + insert into tianxin_order + + order_id, + pay_type, + goods_id, + goods_price, + goods_num, + total_amount, + user_ip, + pay_status, + create_time, + update_time, + user_id, + user_name, + remark, + call_back_order_id, + call_back_msg, + call_back_status, + sign, + subject, + + + #{orderId}, + #{payType}, + #{goodsId}, + #{goodsPrice}, + #{goodsNum}, + #{totalAmount}, + #{userIp}, + #{payStatus}, + #{createTime}, + #{updateTime}, + #{userId}, + #{userName}, + #{remark}, + #{callBackOrderId}, + #{callBackMsg}, + #{callBackStatus}, + #{sign}, + #{subject}, + + + + + update tianxin_order + + pay_type = #{payType}, + goods_id = #{goodsId}, + goods_price = #{goodsPrice}, + goods_num = #{goodsNum}, + total_amount = #{totalAmount}, + user_ip = #{userIp}, + pay_status = #{payStatus}, + create_time = #{createTime}, + update_time = #{updateTime}, + user_id = #{userId}, + user_name = #{userName}, + remark = #{remark}, + call_back_order_id = #{callBackOrderId}, + call_back_msg = #{callBackMsg}, + call_back_status = #{callBackStatus}, + sign = #{sign}, + subject = #{subject}, + + where order_id = #{orderId} + + + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/TtCoinRechargeRecordMapper.xml b/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/TtCoinRechargeRecordMapper.xml new file mode 100644 index 0000000..503f5e8 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/TtCoinRechargeRecordMapper.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + select + id, + coin, + pay_status, + uid, + uname, + create_time, + update_time, + order_no, + callback_no, + callback_msg + from tt_coin_recharge_record + + + + + + + + + + + + + + + + + insert into tt_coin_recharge_record + + id, + coin, + pay_status, + uid, + uname, + create_time, + update_time, + order_no, + callback_no, + callback_msg, + + + #{id}, + #{coin}, + #{payStatus}, + #{uid}, + #{uname}, + #{createTime}, + #{updateTime}, + #{orderNo}, + #{callbackNo}, + #{callbackMsg}, + + + + + update tt_coin_recharge_record + + coin = #{coin}, + pay_status = #{payStatus}, + uid = #{uid}, + uname = #{uname}, + create_time = #{createTime}, + update_time = #{updateTime}, + order_no = #{orderNo}, + callback_no = #{callbackNo}, + callback_msg = #{callbackMsg}, + + where id = #{id} + + + + delete from tt_coin_recharge_record where id = #{id} + + + + delete from tt_coin_recharge_record where id in + + #{id} + + + \ No newline at end of file diff --git a/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/TtCoinRecordMapper.xml b/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/TtCoinRecordMapper.xml new file mode 100644 index 0000000..2993799 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/TtCoinRecordMapper.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + select id, uid, uname, `type`,oper_type, reward_type, money, `time`, status, create_time, update_time from tt_coin_record + + + + + + + + insert into tt_coin_record + + uid, + uname, + `type`, + oper_type, + reward_type, + money, + `time`, + status, + create_time, + update_time, + + + #{uid}, + #{uname}, + #{type}, + #{operType}, + + #{rewardType}, + #{money}, + #{time}, + #{status}, + #{createTime}, + #{updateTime}, + + + + + update tt_coin_record + + uid = #{uid}, + uname = #{uname}, + type = #{type}, + oper_type = #{operType}, + reward_type = #{rewardType}, + money = #{money}, + `time` = #{time}, + status = #{status}, + create_time = #{createTime}, + update_time = #{updateTime}, + + where id = #{id} + + + + delete from tt_coin_record where id = #{id} + + + + delete from tt_coin_record where id in + + #{id} + + + \ No newline at end of file diff --git a/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/TtUserLsjlMapper.xml b/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/TtUserLsjlMapper.xml new file mode 100644 index 0000000..dfe15f8 --- /dev/null +++ b/skins-service/service-thirdparty/src/main/resources/mapper/skinservice/TtUserLsjlMapper.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select id, + tt_user_id, + type, + v_coin_before, + v_coin_after, + v_coin_change, + d_coin_before, + d_coin_change, + d_coin_after, + create_time, + update_time + from tt_user_lsjl + + + + + + + + insert into tt_user_lsjl + + tt_user_id, + type, + v_coin_before, + v_coin_after, + v_coin_change, + d_coin_before, + d_coin_change, + d_coin_after, + create_time, + update_time, + + + #{ttUserId}, + #{type}, + #{vCoinBefore}, + #{vCoinAfter}, + #{vCoinChange}, + #{dCoinBefore}, + #{dCoinChange}, + #{dCoinAfter}, + #{createTime}, + #{updateTime}, + + + + + update tt_user_lsjl + + tt_user_id = #{ttUserId}, + type = #{type}, + v_coin_before = #{vCoinBefore}, + v_coin_after = #{vCoinAfter}, + v_coin_change = #{vCoinChange}, + d_coin_before = #{dCoinBefore}, + d_coin_change = #{dCoinChange}, + d_coin_after = #{dCoinAfter}, + create_time = #{createTime}, + update_time = #{updateTime}, + + where id = #{id} + + + + delete + from tt_user_lsjl + where id = #{id} + + + + delete from tt_user_lsjl where id in + + #{id} + + + \ No newline at end of file diff --git a/skins-service/service-user/pom.xml b/skins-service/service-user/pom.xml new file mode 100644 index 0000000..91b2b57 --- /dev/null +++ b/skins-service/service-user/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + com.ruoyi + skins-service + 4.8.2 + + + service-user + + + 8 + 8 + UTF-8 + + + + + com.ruoyi + ruoyi-framework + + + + com.ruoyi + service-admin + 4.8.2 + + + + com.ruoyi + service-thirdparty + 4.8.2 + + + + \ No newline at end of file diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/consumer/NoticeConsumer.java b/skins-service/service-user/src/main/java/com/ruoyi/user/consumer/NoticeConsumer.java new file mode 100644 index 0000000..975743c --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/consumer/NoticeConsumer.java @@ -0,0 +1,44 @@ +package com.ruoyi.user.consumer; + +import com.rabbitmq.client.Channel; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.other.TtNotice; +import com.ruoyi.user.service.ApiNoticeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.support.AmqpHeaders; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Map; + +@Component +@Slf4j +public class NoticeConsumer { + + @Autowired + private ApiNoticeService apiNoticeService; + + @RabbitListener(queues = "notice_queue") + public void insertNotice(Map noticeMap, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) { + String userId = noticeMap.get("userId"); + String title = noticeMap.get("title"); + String content = noticeMap.get("content"); + String createTime = noticeMap.get("createTime"); + TtNotice ttNotice = new TtNotice(); + ttNotice.setUserId(Long.valueOf(userId)); + ttNotice.setTitle(title); + ttNotice.setContent(content); + ttNotice.setCreateTime(DateUtils.parseDate(createTime)); + apiNoticeService.addNotice(ttNotice); + + try { + //手动确认消费 + channel.basicAck(tag, false); + } catch (IOException e) { + log.error(e.getMessage()); + } + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/contract/PersonalRecordRequest.java b/skins-service/service-user/src/main/java/com/ruoyi/user/contract/PersonalRecordRequest.java new file mode 100644 index 0000000..5349618 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/contract/PersonalRecordRequest.java @@ -0,0 +1,28 @@ +package com.ruoyi.user.contract; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class PersonalRecordRequest { + @NotNull + @Min(value = 1,message = "最小值1") + private Integer page; + @NotNull + @Min(value = 1,message = "最小值1") + @Max(value = 50, message = "最大值20") + private Integer pageSize; + + // 1:今日 2:昨日 3:最近7天 + @NotNull(message = "type不能为空") + private Integer type; + + // 查询的数据源 1:充值 2:消费 3:红包 + @NotNull(message = "source不能为空") + private Integer source; + + // 过滤选项 筛选条件 0:支出 1:收入 + private Integer filteringOptions; +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/contract/PersonalRecordResponse.java b/skins-service/service-user/src/main/java/com/ruoyi/user/contract/PersonalRecordResponse.java new file mode 100644 index 0000000..03880ec --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/contract/PersonalRecordResponse.java @@ -0,0 +1,24 @@ +package com.ruoyi.user.contract; + +import lombok.Data; + +import java.util.List; + +@Data +public class PersonalRecordResponse { + private long total; + private List rows; + + @Data + public static class RecordVO { + private String amount; + + private String finalAmount; + + private String remark; + + private String datetime; + + private Integer source; + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/contract/QueryTransferRecordRequest.java b/skins-service/service-user/src/main/java/com/ruoyi/user/contract/QueryTransferRecordRequest.java new file mode 100644 index 0000000..d441ec4 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/contract/QueryTransferRecordRequest.java @@ -0,0 +1,11 @@ +package com.ruoyi.user.contract; + +import lombok.Data; + +@Data +public class QueryTransferRecordRequest { + // "src": 自己转增的 "dst":转增给自己的 + private String kind; + private Integer pageNum; + private Integer pageSize; +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/contract/QueryTransferRecordResponse.java b/skins-service/service-user/src/main/java/com/ruoyi/user/contract/QueryTransferRecordResponse.java new file mode 100644 index 0000000..305df5d --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/contract/QueryTransferRecordResponse.java @@ -0,0 +1,12 @@ +package com.ruoyi.user.contract; + +import com.ruoyi.user.model.vo.ApiTransferRecordVO; +import lombok.Data; + +import java.util.List; + +@Data +public class QueryTransferRecordResponse { + private long total; + private List rows; +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/contract/QueryVipRewardResponse.java b/skins-service/service-user/src/main/java/com/ruoyi/user/contract/QueryVipRewardResponse.java new file mode 100644 index 0000000..3e56d03 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/contract/QueryVipRewardResponse.java @@ -0,0 +1,15 @@ +package com.ruoyi.user.contract; + +import com.ruoyi.domain.other.TtVipLevel; +import lombok.Data; + +import java.util.List; + +@Data +public class QueryVipRewardResponse { + private String todayAmount; + private String todayReward; + private String yesterdayAmount; + private String yesterdayReward; + private List vipLevels; +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiAdvertisementController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiAdvertisementController.java new file mode 100644 index 0000000..83bcacc --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiAdvertisementController.java @@ -0,0 +1,30 @@ +package com.ruoyi.user.controller; + +import com.ruoyi.admin.service.TtAdvertisementService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.other.TtAdvertisement; +import io.swagger.annotations.Api; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "广告查询") +@RestController +@RequestMapping("/api/advertisement") +public class ApiAdvertisementController { + + private final TtAdvertisementService ttAdvertisementService; + + public ApiAdvertisementController(TtAdvertisementService ttAdvertisementService) { + this.ttAdvertisementService = ttAdvertisementService; + } + + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable("id") Integer id) { + TtAdvertisement ttAdvertisement = ttAdvertisementService.getById(id); + // ttBanner.setPicture(""); + return R.ok(ttAdvertisement); + } + +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiAnnouncementController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiAnnouncementController.java new file mode 100644 index 0000000..e6c4c91 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiAnnouncementController.java @@ -0,0 +1,58 @@ +package com.ruoyi.user.controller; + + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.domain.other.TtAnnouncement; +import com.ruoyi.user.service.ApiAnnouncementService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Objects; + +@Api(tags = "公告") +@RestController +@RequestMapping("/api/announcement") +public class ApiAnnouncementController extends BaseController { + + @Autowired + private ApiAnnouncementService apiAnnouncementService; + + @ApiOperation("获取公告列表") + @ApiResponse(code = 200, message = "read【阅读状态(0未读 1已读)】") + @GetMapping("/list") + public TableDataInfo list() { + startPage(); + Long userId = SecurityUtils.getUserId(); + List announcementList = apiAnnouncementService.getAnnouncementList(userId); + return getDataTable(announcementList); + } + + @ApiOperation("获取公告详情") + @GetMapping("/{announcementId}") + public AjaxResult getAnnouncementByAnnouncementId(@PathVariable Integer announcementId) { + Long userId = SecurityUtils.getUserId(); + TtAnnouncement ttAnnouncement = apiAnnouncementService.getAnnouncementByAnnouncementId(announcementId, userId); + if (Objects.isNull(ttAnnouncement)) { + return AjaxResult.error("公告不存在"); + } + return AjaxResult.success(ttAnnouncement); + } + + @ApiOperation("获取未读公告数量") + @GetMapping("/countUnreadAnnouncement") + public AjaxResult countUnreadAnnouncement() { + Long userId = SecurityUtils.getUserId(); + return AjaxResult.success(apiAnnouncementService.countUnreadAnnouncement(userId)); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiBlendErcashController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiBlendErcashController.java new file mode 100644 index 0000000..9b15a7a --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiBlendErcashController.java @@ -0,0 +1,53 @@ +package com.ruoyi.user.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.admin.service.TtUserBlendErcashService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.ChildDetailByBoss; +import com.ruoyi.domain.vo.ChildDetailByBossVO; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.user.service.ApiShoppingService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "用户收支记录") +@RestController +@RequestMapping("/api/blendErcash") +public class ApiBlendErcashController extends BaseController { + + @Autowired + private TtUserBlendErcashService blendErcashService; + + @Autowired + private ISysConfigService sysConfigService; + + @Autowired + private ApiShoppingService apiShoppingService; + + @Autowired + private TtUserService userService; + + // 下级消费明细 + @ApiOperation("粉丝消费明细") + @PostMapping("/childDetailByBoss") + // @Anonymous + public R> childDetailByBoss(@RequestBody @Validated ChildDetailByBoss param){ + + Long userId = getUserId(); + if (ObjectUtil.isEmpty(userId)) return R.fail(401,"登录过期"); + + param.setBossId(Integer.valueOf(userId.toString())); + return blendErcashService.childDetailByBoss(param); + + } + +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiBonusController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiBonusController.java new file mode 100644 index 0000000..d97c1a4 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiBonusController.java @@ -0,0 +1,57 @@ +package com.ruoyi.user.controller; + +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.user.contract.QueryVipRewardResponse; +import com.ruoyi.user.service.ApiBonusService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "福利补贴") +@RestController +@RequestMapping("/api/bonus") +public class ApiBonusController extends BaseController { + + private final ISysConfigService sysConfigService; + private final ApiBonusService apiBonusService; + private final TtUserService userService; + + public ApiBonusController(ISysConfigService sysConfigService, + ApiBonusService apiBonusService, + TtUserService userService) { + this.sysConfigService = sysConfigService; + this.apiBonusService = apiBonusService; + this.userService = userService; + } + + @ApiOperation("领取红包") + @UpdateUserCache + @GetMapping("/receiveRedPacket/{code}") + public R receiveRedPacket(@PathVariable("code") String code) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + return apiBonusService.receiveRedPacket(code, ttUser); + } + + @ApiOperation("查询vip流水奖励") + @GetMapping("/vipreward/list") + public R vipRewardList() { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + return apiBonusService.vipRewardList(ttUser); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiLoginController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiLoginController.java new file mode 100644 index 0000000..d63b48c --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiLoginController.java @@ -0,0 +1,177 @@ +package com.ruoyi.user.controller; + +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.annotation.NewUserInfo; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.entity.UserData; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.ApiLoginBody; +import com.ruoyi.domain.other.ApiVerificationCodeLoginBody; +import com.ruoyi.domain.other.ApiVerificationCodeLoginOrRegisterBody; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.user.service.ApiLoginService; +import com.ruoyi.user.service.ApiUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 登录Controller + */ +@Api(tags = "客户端用户认证") +@RestController +@RequestMapping("/api") +public class ApiLoginController extends BaseController { + + private final ISysConfigService sysConfigService; + private final ApiLoginService loginService; + private final TokenService tokenService; + @Autowired + private TtUserMapper userMapper; + public ApiLoginController(ISysConfigService sysConfigService, + ApiLoginService loginService, + TokenService tokenService) { + this.sysConfigService = sysConfigService; + this.loginService = loginService; + this.tokenService = tokenService; + } + + @Autowired + private ApiUserService apiUserService; + + @Autowired + private TtUserService userService; + + @ApiOperation("客户端用户登录") + @Anonymous + @PostMapping("/login") + public AjaxResult login(@RequestBody @Validated ApiLoginBody loginBody) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return AjaxResult.error("网站维护中......"); + } + try { + if (StringUtils.isNotNull(getLoginUser())) { + String userName = getUsername(); + tokenService.delLoginUser(getToken()); + AsyncManager.me().execute(AsyncFactory.recordLogininfor("api_" + userName, Constants.LOGOUT, "退出成功")); + } + + } catch (Exception e) { + + } + + AjaxResult ajax = AjaxResult.success(); + Pair pair = loginService.login(loginBody.getUsername(), loginBody.getPassword()); + if (pair.getValue() != null) { + return pair.getValue(); + } + ajax.put(Constants.TOKEN, pair.getKey()); + return ajax; + } + + @ApiOperation("客户端用户验证码登录") + @Anonymous + @PostMapping("/verificationCodeLogin") + public AjaxResult verificationCodeLogin(@RequestBody @Validated ApiVerificationCodeLoginBody apiVerificationCodeLoginBody) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return AjaxResult.error("网站维护中......"); + } + return loginService.verificationCodeLogin(apiVerificationCodeLoginBody.getPhoneNumber(), apiVerificationCodeLoginBody.getCode()); + } + + @ApiOperation("客户端用户验证码登录或注册") + @Anonymous + @PostMapping("/verificationCodeLoginOrRegister") + public AjaxResult verificationCodeLoginOrRegister(@RequestBody @Validated ApiVerificationCodeLoginOrRegisterBody req) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return AjaxResult.error("网站维护中......"); + } + return loginService.verificationCodeLoginOrRegister(req.getPhoneNumber(), req.getCode(), req.getParentInvitationCode()); + } + + + @ApiOperation("获取用户信息") + @NewUserInfo + @GetMapping("/getInfo") + public R getInfo() { + UserData userData = getLoginUser().getUserData(); + TtUser ttUser = userService.getById(getUserId()); + apiUserService.authenticationOk(ttUser); + if (!"1".equals(userData.getIsRealCheck())) { + userData.setRealName(""); + } + userData.setAccountAmount(ttUser.getAccountAmount()); + userData.setAccountCredits(ttUser.getAccountCredits()); + userData.setDeliveryAddress(ttUser.getDeliveryAddress()); + // 获取下级总人数 + userData.setSubordinateNumber(apiUserService.getSubordinateNumber(getUserId())); + userData.setInvitationCode(ttUser.getInvitationCode()); + return R.ok(userData); + } + + @ApiOperation("获取用户余额信息") + @NewUserInfo + @GetMapping("/getAccountAmount") + public R getAccountAmount() { + UserData userData = getLoginUser().getUserData(); + TtUser ttUser = userService.getById(getUserId()); + userData.setAccountAmount(ttUser.getAccountAmount()); + userData.setAccountCredits(ttUser.getAccountCredits()); + userData.setDeliveryAddress(ttUser.getDeliveryAddress()); + userData.setInvitationCode(ttUser.getInvitationCode()); + userData.setVipLevel(ttUser.getVipLevel()); + userData.setPassword(""); + return R.ok(userData); + } + + @ApiOperation("获取用户信息") + @NewUserInfo + @GetMapping("/getParentInfo") + public R getParentInfo() { + TtUser ttUser = userService.getById(getUserId()); + if (ttUser.getParentId() == null) { + return R.ok(); + } + var parent = userService.getById(ttUser.getParentId()); + if (parent == null) { + return R.ok(); + } + TtUser r = new TtUser(); + r.setUserId(parent.getUserId()); + r.setAvatar(parent.getAvatar()); + r.setNickName(parent.getNickName()); + r.setInvitationCode(parent.getInvitationCode()); + return R.ok(r); + } + + @ApiOperation("客户端用户退出") + @PostMapping("/logout") + public R logout() { + if (StringUtils.isNotNull(getLoginUser())) { + String userName = getUsername(); + tokenService.delLoginUser(getToken()); + AsyncManager.me().execute(AsyncFactory.recordLogininfor("api_" + userName, Constants.LOGOUT, "退出成功")); + } + return R.ok("退出成功!"); + } + +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiMessageController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiMessageController.java new file mode 100644 index 0000000..f7f2110 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiMessageController.java @@ -0,0 +1,66 @@ +package com.ruoyi.user.controller; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.ruoyi.admin.service.TtMessageService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.other.TtMessage; +import com.ruoyi.user.service.ApiMessageService; +import com.ruoyi.domain.vo.ApiMessageDataVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@Api(tags = "消息") +@RestController +@RequestMapping("/api/message") +public class ApiMessageController extends BaseController { + + private final ApiMessageService apiMessageService; + + public ApiMessageController(ApiMessageService apiMessageService) { + this.apiMessageService = apiMessageService; + } + + @Autowired + private TtMessageService ttMessageService; + + @GetMapping("/list") + public PageDataInfo list() { + startPage(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.orderByDesc("id"); + List list = ttMessageService.list(queryWrapper); + return getPageData(list); + } + + @ApiOperation("获取消息列表") + @GetMapping("/getMessageList") + public PageDataInfo getMessageList(@RequestParam(required = false) Integer id){ + startPage(); + List list = apiMessageService.getMessageList(getUserId(), id); + return getPageData(list); + } + + @ApiOperation("已读") + @GetMapping("/view") + public R view(Integer id){ + ApiMessageDataVO data = apiMessageService.view(getUserId(), id); + return R.ok(data); + } + + @ApiOperation("分批操作") + @PostMapping("/batchOperation") + public R batchOperation(@RequestParam Integer[] ids, @RequestParam("status") String status){ + String msg = apiMessageService.batchOperation(getUserId(), Arrays.asList(ids), status); + return StringUtils.isEmpty(msg) ? R.ok(true, "操作成功!") : R.fail(false, msg); + } + +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiNoticeController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiNoticeController.java new file mode 100644 index 0000000..f68af33 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiNoticeController.java @@ -0,0 +1,71 @@ +package com.ruoyi.user.controller; + + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.domain.other.TtNotice; +import com.ruoyi.thirdparty.common.mapper.ApiNoticeMapper; +import com.ruoyi.user.model.vo.ApiNoticeVO; +import com.ruoyi.user.service.ApiNoticeService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Objects; + +@Api(tags = "通知") +@RestController +@RequestMapping("/api/notice") +public class ApiNoticeController extends BaseController { + + @Autowired + private ApiNoticeService apiNoticeService; + + @Autowired + private ApiNoticeMapper apiNoticeMapper; + + @ApiOperation("获取通知列表") + @ApiResponse(code = 200, message = "read【阅读状态(0未读 1已读)】") + @GetMapping("/list") + public TableDataInfo list() { + startPage(); + Long userId = SecurityUtils.getUserId(); + List apiNoticeVOList = apiNoticeService.getNoticeList(userId); + return getDataTable(apiNoticeVOList); + } + + @ApiOperation("获取通知详情") + @GetMapping("/{noticeId}") + public AjaxResult getNoticeByNoticeId(@PathVariable Integer noticeId) { + Long userId = SecurityUtils.getUserId(); + ApiNoticeVO apiNoticeVO = apiNoticeService.getNoticeByNoticeId(userId, noticeId); + if (Objects.isNull(apiNoticeVO)) { + return AjaxResult.error("通知不存在"); + } + return AjaxResult.success(apiNoticeVO); + } + + @ApiOperation("获取未读通知数量") + @GetMapping("/countUnreadNotice") + public AjaxResult countUnreadNotice() { + Long userId = SecurityUtils.getUserId(); + return AjaxResult.success(apiNoticeService.countUnreadNotice(userId)); + } + + // 测试添加通知 + /*@ApiOperation("获取通知列表") + @GetMapping("/add") + public void addNotice() { + TtNotice ttNotice = new TtNotice(); + ttNotice.setUserId(1L); + ttNotice.setTitle("测试标题"); + ttNotice.setContent("测试内容"); + apiNoticeMapper.addNotice(ttNotice); + }*/ +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiOrderController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiOrderController.java new file mode 100644 index 0000000..b781c1a --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiOrderController.java @@ -0,0 +1,31 @@ +package com.ruoyi.user.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.ruoyi.admin.service.TtOrderService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@Api(tags = "订单信息") +@RestController +@RequestMapping("/api/order") +public class ApiOrderController extends BaseController { + + @Autowired + private TtOrderService ttOrderService; + + @ApiOperation("充值明细") + @GetMapping("/list") + public R list(Integer page, Integer size) { + Long userId = getUserId(); + if (ObjectUtil.isNull(userId)) return R.fail("登录过期。"); + return ttOrderService.clientList(page,size,userId.intValue()); + } + +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiPromotionRecordController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiPromotionRecordController.java new file mode 100644 index 0000000..ef0222f --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiPromotionRecordController.java @@ -0,0 +1,29 @@ +package com.ruoyi.user.controller; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.domain.vo.promotion.TtPromotionRecordVo; +import com.ruoyi.user.service.ApiPromotionRecordService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/api/promotionRecord") +public class ApiPromotionRecordController extends BaseController { + + @Autowired + private ApiPromotionRecordService apiPromotionRecordService; + + @GetMapping("/getPromotionRecord") + public PageDataInfo getPromotionRecord(){ + startPage(); + Long userId = getUserId(); + List list = apiPromotionRecordService.getPromotionRecord(userId.intValue()); + return getPageData(list); + } + +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiRegisterController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiRegisterController.java new file mode 100644 index 0000000..f24e9a1 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiRegisterController.java @@ -0,0 +1,43 @@ +package com.ruoyi.user.controller; + +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.user.service.ApiRegisterService; +import com.ruoyi.domain.other.ApiRegisterBody; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "用户注册") +@RestController +@RequestMapping("/api/register") +public class ApiRegisterController extends BaseController { + + private final ISysConfigService sysConfigService; + private final ApiRegisterService apiRegisterService; + + public ApiRegisterController(ISysConfigService sysConfigService, + ApiRegisterService apiRegisterService) { + this.sysConfigService = sysConfigService; + this.apiRegisterService = apiRegisterService; + } + + @ApiOperation("用户注册") + @PostMapping + @Anonymous + public R register(@RequestBody @Validated ApiRegisterBody registerBody) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + String msg = apiRegisterService.register(registerBody); + return StringUtils.isEmpty(msg) ? R.ok("注册成功!") : R.fail(msg); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiShoppingController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiShoppingController.java new file mode 100644 index 0000000..4e3bdf7 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiShoppingController.java @@ -0,0 +1,98 @@ +package com.ruoyi.user.controller; + +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.user.service.ApiShoppingService; +import com.ruoyi.domain.other.ApiShoppingBody; +import com.ruoyi.domain.vo.ApiShoppingDataVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Api(tags = "商城") +@RestController +@RequestMapping("/api/shopping") +public class ApiShoppingController extends BaseController { + + private final ISysConfigService sysConfigService; + private final ApiShoppingService apiShoppingService; + private final TtUserService userService; + + public ApiShoppingController(ISysConfigService sysConfigService, + ApiShoppingService apiShoppingService, + TtUserService userService) { + this.sysConfigService = sysConfigService; + this.apiShoppingService = apiShoppingService; + this.userService = userService; + } + + @ApiOperation("获取不同类型的商品") + @Anonymous + @GetMapping("/getShoppingQuery") + public R>> getShoppingQuery(@RequestParam("value") String value){ + List dictCache = null; + if ("0".equals(value)) dictCache = DictUtils.getDictCache("ornaments_type_name"); + if ("1".equals(value)) dictCache = DictUtils.getDictCache("ornaments_rarity_name"); + if ("2".equals(value)) dictCache = DictUtils.getDictCache("ornaments_exterior_name"); + if ("3".equals(value)) dictCache = DictUtils.getDictCache("ornaments_quality_name"); + if (StringUtils.isNull(dictCache)) return R.fail("数据异常!!"); + List> list = dictCache.stream().map(sysDictData -> { + Map map = new HashMap<>(); + map.put(sysDictData.getDictLabel(), sysDictData.getDictValue()); + return map; + }).collect(Collectors.toList()); + return R.ok(list); + } + + @ApiOperation("商品列表") + @GetMapping("/list") + @Anonymous + public PageDataInfo list(@Validated ApiShoppingBody shoppingBody) { + startPage(); + List list = apiShoppingService.list(shoppingBody); + return getPageData(list); + } + + @ApiOperation("商品兑换") + @PostMapping("/exchange") + @UpdateUserCache + public R exchange(Long ornamentsId) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + String shoppingMaintenance = sysConfigService.selectConfigByKey("shoppingMaintenance"); + if ("1".equals(shoppingMaintenance)) { + return R.fail("商城功能正在维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + return apiShoppingService.exchange(ttUser, ornamentsId); + } + + @ApiOperation("积分转换") + @UpdateUserCache + @PostMapping("/integratingConversion") + public R integratingConversion(BigDecimal credits) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) return R.fail("网站维护中......"); + TtUser ttUser = userService.getById(getUserId()); + String msg = apiShoppingService.integratingConversion(ttUser, credits); + return StringUtils.isEmpty(msg) ? R.ok(true, "积分转换成功!") : R.fail(msg); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiTaskCenterController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiTaskCenterController.java new file mode 100644 index 0000000..8a9c041 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiTaskCenterController.java @@ -0,0 +1,57 @@ +package com.ruoyi.user.controller; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.user.model.TtTaskCenterUser; +import com.ruoyi.user.model.vo.ApiTaskCenterVO; +import com.ruoyi.user.service.ApiTaskCenterService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Objects; + +@Api(tags = "任务中心") +@RestController +@RequestMapping("/api/taskCenter") +public class ApiTaskCenterController extends BaseController { + + @Autowired + private ApiTaskCenterService apiTaskCenterService; + + @ApiOperation("查询任务列表") + @ApiResponse(code = 200, message = "status【领取条件(0不满足 1满足)】" + + "
claimed【是否已领取(1已领取)】") + @PreAuthorize("isAuthenticated()") + @GetMapping("/list") + public TableDataInfo list() { + startPage(); + Long userId = getUserId(); + List list = apiTaskCenterService.selectApiTaskCenterVOList(userId.intValue()); + return getDataTable(list); + } + + @ApiOperation("领取任务奖励") + @PreAuthorize("isAuthenticated()") + @GetMapping("/getReward/{taskId}") + public AjaxResult getReward(@PathVariable("taskId") Integer taskId) { + Long userId = getUserId(); + // 判断是否具备领取条件 + TtTaskCenterUser ttTaskCenterUser = apiTaskCenterService.selectTtTaskCenterUserByTaskIdAndUserId(taskId, userId.intValue()); + if (Objects.isNull(ttTaskCenterUser)) { + return AjaxResult.error("不具备领取条件"); + } + if ("1".equals(ttTaskCenterUser.getClaimed())) { + return AjaxResult.error("已领取过"); + } + return apiTaskCenterService.getReward(taskId, userId.intValue()); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiUserAmountRecordsController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiUserAmountRecordsController.java new file mode 100644 index 0000000..ef7b8d8 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiUserAmountRecordsController.java @@ -0,0 +1,561 @@ +package com.ruoyi.user.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.admin.mapper.TtPromotionUpdateMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.TtDeliveryRecordService; +import com.ruoyi.admin.service.TtOrderService; +import com.ruoyi.admin.service.TtRechargeRankingRewardService; +import com.ruoyi.admin.service.TtUserAmountRecordsService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.MoneyUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.dto.sys.TeamUsersParam; +import com.ruoyi.domain.dto.userRecord.AmountRecordsDetailCondition; +import com.ruoyi.domain.dto.userRecord.DeliveryRecordsConfition; +import com.ruoyi.domain.dto.userRecord.OrderCondition; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.other.TtRechargeRankingReward; +import com.ruoyi.domain.vo.TeamDetailVO; +import com.ruoyi.domain.vo.TtUserAccountRecordsRankVO; +import com.ruoyi.domain.vo.TtUserAmountRecords.UserAmountDetailVO; +import com.ruoyi.domain.vo.UserBERankVO; +import com.ruoyi.domain.vo.delivery.DeliveryRecordVO; +import com.ruoyi.domain.vo.order.TtOrderVO; +import com.ruoyi.domain.vo.sys.SimpleTtUserVO; +import com.ruoyi.user.contract.PersonalRecordRequest; +import com.ruoyi.user.contract.PersonalRecordResponse; +import com.ruoyi.user.mapper.ApiUserBlendErcashMapper; +import com.ruoyi.user.model.vo.ApiRechargeRankingVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.TtAccountRecordSource.RECEIVE_RED_PACKET; +import static com.ruoyi.domain.common.constant.TtAccountRecordSource.RECHARGE; + +@Api(tags = "用户充值记录") +@RestController +@RequestMapping("/api/userAmountRecords") +@Slf4j +public class ApiUserAmountRecordsController extends BaseController { + + @Autowired + private TtUserAmountRecordsService ttUserAmountRecordsService; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + + @Autowired + private TtUserAmountRecordsService userAmountRecordsService; + + @Autowired + private TtUserService userService; + + @Autowired + private TtUserMapper userMapper; + + @Autowired + private TtOrderService orderService; + + @Autowired + private TtDeliveryRecordService ttDeliveryRecordService; + + @Autowired + private TtOrderService ttOrderService; + + @Autowired + private TtPromotionUpdateMapper ttPromotionUpdateMapper; + + @Autowired + private ApiUserBlendErcashMapper apiUserBlendErcashMapper; + + @Autowired + private TtRechargeRankingRewardService ttRechargeRankingRewardService; + + /** + * 充值网流水排行榜 + * + * @param type 1今天 2昨天 3近一周 + */ + @ApiOperation("充值网流水排行榜") + @Anonymous + @GetMapping("/amountRank/{type}/{page}/{size}") + public Page amountRank(@PathVariable("type") Integer type, + @PathVariable("page") Integer page, + @PathVariable("size") Integer size) { + + Calendar c = Calendar.getInstance(); + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + Timestamp end = null; + Timestamp begin = null; + + if (type.equals(1)) { + begin = new Timestamp(c.getTimeInMillis()); + end = new Timestamp(System.currentTimeMillis()); + } else if (type.equals(2)) { + end = new Timestamp(c.getTimeInMillis()); + c.add(Calendar.DAY_OF_MONTH, -1); + begin = new Timestamp(c.getTimeInMillis()); + } else if (type.equals(3)) { + end = new Timestamp(c.getTimeInMillis()); + c.add(Calendar.WEEK_OF_MONTH, -1); + begin = new Timestamp(c.getTimeInMillis()); + } + + return ttUserAmountRecordsService.rank(begin, end, page, size); + } + + /** + * 充值网推广奖励明细 + */ + @ApiOperation("充值网推广奖励明细") + @GetMapping("/pWelfareRecords/{page}/{size}") + public R pWelfareRecords(@PathVariable("page") Integer page, + @PathVariable("size") Integer size) { + if (page < 1) { + return R.fail("page > 0"); + } + if (size > 21) { + return R.fail("size <= 20 !!!"); + } + Long userId = getUserId(); + return ttUserAmountRecordsService.pWelfareRecords(userId.intValue(), page, size); + } + + /** + * 综合排行榜 + */ + @ApiOperation("流水排行榜") + @PostMapping("/blendErcashRank") + public R> blendErcashRank(@RequestBody RankParam param) { + param.setPage(1); + param.setSize(10); + + Calendar c = Calendar.getInstance(); + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + Timestamp end = null; + Timestamp begin = null; + + if (param.getType().equals(1)) { + begin = new Timestamp(c.getTimeInMillis()); + end = new Timestamp(System.currentTimeMillis()); + } else if (param.getType().equals(2)) { + end = new Timestamp(c.getTimeInMillis()); + c.add(Calendar.DAY_OF_MONTH, -1); + begin = new Timestamp(c.getTimeInMillis()); + } else if (param.getType().equals(3)) { + end = new Timestamp(c.getTimeInMillis()); + c.add(Calendar.WEEK_OF_MONTH, -1); + begin = new Timestamp(c.getTimeInMillis()); + } + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String beginT = dateFormat.format(begin); + String endT = dateFormat.format(end); + if (StringUtils.isBlank(beginT) || StringUtils.isBlank(beginT)) { + beginT = null; + endT = null; + } + + List source = new ArrayList<>(TtAccountRecordSource.getGameConsumeCodes()); + Integer limit = (param.getPage() - 1) * param.getSize(); + List rank = ttUserBlendErcashMapper.rank( + source, + beginT, + endT, + limit, + param.getSize()); + for (int i = 1; i <= rank.size(); i++) { + var item = rank.get(i - 1); + item.setBeRank(i); + item.setValue(MoneyUtil.toStr(item.getAmount().abs())); + } + return R.ok(rank); + } + + @ApiOperation("获取个人综合收支明细") + @PostMapping("/userAccountDetail") + public R> userAccountDetail(@RequestBody @Validated AmountRecordsDetailCondition param) { + param.setUserId(null); + + param.setUserId(getUserId().intValue()); + List list = userAmountRecordsService.userAccountDetail(param); + return R.ok(list); + } + + @ApiOperation("获取个人记录") + @PostMapping("/personalRecord") + public R queryPersonalRecord(@RequestBody @Validated PersonalRecordRequest request) { + final int TYPE_TODAY = 1; + final int TYPE_YESTERDAY = 2; + final int TYPE_LAST_7_DAYS = 3; + final int SOURCE_RECHARGE = 1; + final int SOURCE_CONSUME = 2; + final int SOURCE_RED_PACKET = 3; + final int Filtering_Options_OutPut = 0; + final int Filtering_Options_Input = 1; + + if (request.getType() != TYPE_TODAY && request.getType() != TYPE_YESTERDAY && request.getType() != TYPE_LAST_7_DAYS) { + return R.fail("参数错误: type 无效"); + } + if (request.getSource() != SOURCE_RECHARGE && request.getSource() != SOURCE_CONSUME && request.getSource() != SOURCE_RED_PACKET) { + return R.fail("参数错误: source 无效"); + } + Integer uid = getUserId().intValue(); + + + Page page = new Page<>(request.getPage(), request.getPageSize()); +// page.setOptimizeCountSql(false); + + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(TtUserBlendErcash::getUserId, uid); + + // 1:今日 2:昨日 3:最近7天 + LocalDate today = LocalDate.now(); + switch (request.getType()) { + case TYPE_TODAY: + LocalDateTime startOfToday = today.atStartOfDay(); + LocalDateTime endOfToday = today.plusDays(1).atStartOfDay().minusNanos(1); + wrapper.between(TtUserBlendErcash::getCreateTime, startOfToday, endOfToday); + break; + case TYPE_YESTERDAY: + LocalDateTime startOfYesterday = today.minusDays(1).atStartOfDay(); + LocalDateTime endOfYesterday = today.atStartOfDay().minusNanos(1); + wrapper.between(TtUserBlendErcash::getCreateTime, startOfYesterday, endOfYesterday); + break; + case TYPE_LAST_7_DAYS: + LocalDateTime startOf7Days = today.minusDays(6).atStartOfDay(); + wrapper.ge(TtUserBlendErcash::getCreateTime, startOf7Days); + break; + default: + // 理论上不会进入,因为上面已经校验过 + break; + } + + + // 查询的数据源 1:充值 2:全部/支出/收入 3:红包 + switch (request.getSource()) { + case SOURCE_RECHARGE: + wrapper.eq(TtUserBlendErcash::getSource, RECHARGE.getCode()); + break; + case SOURCE_CONSUME: + //wrapper.eq(TtUserBlendErcash::getType, TtAccountRecordType.OUTPUT); + if (request.getFilteringOptions() != null) { + if (request.getFilteringOptions() == Filtering_Options_OutPut) { + wrapper.eq(TtUserBlendErcash::getType, TtAccountRecordType.OUTPUT.getCode()); + } else if (request.getFilteringOptions() == Filtering_Options_Input) { + wrapper.eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()); + } + } + break; + case SOURCE_RED_PACKET: + wrapper.eq(TtUserBlendErcash::getSource, RECEIVE_RED_PACKET.getCode()); + break; + default: + break; + } + + wrapper.orderByDesc(TtUserBlendErcash::getCreateTime); + IPage listPage = ttUserBlendErcashMapper.selectPage(page, wrapper); + + List userList = listPage.getRecords(); + + var formatter = DateTimeFormatter.ofPattern("MM-dd HH:mm:ss"); + var r = userList.stream().map(v -> { + PersonalRecordResponse.RecordVO item = new PersonalRecordResponse.RecordVO(); + item.setAmount(MoneyUtil.toStr(v.getAmount())); + item.setFinalAmount(MoneyUtil.toStr(v.getFinalAmount())); + item.setRemark(v.getRemark()); + item.setDatetime(v.getCreateTime().toLocalDateTime().format(formatter)); + item.setSource(v.getSource()); + return item; + }).collect(Collectors.toList()); + PersonalRecordResponse response = new PersonalRecordResponse(); + response.setRows(r); + response.setTotal(listPage.getTotal()); + + return R.ok(response); + } + + @ApiOperation("获取个人提货记录") + @PostMapping("/deliveryRecords") + public R deliveryRecords(@RequestBody @Validated DeliveryRecordsConfition param) { + param.setUIdList(null); + int userId = getUserId().intValue(); + if (ObjectUtil.isNull(userId)) return R.fail(401, "登录过期请重新登录。"); + param.setUIdList(Arrays.asList(userId)); + + List list = ttDeliveryRecordService.byCondition(param); + + return R.ok(list); + } + + @ApiOperation("获取团队用户列表") + // @Anonymous + @PostMapping("/teamUsers") + public R> teamUsers(@RequestBody @Validated TeamUsersParam param) { + param.setBossIds(null); + param.setEmployeeIds(null); + param.setBossIds(Arrays.asList(getUserId().intValue())); + if (ObjectUtil.isNull(param.getOrderByFie())) param.setOrderByFie(1); + + // 查询所有下级id + List allEmployeesId = userMapper.allEmployeesByParents(param.getBossIds()); + if (allEmployeesId.isEmpty()) { + return R.ok(new ArrayList<>()); + } + + // 解析时间 + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date beginTime = null; + try { + beginTime = dateFormat.parse(param.getBeginTime()); + } catch (ParseException e) { + return R.fail("日期解析异常,检查日期格式是否正确。"); + } + + // main + if (param.getOrderByFie().equals(1)) { + + // 查询下级的最近一个绑定时间 + List mit = ttPromotionUpdateMapper.latelyUpdate(allEmployeesId); + if (mit.size() < allEmployeesId.size()) log.warn("下级已绑定上级,但未写入更新日志,请及时检查!!!"); + // 如果最近绑定时间大于本次查询的起始时间,以最近绑定时间为准 + List empIds1 = new ArrayList<>(); + List empIds2 = new ArrayList<>(); + for (TeamDetailVO item : mit) { + Timestamp latelyTime = item.getBeginTime(); + if (latelyTime.compareTo(new Timestamp(beginTime.getTime())) > 0) { + empIds2.add(item); + } else { + empIds1.add(item.getEmployeeId()); + } + } + + List batchRechargeTotal1 = new ArrayList<>(); + if (!empIds1.isEmpty()) { + // 主表充值统计 + batchRechargeTotal1 = orderService.batchRechargeTotal( + empIds1, + param.getBeginTime(), + param.getEndTime(), + param.getOrderType(), + param.getPage(), + param.getSize()); + System.err.println(batchRechargeTotal1); + } + + List batchRechargeTotal2 = new ArrayList<>(); + if (!empIds2.isEmpty()) { + + for (TeamDetailVO vo : empIds2) { + + String beginT = dateFormat.format(vo.getBeginTime()); + + List data = orderService.batchRechargeTotal( + Arrays.asList(vo.getEmployeeId()), + beginT, + param.getEndTime(), + param.getOrderType(), + param.getPage(), + param.getSize()); + + batchRechargeTotal2.addAll(data); + } + } + + batchRechargeTotal1.addAll(batchRechargeTotal2); + return R.ok(batchRechargeTotal1); + + } else if (param.getOrderByFie().equals(2) || param.getOrderByFie().equals(3) || param.getOrderByFie().equals(4)) { + if (StringUtils.isBlank(param.getBeginTime())) { + param.setBeginTime(null); + } + if (StringUtils.isBlank(param.getEndTime())) { + param.setEndTime(null); + } + if (ObjectUtil.isNull(param.getOrderType())) { + param.setOrderType(1); + } + + // 查询下级的最近一个绑定时间 + List mit = ttPromotionUpdateMapper.latelyUpdate(allEmployeesId); + if (mit.size() < allEmployeesId.size()) { + log.warn("下级已绑定上级,但未写入更新日志,请及时检查!!!"); + } + // 如果最近绑定时间大于本次查询的起始时间,以最近绑定时间为准 + List empIds1 = new ArrayList<>(); + List empIds2 = new ArrayList<>(); + for (TeamDetailVO item : mit) { + Timestamp latelyTime = item.getBeginTime(); + if (latelyTime.compareTo(new Timestamp(beginTime.getTime())) > 0) { + empIds2.add(item); + } else { + empIds1.add(item.getEmployeeId()); + } + } + + List batchConsumeTotal1 = new ArrayList<>(); + if (!empIds1.isEmpty()) { + // 主表消费统计 + batchConsumeTotal1 = ttUserBlendErcashMapper.batchConsumeTotal( + empIds1, + param.getBeginTime(), + param.getEndTime(), + param.getOrderByFie(), + param.getOrderType(), + (param.getPage() - 1) * param.getSize(), + param.getSize()); + } + + List batchConsumeTotal2 = new ArrayList<>(); + if (!empIds2.isEmpty()) { + for (TeamDetailVO vo : empIds2) { + + String beginT = dateFormat.format(vo.getBeginTime()); + + List data = ttUserBlendErcashMapper.batchConsumeTotal( + Arrays.asList(vo.getEmployeeId()), + beginT, + param.getEndTime(), + param.getOrderByFie(), + param.getOrderType(), + (param.getPage() - 1) * param.getSize(), + param.getSize() + ); + + batchConsumeTotal2.addAll(data); + } + } + + batchConsumeTotal1.addAll(batchConsumeTotal2); + + return R.ok(batchConsumeTotal1); + + } else { + return R.fail("非法的排序字段"); + } + + // return userService.teamUsers(param); + } + + @ApiOperation("获取个人充值明细") + @PostMapping("/rechargeRecords") + public R rechargeRecords(@RequestBody @Validated OrderCondition param) { + Long userId = getUserId(); + if (ObjectUtil.isNull(userId)) return R.fail(401, "登录过期,请重新登录。"); + + param.setUserIdList(Arrays.asList(userId.intValue())); + + List list = ttOrderService.byCondition(param); + return R.ok(list); + } + + @Deprecated + @ApiOperation("获取消费排行榜") + @Anonymous + @GetMapping("/rechargeRanking") + public AjaxResult getRechargeRanking() { + // 创建日期格式化对象,指定日期格式 + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + + // 获取今天日期 + Date today = new Date(); + String formattedTodayDate = formatter.format(today); + List todayFightRanking = apiUserBlendErcashMapper.getRechargeRankingByDate(formattedTodayDate); + + // 获取昨天日期 + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DATE, -1); + Date yesterday = cal.getTime(); + String formattedYesterdayDate = formatter.format(yesterday); + List yesterdayFightRanking = apiUserBlendErcashMapper.getRechargeRankingByDate(formattedYesterdayDate); + + Map> map = new HashMap<>(); + map.put("todayFightRanking", todayFightRanking); + map.put("yesterdayFightRanking", yesterdayFightRanking); + + return AjaxResult.success(map); + } + + @ApiOperation("消费排行榜奖励说明") + @GetMapping("/rechargeRankingRewardsIntroduction") + public AjaxResult rechargeRankingRewardsIntroduction() { + List ttRechargeRankingRewards = ttRechargeRankingRewardService.selectTtRechargeRankingRewardList(null); + ttRechargeRankingRewards.forEach(r -> r.setRewardStr(MoneyUtil.toStr(r.getReward()))); + return success(ttRechargeRankingRewards); + } + + public R checkLogin() { + Long userId; + try { + userId = getUserId(); + if (ObjectUtil.isEmpty(userId)) AjaxResult.error(401, "登录过期,请重新登录。"); + return R.ok(userId); + } catch (Exception e) { + return R.fail(401, "登录过期,请重新登录。"); + } + } + + @NoArgsConstructor + @AllArgsConstructor + @Data + public static class RankParam { + + @ApiModelProperty("类型(1今天 2昨天 3近七天)") + @NotEmpty(message = "时间区间不能为空") + private Integer type; + @Min(value = 1, message = "最小1") + private Integer page; + @Min(value = 1, message = "最小1") + private Integer size; + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiUserController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiUserController.java new file mode 100644 index 0000000..b2f0161 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiUserController.java @@ -0,0 +1,397 @@ +package com.ruoyi.user.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.ruoyi.admin.mapper.TtOrderMapper; +import com.ruoyi.admin.service.TtDeliveryRecordService; +import com.ruoyi.admin.service.TtUserAmountRecordsService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.service.TtVipLevelService; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.entity.TtOrder; +import com.ruoyi.domain.entity.delivery.TtDeliveryRecord; +import com.ruoyi.domain.entity.recorde.TtUserAmountRecords; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.ApiForgetPasswordBody; +import com.ruoyi.domain.other.ApiUpdateUserDetailsBody; +import com.ruoyi.domain.other.RealNameAuthenticationBody; +import com.ruoyi.domain.other.TtDeliveryRecordBody; +import com.ruoyi.domain.other.TtUserAmountRecordsBody; +import com.ruoyi.domain.other.TtVipLevel; +import com.ruoyi.domain.vo.TtDeliveryRecordDataVO; +import com.ruoyi.domain.vo.TtUserPackSackDataVO; +import com.ruoyi.domain.vo.TtUserVipLevelVo; +import com.ruoyi.domain.vo.user.TtUseChildInfoVo; +import com.ruoyi.domain.vo.user.TtUserInfoVo; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.baidu.service.BaiduService; +import com.ruoyi.user.service.ApiUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.stream.Collectors; + +@Api(tags = "用户管理") +@RestController +@RequestMapping("/api/user") +@Slf4j +public class ApiUserController extends BaseController { + + private final ISysConfigService sysConfigService; + private final TtUserService userService; + private final ApiUserService apiUserService; + private final TtOrderMapper ttOrderMapper; + private final TtDeliveryRecordService ttDeliveryRecordService; + private final TtUserAmountRecordsService userAmountRecordsService; + private final TtDeliveryRecordService deliveryRecordService; + + public ApiUserController(ISysConfigService sysConfigService, + TtUserService userService, + TtDeliveryRecordService ttDeliveryRecordService, + ApiUserService apiUserService, TtOrderMapper ttOrderMapper, + TtUserAmountRecordsService userAmountRecordsService, + TtDeliveryRecordService deliveryRecordService) { + this.sysConfigService = sysConfigService; + this.userService = userService; + this.ttDeliveryRecordService = ttDeliveryRecordService; + this.apiUserService = apiUserService; + this.ttOrderMapper = ttOrderMapper; + this.userAmountRecordsService = userAmountRecordsService; + this.deliveryRecordService = deliveryRecordService; + } + + @Autowired + private TtVipLevelService vipLevelService; + + @Autowired + private BaiduService baiduService; + + @ApiOperation("头像上传") + @UpdateUserCache + @PostMapping(value = "/profilePictureUpload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R profilePictureUpload(@RequestPart("file") MultipartFile file) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + if (StringUtils.isNull(file)) { + return R.fail("未选择头像文件!"); + } + TtUser ttUser = userService.getById(getUserId()); + String msg = apiUserService.profilePictureUpload(ttUser, file); + return StringUtils.isEmpty(msg) ? R.ok("头像上传成功!") : R.fail(msg); + } + + @ApiOperation("更新个人信息") + @UpdateUserCache + @PostMapping("/updateUserDetails") + public R updateUserDetails(@RequestBody ApiUpdateUserDetailsBody updateUserDetailsBody) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + + String msg = apiUserService.updateUserDetails(ttUser, updateUserDetailsBody); + + return StringUtils.isEmpty(msg) ? R.ok("个人信息更新成功!") : R.fail(msg); + } + + @ApiOperation("更新收货地址") + @UpdateUserCache + @PostMapping("/updateDeliveryAddress") + public R updateDeliveryAddress(@RequestBody ApiUpdateUserDetailsBody updateUserDetailsBody) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + + String msg = apiUserService.updateUserDetails(ttUser, updateUserDetailsBody); + + return StringUtils.isEmpty(msg) ? R.ok("收货地址更新成功!") : R.fail(msg); + } + + @ApiOperation("绑定推广人") + @UpdateUserCache + @PostMapping("/bindBoss") + public R bindBoss(@RequestBody ApiUpdateUserDetailsBody updateUserDetailsBody) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + + return apiUserService.bindBoss(ttUser, updateUserDetailsBody); + } + + @ApiOperation("修改密码") + // @UpdateUserCache + @PostMapping("/changePW") + public R changePW(@RequestBody @Validated ApiUpdateUserDetailsBody updateUserDetailsBody, HttpServletRequest request) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + + String token = CacheConstants.LOGIN_TOKEN_KEY + getToken(); + + return apiUserService.changePW(ttUser, updateUserDetailsBody, token); + } + + @ApiOperation("忘记密码") + @Anonymous + @PostMapping("/forgetPassword") + public R forgetPassword(@RequestBody ApiForgetPasswordBody apiForgetPasswordBody) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + String msg = apiUserService.forgetPassword(apiForgetPasswordBody); + return StringUtils.isEmpty(msg) ? R.ok(true, "密码修改成功成功!") : R.fail(msg); + } + + @ApiOperation("实名认证") + @RepeatSubmit(interval = 60000, message = "操作过于频繁,请60秒后重试!") + @UpdateUserCache + @PostMapping("/realNameAuthentication") + public R realNameAuthentication(@RequestBody RealNameAuthenticationBody realNameAuthenticationBody) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + TtUser ttUser = userService.getById(getUserId()); + String msg = apiUserService.realNameAuthentication2(ttUser, realNameAuthenticationBody.getRealName(), + realNameAuthenticationBody.getIdNum(), ttUser.getPhoneNumber()); + return msg.startsWith("alipays") ? R.ok(msg) : R.fail(msg); + } + + @ApiOperation("认证成功") + @UpdateUserCache + @GetMapping("/authenticationOk") + public R authenticationOk() { + TtUser ttUser = userService.getById(getUserId()); + String msg = apiUserService.authenticationOk(ttUser); + return StringUtils.isEmpty(msg) ? R.ok(true, "认证成功!") : R.fail(false, msg); + } + + @ApiOperation("获取下级人数") + // @UpdateUserCache + @GetMapping("/rechargeCount") + public R rechargeCount() { + TtUser ttUser = userService.getById(getUserId()); + return R.ok((int) userService.count(new QueryWrapper().eq("parent_id", ttUser.getUserId()))); + } + + @ApiOperation("获取下级提取数") + @UpdateUserCache + @GetMapping("/getExtracts") + public R getExtracts() { + TtUser ttUser = userService.getById(getUserId()); + List ttUsers = userService.list(new QueryWrapper().eq("parent_id", ttUser.getUserId())); + // 判断是否有下级用户 + if (ttUsers == null || ttUsers.size() == 0) { + return R.ok(new BigDecimal(0)); + } + List collect = ttUsers.stream().map(i -> i.getUserId()).collect(Collectors.toList()); + List ttDeliveryRecords = ttDeliveryRecordService.list(new QueryWrapper().in("user_id", collect).eq("status", "10")); + BigDecimal bigDecimal = new BigDecimal(0); + ttDeliveryRecords.forEach(o -> { + bigDecimal.add(o.getOrnamentsPrice()); + }); + + return R.ok(bigDecimal); + } + + @ApiOperation("获取下级充值总金额") + // @UpdateUserCache + @GetMapping("/getOrdersAmounts") + public R getOrdersAmounts() { + TtUser ttUser = userService.getById(getUserId()); + List ttUsers = userService.list(new QueryWrapper().eq("parent_id", ttUser.getUserId())); + // 判断是否有下级用户 + if (ttUsers == null || ttUsers.size() == 0) { + return R.ok(new BigDecimal(0)); + } + List collect = ttUsers.stream().map(i -> i.getUserId()).collect(Collectors.toList()); + List ttOrders = ttOrderMapper.selectList(new QueryWrapper().in("user_id", collect).and(k -> k.eq("status", "3").or().eq("status", "4"))); + + BigDecimal bigDecimal = new BigDecimal(0); + ttOrders.forEach(o -> { + bigDecimal.add(o.getGoodsPrice()); + }); + + return R.ok(bigDecimal); + } + + @ApiOperation("获取下级流水详情") + @GetMapping("/getLsjlList") + public PageDataInfo getLsjlList(TtUserAmountRecordsBody ttUserAmountRecordsBody) { + startPage(); + TtUser ttUser = userService.getById(getUserId()); + List ttUsers = userService.list(new QueryWrapper().eq("parent_id", ttUser.getUserId())); + // 判断是否有下级用户 + if (ttUsers == null || ttUsers.size() == 0) { + return getPageData(new ArrayList()); + } + List userIds = ttUsers.stream().map(i -> i.getUserId()).collect(Collectors.toList()); + QueryWrapper ttUserAmountRecordsQueryWrapper = new QueryWrapper<>(); + // 时间段筛选 + if (ttUserAmountRecordsBody.getStartTime() != null) { + ttUserAmountRecordsQueryWrapper.ge("create_time", ttUserAmountRecordsBody.getStartTime()); + } + if (ttUserAmountRecordsBody.getEndTime() != null) { + ttUserAmountRecordsQueryWrapper.lt("create_time", ttUserAmountRecordsBody.getEndTime()); + } + if (ttUserAmountRecordsBody.getUserId() != null) { + ttUserAmountRecordsQueryWrapper.eq("user_id", ttUserAmountRecordsBody.getUserId()); + } + + List list = userAmountRecordsService.list(ttUserAmountRecordsQueryWrapper.in("user_id", userIds).orderByDesc("create_time")); + + return getPageData(list); + } + + @ApiOperation("获取出货记录") + @GetMapping("/getDeliveryRecordList") + public PageDataInfo getDeliveryRecordList(TtDeliveryRecordBody deliveryRecordBody) { + startPage(); + TtUser ttUser = userService.getById(getUserId()); + deliveryRecordBody.setUserId(ttUser.getUserId()); + List list = deliveryRecordService.getDeliveryRecordByUserList(deliveryRecordBody); + return getPageData(list); + } + + /** + * 每日出货排行榜 + * + * @param type 1今天 2昨天 3近一周 + */ + @ApiOperation("每日出货排行榜") + @GetMapping("/propRankOfDay/{type}/{number}") + public R propRankOfDay(@PathVariable("type") Integer type, + @PathVariable(value = "number", required = false) Integer number) { + + Calendar c = Calendar.getInstance(); + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + + Timestamp end = null; + Timestamp begin = null; + if (type.equals(1)) { + begin = new Timestamp(c.getTimeInMillis()); + end = new Timestamp(System.currentTimeMillis()); + } else if (type.equals(2)) { + end = new Timestamp(c.getTimeInMillis()); + c.add(Calendar.DAY_OF_MONTH, -1); + begin = new Timestamp(c.getTimeInMillis()); + } else if (type.equals(3)) { + end = new Timestamp(c.getTimeInMillis()); + c.add(Calendar.WEEK_OF_MONTH, -1); + begin = new Timestamp(c.getTimeInMillis()); + } + + number = 10; + List list = userService.propRankOfDay(begin, end, number); + // return getPageData(list); + return R.ok(list); + } + + @ApiOperation("VIP等级说明") + @GetMapping("/vipLevelIntroduction") + public AjaxResult vipLevelIntroduction() { + List list = vipLevelService.list(); + return success(list); + } + + @ApiOperation("VIP等级进度") + @GetMapping("/userVipLeverRate") + public AjaxResult userVipLeverRate() { + TtUserVipLevelVo vipLevel = userService.userVipLeverRate(getUserId().intValue()); + return success(vipLevel); + } + + @ApiOperation("用户信息包含下级信息") + @GetMapping("/userInfo") + public AjaxResult userInfo() { + TtUserInfoVo userInfo = userService.userInfo(getUserId().intValue()); + return success(userInfo); + } + + @ApiOperation("用户信息包含下级用户信息") + @GetMapping("/userChildInfo") + public AjaxResult userChildInfo() { + List userInfo = userService.userChildInfos(getUserId().intValue()); + return success(userInfo); + } + + @ApiOperation("用户领取VIP等级") + @UpdateUserCache + @GetMapping("/userVipLevelRechargeThreshold") + public AjaxResult userVipLevelRechargeThreshold(@RequestParam(required = true) Integer vipLevel) { + return userService.userVipLevelRechargeThreshold(getUserId().intValue(), vipLevel); + } + + @ApiOperation("用户已经领取VIP等级") + @GetMapping("/userHasVipLevelRechargeThreshold") + public AjaxResult userHasVipLevelRechargeThreshold() { + return userService.userHasVipLevelRechargeThreshold(getUserId().intValue()); + } + + @ApiOperation("用户领取充值门槛达标奖励") + @UpdateUserCache + @GetMapping("/userRechargeBonus") + public AjaxResult userRechargeBonus(@RequestParam(required = true) Integer recharge, @RequestParam(required = true) Double bonus) { + return userService.userRechargeBonus(getUserId().intValue(), recharge, bonus); + } + + @ApiOperation("用户已领取充值门槛达标奖励") + @GetMapping("/userHasRechargeBonus") + public AjaxResult userHasRechargeBonus() { + return userService.userHasRechargeBonus(getUserId().intValue()); + } + + @ApiOperation("用户积分兑换金币") + @UpdateUserCache + @GetMapping("/userCreditsToAmount") + public AjaxResult userCreditsToAmount(@RequestParam(required = true) BigDecimal credits) { + return userService.userCreditsToAmount(getUserId().intValue(), credits); + } + + @ApiOperation("回调百度接口") + @UpdateUserCache + @GetMapping("/baidu") + public AjaxResult baidu(@RequestParam(required = true) Integer newType, @RequestParam(required = true) String bdVid) { + baiduService.upload(getUserId().intValue(), newType, bdVid); + return AjaxResult.success(); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiUserCreditsRecordsController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiUserCreditsRecordsController.java new file mode 100644 index 0000000..7f61f91 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiUserCreditsRecordsController.java @@ -0,0 +1,79 @@ +package com.ruoyi.user.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.admin.service.TtUserCreditsRecordsService; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.domain.entity.recorde.TtUserCreditsRecords; +import com.ruoyi.domain.vo.TtUserCreditsRecordsRankVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.sql.Timestamp; +import java.util.Calendar; + +@Api(tags = "用户信用记录") +@RestController +@RequestMapping("/api/userCreditsRecords") +@Slf4j +public class ApiUserCreditsRecordsController extends BaseController { + + private final TtUserCreditsRecordsService ttUserCreditsRecordsService; + + public ApiUserCreditsRecordsController(TtUserCreditsRecordsService ttUserCreditsRecordsService) { + this.ttUserCreditsRecordsService = ttUserCreditsRecordsService; + } + + /** + * 积分排行榜 + * @param type 1今天 2昨天 3近一周 + */ + @ApiOperation("流水网流水排行榜") + @GetMapping("/creditsRank/{type}/{page}/{size}") + @Anonymous + public Page creditsRank(@PathVariable("type") Integer type, + @PathVariable("page") Integer page, + @PathVariable("size") Integer size) { + + Calendar c = Calendar.getInstance(); + c.set(Calendar.HOUR_OF_DAY,0); + c.set(Calendar.MINUTE,0); + c.set(Calendar.SECOND,0); + c.set(Calendar.MILLISECOND,0); + Timestamp end = null; + Timestamp begin = null; + + if (type.equals(1)) { + begin = new Timestamp(c.getTimeInMillis()); + end = new Timestamp(System.currentTimeMillis()); + } else if (type.equals(2)) { + end = new Timestamp(c.getTimeInMillis()); + c.add(Calendar.DAY_OF_MONTH,-1); + begin = new Timestamp(c.getTimeInMillis()); + } else if (type.equals(3)) { + end = new Timestamp(c.getTimeInMillis()); + c.add(Calendar.WEEK_OF_MONTH,-1); + begin = new Timestamp(c.getTimeInMillis()); + } + + return ttUserCreditsRecordsService.rank(begin, end, page, size); + } + + /** + * 流水网推广奖励明细 + * @param type 1、今天;2、昨天;3、近七天 + */ + @ApiOperation("流水网推广奖励明细") + @GetMapping("/pWelfareRecords/{type}/{page}/{size}") + public Page pWelfareRecords(@PathVariable("type") Integer type, + @PathVariable("page") Integer page, + @PathVariable("size") Integer size) { + Long userId = getUserId(); + return ttUserCreditsRecordsService.pWelfareRecords(Integer.valueOf(String.valueOf(userId)),type,page,size); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiUserPackSackController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiUserPackSackController.java new file mode 100644 index 0000000..0b4e90a --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiUserPackSackController.java @@ -0,0 +1,218 @@ +package com.ruoyi.user.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.admin.mapper.TtOrnamentsLevelMapper; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.annotation.UpdateUserCache; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.page.PageDataInfo; +import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.domain.dto.packSack.DecomposeLogCondition; +import com.ruoyi.domain.dto.packSack.DecomposeParam; +import com.ruoyi.domain.dto.packSack.DeliveryParam; +import com.ruoyi.domain.dto.packSack.PackSackCondition; +import com.ruoyi.domain.entity.BoxTransferRecord; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtOrnamentsLevel; +import com.ruoyi.domain.vo.TtBoxRecordsDataVO; +import com.ruoyi.domain.vo.UserPackSackDataVO; +import com.ruoyi.domain.vo.client.PackSackGlobalData; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.user.contract.QueryTransferRecordRequest; +import com.ruoyi.user.contract.QueryTransferRecordResponse; +import com.ruoyi.user.mapper.ApiBoxTransferRecordMapper; +import com.ruoyi.user.model.TransferParam; +import com.ruoyi.user.model.vo.ApiTransferRecordVO; +import com.ruoyi.user.service.ApiUserPackSackService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Api(tags = "玩家背包") +@RestController +@RequestMapping("/api/userPackSack") +public class ApiUserPackSackController extends BaseController { + + private final ISysConfigService sysConfigService; + private final ApiUserPackSackService userPackSackService; + private final TtUserService userService; + + @Autowired + private ApiBoxTransferRecordMapper boxTransferRecordMapper; + + @Autowired + private TtOrnamentMapper ttOrnamentMapper; + + @Autowired + private TtOrnamentsLevelMapper ttOrnamentsLevelMapper; + + public ApiUserPackSackController(ISysConfigService sysConfigService, + ApiUserPackSackService userPackSackService, + TtUserService userService) { + this.sysConfigService = sysConfigService; + this.userPackSackService = userPackSackService; + this.userService = userService; + } + + @ApiOperation("用户申请提货") + @PostMapping("/delivery") + public R delivery(@RequestBody @Validated DeliveryParam param) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + // 获取用户信息 + TtUser ttUser = userService.getById(getUserId()); + if ("1".equals(ttUser.getDeliveryStatus())) { + return R.fail(false, "您的帐号提货功能已被禁用,请联系管理员!"); + } + return userPackSackService.delivery(param, ttUser); + } + + @ApiOperation("分解饰品") + @UpdateUserCache + @PostMapping("/decompose") + public R decompose(@RequestBody @Validated DecomposeParam param) { + String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance"); + if ("1".equals(websiteMaintenance)) { + return R.fail("网站维护中......"); + } + if (!param.getIsAll() && ObjectUtils.isEmpty(param.getPackSackIds())) { + return R.fail("请传入待分解饰品ID"); + } + TtUser ttUser = userService.getById(getUserId()); + int result = userPackSackService.decompose(param, ttUser); + return R.ok("成功分解" + result + "件饰品"); + } + + @ApiOperation("饰品分解记录") + //@UpdateUserCache + @PostMapping("/decomposeLog") + public PageDataInfo decomposeLog(@RequestBody @Validated DecomposeLogCondition param) { + param.setUserId(null); + param.setUserId(getUserId().intValue()); + // TtUser ttUser = userService.getById(getUserId()); + List ttBoxRecords = userPackSackService.decomposeLog(param); + return getPageData(ttBoxRecords); + } + + @ApiOperation("玩家背包") + @PostMapping("/getPackSack") + public R> getPackSack(@RequestBody @Validated PackSackCondition condition) { + Long userId = getUserId(); + if (ObjectUtil.isNull(userId)) { + return R.fail("登录过期。"); + } + if (!Objects.isNull(condition.getOrderByType())) { + if (Objects.isNull(condition.getOrderByFie())) { + return R.fail("排序类型不能为空"); + } + } + condition.setUidList(Arrays.asList(userId.intValue())); + condition.setStatusList(Arrays.asList(0)); + PageUtils.clearPage(); + List packSackData = userPackSackService.clientPackSack(condition); + return R.ok(packSackData); + } + + @ApiOperation("玩家背包统计数据") + @GetMapping("/packSackGlobalData") + public R packSackGlobalData() { + Long userId = getUserId(); + if (ObjectUtil.isNull(userId)) { + return R.fail("登录过期。"); + } + return userPackSackService.packSackGlobalData(userId.intValue()); + } + + @ApiOperation("饰品转赠") + @PostMapping({"/transfer"}) + public R transfer(@RequestBody TransferParam param) { + TtUser ttUser = this.userService.getById(this.getUserId()); + String result = this.userPackSackService.transfer(param, ttUser); + return StringUtils.isNotBlank(result) ? R.fail(result) : R.ok("转赠成功!"); + } + + @ApiOperation("饰品转赠记录") + @GetMapping({"/transfer/records"}) + public R queryTransferRecords(QueryTransferRecordRequest param) { + if (param.getPageNum() == null) { + param.setPageNum(1); + } + if (param.getPageSize() == null) { + param.setPageSize(10); + } + + var userId = this.getUserId(); + Page page = Page.of(param.getPageNum(), param.getPageSize()); + var query = new LambdaQueryChainWrapper<>(this.boxTransferRecordMapper); + if (Objects.equals("src", param.getKind())) { + query.eq(BoxTransferRecord::getSrcUid, userId); + } else { + query.eq(BoxTransferRecord::getDstUid, userId); + } + var pageData = query.page(page); + + var records = pageData.getRecords().stream().map(v -> { + ApiTransferRecordVO vo = new ApiTransferRecordVO(); + BeanUtils.copyProperties(v, vo); + vo.setId(v.getId()); + vo.setSrcUid(v.getSrcUid()); + vo.setDstUid(v.getDstUid()); + vo.setBoxRecordsId(v.getBoxRecordId()); + vo.setOrnamentImg(v.getImageUrl()); + + // 补全:数据中饰品名称/图片/等级图片为空时,通过 ornamentId 查询再去补全 + if (v.getOrnamentId() != null && + (StringUtils.isBlank(v.getOrnamentName()) || StringUtils.isBlank(v.getImageUrl()))) { + TtOrnament ornament = new LambdaQueryChainWrapper<>(ttOrnamentMapper) + .eq(TtOrnament::getId, v.getOrnamentId()) + .one(); + if (ornament != null) { + if (StringUtils.isBlank(v.getOrnamentName())) { + vo.setOrnamentName(ornament.getShortName()); + } + if (StringUtils.isBlank(v.getImageUrl())) { + vo.setOrnamentImg(ornament.getImageUrl()); + } + if (StringUtils.isBlank(v.getMarketHashName())) { + vo.setMarketHashName(ornament.getMarketHashName()); + } + // 补全等级图片:tt_ornament.exterior 对应 tt_ornaments_level.id + if (StringUtils.isBlank(v.getOrnamentLevelImg()) && ornament.getQuality() != null) { + Integer levelId = Integer.valueOf(ornament.getExterior()); + TtOrnamentsLevel level = new LambdaQueryChainWrapper<>(ttOrnamentsLevelMapper) + .eq(TtOrnamentsLevel::getId, levelId) + .one(); + if (level != null) { + vo.setOrnamentLevelImg(level.getLevelImg()); + } + } + } + } + + return vo; + }).collect(Collectors.toList()); + + QueryTransferRecordResponse response = new QueryTransferRecordResponse(); + response.setTotal(pageData.getTotal()); + response.setRows(records); + + return R.ok(response); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiWebsiteSetupController.java b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiWebsiteSetupController.java new file mode 100644 index 0000000..b6096e3 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/controller/ApiWebsiteSetupController.java @@ -0,0 +1,42 @@ +package com.ruoyi.user.controller; + +import com.ruoyi.domain.other.TtBanner; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.user.service.ApiWebsiteSetupService; +import com.ruoyi.domain.vo.ApiContentDataVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Api(tags = "网站设置") +@RestController +@RequestMapping("/api/websiteSetup") +public class ApiWebsiteSetupController { + + private final ApiWebsiteSetupService apiWebsiteSetupService; + + public ApiWebsiteSetupController(ApiWebsiteSetupService apiWebsiteSetupService) { + this.apiWebsiteSetupService = apiWebsiteSetupService; + } + + @ApiOperation("获取横幅列表") + @Anonymous + @GetMapping("/getBannerList") + public R> getBannerList() { + List list = apiWebsiteSetupService.getBannerList(); + return R.ok(list); + } + + @ApiOperation("获取类型内容") + @GetMapping("/getContentByType") + @Anonymous + public R getContentByType(String alias) { + ApiContentDataVO data = apiWebsiteSetupService.getContentByType(alias); + return R.ok(data); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiAnnouncementMapper.java b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiAnnouncementMapper.java new file mode 100644 index 0000000..41082eb --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiAnnouncementMapper.java @@ -0,0 +1,16 @@ +package com.ruoyi.user.mapper; + +import com.ruoyi.domain.other.TtAnnouncement; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface ApiAnnouncementMapper { + + List getAnnouncementList(Long userId); + + TtAnnouncement getAnnouncementByAnnouncementId(Integer announcementId); + + int countUnreadAnnouncement(Long userId); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiAnnouncementReadMapper.java b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiAnnouncementReadMapper.java new file mode 100644 index 0000000..861d534 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiAnnouncementReadMapper.java @@ -0,0 +1,12 @@ +package com.ruoyi.user.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +@Mapper +public interface ApiAnnouncementReadMapper { + + int countAnnouncementRead(@Param("announcementId") Integer announcementId, @Param("userId") Long userId); + + int addAnnouncementRead(@Param("announcementId") Integer announcementId, @Param("userId") Long userId); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiBoxTransferRecordMapper.java b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiBoxTransferRecordMapper.java new file mode 100644 index 0000000..68a54ac --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiBoxTransferRecordMapper.java @@ -0,0 +1,9 @@ +package com.ruoyi.user.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.domain.entity.BoxTransferRecord; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ApiBoxTransferRecordMapper extends BaseMapper { +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiMessageMapper.java b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiMessageMapper.java new file mode 100644 index 0000000..4d6490d --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiMessageMapper.java @@ -0,0 +1,12 @@ +package com.ruoyi.user.mapper; + +import com.ruoyi.domain.vo.ApiMessageDataVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface ApiMessageMapper { + List getMessageList(@Param("userId") Long userId, @Param("id") Integer id); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiNoticeMapper.java b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiNoticeMapper.java new file mode 100644 index 0000000..cff167d --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiNoticeMapper.java @@ -0,0 +1,24 @@ +package com.ruoyi.user.mapper; + +import com.ruoyi.domain.other.TtNotice; +import com.ruoyi.user.model.vo.ApiNoticeVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface ApiNoticeMapper { + + List getNoticeList(Long userId); + + ApiNoticeVO getNoticeByNoticeId(@Param("userId") Long userId, @Param("noticeId") Integer noticeId); + + int countUnreadNotice(Long userId); + + int addNotice(TtNotice ttNotice); + + int editNotice(TtNotice ttNotice); + + int removeNoticeByNoticeId(Integer noticeId); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiShoppingMapper.java b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiShoppingMapper.java new file mode 100644 index 0000000..6eb3c13 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiShoppingMapper.java @@ -0,0 +1,13 @@ +package com.ruoyi.user.mapper; + +import com.ruoyi.domain.other.ApiShoppingBody; +import com.ruoyi.domain.vo.ApiShoppingDataVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface ApiShoppingMapper { + List list(@Param("shoppingBody") ApiShoppingBody shoppingBody, @Param("exchangePriceRatio") Integer exchangePriceRatio); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiTaskCenterMapper.java b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiTaskCenterMapper.java new file mode 100644 index 0000000..a4604e1 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiTaskCenterMapper.java @@ -0,0 +1,31 @@ +package com.ruoyi.user.mapper; + +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.user.model.TtTaskCenterUser; +import com.ruoyi.user.model.dto.YesterdayExpenditureDTO; +import com.ruoyi.user.model.vo.ApiTaskCenterVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; + +@Mapper +public interface ApiTaskCenterMapper { + + List selectApiTaskCenterVOList(Integer userId); + + List getYesterdayExpenditure(); + + BigDecimal selectCreditByTaskIdAndUserId(@Param("taskId") Integer taskId, @Param("userId") Integer userId); + + TtTaskCenterUser selectTtTaskCenterUserByTaskIdAndUserId(@Param("taskId") Integer taskId, @Param("userId") Integer userId); + + int insertYesterdayExpenditureBonusPoints(List ttTaskCenterUserList); + + int deleteYesterdayExpenditureBonusPoints(Integer taskId); + + int markAsClaimedByUserIdAndType(@Param("taskId") Integer taskId, @Param("userId") Integer userId); + + int insertTaskCenterRecord(TtTaskCenterUser ttTaskCenterUser); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiUserBlendErcashMapper.java b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiUserBlendErcashMapper.java new file mode 100644 index 0000000..dc12339 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiUserBlendErcashMapper.java @@ -0,0 +1,15 @@ +package com.ruoyi.user.mapper; + +import com.ruoyi.user.model.vo.ApiRechargeRankingVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface ApiUserBlendErcashMapper { + + /** + * 获取指定日期的消费排行榜 + */ + List getRechargeRankingByDate(String date); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiUserPackSackMapper.java b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiUserPackSackMapper.java new file mode 100644 index 0000000..56dd0c6 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/mapper/ApiUserPackSackMapper.java @@ -0,0 +1,26 @@ +package com.ruoyi.user.mapper; + +import com.ruoyi.domain.vo.UserPackSackDataVO; +import com.ruoyi.domain.vo.client.PackSackGlobalData; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface ApiUserPackSackMapper { + List getPackSack(Integer userId); + + List clientPackSack( + @Param("uidList") List uidList, + @Param("statusList") List statusList, + @Param("name") String name, + @Param("beginTime") String beginTime, + @Param("endTime") String endTime, + @Param("orderByFie") Integer orderByFie, + @Param("orderByType") Integer orderByType, + @Param("limit") Integer limit, + @Param("size") Integer size); + + PackSackGlobalData packSackGlobalData(@Param("userId") Integer userId); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/model/TransferParam.java b/skins-service/service-user/src/main/java/com/ruoyi/user/model/TransferParam.java new file mode 100644 index 0000000..7539944 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/model/TransferParam.java @@ -0,0 +1,11 @@ +package com.ruoyi.user.model; + +import lombok.Data; + +import java.util.List; + +@Data +public class TransferParam { + private List packSackIds; + private Integer targetUid; +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/model/TtTaskCenterUser.java b/skins-service/service-user/src/main/java/com/ruoyi/user/model/TtTaskCenterUser.java new file mode 100644 index 0000000..a19dcf1 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/model/TtTaskCenterUser.java @@ -0,0 +1,13 @@ +package com.ruoyi.user.model; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class TtTaskCenterUser { + private Integer taskId; + private Integer userId; + private BigDecimal credit; + private String claimed; +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/model/dto/RealNameAuthenticationDTO.java b/skins-service/service-user/src/main/java/com/ruoyi/user/model/dto/RealNameAuthenticationDTO.java new file mode 100644 index 0000000..255c922 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/model/dto/RealNameAuthenticationDTO.java @@ -0,0 +1,13 @@ +package com.ruoyi.user.model.dto; + +import lombok.Data; + +@Data +public class RealNameAuthenticationDTO { + + private String realName; + + private String idNum; + + private String certifyId; +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/model/dto/YesterdayExpenditureDTO.java b/skins-service/service-user/src/main/java/com/ruoyi/user/model/dto/YesterdayExpenditureDTO.java new file mode 100644 index 0000000..a9b6f36 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/model/dto/YesterdayExpenditureDTO.java @@ -0,0 +1,11 @@ +package com.ruoyi.user.model.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class YesterdayExpenditureDTO { + private Integer userId; + private BigDecimal totalRecharge; +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/model/vo/ApiNoticeVO.java b/skins-service/service-user/src/main/java/com/ruoyi/user/model/vo/ApiNoticeVO.java new file mode 100644 index 0000000..d6a8ee2 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/model/vo/ApiNoticeVO.java @@ -0,0 +1,21 @@ +package com.ruoyi.user.model.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +@Data +public class ApiNoticeVO { + + private Integer noticeId; + + private String title; + + private String content; + + private String read; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/model/vo/ApiRechargeRankingVO.java b/skins-service/service-user/src/main/java/com/ruoyi/user/model/vo/ApiRechargeRankingVO.java new file mode 100644 index 0000000..04e076b --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/model/vo/ApiRechargeRankingVO.java @@ -0,0 +1,25 @@ +package com.ruoyi.user.model.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ApiRechargeRankingVO { + + private Integer ranking; + + private Long userId; + + private String nickName; + + private String avatar; + + /** 金币 */ + private BigDecimal amount; + + /** 积分 */ + private BigDecimal credits; + + private BigDecimal totalRecharge; +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/model/vo/ApiTaskCenterVO.java b/skins-service/service-user/src/main/java/com/ruoyi/user/model/vo/ApiTaskCenterVO.java new file mode 100644 index 0000000..197b06a --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/model/vo/ApiTaskCenterVO.java @@ -0,0 +1,23 @@ +package com.ruoyi.user.model.vo; + +import lombok.Data; + +@Data +public class ApiTaskCenterVO { + + private Integer taskId; + + private String taskName; + + private String description; + + /** + * 领取条件(0不满足 1满足) + */ + private String status; + + /** + * 是否已领取(1已领取) + */ + private String claimed; +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/model/vo/ApiTransferRecordVO.java b/skins-service/service-user/src/main/java/com/ruoyi/user/model/vo/ApiTransferRecordVO.java new file mode 100644 index 0000000..2da55d9 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/model/vo/ApiTransferRecordVO.java @@ -0,0 +1,41 @@ +package com.ruoyi.user.model.vo; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +public class ApiTransferRecordVO { + + @TableId + private Long id; + + private Long srcUid; + private Long dstUid; + + private Long boxRecordsId; + + + private Long ornamentId; + + + private String ornamentName; + + + private String ornamentImg; + + + private String ornamentLevelImg; + + + private String marketHashName; + + + private BigDecimal ornamentsPrice; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createTime; +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiAnnouncementService.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiAnnouncementService.java new file mode 100644 index 0000000..739a639 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiAnnouncementService.java @@ -0,0 +1,14 @@ +package com.ruoyi.user.service; + +import com.ruoyi.domain.other.TtAnnouncement; + +import java.util.List; + +public interface ApiAnnouncementService { + + List getAnnouncementList(Long userId); + + TtAnnouncement getAnnouncementByAnnouncementId(Integer announcementId, Long userId); + + int countUnreadAnnouncement(Long userId); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiBonusService.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiBonusService.java new file mode 100644 index 0000000..3b51621 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiBonusService.java @@ -0,0 +1,14 @@ +package com.ruoyi.user.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.user.contract.QueryVipRewardResponse; + +import java.math.BigDecimal; + +public interface ApiBonusService { + + R receiveRedPacket(String code, TtUser ttUser); + + R vipRewardList(TtUser ttUser); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiLoginService.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiLoginService.java new file mode 100644 index 0000000..cbc3c31 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiLoginService.java @@ -0,0 +1,13 @@ +package com.ruoyi.user.service; + +import com.ruoyi.common.core.domain.AjaxResult; +import org.apache.commons.lang3.tuple.Pair; + +public interface ApiLoginService { + + Pair login(String username, String password); + + AjaxResult verificationCodeLogin(String phoneNumber, String code); + + AjaxResult verificationCodeLoginOrRegister(String phoneNumber, String code, String parentInvitationCode); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiMessageService.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiMessageService.java new file mode 100644 index 0000000..bc2a8e4 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiMessageService.java @@ -0,0 +1,14 @@ +package com.ruoyi.user.service; + +import com.ruoyi.domain.vo.ApiMessageDataVO; + +import java.util.List; + +public interface ApiMessageService { + + List getMessageList(Long userId, Integer id); + + ApiMessageDataVO view(Long userId, Integer id); + + String batchOperation(Long userId, List ids, String status); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiNoticeService.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiNoticeService.java new file mode 100644 index 0000000..9e0d2e1 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiNoticeService.java @@ -0,0 +1,21 @@ +package com.ruoyi.user.service; + +import com.ruoyi.domain.other.TtNotice; +import com.ruoyi.user.model.vo.ApiNoticeVO; + +import java.util.List; + +public interface ApiNoticeService { + + List getNoticeList(Long userId); + + ApiNoticeVO getNoticeByNoticeId(Long userId, Integer noticeId); + + int countUnreadNotice(Long userId); + + int addNotice(TtNotice ttNotice); + + int editNotice(TtNotice ttNotice); + + int removeNoticeByNoticeId(Integer noticeId); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiPromotionRecordService.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiPromotionRecordService.java new file mode 100644 index 0000000..4351af1 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiPromotionRecordService.java @@ -0,0 +1,10 @@ +package com.ruoyi.user.service; + +import com.ruoyi.domain.vo.promotion.TtPromotionRecordVo; + +import java.util.List; + +public interface ApiPromotionRecordService { + + List getPromotionRecord(Integer userId); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiRegisterService.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiRegisterService.java new file mode 100644 index 0000000..72aedc7 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiRegisterService.java @@ -0,0 +1,8 @@ +package com.ruoyi.user.service; + +import com.ruoyi.domain.other.ApiRegisterBody; + +public interface ApiRegisterService { + + String register(ApiRegisterBody registerBody); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiShoppingService.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiShoppingService.java new file mode 100644 index 0000000..0d6aced --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiShoppingService.java @@ -0,0 +1,18 @@ +package com.ruoyi.user.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.ApiShoppingBody; +import com.ruoyi.domain.vo.ApiShoppingDataVO; + +import java.math.BigDecimal; +import java.util.List; + +public interface ApiShoppingService { + + List list(ApiShoppingBody shoppingBody); + + R exchange(TtUser ttUser, Long ornamentsId); + + String integratingConversion(TtUser ttUser, BigDecimal credits); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiTaskCenterService.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiTaskCenterService.java new file mode 100644 index 0000000..7859cc4 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiTaskCenterService.java @@ -0,0 +1,18 @@ +package com.ruoyi.user.service; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.user.model.TtTaskCenterUser; +import com.ruoyi.user.model.vo.ApiTaskCenterVO; + +import java.util.List; + +public interface ApiTaskCenterService { + + List selectApiTaskCenterVOList(Integer userId); + + TtTaskCenterUser selectTtTaskCenterUserByTaskIdAndUserId(Integer taskId, Integer userId); + + void updateYesterdayBonusPoints(); + + AjaxResult getReward(Integer taskId, Integer userId); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiUserPackSackService.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiUserPackSackService.java new file mode 100644 index 0000000..fe2e48f --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiUserPackSackService.java @@ -0,0 +1,33 @@ +package com.ruoyi.user.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.dto.packSack.DecomposeLogCondition; +import com.ruoyi.domain.dto.packSack.DecomposeParam; +import com.ruoyi.domain.dto.packSack.DeliveryParam; +import com.ruoyi.domain.dto.packSack.PackSackCondition; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.vo.TtBoxRecordsDataVO; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.vo.UserPackSackDataVO; +import com.ruoyi.domain.vo.client.PackSackGlobalData; +import com.ruoyi.user.model.TransferParam; + +import java.util.List; + +public interface ApiUserPackSackService { + R delivery(DeliveryParam param, TtUser ttUser); + + int decompose(DecomposeParam param, TtUser ttUser); + + List getPackSack(Integer userId); + + List packSackHandle(List packSackIds, TtUser ttUser, Integer status); + + List decomposeLog(DecomposeLogCondition param); + + List clientPackSack(PackSackCondition condition); + + R packSackGlobalData(Integer userId); + + String transfer(TransferParam param, TtUser ttUser); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiUserService.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiUserService.java new file mode 100644 index 0000000..0d48e7a --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiUserService.java @@ -0,0 +1,31 @@ +package com.ruoyi.user.service; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.ApiForgetPasswordBody; +import com.ruoyi.domain.other.ApiUpdateUserDetailsBody; +import com.ruoyi.domain.other.RealNameAuthenticationBody; +import org.springframework.web.multipart.MultipartFile; + +public interface ApiUserService { + + String profilePictureUpload(TtUser ttUser, MultipartFile file); + + String updateUserDetails(TtUser ttUser, ApiUpdateUserDetailsBody updateUserDetailsBody); + + String forgetPassword(ApiForgetPasswordBody apiForgetPasswordBody); + + String realNameAuthentication(TtUser ttUser, RealNameAuthenticationBody realNameAuthenticationBody); + + String realNameAuthentication2(TtUser ttUser, String realName, String idNum, String phoneNum); + + String authenticationOk(TtUser ttUser); + + R changePW(TtUser ttUser, ApiUpdateUserDetailsBody updateUserDetailsBody,String token); + + R bindBoss(TtUser ttUser, ApiUpdateUserDetailsBody updateUserDetailsBody); + + Integer getSubordinateNumber(Long userId); + + R checkRecharge(Integer userId); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiWebsiteSetupService.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiWebsiteSetupService.java new file mode 100644 index 0000000..ced61e7 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/ApiWebsiteSetupService.java @@ -0,0 +1,13 @@ +package com.ruoyi.user.service; + +import com.ruoyi.domain.other.TtBanner; +import com.ruoyi.domain.vo.ApiContentDataVO; + +import java.util.List; + +public interface ApiWebsiteSetupService { + + List getBannerList(); + + ApiContentDataVO getContentByType(String alias); +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/UserIdGenerator.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/UserIdGenerator.java new file mode 100644 index 0000000..30e0a3c --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/UserIdGenerator.java @@ -0,0 +1,40 @@ +package com.ruoyi.user.service; + +import com.ruoyi.admin.mapper.TtUserMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.security.SecureRandom; +import java.util.Objects; + +@Service +public class UserIdGenerator { + @Autowired + private TtUserMapper ttUserMapper; + + + // 生成随机的六位数,不能与数据库中已有账号重复 + public Integer generateUserId() { + SecureRandom random = new SecureRandom(); + int maxRetries = 50; // 防止死循环的最大重试次数 + int retryCount = 0; + + while (retryCount < maxRetries) { + // 生成 100000 到 999999 之间的随机数 + int candidateId = 100000 + random.nextInt(900000); + + // 检查数据库中是否存在该 ID + // 假设 TtUserMapper 中有一个 checkUserIdExists 方法返回 boolean 或 count + // 如果该方法不存在,请根据实际情况调整,例如 selectById 返回 null + var exists = ttUserMapper.selectTtUserById((long)candidateId); + + if (Objects.isNull(exists)) { + return candidateId; + } + + retryCount++; + } + + throw new RuntimeException("生成用户 ID 失败:达到最大重试次数,可能六位数 ID 已耗尽"); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiAnnouncementServiceImpl.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiAnnouncementServiceImpl.java new file mode 100644 index 0000000..8fec5e4 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiAnnouncementServiceImpl.java @@ -0,0 +1,39 @@ +package com.ruoyi.user.service.impl; + +import com.ruoyi.domain.other.TtAnnouncement; +import com.ruoyi.user.mapper.ApiAnnouncementMapper; +import com.ruoyi.user.mapper.ApiAnnouncementReadMapper; +import com.ruoyi.user.service.ApiAnnouncementService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class ApiAnnouncementServiceImpl implements ApiAnnouncementService { + + @Autowired + private ApiAnnouncementMapper apiAnnouncementMapper; + + @Autowired + private ApiAnnouncementReadMapper apiAnnouncementReadMapper; + + @Override + public List getAnnouncementList(Long userId) { + return apiAnnouncementMapper.getAnnouncementList(userId); + } + + @Override + public TtAnnouncement getAnnouncementByAnnouncementId(Integer announcementId, Long userId) { + // 新增用户和公告关联,表示已读 + if (apiAnnouncementReadMapper.countAnnouncementRead(announcementId, userId) == 0) { + apiAnnouncementReadMapper.addAnnouncementRead(announcementId, userId); + } + return apiAnnouncementMapper.getAnnouncementByAnnouncementId(announcementId); + } + + @Override + public int countUnreadAnnouncement(Long userId) { + return apiAnnouncementMapper.countUnreadAnnouncement(userId); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiBonusServiceImpl.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiBonusServiceImpl.java new file mode 100644 index 0000000..6a45e22 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiBonusServiceImpl.java @@ -0,0 +1,265 @@ +package com.ruoyi.user.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.config.RedisConstants; +import com.ruoyi.admin.mapper.TtRedPacketMapper; +import com.ruoyi.admin.mapper.TtRedPacketRecordMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.service.TtVipLevelService; +import com.ruoyi.admin.util.RandomUtils; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.redis.config.RedisLock; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.MoneyUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtRedPacket; +import com.ruoyi.domain.other.TtRedPacketRecord; +import com.ruoyi.domain.other.TtVipLevel; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.user.contract.QueryVipRewardResponse; +import com.ruoyi.user.service.ApiBonusService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +@Service +@Slf4j +public class ApiBonusServiceImpl implements ApiBonusService { + + private final TtUserService userService; + private final TtRedPacketMapper redPacketMapper; + private final TtRedPacketRecordMapper redPacketRecordMapper; + private final RedisLock redisLock; + + public ApiBonusServiceImpl(TtUserService userService, + TtRedPacketMapper redPacketMapper, + TtRedPacketRecordMapper redPacketRecordMapper, + RedisLock redisLock) { + this.userService = userService; + this.redPacketMapper = redPacketMapper; + this.redPacketRecordMapper = redPacketRecordMapper; + this.redisLock = redisLock; + } + + @Autowired + private TtUserBlendErcashMapper userBlendErcashMapper; + + @Autowired + private TtVipLevelService vipLevelService; + + // 领红包检查 + private R receiveRedPacketCheck(String code, TtUser ttUser) { + + if (StringUtils.isBlank(code)) return R.fail("口令错误。"); + + TtRedPacket ttRedPacket = new LambdaQueryChainWrapper<>(redPacketMapper) + .eq(TtRedPacket::getPassword, code) + .eq(TtRedPacket::getDelFlag, 0) + .one(); + if (ObjectUtil.isNull(ttRedPacket)) return R.fail("口令错误,没有合法的红包。"); + if (ttRedPacket.getStatus().equals(1)) return R.fail("红包已经抢光了。"); + + List allRedPackRecord = new LambdaQueryChainWrapper<>(redPacketRecordMapper) + .eq(TtRedPacketRecord::getRedPacketId, ttRedPacket.getId()) + .list(); + if (allRedPackRecord.size() >= ttRedPacket.getNum()) return R.fail("红包已经抢光了!"); + if (Objects.isNull(ttRedPacket.getUseStatus()) || ttRedPacket.getUseStatus().equals(1)) + return R.fail("该红包被禁用,请联系管理员。"); + if (ObjectUtil.isNotEmpty(ttRedPacket.getUserId()) && Objects.equals(ttUser.getUserId(), ttRedPacket.getUserId())) + return R.fail("给粉丝的专属红包,您自己无法领取。"); + if (ObjectUtil.isNotEmpty(ttRedPacket.getUserId()) + && ttRedPacket.getUserId() != 0 + && !Objects.equals(ttUser.getParentId(), ttRedPacket.getUserId())) + return R.fail("粉丝专属红包,您尚未关注该主播。"); + + Date openingTime = ttRedPacket.getOpeningTime(); + if (DateUtils.getNowDate().compareTo(ttRedPacket.getOpeningTime()) < 0) + return R.fail("红包开启时间:" + DateUtil.format(openingTime, "yyyy-MM-dd HH:mm:ss") + ",敬请期待。"); + + Date validity = ttRedPacket.getValidity(); + if (StringUtils.isNotNull(validity) && DateUtils.getNowDate().compareTo(validity) > 0) { + ttRedPacket.setStatus(1); + new LambdaUpdateChainWrapper<>(redPacketMapper) + .eq(TtRedPacket::getId, ttRedPacket.getId()) + .set(TtRedPacket::getStatus, 1) + .update(); + return R.fail("红包已结束"); + } + + for (TtRedPacketRecord record : allRedPackRecord) { + if (record.getUserId().equals(ttUser.getUserId())) return R.fail("您已领取过该红包,请勿重复领取。"); + break; + } + + return R.ok(ttRedPacket); + } + + @Override + public R receiveRedPacket(String code, TtUser ttUser) { + + R check = receiveRedPacketCheck(code, ttUser); + if (!check.getCode().equals(200)) return check; + + TtRedPacket ttRedPacket = check.getData(); + + Boolean lock = false; + for (int l = 0; l < 5; l++) { + lock = redisLock.tryLock(RedisConstants.RECEIVE_RED_PACKET_LOCK + ttRedPacket.getId(), 5L, 10L, TimeUnit.SECONDS); + } + + if (!lock) return R.fail("没抢到红包。"); + + try { + + List redPacketRecords = new LambdaQueryChainWrapper<>(redPacketRecordMapper) + .eq(TtRedPacketRecord::getRedPacketId, ttRedPacket.getId()) + .list(); + + if (ttRedPacket.getNum() > redPacketRecords.size()) { + + // 构建抢红包记录 + BigDecimal receiveAmount = RandomUtils.getRandomPrice(ttRedPacket.getAmount()); + receiveAmount = receiveAmount.setScale(2, RoundingMode.HALF_UP); + + TtRedPacketRecord redPacketRecord = new TtRedPacketRecord(); + redPacketRecord.setRedPacketId(ttRedPacket.getId()); + redPacketRecord.setUserId(ttUser.getUserId()); + redPacketRecord.setReceivePassword(code); + redPacketRecord.setReceiveAmount(receiveAmount); + redPacketRecord.setReceiveTime(DateUtils.getNowDate()); + + // 保存记录 + redPacketRecordMapper.insert(redPacketRecord); + + // 更新用户账户 + userService.updateOnlyUserAccount(ttUser.getUserId(), receiveAmount, TtAccountRecordSource.RECEIVE_RED_PACKET); + + // 更新红包状态 + Integer count = new LambdaQueryChainWrapper<>(redPacketRecordMapper) + .eq(TtRedPacketRecord::getRedPacketId, ttRedPacket.getId()) + .count().intValue(); + if (ttRedPacket.getNum() <= count) { + // 抢完更新为已结束 + ttRedPacket.setStatus(1); + new LambdaUpdateChainWrapper<>(redPacketMapper) + .eq(TtRedPacket::getId, ttRedPacket.getId()) + .set(TtRedPacket::getStatus, 1) + .update(); + } + + return R.ok(receiveAmount); + + } else { + new LambdaUpdateChainWrapper<>(redPacketMapper) + .eq(TtRedPacket::getId, ttRedPacket.getId()) + .set(TtRedPacket::getStatus, 1) + .update(); + return R.fail("红包已经抢光了!"); + } + + } catch (Exception e) { + log.error("任务执行发生异常", e); + return R.fail("系统繁忙,请稍后重试。"); + } finally { + redisLock.unlock(RedisConstants.RECEIVE_RED_PACKET_LOCK + ttRedPacket.getId()); + } + } + + @Override + public R vipRewardList(TtUser ttUser) { + var todayAmountCf = AsyncManager.me().supply(() -> { + LocalDateTime beginTime = LocalDate.now().atStartOfDay(); + var records = new LambdaQueryChainWrapper<>(userBlendErcashMapper) + .eq(TtUserBlendErcash::getUserId, ttUser.getUserId()) + .ge(TtUserBlendErcash::getCreateTime, beginTime) + .in(TtUserBlendErcash::getSource, TtAccountRecordSource.getGameConsumeCodes()).list(); + if (CollectionUtils.isEmpty(records)) { + return BigDecimal.ZERO; + } + return records.stream() + .map(TtUserBlendErcash::getAmount) + .reduce(BigDecimal.ZERO, BigDecimal::add).abs(); + }); + + var yesterdayAmountCf = AsyncManager.me().supply(() -> { + LocalDate yesterday = LocalDate.now().minusDays(1); + LocalDateTime beginTime = yesterday.atStartOfDay(); + LocalDateTime endTime = yesterday.atTime(23, 59, 59); + + var records = new LambdaQueryChainWrapper<>(userBlendErcashMapper) + .eq(TtUserBlendErcash::getUserId, ttUser.getUserId()) + .between(TtUserBlendErcash::getCreateTime, beginTime, endTime) + .in(TtUserBlendErcash::getSource, TtAccountRecordSource.getGameConsumeCodes()).list(); + if (CollectionUtils.isEmpty(records)) { + return BigDecimal.ZERO; + } + return records.stream() + .map(TtUserBlendErcash::getAmount) + .reduce(BigDecimal.ZERO, BigDecimal::add).abs(); + }); + + var yesterdayReward = new LambdaQueryChainWrapper<>(userBlendErcashMapper) + .eq(TtUserBlendErcash::getUserId, ttUser.getUserId()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.VIP_CONSUME_AWARD.getCode()) + .gt(TtUserBlendErcash::getCreateTime, LocalDate.now().atStartOfDay()) + .orderByDesc(TtUserBlendErcash::getCreateTime).one(); + + var todayAmount = AsyncManager.getMs(todayAmountCf, 1000); + var yesterdayAmount = AsyncManager.getMs(yesterdayAmountCf, 1000); + + QueryVipRewardResponse response = new QueryVipRewardResponse(); + if (todayAmount != null) { + response.setTodayAmount(MoneyUtil.toStr(todayAmount)); + if (ttUser.getVipLevel() > 0) { + TtVipLevel ttVipLevel = vipLevelService.getById(ttUser.getVipLevel()); + if (ttVipLevel == null) { + log.warn("用户【{}】VIP等级不存在![{}]", ttUser.getUserId(), ttUser.getVipLevel()); + response.setTodayReward("0.00"); + } else { + BigDecimal rebate = ttVipLevel.getCommissions().divide(BigDecimal.valueOf(100), 4, + RoundingMode.HALF_UP) + .multiply(todayAmount).setScale(2, RoundingMode.HALF_UP); + response.setTodayReward(MoneyUtil.toStr(rebate)); + } + + } else { + response.setTodayReward("0.00"); + } + } else { + response.setTodayReward("0.00"); + response.setTodayAmount("0.00"); + } + + if (yesterdayAmount != null) { + response.setYesterdayAmount(MoneyUtil.toStr(yesterdayAmount)); + } else { + response.setYesterdayAmount("0.00"); + } + if (yesterdayReward != null) { + response.setYesterdayReward(MoneyUtil.toStr(yesterdayReward.getAmount())); + } else { + response.setYesterdayReward("0.00"); + } + + response.setVipLevels(vipLevelService.list()); + + return R.ok(response); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiLoginServiceImpl.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiLoginServiceImpl.java new file mode 100644 index 0000000..672a11d --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiLoginServiceImpl.java @@ -0,0 +1,263 @@ +package com.ruoyi.user.service.impl; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.RandomUtil; +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.TtPromotionUpdateMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.admin.service.TtUserAvatarService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.admin.util.RandomUtils; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.domain.common.constant.UserType; +import com.ruoyi.domain.entity.sys.TtPromotionUpdate; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtUserAvatar; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.web.service.SysLoginService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.framework.websocket.WebSocketUsers; +import com.ruoyi.framework.websocket.pojo.ResultData; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.common.service.ApiSmsService; +import com.ruoyi.user.service.ApiLoginService; +import com.ruoyi.user.service.UserIdGenerator; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Collection; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +@Service +public class ApiLoginServiceImpl implements ApiLoginService { + + @Autowired + private ApiSmsService apiSmsService; + @Autowired + private TtUserMapper userMapper; + @Autowired + private SysLoginService sysLoginService; + @Autowired + private TokenService tokenService; + @Autowired + private RedisCache redisCache; + @Autowired + private TtUserService userService; + @Autowired + private TtUserAvatarService userAvatarService; + @Autowired + private ISysConfigService configService; + @Autowired + private TtPromotionUpdateMapper ttPromotionUpdateMapper; + @Autowired + private UserIdGenerator userIdGenerator; + + @Override + public Pair login(String username, String password) { + AjaxResult result = replicatedLogin(username); + if (result != null) { + return Pair.of(null, result); + } + sysLoginService.loginPreCheck(username, password); + LoginUser loginUser = sysLoginService.createLoginUser("api_" + username, password); + recordLoginInfo(loginUser.getUserId()); + return Pair.of(tokenService.createToken(loginUser), null); + } + + @Override + public AjaxResult verificationCodeLogin(String phoneNumber, String code) { + if (StringUtils.isEmpty(phoneNumber)) { + return AjaxResult.error("手机号不能为空"); + } + if (!Validator.isMobile(phoneNumber)) { + return AjaxResult.error("手机号格式错误,请检查手机号是否输入正确!"); + } + if (StringUtils.isEmpty(code)) { + return AjaxResult.error("验证码不能为空"); + } + if (!NumberUtil.isNumber(code) || code.trim().length() != 4) { + return AjaxResult.error("验证码错误"); + } + TtUser ttUser = new LambdaQueryChainWrapper<>(userMapper).eq(TtUser::getPhoneNumber, phoneNumber).one(); + if (StringUtils.isNull(ttUser)) { + return AjaxResult.error("该手机号未在本站注册!"); + } + String validateCaptcha = apiSmsService.validateCaptcha(code.trim(), "ApiLogin_" + phoneNumber); + if (!"success".equals(validateCaptcha)) { + return AjaxResult.error(validateCaptcha); + } + String password = ttUser.getRemark().substring(ttUser.getRemark().indexOf(":") + 1); + Pair pair = this.login(ttUser.getUserName(), password); + if (pair.getValue() != null) { + return pair.getValue(); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put(Constants.TOKEN, pair.getKey()); + return ajax; + } + + @Override + public AjaxResult verificationCodeLoginOrRegister(String phoneNumber, String code, String parentInvitationCode) { + if (StringUtils.isEmpty(phoneNumber)) { + return AjaxResult.error("手机号不能为空"); + } + if (!Validator.isMobile(phoneNumber)) { + return AjaxResult.error("手机号格式错误,请检查手机号是否输入正确!"); + } + if (StringUtils.isEmpty(code)) { + return AjaxResult.error("验证码不能为空"); + } + if (!NumberUtil.isNumber(code) || code.trim().length() != 4) { + return AjaxResult.error("验证码错误"); + } + String validateCaptcha = apiSmsService.validateCaptcha(code.trim(), "ApiLogin_" + phoneNumber); + if (!"success".equals(validateCaptcha)) { + return AjaxResult.error(validateCaptcha); + } + + TtUser ttUser = new LambdaQueryChainWrapper<>(userMapper).eq(TtUser::getPhoneNumber, phoneNumber).one(); + if (StringUtils.isNull(ttUser)) { + String result = register(phoneNumber, parentInvitationCode); + if (StringUtils.isNotEmpty(result)) { + return AjaxResult.error(result); + } + ttUser = new LambdaQueryChainWrapper<>(userMapper).eq(TtUser::getPhoneNumber, phoneNumber).one(); + } + + String password = ttUser.getRemark().substring(ttUser.getRemark().indexOf(":") + 1); + Pair pair = this.login(ttUser.getUserName(), password); + if (pair.getValue() != null) { + return pair.getValue(); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put(Constants.TOKEN, pair.getKey()); + return ajax; + } + + private String register(String phoneNumber, String parentInvitationCode) { + String nickName = RandomUtils.getRandomName(new Random().nextInt(2)); + String password = RandomUtil.randomString(10); + + if (StringUtils.isEmpty(nickName)) return "用户昵称不能为空"; + if (StringUtils.isEmpty(phoneNumber)) return "手机号不能为空"; + if (!Validator.isMobile(phoneNumber)) return "手机号格式错误,请检查手机号是否输入正确!"; + if (StringUtils.isEmpty(password)) return "用户密码不能为空"; + + TtUser ttUser = TtUser.builder().build(); + ttUser.setUserName(phoneNumber); + ttUser.setEmail(phoneNumber + "@qq.com"); + ttUser.setPhoneNumber(phoneNumber); + ttUser.setAccountAmount(BigDecimal.ZERO); + ttUser.setAccountCredits(BigDecimal.ZERO); + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { + return "密码长度必须在5到20个字符之间"; + } + if (!userService.checkPhoneUnique(ttUser)) return "注册失败," + "手机号'" + phoneNumber + "'已被注册!"; + if (!userService.checkUserNameUnique(ttUser)) + return "注册失败," + "用户名'" + ttUser.getUserName() + "'已存在!"; + ttUser.setNickName(nickName); + ttUser.setUserType(UserType.COMMON_USER.getCode()); + List userAvatarList = new LambdaQueryChainWrapper<>(userAvatarService.getBaseMapper()).eq(TtUserAvatar::getIsDefault, "1").list(); + if (!userAvatarList.isEmpty()) { + ttUser.setAvatar(userAvatarList.get(0).getAvatar()); + } else ttUser.setAvatar(""); + ttUser.setPassword(SecurityUtils.encryptPassword(password)); + String registerRedPacketStr = configService.selectConfigByKey("registerRedPacket"); + BigDecimal registerRedPacket = new BigDecimal(registerRedPacketStr); + ttUser.setAccountAmount(registerRedPacket); + ttUser.setInvitationCode(userService.getInvitationCode().toLowerCase()); + if (StringUtils.isNotEmpty(parentInvitationCode) && parentInvitationCode.trim().length() == 6) { + TtUser parentUser = new LambdaQueryChainWrapper<>(userService.getBaseMapper()) + .eq(TtUser::getInvitationCode, parentInvitationCode.trim().toUpperCase()) + .eq(TtUser::getDelFlag, "0").one(); + if (StringUtils.isNull(parentUser)) { + return "上级邀请码填写错误!"; + } else { + + } + ttUser.setParentId(parentUser.getUserId()); + } + ttUser.setIsRealCheck("0"); + ttUser.setCreateBy("网站注册"); + ttUser.setCreateTime(DateUtils.getNowDate()); + ttUser.setRemark("明文密码:" + password); + + var id = userIdGenerator.generateUserId(); + ttUser.setUserId(id); + + boolean regFlag = userService.save(ttUser); + if (!regFlag) return "注册失败,请联系系统管理人员"; + +// if (BigDecimal.ZERO.compareTo(registerRedPacket) < 0) { +// userService.insertUserAmountRecords(ttUser.getUserId(), TtAccountRecordType.INPUT, TtAccountRecordSource.REGIST_AWARD, registerRedPacket, ttUser.getAccountAmount()); +// } + AsyncManager.me().execute(AsyncFactory + .recordLogininfor("api_" + ttUser.getUserName(), Constants.REGISTER, MessageUtils.message("user.register.success"))); + + // 保存推广更新记录 + TtPromotionUpdate build = TtPromotionUpdate.builder() + .employeeId(ttUser.getUserId()) + .bossId(ttUser.getParentId()) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + ttPromotionUpdateMapper.insert(build); + return ""; + } + + + /** + * 检测用户是否在其它地方登录,如果存在则发送通知给当前用户, + * 并执行相应的清理工作,例如删除已登录用户的缓存信息 + */ + private AjaxResult replicatedLogin(String username) { + Collection redisKeys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); + List loginUserList = redisKeys.stream().map(redisCache::getCacheObject) + .filter(loginUser -> loginUser.getUser() == null) + .filter(loginUser -> username.equals(loginUser.getUsername())) + .collect(Collectors.toList()); + if (!loginUserList.isEmpty()) { + // return AjaxResult.error("您的账号在别处已登录,请退出后再登录,如果不是本人操作,请尽快修改密码!"); + ResultData resultData = new ResultData<>(); + resultData.setCode(10); + resultData.setTypeName("success"); + resultData.setData("您的账号在别处登录,如果不是本人操作,请尽快修改密码!"); + WebSocketUsers.sendMessageToUserByText(loginUserList.get(0).getUserId().intValue(), + JSON.toJSONString(resultData)); + String token = loginUserList.get(0).getToken(); + redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + token); + AsyncManager.me().execute(AsyncFactory.recordLogininfor("api_" + username, + Constants.LOGOUT, "退出成功,账号已换机登录!")); + } + return null; + } + + /** + * 记录登录信息 + */ + public void recordLoginInfo(Long userId) { + new LambdaUpdateChainWrapper<>(userMapper).eq(TtUser::getUserId, userId) + .set(TtUser::getLoginIp, IpUtils.getIpAddr()) + .set(TtUser::getLoginDate, DateUtils.getNowDate()).update(); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiMessageServiceImpl.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiMessageServiceImpl.java new file mode 100644 index 0000000..2c77cc3 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiMessageServiceImpl.java @@ -0,0 +1,59 @@ +package com.ruoyi.user.service.impl; + +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.domain.other.TtMessageSend; +import com.ruoyi.admin.service.TtMessageSendService; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.user.mapper.ApiMessageMapper; +import com.ruoyi.user.service.ApiMessageService; +import com.ruoyi.domain.vo.ApiMessageDataVO; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class ApiMessageServiceImpl implements ApiMessageService { + + private final ApiMessageMapper apiMessageMapper; + private final TtMessageSendService messageSendService; + + public ApiMessageServiceImpl(ApiMessageMapper apiMessageMapper, + TtMessageSendService messageSendService) { + this.apiMessageMapper = apiMessageMapper; + this.messageSendService = messageSendService; + } + + @Override + public List getMessageList(Long userId, Integer id) { + return apiMessageMapper.getMessageList(userId, id); + } + + @Override + public ApiMessageDataVO view(Long userId, Integer id) { + TtMessageSend ttMessageSend = messageSendService.getById(id); + ttMessageSend.setStatus("1"); + ttMessageSend.setReadingTime(DateUtils.getNowDate()); + messageSendService.updateById(ttMessageSend); + List messageList = apiMessageMapper.getMessageList(userId, id); + if (messageList.isEmpty()) return null; + return messageList.get(0); + } + + @Override + public String batchOperation(Long userId, List ids, String status) { + List list = new LambdaQueryChainWrapper<>(messageSendService.getBaseMapper()) + .eq(TtMessageSend::getRecId, userId) + .in(TtMessageSend::getId, ids) + .list(); + list = list.stream().peek(ttMessageSend -> { + ttMessageSend.setStatus(status); + if (StringUtils.isNull(ttMessageSend.getReadingTime())) { + ttMessageSend.setReadingTime(DateUtils.getNowDate()); + } + }).collect(Collectors.toList()); + messageSendService.updateBatchById(list, 1); + return ""; + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiNoticeServiceImpl.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiNoticeServiceImpl.java new file mode 100644 index 0000000..bbbf07f --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiNoticeServiceImpl.java @@ -0,0 +1,58 @@ +package com.ruoyi.user.service.impl; + +import com.ruoyi.domain.other.TtNotice; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.user.mapper.ApiNoticeMapper; +import com.ruoyi.user.model.vo.ApiNoticeVO; +import com.ruoyi.user.service.ApiNoticeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; + +@Slf4j +@Service +public class ApiNoticeServiceImpl implements ApiNoticeService { + + @Autowired + private ApiNoticeMapper apiNoticeMapper; + + @Override + public List getNoticeList(Long userId) { + return apiNoticeMapper.getNoticeList(userId); + } + + @Override + public ApiNoticeVO getNoticeByNoticeId(Long userId, Integer noticeId) { + // 读取详情后更新为已读状态 + TtNotice ttNotice = new TtNotice(); + ttNotice.setNoticeId(noticeId); + ttNotice.setRead("1"); + editNotice(ttNotice); + return apiNoticeMapper.getNoticeByNoticeId(userId, noticeId); + } + + @Override + public int countUnreadNotice(Long userId) { + return apiNoticeMapper.countUnreadNotice(userId); + } + + @Override + public int addNotice(TtNotice ttNotice) { + ttNotice.setCreateTime(new Date()); + return apiNoticeMapper.addNotice(ttNotice); + } + + @Override + public int editNotice(TtNotice ttNotice) { + return apiNoticeMapper.editNotice(ttNotice); + } + + @Override + public int removeNoticeByNoticeId(Integer noticeId) { + return apiNoticeMapper.removeNoticeByNoticeId(noticeId); + } + +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiPromotionRecordServiceImpl.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiPromotionRecordServiceImpl.java new file mode 100644 index 0000000..454bf22 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiPromotionRecordServiceImpl.java @@ -0,0 +1,65 @@ +package com.ruoyi.user.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.admin.mapper.TtPromotionRecordMapper; +import com.ruoyi.admin.mapper.TtUserMapper; +import com.ruoyi.domain.entity.TtPromotionRecord; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.vo.promotion.TtPromotionRecordVo; +import com.ruoyi.user.service.ApiPromotionRecordService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class ApiPromotionRecordServiceImpl implements ApiPromotionRecordService { + + @Autowired + private TtPromotionRecordMapper ttPromotionRecordMapper; + + @Autowired + private TtUserMapper ttUserMapper; + + @Override + public List getPromotionRecord(Integer userId) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(TtPromotionRecord::getUserId, userId); + wrapper.orderByDesc(TtPromotionRecord::getId); + List list = ttPromotionRecordMapper.selectList(wrapper); + if (CollectionUtils.isEmpty(list)) { + return Collections.emptyList(); + } + List vos = new ArrayList<>(); + List userIds = new ArrayList<>(); + for (TtPromotionRecord record : list) { + userIds.add(record.getSubordinateUserId()); + TtPromotionRecordVo vo = new TtPromotionRecordVo(); + BeanUtils.copyProperties(record, vo); + if (record.getRebate() == null || record.getRebate().compareTo(new BigDecimal(0)) <= 0) { + vo.setCommissions(new BigDecimal(0)); + } else if (record.getRechargePrice().compareTo(new BigDecimal(0)) > 0) { + vo.setCommissions(record.getRebate().multiply(BigDecimal.valueOf(100)) + .divide(record.getRechargePrice(), 1, RoundingMode.HALF_UP)); + } + vos.add(vo); + } + List ttUsers = ttUserMapper.selectBatchIds(userIds); + Map ttUserMap = ttUsers.stream().filter(Objects::nonNull).collect(Collectors.toMap( + TtUser::getUserId, // 使用TtUser的userId作为Map的键 + TtUser::getNickName // 使用TtUser的username作为Map的值 + )); + for (TtPromotionRecordVo vo : vos) { + vo.setSubordinateUserName(ttUserMap.getOrDefault(vo.getSubordinateUserId(), "")); + } + return vos; + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiRegisterServiceImpl.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiRegisterServiceImpl.java new file mode 100644 index 0000000..63dd7c5 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiRegisterServiceImpl.java @@ -0,0 +1,130 @@ +package com.ruoyi.user.service.impl; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.NumberUtil; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.mapper.TtPromotionUpdateMapper; +import com.ruoyi.admin.service.TtUserAvatarService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.utils.*; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.sys.TtPromotionUpdate; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.ApiRegisterBody; +import com.ruoyi.domain.other.TtUserAvatar; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.baidu.service.BaiduService; +import com.ruoyi.thirdparty.common.service.ApiSmsService; +import com.ruoyi.user.service.ApiRegisterService; +import com.ruoyi.user.service.UserIdGenerator; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.List; + +@Service +@Slf4j +public class ApiRegisterServiceImpl implements ApiRegisterService { + + @Autowired + private TtUserAvatarService userAvatarService; + @Autowired + private TtUserService userService; + @Autowired + private ISysConfigService configService; + @Autowired + private ApiSmsService apiSmsService; + + @Autowired + private TtPromotionUpdateMapper ttPromotionUpdateMapper; + + @Autowired + private BaiduService baiduService; + + @Autowired + private UserIdGenerator userIdGenerator; + + @Override + public String register(ApiRegisterBody registerBody) { + String nickName = registerBody.getNickName(), phoneNumber = registerBody.getPhoneNumber(), + password = registerBody.getPassword(), parentInvitationCode = registerBody.getParentInvitationCode(), + code = registerBody.getCode(); + if (StringUtils.isEmpty(nickName) || nickName.length() > UserConstants.NICKNAME_MAX_LENGTH) { + return "昵称必须在1到7个字符之间"; + } + if (StringUtils.isEmpty(phoneNumber)) return "手机号不能为空"; + if (!Validator.isMobile(phoneNumber)) return "手机号格式错误,请检查手机号是否输入正确!"; + if (StringUtils.isEmpty(password)) return "用户密码不能为空"; + if (StringUtils.isEmpty(code)) return "验证码不能为空"; + if (!NumberUtil.isNumber(code) || code.trim().length() != 4) return "验证码错误"; + TtUser ttUser = TtUser.builder().build(); + ttUser.setUserName(phoneNumber); + ttUser.setEmail(phoneNumber + "@qq.com"); + ttUser.setPhoneNumber(phoneNumber); + ttUser.setAccountAmount(BigDecimal.ZERO); + ttUser.setAccountCredits(BigDecimal.ZERO); + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { + return "密码长度必须在5到20个字符之间"; + } + if (!userService.checkPhoneUnique(ttUser)) return "注册失败," + "手机号'" + phoneNumber + "'已被注册!"; + if (!userService.checkUserNameUnique(ttUser)) + return "注册失败," + "用户名'" + ttUser.getUserName() + "'已存在!"; + ttUser.setNickName(nickName); + ttUser.setUserType("02"); + List userAvatarList = new LambdaQueryChainWrapper<>(userAvatarService.getBaseMapper()).eq(TtUserAvatar::getIsDefault, "1").list(); + if (!userAvatarList.isEmpty()) { + ttUser.setAvatar(RandomUtil.choice(userAvatarList).getAvatar()); + } else ttUser.setAvatar(""); + ttUser.setPassword(SecurityUtils.encryptPassword(password)); + String registerRedPacketStr = configService.selectConfigByKey("registerRedPacket"); + BigDecimal registerRedPacket = new BigDecimal(registerRedPacketStr); + ttUser.setAccountAmount(registerRedPacket); + ttUser.setInvitationCode(userService.getInvitationCode().toLowerCase()); + if (StringUtils.isNotEmpty(parentInvitationCode) && parentInvitationCode.trim().length() == 6) { + TtUser parentUser = new LambdaQueryChainWrapper<>(userService.getBaseMapper()) + .eq(TtUser::getInvitationCode, parentInvitationCode.trim().toUpperCase()) + .eq(TtUser::getDelFlag, "0").one(); + if (StringUtils.isNull(parentUser)) { + return "上级邀请码填写错误!"; + } else { + + } + ttUser.setParentId(parentUser.getUserId()); + } + ttUser.setIsRealCheck("0"); + ttUser.setCreateBy("网站注册"); + ttUser.setCreateTime(DateUtils.getNowDate()); + ttUser.setRemark("明文密码:" + password); + String validateCaptcha = apiSmsService.validateCaptcha(code.trim(), "ApiRegister_" + phoneNumber); + if (!"success".equals(validateCaptcha)) return validateCaptcha; + ttUser.setUserId(userIdGenerator.generateUserId()); + boolean regFlag = userService.save(ttUser); + if (!regFlag) return "注册失败,请联系系统管理人员"; + else { + if (BigDecimal.ZERO.compareTo(registerRedPacket) < 0) { + userService.insertUserAmountRecords(ttUser.getUserId(), TtAccountRecordType.INPUT, TtAccountRecordSource.REGIST_AWARD, registerRedPacket, ttUser.getAccountAmount()); + } + AsyncManager.me().execute(AsyncFactory.recordLogininfor("api_" + ttUser.getUserName(), Constants.REGISTER, MessageUtils.message("user.register.success"))); + + // 保存推广更新记录 + TtPromotionUpdate build = TtPromotionUpdate.builder() + .employeeId(ttUser.getUserId()) + .bossId(ttUser.getParentId()) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + ttPromotionUpdateMapper.insert(build); +// baiduService.upload(ttUser.getUserId(), 49, registerBody.getBdVid()); + } + return ""; + } + +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiShoppingImpl.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiShoppingImpl.java new file mode 100644 index 0000000..33a1b57 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiShoppingImpl.java @@ -0,0 +1,202 @@ +package com.ruoyi.user.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.mapper.TtBoxRecordsMapper; +import com.ruoyi.admin.mapper.TtOrnamentMapper; +import com.ruoyi.admin.mapper.TtOrnamentsLevelMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.TtOrnamentService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.domain.other.TtOrnamentsLevel; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.common.constant.TtboxRecordStatus; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.TtOrnament; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.ApiShoppingBody; +import com.ruoyi.domain.vo.ApiShoppingDataVO; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.user.mapper.ApiShoppingMapper; +import com.ruoyi.user.service.ApiShoppingService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +import static com.ruoyi.domain.common.constant.TtboxRecordSource.MALL_EXCHANGE; + +@Service +@Slf4j +public class ApiShoppingImpl implements ApiShoppingService { + + private final TtUserService userService; + private final TtBoxRecordsMapper boxRecordsMapper; + private final ApiShoppingMapper shoppingMapper; + private final ISysConfigService configService; + private final TtOrnamentMapper ornamentsMapper; + private final TtOrnamentsLevelMapper ornamentsLevelMapper; + + @Autowired + private RabbitTemplate rabbitTemplate; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + public ApiShoppingImpl(TtUserService userService, + TtBoxRecordsMapper boxRecordsMapper, + ApiShoppingMapper shoppingMapper, + ISysConfigService configService, + TtOrnamentMapper ornamentsMapper, + TtOrnamentsLevelMapper ornamentsLevelMapper) { + this.userService = userService; + this.boxRecordsMapper = boxRecordsMapper; + this.shoppingMapper = shoppingMapper; + this.configService = configService; + this.ornamentsMapper = ornamentsMapper; + this.ornamentsLevelMapper = ornamentsLevelMapper; + } + + @Autowired + private TtOrnamentService ttOrnamentService; + + @Override + public List list(ApiShoppingBody param) { + + // 商城兑换比例 + String exchangePriceRatio = configService.selectConfigByKey("exchangePriceRatio"); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 名称 + wrapper.like(ObjectUtil.isNotEmpty(param.getName()), TtOrnament::getShortName, param.getName()); + // 类型 + wrapper.eq(ObjectUtil.isNotEmpty(param.getType()), TtOrnament::getType, param.getType()); + // 外观 + wrapper.eq(ObjectUtil.isNotEmpty(param.getExterior()), TtOrnament::getExterior, param.getExterior()); + // 品质 + wrapper.eq(ObjectUtil.isNotEmpty(param.getQuality()), TtOrnament::getQuality, param.getQuality()); + // 稀有程度 + wrapper.eq(ObjectUtil.isNotEmpty(param.getRarity()), TtOrnament::getRarity, param.getRarity()); + // 是否上架 + wrapper.eq(TtOrnament::getIsPutaway, "0"); + // 价格大于0 +// wrapper.ge(TtOrnament::getUsePrice, 70); + + // 价格区间 + if (ObjectUtil.isNotNull(param.getMaxPrice())) { + if (BigDecimal.ZERO.compareTo(param.getMaxPrice()) > 0) param.setMaxPrice(null); + if (param.getMinPrice().compareTo(param.getMaxPrice()) > 0) param.setMaxPrice(null); + if (BigDecimal.ZERO.compareTo(param.getMinPrice()) > 0) param.setMinPrice(BigDecimal.ZERO); + } + if (ObjectUtil.isNotNull(param.getMaxPrice())) { + wrapper.between(TtOrnament::getUsePrice, param.getMinPrice(), param.getMaxPrice()); + } + + // 排序 + if (ObjectUtil.isEmpty(param.getSortBy())) wrapper.orderByDesc(TtOrnament::getUsePrice); + if (ObjectUtil.isNotEmpty(param.getSortBy()) && param.getSortBy().equals(1)) + wrapper.orderByAsc(TtOrnament::getUsePrice); + if (ObjectUtil.isNotEmpty(param.getSortBy()) && param.getSortBy().equals(2)) + wrapper.orderByDesc(TtOrnament::getUsePrice); + if (ObjectUtil.isNotEmpty(param.getSortBy()) && param.getSortBy().equals(3)) + wrapper.orderByAsc(TtOrnament::getUpdateTime); + if (ObjectUtil.isNotEmpty(param.getSortBy()) && param.getSortBy().equals(4)) + wrapper.orderByDesc(TtOrnament::getUpdateTime); + + List list = ttOrnamentService.list(wrapper); + + List res = list.stream().map(ornament -> { + ApiShoppingDataVO vo = new ApiShoppingDataVO(); + BeanUtil.copyProperties(ornament, vo); + if (ObjectUtil.isNotEmpty(vo.getUsePrice())) { + vo.setCreditsPrice(vo.getUsePrice().multiply(new BigDecimal(exchangePriceRatio))); + } + return vo; + }).collect(Collectors.toList()); + + return res; + + // return shoppingMapper.list(shoppingBody, Integer.parseInt(exchangePriceRatio)); + } + + @Override + public R exchange(TtUser ttUser, Long ornamentsId) { + + TtOrnament ttOrnament = new LambdaQueryChainWrapper<>(ornamentsMapper) + .eq(TtOrnament::getId, ornamentsId) + .eq(TtOrnament::getIsPutaway, "0") + .one(); + if (StringUtils.isNull(ttOrnament)) return R.fail("未查询到该饰品信息,请联系管理员!"); + + String exchangePriceRatio = configService.selectConfigByKey("exchangePriceRatio"); // 积分价格比例 + BigDecimal exchange = ttOrnament.getUsePrice().multiply(new BigDecimal(exchangePriceRatio)); + + if (exchange.compareTo(ttUser.getAccountCredits()) > 0) return R.fail("您的积分不足!"); + + R r = userService.updateUserCredits(ttUser.getUserId(), exchange.negate(), TtAccountRecordSource.EXCHANGE); + if (R.isError(r)) { + return R.fail(r.getMsg()); + } + + TtBoxRecords boxRecords = TtBoxRecords.builder().build(); + boxRecords.setUserId(ttUser.getUserId()); + boxRecords.setOrnamentId(ornamentsId); + boxRecords.setOrnamentName(ttOrnament.getShortName());//饰品名称 + boxRecords.setImageUrl(ttOrnament.getImageUrl());//饰品图片 + boxRecords.setMarketHashName(ttOrnament.getMarketHashName()); + boxRecords.setOrnamentsPrice(ttOrnament.getUsePrice()); + + // exterior 字段对应 tt_ornaments_level.id,补全等级图片信息 + if (ttOrnament.getExterior() != null) { + Integer levelId = Integer.valueOf(ttOrnament.getExterior()); + TtOrnamentsLevel level = new LambdaQueryChainWrapper<>(ornamentsLevelMapper) + .eq(TtOrnamentsLevel::getId, levelId) + .one(); + if (level != null) { + boxRecords.setOrnamentsLevelId(levelId);//饰品等级 ID + boxRecords.setOrnamentLevelImg(level.getLevelImg());//饰品等级图片 + } + } + + boxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode()); + boxRecords.setCreateTime(new Date()); + boxRecords.setSource(MALL_EXCHANGE.getCode()); + boxRecords.setHolderUserId(ttUser.getUserId()); + boxRecordsMapper.insert(boxRecords); + + return R.ok(); + } + + // 废弃方法 + public void parentAward(TtUser ttUser, BigDecimal exchange) { + BigDecimal t = exchange.multiply(new BigDecimal("0.03")); + TtUser parent = userService.getById(ttUser.getParentId()); + parent.setAccountCredits(parent.getAccountCredits().add(t)); + userService.updateUserById(parent); + userService.insertUserCreditsRecords(parent.getUserId(), TtAccountRecordType.INPUT, TtAccountRecordSource.P_WELFARE, t, parent.getAccountCredits()); + } + + @Override + public String integratingConversion(TtUser ttUser, BigDecimal credits) { + if (credits.compareTo(ttUser.getAccountCredits()) > 0) return "您的积分不足!"; + ttUser.setAccountAmount(ttUser.getAccountAmount().add(credits)); + ttUser.setAccountCredits(ttUser.getAccountCredits().subtract(credits)); + if (userService.updateById(ttUser)) { + userService.insertUserAmountRecords(ttUser.getUserId(), TtAccountRecordType.INPUT, TtAccountRecordSource.INTEGRATING_CONVERSION, credits, ttUser.getAccountAmount()); + userService.insertUserCreditsRecords(ttUser.getUserId(), TtAccountRecordType.OUTPUT, TtAccountRecordSource.INTEGRATING_CONVERSION, credits.negate(), ttUser.getAccountCredits()); + return ""; + } + return "积分转换异常!"; + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiTaskCenterServiceImpl.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiTaskCenterServiceImpl.java new file mode 100644 index 0000000..5a1ef9f --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiTaskCenterServiceImpl.java @@ -0,0 +1,120 @@ +package com.ruoyi.user.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.ruoyi.admin.mapper.*; +import com.ruoyi.admin.service.TtUserCreditsRecordsService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.recorde.TtUserCreditsRecords; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtTaskCenter; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.user.mapper.ApiTaskCenterMapper; +import com.ruoyi.user.model.TtTaskCenterUser; +import com.ruoyi.user.model.dto.YesterdayExpenditureDTO; +import com.ruoyi.user.model.vo.ApiTaskCenterVO; +import com.ruoyi.user.service.ApiTaskCenterService; +import io.swagger.models.auth.In; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +@Service +public class ApiTaskCenterServiceImpl implements ApiTaskCenterService { + + @Autowired + private ApiTaskCenterMapper apiTaskCenterMapper; + + @Autowired + private TtTaskCenterMapper ttTaskCenterMapper; + + @Autowired + private TtUserMapper ttUserMapper; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + @Autowired + private ISysConfigService configService; + + @Override + public List selectApiTaskCenterVOList(Integer userId) { + return apiTaskCenterMapper.selectApiTaskCenterVOList(userId); + } + + @Override + public TtTaskCenterUser selectTtTaskCenterUserByTaskIdAndUserId(Integer taskId, Integer userId) { + return apiTaskCenterMapper.selectTtTaskCenterUserByTaskIdAndUserId(taskId, userId); + } + + @Transactional + @Override + public void updateYesterdayBonusPoints() { + // 根据任务标识获取任务详情 + TtTaskCenter ttTaskCenter = ttTaskCenterMapper.selectTtTaskCenterByIdentify("yesterday.expenditure.task"); + // 清空tt_task_center_user表中活动类型为昨日消费奖励的数据 + apiTaskCenterMapper.deleteYesterdayExpenditureBonusPoints(ttTaskCenter.getTaskId()); + + List yesterdayExpenditureDTOList = apiTaskCenterMapper.getYesterdayExpenditure(); + if (yesterdayExpenditureDTOList.size() == 0) { + return; + } + // 获取参数设置中消费返积分比例 + String rechargePointsRebateRatio = configService.selectConfigByKey("expenditure.points.rebate.ratio"); + BigDecimal ratio = new BigDecimal(rechargePointsRebateRatio); + List ttTaskCenterUserList = new ArrayList<>(); + + for (YesterdayExpenditureDTO yesterdayRechargeDTO : yesterdayExpenditureDTOList) { + TtTaskCenterUser ttTaskCenterUser = new TtTaskCenterUser(); + ttTaskCenterUser.setTaskId(ttTaskCenter.getTaskId()); + ttTaskCenterUser.setUserId(yesterdayRechargeDTO.getUserId()); + ttTaskCenterUser.setCredit(yesterdayRechargeDTO.getTotalRecharge().multiply(ratio)); + ttTaskCenterUserList.add(ttTaskCenterUser); + } + // 批量增加昨日的积分奖励数据 + apiTaskCenterMapper.insertYesterdayExpenditureBonusPoints(ttTaskCenterUserList); + } + + @Transactional + @Override + public AjaxResult getReward(Integer taskId, Integer userId) { + // 根据ID查询奖励积分 + BigDecimal credit = apiTaskCenterMapper.selectCreditByTaskIdAndUserId(taskId, userId); + // 将奖励积分写入到tt_user表 + TtUser ttUser = ttUserMapper.selectById(userId); + ttUser.setAccountCredits(ttUser.getAccountCredits().add(credit)); + int row = ttUserMapper.updateById(ttUser); + // 将奖励积分写入tt_user_blend_ercash表 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TtUserBlendErcash::getUserId, userId) + .orderByDesc(TtUserBlendErcash::getCreateTime) + .last("LIMIT 1"); + TtUserBlendErcash lastTtUserBlendErcash = ttUserBlendErcashMapper.selectOne(queryWrapper); + TtUserBlendErcash ttUserBlendErcash = new TtUserBlendErcash(); + ttUserBlendErcash.setUserId(userId); + ttUserBlendErcash.setCredits(credit); + ttUserBlendErcash.setCreateTime(new Timestamp(new Date().getTime())); + if (!Objects.isNull(lastTtUserBlendErcash) && !Objects.isNull(lastTtUserBlendErcash.getFinalCredits())) { + ttUserBlendErcash.setFinalCredits(lastTtUserBlendErcash.getFinalCredits().add(credit)); + } else { + ttUserBlendErcash.setFinalCredits(credit); + } + ttUserBlendErcash.setTotal(credit); + ttUserBlendErcash.setSource(6); + ttUserBlendErcash.setType(1); + ttUserBlendErcash.setRemark("昨日消费返积分"); + ttUserBlendErcashMapper.insert(ttUserBlendErcash); + // 标记为已领取 + apiTaskCenterMapper.markAsClaimedByUserIdAndType(taskId, userId); + return row > 0 ? AjaxResult.success("成功领取" + credit + "积分") : AjaxResult.error("领取失败"); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiUserPackSackServiceImpl.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiUserPackSackServiceImpl.java new file mode 100644 index 0000000..df44a62 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiUserPackSackServiceImpl.java @@ -0,0 +1,423 @@ +package com.ruoyi.user.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.admin.mapper.TtBoxRecordsMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.TtBoxRecordsService; +import com.ruoyi.admin.service.TtDeliveryRecordService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.domain.common.constant.*; +import com.ruoyi.domain.dto.packSack.DecomposeLogCondition; +import com.ruoyi.domain.dto.packSack.DecomposeParam; +import com.ruoyi.domain.dto.packSack.DeliveryParam; +import com.ruoyi.domain.dto.packSack.PackSackCondition; +import com.ruoyi.domain.entity.BoxTransferRecord; +import com.ruoyi.domain.entity.TtBoxRecords; +import com.ruoyi.domain.entity.delivery.TtDeliveryRecord; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.vo.TtBoxRecordsDataVO; +import com.ruoyi.domain.vo.UserPackSackDataVO; +import com.ruoyi.domain.vo.client.PackSackGlobalData; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.common.service.DeliverGoodsService; +import com.ruoyi.user.mapper.ApiBoxTransferRecordMapper; +import com.ruoyi.user.mapper.ApiUserPackSackMapper; +import com.ruoyi.user.model.TransferParam; +import com.ruoyi.user.service.ApiUserPackSackService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class ApiUserPackSackServiceImpl implements ApiUserPackSackService { + + private final ISysConfigService configService; + private final TtUserService userService; + private final ApiUserPackSackMapper userPackSackMapper; + private final TtBoxRecordsService boxRecordsService; + + + private final TtBoxRecordsMapper ttBoxRecordsMapper; + private final TtDeliveryRecordService deliveryRecordService; + private final RabbitTemplate rabbitTemplate; + + @Autowired + private DeliverGoodsService deliverGoodsService; + + @Autowired + private Executor customThreadPoolExecutor; + + @Autowired + private TtUserBlendErcashMapper TtUserBlendErcashMapper; + + @Autowired + private ApiBoxTransferRecordMapper boxTransferRecordMapper; + + public ApiUserPackSackServiceImpl(ISysConfigService configService, TtUserService userService, + ApiUserPackSackMapper userPackSackMapper, + TtBoxRecordsService boxRecordsService, + TtBoxRecordsMapper ttBoxRecordsMapper, + TtDeliveryRecordService deliveryRecordService, + RabbitTemplate rabbitTemplate) { + this.configService = configService; + this.userService = userService; + this.userPackSackMapper = userPackSackMapper; + this.boxRecordsService = boxRecordsService; + this.ttBoxRecordsMapper = ttBoxRecordsMapper; + this.deliveryRecordService = deliveryRecordService; + this.rabbitTemplate = rabbitTemplate; + } + + @Override + @Transactional + public R delivery(DeliveryParam param, TtUser ttUser) { + + if (!param.getIsAll()) { + String transactionLink = ttUser.getTransactionLink(); + Long steamId = ttUser.getSteamId(); + + if (ObjectUtils.isEmpty(param.getPackSackIds())) { + return R.fail(false, "请选择需要提取的饰品!"); + } + + // 查询并修改boxRecord的状态(改为申请提货状态,不改数据库) + List boxRecordsList = packSackHandle(param.getPackSackIds(), ttUser, TtboxRecordStatus.APPLY_DELIVERY.getCode()); + + if (boxRecordsList.isEmpty()) { + return R.fail(false, "请选择需要提取的饰品!"); + } else { + // 检查是否包含道具 + Integer deliveryAble = ttBoxRecordsMapper.checkDeliveryAble(param.getPackSackIds()); + if (deliveryAble > 0) { + return R.fail("道具不可用于提货"); + } + } + + if (StringUtils.isEmpty(transactionLink)) { + return R.fail(false, "您未绑定交易链接,请绑定Steam交易链接后重试!"); + } + + // 更新开箱记录状态 + if (!boxRecordsService.updateBatchById(boxRecordsList, 1)) { + R.fail("饰品提取异常,请联系管理员!"); + } + + // 构建提货记录数据 + List deliveryRecordList = new ArrayList<>(); + for (TtBoxRecords ttBoxRecords : boxRecordsList) { + TtDeliveryRecord ttDeliveryRecord = TtDeliveryRecord.builder() + .userId(ttUser.getUserId()) + .boxRecordsId(ttBoxRecords.getId()) + .ornamentId(ttBoxRecords.getOrnamentId()) + .marketHashName(ttBoxRecords.getMarketHashName()) + .ornamentsPrice(ttBoxRecords.getOrnamentsPrice()) + .outTradeNo(IdUtils.fastSimpleUUID().toUpperCase()) + .build(); + // 自动发货最小价格 + String autoDeliveryMinPriceStr = configService.selectConfigByKey("autoDeliveryMinPrice"); + if (new BigDecimal(autoDeliveryMinPriceStr).compareTo(ttBoxRecords.getOrnamentsPrice()) > 0) { + ttDeliveryRecord.setDelivery(DeliveryPattern.AUTO.getCode()); + } else { + ttDeliveryRecord.setDelivery(DeliveryPattern.MANUAL.getCode()); + } + ttDeliveryRecord.setStatus(DeliveryOrderStatus.DELIVERY_BEFORE.getCode()); + // TODO 如果移除一键同步,需要解除注释,并且修改后台查询SQL + // ttDeliveryRecord.setMessage(DeliveryOrderStatus.DELIVERY_BEFORE.getMsg()); + ttDeliveryRecord.setCreateBy(ttUser.getNickName()); + ttDeliveryRecord.setCreateTime(DateUtils.getNowDate()); + deliveryRecordList.add(ttDeliveryRecord); + } + + if (!deliveryRecordService.saveBatch(deliveryRecordList, 1)) { + R.fail("提货记录更新失败。"); + } + + // 异步处理自动发货 + // CompletableFuture.runAsync(() -> { + // deliverGoodsService.autoDelivery(ttUser.getUserId()); + // }, customThreadPoolExecutor); + + // String message = String.valueOf(ttUser.getUserId()); + // rabbitTemplate.convertAndSend(DelayedQueueConfig.DELIVERY_QUEUE, message); + return R.ok(true); + } else { + + String transactionLink = ttUser.getTransactionLink(); + Long steamId = ttUser.getSteamId(); + + List boxRecordsList = new LambdaQueryChainWrapper<>(boxRecordsService.getBaseMapper()) + .eq(TtBoxRecords::getHolderUserId, ttUser.getUserId()) + .eq(TtBoxRecords::getStatus, TtboxRecordStatus.IN_PACKSACK_ON.getCode()) + .list(); + + if (boxRecordsList.isEmpty()) { + return R.ok("操作完成,背包没有物品。"); + } + + // 检查是否包含道具 + //Integer deliveryAble = ttBoxRecordsMapper.checkDeliveryAble(param.getPackSackIds()); + Integer deliveryAble = ttBoxRecordsMapper.checkAllDeliveryAble(ttUser.getUserId()); + if (deliveryAble > 0) { + return R.fail("道具不可用于提货"); + } + + boxRecordsList = boxRecordsList.stream().peek(ttBoxRecords -> { + ttBoxRecords.setStatus(TtboxRecordStatus.APPLY_DELIVERY.getCode()); + ttBoxRecords.setUpdateTime(DateUtils.getNowDate()); + }).collect(Collectors.toList()); + + if (boxRecordsList.isEmpty()) return R.fail(false, "请选择需要提取的饰品!"); + if (StringUtils.isEmpty(transactionLink)) + return R.fail(false, "您未绑定交易链接,请绑定Steam交易链接后重试!"); + + // 更新开箱记录状态 + if (!boxRecordsService.updateBatchById(boxRecordsList, 1)) { + R.fail("饰品提取异常,请联系管理员!"); + } + + // 构建提货记录数据 + List deliveryRecordList = new ArrayList<>(); + for (TtBoxRecords ttBoxRecords : boxRecordsList) { + TtDeliveryRecord ttDeliveryRecord = TtDeliveryRecord.builder() + .userId(ttUser.getUserId()) + .boxRecordsId(ttBoxRecords.getId()) + .ornamentId(ttBoxRecords.getOrnamentId()) + .marketHashName(ttBoxRecords.getMarketHashName()) + .ornamentsPrice(ttBoxRecords.getOrnamentsPrice()) + .outTradeNo(IdUtils.fastSimpleUUID().toUpperCase()) + .build(); + if (ttUser.getUserType().equals(UserType.ANCHOR.getCode())) { + // 主播直接发货完成 + ttDeliveryRecord.setDelivery(DeliveryPattern.ANCHOR.getCode()); + ttDeliveryRecord.setStatus(DeliveryOrderStatus.ORDER_COMPLETE.getCode()); + ttDeliveryRecord.setMessage("订单完成"); + ttDeliveryRecord.setCreateBy(ttUser.getNickName()); + ttDeliveryRecord.setCreateTime(DateUtils.getNowDate()); + } else if (ttUser.getUserType().equals(UserType.COMMON_USER.getCode())) { + // 自动发货最小价格 + String autoDeliveryMinPriceStr = configService.selectConfigByKey("autoDeliveryMinPrice"); + if (new BigDecimal(autoDeliveryMinPriceStr).compareTo(ttBoxRecords.getOrnamentsPrice()) > 0) { + ttDeliveryRecord.setDelivery(DeliveryPattern.AUTO.getCode()); + } + ttDeliveryRecord.setDelivery(DeliveryPattern.MANUAL.getCode()); + ttDeliveryRecord.setCreateBy(ttUser.getNickName()); + ttDeliveryRecord.setCreateTime(DateUtils.getNowDate()); + } + + // 自动发货最小价格 + String autoDeliveryMinPriceStr = configService.selectConfigByKey("autoDeliveryMinPrice"); + if (new BigDecimal(autoDeliveryMinPriceStr).compareTo(ttBoxRecords.getOrnamentsPrice()) > 0) { + ttDeliveryRecord.setDelivery(DeliveryPattern.AUTO.getCode()); + } + ttDeliveryRecord.setDelivery(DeliveryPattern.MANUAL.getCode()); + ttDeliveryRecord.setCreateBy(ttUser.getNickName()); + ttDeliveryRecord.setCreateTime(DateUtils.getNowDate()); + + deliveryRecordList.add(ttDeliveryRecord); + } + + if (!deliveryRecordService.saveBatch(deliveryRecordList, 1)) { + R.fail("提货记录更新失败。"); + } + + // todo 异步处理自动发货(整个方法可以优化) +// CompletableFuture.runAsync(() -> { +// deliverGoodsService.autoDelivery(ttUser.getUserId()); +// }, customThreadPoolExecutor); + + // String message = String.valueOf(ttUser.getUserId()); + // rabbitTemplate.convertAndSend(DelayedQueueConfig.DELIVERY_QUEUE, message); + return R.ok(true); + } + } + + @Override + @Transactional + public int decompose(DecomposeParam param, TtUser ttUser) { + + if (!param.getIsAll()) { + List boxRecordsList = new LambdaQueryChainWrapper<>(boxRecordsService.getBaseMapper()) + .eq(TtBoxRecords::getHolderUserId, ttUser.getUserId()) + .eq(TtBoxRecords::getStatus, TtboxRecordStatus.IN_PACKSACK_ON.getCode()) + .in(TtBoxRecords::getId, param.getPackSackIds()) + .list(); + if (boxRecordsList.isEmpty()) { + return 0; + } + boxRecordsList = boxRecordsList.stream().peek(ttBoxRecords -> { + ttBoxRecords.setStatus(TtboxRecordStatus.RESOLVE.getCode()); + ttBoxRecords.setUpdateTime(DateUtils.getNowDate()); + }).collect(Collectors.toList()); + + //List boxRecordsList = packSackHandle(param.getPackSackIds(), ttUser, TtboxRecordStatus.RESOLVE.getCode()); + + boxRecordsService.updateBatchById(boxRecordsList, 1); + + // 累加 + BigDecimal decomposeTotal = boxRecordsList.stream() + .map(TtBoxRecords::getOrnamentsPrice) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + // 获得积分 + userService.updateUserAccount(ttUser.getUserId(), decomposeTotal, TtAccountRecordSource.DECOMPOSE_ORNAMENT); + + return boxRecordsList.size(); + } else { + + // 全部分解 + LambdaQueryWrapper wrapper1 = new LambdaQueryWrapper<>(); + wrapper1 + .eq(TtBoxRecords::getHolderUserId, ttUser.getUserId()) + .eq(TtBoxRecords::getStatus, TtboxRecordStatus.IN_PACKSACK_ON.getCode()); + List list = boxRecordsService.list(wrapper1); + + // 更新物品状态 + Timestamp now = new Timestamp(System.currentTimeMillis()); + List collect = list.stream().peek(item -> { + item.setStatus(TtboxRecordStatus.RESOLVE.getCode()); + item.setUpdateTime(now); + }).collect(Collectors.toList()); + boxRecordsService.updateBatchById(collect, 1); + + // 累加积分 + BigDecimal total = list.stream() + .map(TtBoxRecords::getOrnamentsPrice) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + userService.updateUserAccount(ttUser.getUserId(), total, TtAccountRecordSource.DECOMPOSE_ORNAMENT); + + return list.size(); + } + + } + + @Override + public List getPackSack(Integer userId) { + return userPackSackMapper.getPackSack(userId); + } + + // 查询并修改boxRecord的状态(不改数据库) + @Override + public List packSackHandle(List packSackIds, TtUser ttUser, Integer status) { + + List boxRecordsList = new LambdaQueryChainWrapper<>(boxRecordsService.getBaseMapper()) + .eq(TtBoxRecords::getHolderUserId, ttUser.getUserId()) + .eq(TtBoxRecords::getStatus, TtboxRecordStatus.IN_PACKSACK_ON.getCode()) + .in(TtBoxRecords::getId, packSackIds) + .list(); + if (!boxRecordsList.isEmpty()) { + boxRecordsList = boxRecordsList.stream().peek(ttBoxRecords -> { + ttBoxRecords.setStatus(status); + ttBoxRecords.setUpdateTime(DateUtils.getNowDate()); + }).collect(Collectors.toList()); + } + return boxRecordsList; + } + + @Override + public List decomposeLog(DecomposeLogCondition param) { + param.setLimit((param.getPage() - 1) * param.getSize()); + param.setBoxRecordStatus(TtboxRecordStatus.RESOLVE.getCode()); + return ttBoxRecordsMapper.decomposeLog(param); + } + + @Override + public List clientPackSack(PackSackCondition condition) { + + condition.setLimit((condition.getPage() - 1) * condition.getSize()); + if (StringUtils.isBlank(condition.getBeginTime()) || StringUtils.isBlank(condition.getBeginTime())) { + condition.setBeginTime(null); + condition.setEndTime(null); + } + if (StringUtils.isBlank(condition.getName())) condition.setName(null); + + return userPackSackMapper.clientPackSack( + condition.getUidList(), + condition.getStatusList(), + condition.getName(), + condition.getBeginTime(), + condition.getEndTime(), + condition.getOrderByFie(), + condition.getOrderByType(), + condition.getLimit(), + condition.getSize()); + } + + @Override + public R packSackGlobalData(Integer userId) { + + PackSackGlobalData data = userPackSackMapper.packSackGlobalData(userId); + return R.ok(data); + } + + @Override + public String transfer(TransferParam param, TtUser ttUser) { + if (param == null || CollectionUtils.isEmpty(param.getPackSackIds()) || Objects.isNull(param.getTargetUid())) { + log.info("param is null or packsackIds is null or phoneNumber is empty"); + return "param is null"; + } + + TtUser byPhoneNumber = new LambdaQueryChainWrapper<>(this.userService.getBaseMapper()) + .eq(TtUser::getUserId, param.getTargetUid()) + .eq(TtUser::getDelFlag, 0) + .eq(TtUser::getStatus, 0).one(); + + if (byPhoneNumber == null) { + log.info("phoneNumber is not exist"); + return "此用户ID" + param.getTargetUid() + "的用户不存在"; + } + + List boxRecordsList = new LambdaQueryChainWrapper<>(this.boxRecordsService.getBaseMapper()) + .eq(TtBoxRecords::getHolderUserId, ttUser.getUserId()) + .eq(TtBoxRecords::getStatus, TtboxRecordStatus.IN_PACKSACK_ON.getCode()) + .in(TtBoxRecords::getId, param.getPackSackIds()).list(); + if (boxRecordsList.isEmpty()) { + return "饰品不存在"; + } + for (TtBoxRecords ttBoxRecords : boxRecordsList) { + ttBoxRecords.setUserId(byPhoneNumber.getUserId()); + ttBoxRecords.setHolderUserId(byPhoneNumber.getUserId()); + ttBoxRecords.setSource(TtboxRecordSource.TRANSFER.getCode()); + ttBoxRecords.setUpdateTime(DateUtils.getNowDate()); + this.boxRecordsService.updateById(ttBoxRecords); + + AsyncManager.me().run(() -> { + var record = new BoxTransferRecord(); + record.setSrcUid(ttUser.getUserId().longValue()); + record.setDstUid(param.getTargetUid().longValue()); + record.setBoxRecordId(ttBoxRecords.getId()); + record.setOrnamentId(ttBoxRecords.getOrnamentId()); + record.setMarketHashName(ttBoxRecords.getMarketHashName()); + record.setOrnamentsZbtId(ttBoxRecords.getOrnamentsZbtId()); + record.setOrnamentsYyId(ttBoxRecords.getOrnamentsYyId()); + record.setOrnamentName(ttBoxRecords.getOrnamentName()); + record.setOrnamentsPrice(ttBoxRecords.getOrnamentsPrice()); + record.setImageUrl(ttBoxRecords.getImageUrl()); + record.setOrnamentsLevelId(ttBoxRecords.getOrnamentsLevelId()); + record.setOrnamentLevelImg(ttBoxRecords.getOrnamentLevelImg()); + boxTransferRecordMapper.insert(record); + }); + } + + return ""; + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiUserServiceImpl.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiUserServiceImpl.java new file mode 100644 index 0000000..3c378e8 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiUserServiceImpl.java @@ -0,0 +1,457 @@ +package com.ruoyi.user.service.impl; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.IdcardUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.ruoyi.admin.mapper.TtPromotionUpdateMapper; +import com.ruoyi.admin.mapper.TtTaskCenterMapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.TtUserAvatarService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.ApiStringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.MimeTypeUtils; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtPromotionUpdate; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.ApiForgetPasswordBody; +import com.ruoyi.domain.other.ApiUpdateUserDetailsBody; +import com.ruoyi.domain.other.RealNameAuthenticationBody; +import com.ruoyi.domain.other.TtTaskCenter; +import com.ruoyi.domain.other.TtUserAvatar; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.thirdparty.alipay.service.RealNameAuthenticationService; +import com.ruoyi.thirdparty.common.service.ApiSmsService; +import com.ruoyi.thirdparty.v5item.service.V5ItemService; +import com.ruoyi.thirdparty.zbt.service.ZBTService; +import com.ruoyi.user.mapper.ApiTaskCenterMapper; +import com.ruoyi.user.model.TtTaskCenterUser; +import com.ruoyi.user.service.ApiUserService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class ApiUserServiceImpl implements ApiUserService { + + private final TtUserAvatarService userAvatarService; + private final TtUserService userService; + private final ZBTService zbtService; + private final ApiSmsService apiSmsService; + private final RealNameAuthenticationService realNameAuthenticationService; + + public ApiUserServiceImpl(TtUserAvatarService userAvatarService, + TtUserService userService, + ZBTService zbtService, + ApiSmsService apiSmsService, + RealNameAuthenticationService realNameAuthenticationService) { + this.userAvatarService = userAvatarService; + this.userService = userService; + this.zbtService = zbtService; + this.apiSmsService = apiSmsService; + this.realNameAuthenticationService = realNameAuthenticationService; + } + + @Autowired + private TokenService tokenService; + + @Autowired + private RedisCache redisCache; + + @Autowired + private TtPromotionUpdateMapper ttPromotionUpdateMapper; + + @Autowired + private ISysConfigService configService; + + @Autowired + private ApiTaskCenterMapper apiTaskCenterMapper; + + @Autowired + private TtTaskCenterMapper ttTaskCenterMapper; + + @Autowired + private V5ItemService v5ItemService; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + @Override + public String profilePictureUpload(TtUser ttUser, MultipartFile file) { + try { + String portrait = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION); + String avatarSet = RuoYiConfig.getDomainName() + portrait; + if (avatarSet.length() > 130) { + String msg = ApiStringUtils.delAvatar(avatarSet); + log.info(msg); + return "头像名称过长,请先进行重命名后,再进行上传!"; + } + String avatar = ttUser.getAvatar(); + List avatarList = userAvatarService.list().stream().map(TtUserAvatar::getAvatar).collect(Collectors.toList()); + if (StringUtils.isNotEmpty(avatar) && !avatarList.contains(avatar)) { + String msg = ApiStringUtils.delAvatar(avatar); + if (StringUtils.isNotEmpty(msg)) log.info(msg); + } + ttUser.setAvatar(avatarSet); + ttUser.setUpdateBy(ttUser.getUserName()); + ttUser.setUpdateTime(DateUtils.getNowDate()); + if (userService.updateById(ttUser)) return ""; + } catch (Exception e) { + return e.getMessage(); + } + return "头像上传失败,请联系管理员!"; + } + + @Override + public String updateUserDetails(TtUser ttUser, ApiUpdateUserDetailsBody updateUserDetailsBody) { + + String nickName = updateUserDetailsBody.getNickName(); + if (ObjectUtil.isNotEmpty(nickName)) { + if (nickName.length() < 2 || nickName.length() > 12) return "长度2-12。"; + } + + String email = updateUserDetailsBody.getEmail(); + String phoneNumber = updateUserDetailsBody.getPhoneNumber(); + String code = updateUserDetailsBody.getCode(); + String password = updateUserDetailsBody.getPassword(); + String parentInvitationCode = updateUserDetailsBody.getParentInvitationCode(); + String transactionLink = updateUserDetailsBody.getTransactionLink(); + + if (StringUtils.isNotEmpty(nickName)) { + if (ttUser.getNickName().equals(nickName)) return ""; + ttUser.setNickName(nickName); + } + if (StringUtils.isNotEmpty(email)) { + if (email.equals(ttUser.getEmail())) return ""; + if (!Validator.isEmail(email)) return "邮箱格式错误,请检查邮箱是否输入正确!"; + ttUser.setEmail(email); + } + if (StringUtils.isNotEmpty(phoneNumber)) { + if (ttUser.getPhoneNumber().equals(phoneNumber)) return ""; + if (!Validator.isMobile(phoneNumber)) return "手机号格式错误,请检查手机号是否输入正确!"; + if (StringUtils.isEmpty(code)) return "验证码不能为空"; + if (!NumberUtil.isNumber(code) || code.trim().length() != 4) return "验证码错误"; + if (!userService.checkPhoneUnique(TtUser.builder().phoneNumber(phoneNumber).build())) + return "个人信息更新失败," + "手机号'" + phoneNumber + "'已被注册!"; + String validateCaptcha = apiSmsService.validateCaptcha(code.trim(), "UpdatePhoneNumber_" + phoneNumber); + if (!"success".equals(validateCaptcha)) return validateCaptcha; + ttUser.setPhoneNumber(phoneNumber); + ttUser.setUserName(phoneNumber); + } + if (StringUtils.isNotEmpty(password)) { + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { + return "密码长度必须在5到20个字符之间"; + } + if (SecurityUtils.matchesPassword(password, ttUser.getPassword())) return ""; + ttUser.setPassword(SecurityUtils.encryptPassword(password)); + ttUser.setRemark("明文密码:" + password); + } + if (StringUtils.isNotEmpty(parentInvitationCode)) { + if (ObjectUtil.isNotEmpty(ttUser.getParentId())) return "绑定失败,您已绑定上级邀请码!"; + if (parentInvitationCode.trim().length() != 6) return "绑定失败,上级邀请码填写错误"; + if (ttUser.getInvitationCode().equals(parentInvitationCode)) return "绑定失败,禁止绑定自身邀请码!"; + TtUser parentUser = new LambdaQueryChainWrapper<>(userService.getBaseMapper()) + .eq(TtUser::getInvitationCode, parentInvitationCode.trim().toUpperCase()) + .eq(TtUser::getDelFlag, "0") + .one(); + if (StringUtils.isNull(parentUser)) return "绑定失败,上级邀请码填写错误"; + if (Objects.equals(parentUser.getParentId(), ttUser.getUserId())) + return "绑定失败,您的下级用户不能作为绑定对象!"; + ttUser.setParentId(parentUser.getUserId()); + } + if (StringUtils.isNotEmpty(transactionLink)) { + if (transactionLink.equals(ttUser.getTransactionLink())) return ""; + // TODO 将此处ZBT验证换为V5item + // UserSteamInfoParams params = new UserSteamInfoParams(); + // params.setType("1"); + // params.setTradeUrl(transactionLink); + // ResultZbt userSteamInfo = zbtService.userSteamInfo(params); + // if (!userSteamInfo.getSuccess()) return "Steam交易链接绑定失败,请检查Steam交易链接是否输入正确"; + // ttUser.setSteamId(Long.valueOf(userSteamInfo.getData().getSteamInfo().getSteamId())); + /*V5ItemResult checkTradeUrlResult = v5ItemService.checkTradeUrl(transactionLink); + if (checkTradeUrlResult.getCode() != 0) { + return "Steam交易链接绑定失败,请检查Steam交易链接是否输入正确"; + } + String steamId = checkTradeUrlResult.getData().getSteamId();*/ + String steamId = "0"; + ttUser.setSteamId(Long.valueOf(steamId)); + ttUser.setTransactionLink(transactionLink); + } + if (StringUtils.isNotEmpty(updateUserDetailsBody.getDeliveryAddress())) { + ttUser.setDeliveryAddress(updateUserDetailsBody.getDeliveryAddress()); + } + ttUser.setUpdateTime(DateUtils.getNowDate()); + if (updateUserDetailsBody.getAvatar() != null) { + ttUser.setAvatar(updateUserDetailsBody.getAvatar()); + } + + boolean isSuccess = userService.updateById(ttUser); + + if (isSuccess) return ""; + + return "个人信息更新失败,请联系管理员!"; + } + + @Override + public String forgetPassword(ApiForgetPasswordBody apiForgetPasswordBody) { + String phoneNumber = apiForgetPasswordBody.getPhoneNumber(), code = apiForgetPasswordBody.getCode(), + password = apiForgetPasswordBody.getPassword(), confirmPassword = apiForgetPasswordBody.getConfirmPassword(); + if (StringUtils.isEmpty(phoneNumber)) return "手机号不能为空"; + if (!Validator.isMobile(phoneNumber)) return "手机号格式错误,请检查手机号是否输入正确!"; + if (StringUtils.isEmpty(password)) return "密码不能为空"; + if (StringUtils.isEmpty(confirmPassword)) return "确认密码不能为空"; + if (StringUtils.isEmpty(code)) return "验证码不能为空"; + if (!NumberUtil.isNumber(code) || code.trim().length() != 4) return "验证码错误"; + if (!password.equals(confirmPassword)) return "确认密码与密码输入不一致!"; + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { + return "密码长度必须在5到20个字符之间"; + } + TtUser ttUser = new LambdaQueryChainWrapper<>(userService.getBaseMapper()).eq(TtUser::getPhoneNumber, phoneNumber).one(); + if (StringUtils.isNull(ttUser)) return "该手机号未在本站注册!"; + ttUser.setPassword(SecurityUtils.encryptPassword(password)); + ttUser.setRemark("明文密码:" + password); + String validateCaptcha = apiSmsService.validateCaptcha(code.trim(), "ApiForgetPassword_" + phoneNumber); + if (!"success".equals(validateCaptcha)) return validateCaptcha; + if (userService.updateById(ttUser)) return ""; + return "更新密码异常,请联系管理员!"; + } + + @Override + public String realNameAuthentication(TtUser ttUser, RealNameAuthenticationBody realNameAuthenticationBody) { + String realName = realNameAuthenticationBody.getRealName(), idNum = realNameAuthenticationBody.getIdNum(); + if ("1".equals(ttUser.getIsRealCheck())) { + return "您的账号已实名认证通过,无需重复认证!"; + } + if (StringUtils.isEmpty(realName)) { + return "姓名不能为空!"; + } + if (StringUtils.isEmpty(idNum)) { + return "身份证号不能为空!"; + } + if (!IdcardUtil.isValidCard(idNum)) { + return "身份证号码填写错误,请检查!"; + } + if (!userService.checkIdNumUnique(TtUser.builder().idNum(idNum).build())) { + return "实名认证失败," + "身份证号'" + idNum + "'已被实名认证使用!"; + } + /*String certifyId = realNameAuthenticationService.authInitialize(realName, idNum); + if (StringUtils.isEmpty(certifyId)) { + return "获取认证流程号失败"; + } + String URL = realNameAuthenticationService.startCertify(certifyId); + if (StringUtils.isEmpty(URL)) { + return "获取认证地址URL失败"; + }*/ + String certifyId = ""; + String msg = realNameAuthenticationService.authentication(realName, idNum); + if (StringUtils.isNotEmpty(msg)) { + return msg; + } + ttUser.setRealName(realName); + ttUser.setIdNum(idNum); + ttUser.setCertifyId(certifyId); + ttUser.setIsRealCheck("1"); // 临时实名 + boolean isSuccess = userService.updateById(ttUser); + if (isSuccess) { + return "alipays"; + } + return "异常错误,请联系管理员"; + // return "更新用户数据时出现异常,请检查代码!"; + } + + @Override + public String realNameAuthentication2(TtUser ttUser, String realName, String idNum, String phoneNum) { + if ("1".equals(ttUser.getIsRealCheck())) { + return "您的账号已实名认证通过,无需重复认证!"; + } + if (StringUtils.isEmpty(realName)) { + return "姓名不能为空!"; + } + if (StringUtils.isEmpty(idNum)) { + return "身份证号不能为空!"; + } + if (!IdcardUtil.isValidCard(idNum)) { + return "身份证号码填写错误,请检查!"; + } + if (!userService.checkIdNumUnique(TtUser.builder().idNum(idNum).build())) { + return "实名认证失败," + "身份证号'" + idNum + "'已被实名认证使用!"; + } + String certifyId = ""; + String msg = realNameAuthenticationService.authentication2(realName, idNum, phoneNum); + if (StringUtils.isNotEmpty(msg)) { + return msg; + } + ttUser.setRealName(realName); + ttUser.setIdNum(idNum); + ttUser.setCertifyId(certifyId); + ttUser.setIsRealCheck("1"); // 临时实名 + boolean isSuccess = userService.updateById(ttUser); + if (isSuccess) { + return "alipays"; + } + return "异常错误,请联系管理员"; + } + + @Override + public String authenticationOk(TtUser ttUser) { + if ("1".equals(ttUser.getIsRealCheck())) { + // 实名认证任务 + realNameTask(ttUser); + return ""; // 空代表认证成功 + } + String certifyId = ttUser.getCertifyId(); + if (StringUtils.isEmpty(certifyId)) { + return "请实名认证后,在获取认证结果!"; + } + String msg = realNameAuthenticationService.queryCertifyResult(certifyId); + if (StringUtils.isNotEmpty(msg)) { + return msg; + } + ttUser.setIsRealCheck("1"); + boolean isSuccess = userService.updateById(ttUser); + if (isSuccess) return ""; + return "更新用户数据时出现异常,请检查代码!"; + } + + @Override + public R changePW(TtUser ttUser, ApiUpdateUserDetailsBody param, String token) { + + if (!param.getPassword().equals(param.getPasswordAgain())) return R.fail("两次输入不一致。"); + + if (!SecurityUtils.matchesPassword(param.getOldPassword(), ttUser.getPassword())) + return R.fail("旧密码不正确。"); + + String newPw = SecurityUtils.encryptPassword(param.getPasswordAgain()); + + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(TtUser::getUserId, ttUser.getUserId()) + .set(TtUser::getPassword, newPw) + .set(TtUser::getRemark, "@" + param.getPasswordAgain()); + userService.update(wrapper); + + redisCache.deleteObject(token); + // tokenService.(token); + AsyncManager.me().execute(AsyncFactory.recordLogininfor("api_" + ttUser.getUserName(), Constants.LOGOUT, "修改密码,退出成功")); + // boolean b = redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + token); + + return R.ok("修改密码成功。"); + // return R.ok("修改密码成功,登出失败。"); + } + + @Override + @Transactional + public R bindBoss(TtUser ttUser, ApiUpdateUserDetailsBody param) { + + String parentInvitationCode = param.getParentInvitationCode(); + + if (ObjectUtil.isEmpty(parentInvitationCode)) return R.ok("推广码为空。"); + if (ObjectUtil.isNotEmpty(ttUser.getParentId())) return R.fail("绑定失败,您已绑定上级邀请码!"); + if (parentInvitationCode.trim().length() != 6) return R.fail("绑定失败,上级邀请码填写错误"); + if (ttUser.getInvitationCode().equals(parentInvitationCode)) return R.fail("绑定失败,禁止绑定自身邀请码!"); + + TtUser parent = new LambdaQueryChainWrapper<>(userService.getBaseMapper()) + .eq(TtUser::getInvitationCode, parentInvitationCode.trim().toUpperCase()) + .eq(TtUser::getDelFlag, "0") + .one(); + if (StringUtils.isNull(parent)) return R.fail("绑定失败,上级邀请码填写错误"); + if (Objects.equals(parent.getParentId(), ttUser.getUserId())) + return R.fail("绑定失败,您的下级用户不能作为绑定对象!"); + + ttUser.setParentId(parent.getUserId()); + + new LambdaUpdateChainWrapper<>(userService.getBaseMapper()) + .eq(TtUser::getUserId, ttUser.getUserId()) + .set(TtUser::getParentId, parent.getUserId()) + .set(TtUser::getUpdateTime, new Date()) + .set(TtUser::getUpdateBy, ttUser.getUserName()) + .update(); + + // 保存推广更新记录 + TtPromotionUpdate build = TtPromotionUpdate.builder() + .employeeId(ttUser.getUserId()) + .bossId(parent.getUserId()) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + ttPromotionUpdateMapper.insert(build); + + return R.ok(); + } + + @Override + public Integer getSubordinateNumber(Long userId) { + // return new LambdaQueryChainWrapper<>(userService.getBaseMapper()) + // .eq(TtUser::getParentId, userId) + // .count(); + return new LambdaQueryChainWrapper<>(ttPromotionUpdateMapper) + .eq(TtPromotionUpdate::getBossId, userId) + .isNotNull(TtPromotionUpdate::getEmployeeId) + .count().intValue(); + } + + @Override + public R checkRecharge(Integer userId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(TtUserBlendErcash::getUserId, userId) + .eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode()) + .eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode()) + .select(TtUserBlendErcash::getTotal); + List ercash = ttUserBlendErcashMapper.selectList(queryWrapper); + if (CollectionUtils.isEmpty(ercash)) { + return R.fail("您的账户未进行充过值!"); + } + return R.ok(); + } + + // 实名认证任务 + private void realNameTask(TtUser ttUser) { + // 根据任务标识获取任务详情 + TtTaskCenter ttTaskCenter = ttTaskCenterMapper.selectTtTaskCenterByIdentify("real.name.task"); + Integer taskId = ttTaskCenter.getTaskId(); + Integer userId = ttUser.getUserId(); + // 查询是否领取过奖励 + TtTaskCenterUser ttTaskCenterRecord = apiTaskCenterMapper.selectTtTaskCenterUserByTaskIdAndUserId(taskId, userId); + if (!Objects.isNull(ttTaskCenterRecord)) { + return; + } + // 处理实名认证任务奖励 + // 获取参数设置中消费返积分比例 + String realNameBonusPoints = configService.selectConfigByKey("real.name.bonus.points"); + BigDecimal credit = new BigDecimal(realNameBonusPoints); + TtTaskCenterUser ttTaskCenterUser = new TtTaskCenterUser(); + ttTaskCenterUser.setTaskId(taskId); + ttTaskCenterUser.setUserId(userId); + ttTaskCenterUser.setCredit(credit); + apiTaskCenterMapper.insertTaskCenterRecord(ttTaskCenterUser); + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiWebsiteSetupServiceImpl.java b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiWebsiteSetupServiceImpl.java new file mode 100644 index 0000000..12b31ce --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/service/impl/ApiWebsiteSetupServiceImpl.java @@ -0,0 +1,54 @@ +package com.ruoyi.user.service.impl; + +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.domain.other.TtBanner; +import com.ruoyi.domain.other.TtContent; +import com.ruoyi.domain.other.TtContentType; +import com.ruoyi.admin.mapper.TtBannerMapper; +import com.ruoyi.admin.mapper.TtContentMapper; +import com.ruoyi.admin.mapper.TtContentTypeMapper; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.user.service.ApiWebsiteSetupService; +import com.ruoyi.domain.vo.ApiContentDataVO; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class ApiWebsiteSetupServiceImpl implements ApiWebsiteSetupService { + + private final TtBannerMapper bannerMapper; + private final TtContentTypeMapper contentTypeMapper; + private final TtContentMapper contentMapper; + + public ApiWebsiteSetupServiceImpl(TtBannerMapper bannerMapper, + TtContentTypeMapper contentTypeMapper, + TtContentMapper contentMapper) { + this.bannerMapper = bannerMapper; + this.contentTypeMapper = contentTypeMapper; + this.contentMapper = contentMapper; + } + + @Override + public List getBannerList() { + return new LambdaQueryChainWrapper<>(bannerMapper) + .eq(TtBanner::getStatus, "1") + .orderByAsc(TtBanner::getSort) + .list(); + } + + @Override + public ApiContentDataVO getContentByType(String alias) { + ApiContentDataVO result = ApiContentDataVO.builder().build(); + TtContentType contentType = new LambdaQueryChainWrapper<>(contentTypeMapper).eq(TtContentType::getAlias, alias) + .eq(TtContentType::getStatus, "0").one(); + if (StringUtils.isNull(contentType)) return result; + result.setContentTypeId(contentType.getId()); + result.setContentTypeName(contentType.getName()); + result.setContentAlias(contentType.getAlias()); + List list = new LambdaQueryChainWrapper<>(contentMapper).eq(TtContent::getTypeId, contentType.getId()) + .eq(TtContent::getStatus, "0").list(); + result.setContentList(list); + return result; + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/task/RechargeRankingRewardTask.java b/skins-service/service-user/src/main/java/com/ruoyi/user/task/RechargeRankingRewardTask.java new file mode 100644 index 0000000..0d9a5c4 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/task/RechargeRankingRewardTask.java @@ -0,0 +1,78 @@ +package com.ruoyi.user.task; + +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.ruoyi.admin.mapper.TtUserBlendErcashMapper; +import com.ruoyi.admin.service.TtRechargeRankingRewardService; +import com.ruoyi.admin.service.TtUserService; +import com.ruoyi.domain.common.constant.TtAccountRecordSource; +import com.ruoyi.domain.common.constant.TtAccountRecordType; +import com.ruoyi.domain.entity.TtUserBlendErcash; +import com.ruoyi.domain.entity.sys.TtUser; +import com.ruoyi.domain.other.TtRechargeRankingReward; +import com.ruoyi.user.mapper.ApiUserBlendErcashMapper; +import com.ruoyi.user.model.vo.ApiRechargeRankingVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +@Slf4j +@Component("RechargeRankingRewardTask") +public class RechargeRankingRewardTask { + + @Autowired + private ApiUserBlendErcashMapper apiUserBlendErcashMapper; + + @Autowired + private TtRechargeRankingRewardService ttRechargeRankingRewardService; + + @Autowired + private TtUserService userService; + + @Autowired + private TtUserBlendErcashMapper ttUserBlendErcashMapper; + + /** 发放消费排行榜奖励 */ + public void distributeRechargeRankingReward() { + log.info("开始发放消费排行榜奖励"); + // 查询昨日排行榜 + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DATE, -1); + Date yesterday = cal.getTime(); + String formattedYesterdayDate = formatter.format(yesterday); + List yesterdayRechargeRanking = apiUserBlendErcashMapper.getRechargeRankingByDate(formattedYesterdayDate); + for (int i = 0; i < yesterdayRechargeRanking.size(); i++) { + // 查询排行榜对应奖励 + TtRechargeRankingReward ttRechargeRankingReward = ttRechargeRankingRewardService.selectTtRechargeRankingRewardById(i + 1); + Long userId = yesterdayRechargeRanking.get(i).getUserId(); + BigDecimal reward = ttRechargeRankingReward.getReward(); + // 加钱 + LambdaUpdateWrapper userUpdate = new LambdaUpdateWrapper<>(); + userUpdate + .eq(TtUser::getUserId, userId) + .setSql("account_amount = account_amount + " + reward); + userService.update(userUpdate); + // 综合消费日志 + TtUser ttUser = userService.getById(userId); + TtUserBlendErcash blendErcash = TtUserBlendErcash.builder() + .userId(userId.intValue()) + .amount(reward.compareTo(BigDecimal.ZERO) > 0 ? reward : null) + .finalAmount(reward.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(reward) : null) + .total(reward.add(reward)) // 收支合计 + .type(TtAccountRecordType.INPUT.getCode()) + .source(TtAccountRecordSource.RECHARGE_RANKING_REWARD.getCode()) + .remark(TtAccountRecordSource.RECHARGE_RANKING_REWARD.getMsg()) + .createTime(new Timestamp(System.currentTimeMillis())) + .updateTime(new Timestamp(System.currentTimeMillis())) + .build(); + ttUserBlendErcashMapper.insert(blendErcash); + } + } +} diff --git a/skins-service/service-user/src/main/java/com/ruoyi/user/task/TaskCenterTask.java b/skins-service/service-user/src/main/java/com/ruoyi/user/task/TaskCenterTask.java new file mode 100644 index 0000000..19a7ac2 --- /dev/null +++ b/skins-service/service-user/src/main/java/com/ruoyi/user/task/TaskCenterTask.java @@ -0,0 +1,21 @@ +package com.ruoyi.user.task; + +import com.ruoyi.user.service.ApiTaskCenterService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Slf4j +@Component("TaskCenterTask") +public class TaskCenterTask { + + @Autowired + private ApiTaskCenterService apiTaskCenterService; + + /** + * 获取昨日消费量返积分 + */ + public void getYesterdayExpenditureBonusPoints() { + apiTaskCenterService.updateYesterdayBonusPoints(); + } +} diff --git a/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiAnnouncementMapper.xml b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiAnnouncementMapper.xml new file mode 100644 index 0000000..1360f8e --- /dev/null +++ b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiAnnouncementMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + + + \ No newline at end of file diff --git a/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiAnnouncementReadMapper.xml b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiAnnouncementReadMapper.xml new file mode 100644 index 0000000..053f361 --- /dev/null +++ b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiAnnouncementReadMapper.xml @@ -0,0 +1,14 @@ + + + + + + + + INSERT INTO tt_announcement_read(announcement_id, user_id) VALUES (#{announcementId}, #{userId}) + + \ No newline at end of file diff --git a/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiMessageMapper.xml b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiMessageMapper.xml new file mode 100644 index 0000000..7f39405 --- /dev/null +++ b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiMessageMapper.xml @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiNoticeMapper.xml b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiNoticeMapper.xml new file mode 100644 index 0000000..ac3e792 --- /dev/null +++ b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiNoticeMapper.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + insert into tt_notice + + user_id, + title, + content, + `read`, + create_time, + + + #{userId}, + #{title}, + #{content}, + #{read}, + #{createTime}, + + + + + update tt_notice + + title = #{title}, + content = #{content}, + `read` = #{read}, + + where notice_id = #{noticeId} + + + + delete from tt_notice where notice_id = #{noticeId} + + \ No newline at end of file diff --git a/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiShoppingMapper.xml b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiShoppingMapper.xml new file mode 100644 index 0000000..dc5d004 --- /dev/null +++ b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiShoppingMapper.xml @@ -0,0 +1,40 @@ + + + + + + + \ No newline at end of file diff --git a/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiTaskCenterMapper.xml b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiTaskCenterMapper.xml new file mode 100644 index 0000000..908c37e --- /dev/null +++ b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiTaskCenterMapper.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + INSERT INTO tt_task_center_user (task_id, user_id, credit) + VALUES + + (#{item.taskId}, #{item.userId}, #{item.credit}) + + + + + DELETE FROM tt_task_center_user WHERE task_id = 1 + + + + UPDATE tt_task_center_user SET claimed = 1 WHERE user_id = #{userId} AND type = #{type} + + + + insert into tt_task_center_user + + task_id, + user_id, + credit, + + + #{taskId}, + #{userId}, + #{credit}, + + + \ No newline at end of file diff --git a/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiUserBlendErcashMapper.xml b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiUserBlendErcashMapper.xml new file mode 100644 index 0000000..4e8a7de --- /dev/null +++ b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiUserBlendErcashMapper.xml @@ -0,0 +1,33 @@ + + + + + + \ No newline at end of file diff --git a/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiUserPackSackMapper.xml b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiUserPackSackMapper.xml new file mode 100644 index 0000000..cd2f09d --- /dev/null +++ b/skins-service/service-user/src/main/resources/com/ruoyi/user/mapper/ApiUserPackSackMapper.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + \ No newline at end of file