1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-08-01 04:18:09 +08:00

Compare commits

..

No commits in common. "master" and "v1.0.0-beta1" have entirely different histories.

1853 changed files with 67242 additions and 96849 deletions

8
.gitignore vendored
View File

@ -1,15 +1,11 @@
/.git
/.idea
/vendor
/config/config.php
/config/xs.course.ini
/config/xs.article.ini
/config/xs.question.ini
/config/xs.group.ini
/config/xs.user.ini
/config/alipay/*.crt
/config/wxpay/*.pem
/db/migrations/schema.php
/public/robots.txt
/public/sitemap.xml
/public/h5
/storage/cache/purifier
*KgTest*

View File

@ -1,861 +0,0 @@
### [v1.7.8](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.8)(2025-06-20)
- 移除ThrottleLimit
- 增加CloseLiveTask
- 增加搜索页图片alt属性striptags过滤
- 后台增加返回顶部快捷方式
- 前台fixbar增加联系电话
- 优化安装脚本
- 优化课时列表直播提示
- 优化后台返回链接
- 优化统计分析代码位置
- 直播回调后更新课时缓存
- 后台清空头像->上传头像
- sitemap.xml直接写入网站根目录
### [v1.7.7](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.7)(2025-04-20)
- 优化索引管理工具
- 优化章节等页面UI
- 修正workerman中onMessage问题
- 修正非免费课程试听问题
- 优化layer窗口中的表单跳转
- 文件清理以及命名优化
- 优化倒计时
### [v1.7.6](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.6)(2025-03-22)
- 升级layui-v2.9.25
- 去除发货中不必要的异常抛出
- 去除文章和问题缓存重建
- 去除多余的文件引用
- 修正每日访问站点积分问题
- 限制全文搜索关键字长度
- 统一规划二维码样式
### [v1.7.5](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.5)(2025-02-22)
- 优化后台统计图表
- 优化图片放大查看
- 优化错误处理机制
- 优化前台编辑器页面
- 去除一些过度的设计
- 精简属性空判断
- 规整redirect
- 优化bootstrap
- 优化logger
- 优化contact
- 优化logo
- 优化nav
### [v1.7.4](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.4)(2024-12-10)
- 更新layui-v2.9.20
- 优化编辑器内容自动提交
- 修正课时详情页目录高亮问题
- 修正CommentInfo中点赞判断
- 精简AccountSearchTrait
- 优化kg_h5_index_url()
- 优化CourseUserTrait
- 优化kg_setting()
- 优化CsrfToken
### [v1.7.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.3)(2024-10-10)
- 更新layui-v2.9.16
- 增加编辑器内容自动提交
- 修改文章和提问可用tag数量
- 优化findUserActiveSessions
- 优化findUserActiveTokens
- 优化上传文件失败抛出异常
- 优化默认文件上传
- 优化用户锁定相关
### [v1.7.2](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.2)(2024-07-31)
- 更新layui-v2.9.14
- 优化docker自动化脚本
- 修正教师直播通知
- 修正课程分类删选问题
- 后台增加客户服务入口
- redis增加expire方法
- 日志记录增加log.trace参数
- 精简代码
### [v1.7.1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.1)(2024-06-31)
- 更新layui-v2.9.10
- 更新docker国内镜像地址
- 增加导入镜像构建容器的方式
- 调整微信公众号模板消息
- 移除加载富文本编辑器初始化的语言文件
- 移除consult中多余的chapter_id属性
- 修正课程列表顶部过滤条件区块不能收缩问题
- 用户中心第三方登录列表增加过滤条件
- 后台增加打开/关闭左侧菜单提示
- 优化整理文件mimeType
- iconfont资源本地化
- 优化UploadController
- 优化富文本内容显示样式
- 简化内容图片放大监听
- 去除课程打赏相关内容
- 课程增加能否发布检查
### [v1.7.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.7.0)(2024-05-15)
- 升级layui-2.9.8
- 调整html编辑器属性
- 增加代码块内容复制
- 清理无用的Captcha配置
- 联系人QQ改为上传二维码图片
- 修正logo,favicon上传路径
- 登录后台同时登录前台
- 移动端修正评论发表
### [v1.6.9](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.9)(2024-04-15)
- 增加用户删除和还原功能
- 增加unauthorized响应
- 增加post方式传递csrf_token
- 删除chapter中resource_count,consult_count属性
- 精简csrf_token白名单
- 拆解优化migrations创建表脚本
- 修正chapter_user时长重复计数问题
- 修正后台刷新首页缓存问题
- 修正home模块中编辑器图片上传
- 优化文章和提问搜索条件
- 优化课程详情页排版
- 优化storage上传
- 优化CategoryTreeList
- 优化CourseUserTrait
- 更新layui-v2.9.7
### [v1.6.8](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.8)(2024-01-30)
- 修正course_user中active_time未更新问题
- 修正主页simple模式免费课程模块样式问题
- 修正chapter_user中plan_id=0问题
- 修正课时评论管理链接
- 修正用户active_time搜索条件
- 修正课时发布switch开关
- 精简chapter/lessons.volt
- 去除league/commonmark包
- 去除分类等必选判断
- 更新layui-v2.9.3
- 使用ServiceTrait精简代码
- 优化AccountTrait
- 优化错误处理
### [v1.6.7](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.7)(2023-12-15)
- 增加文章分类功能
- 增加问题分类功能
- 增加审核等批量功能
- 增加若干业务插件埋点
- 精简重构大量业务逻辑
- 移除秒杀营销功能
- 已发现的问题修复
### [v1.6.6](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.6)(2023-08-30)
- 还原意外删除的AnswerList.php文件
- 修正邮箱注册提交按钮不可用问题
- 去除删除远程课件逻辑
- 增加课程课件资料总览
- 优化cleanDemoDataTask脚本
- 优化tag表migration脚本
- 命名结构等常规优化
### [v1.6.5](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.5)(2023-07-15)
- 升级layui-v2.8.8
- 使用本地图像验证码
- 优化计划任务脚本
- 优化日志清理脚本
- 优化钉钉webhook
- 修正图文分享参数问题
### [v1.6.4](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.4)(2023-06-15)
- 增加推荐课程等Widget
- 更新Composer包
- 修正验证空口令问题
- 优化订单确认页样式
- 优化课程等Me相关信息
- 优化分享URL
- 优化用户课程查找
- 优化通知相关
- 优化Providers
- 优化课程章节权限
- 优化钉钉机器人
### [v1.6.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.3)(2023-05-08)
- 强化文章|提问|课程列表参数检查
- 优化HtmlPurifier内容过滤
- 优化排序条件和分页重复问题
- 优化课程搜索分组条件样式
- 优化课程学习时长同步
- 优化程序语法层面
- 更新Layui-v2.8.2
- 替换ip2region包
- 去除未支付“新鲜”订单检查
- 修正手续费率设置为0无效问题
### [v1.6.2](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.2)(2023-02-12)
- 增加ServerMonitor监控指标配置
- 同步更新腾讯云短信内容规则
- 文章和问答增加评论开关属性
- 修正视频记忆播放无效问题
- 升级composer包版本
- 优化Repo查询默认排序
- 优化管理后台细节
- 优化二维码输出
- 优化评分检查
### [v1.6.1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.1)(2022-12-12)
- 富文本编辑器增加粘贴图片和远程图片本地化
- 修正用户通知标记为已读,计数不归零问题
- 修正播放器中央按钮显示问题
- 优化腾讯云播放地址鉴权参数
- 优化热门作者,答主和问题
- 优化学员学习记录显示
- 优化表单数据提交体验
- 优化单章节层级显示
- 优化ServerInfo类
### [v1.6.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.6.0)(2022-10-26)
- 播放器中间增加大号播放按钮
- 单页和帮助增加浏览计数属性
- logo上增加首页链接
- 修正分类默认图标问题
- 修正layui-main样式更新带来的问题
- 更新composer包
- 调整退款手续费范围
- 导航部分,教师->师资
- 优化分页组件参数
- 优化内容表格样式
- 优化热门问题和热门答主
- 优化通知计数方式
### [v1.5.9](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.9)(2022-09-20)
- 修正内容图片上传问题
- 去除user全文索引
- 调整notice目录结构
- 更新默认图片
- 更新直播名格式化
- 更新微博分享链接
- 文章单页等增加SEO关键字
- 专题增加封面上传
- 优化router扫描规则
- 升级layui至v2.7.6
- 增加用户协议和隐私政策
- 优化错误日志
### [v1.5.8](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.8)(2022-08-28)
- 整理migrations
- 更新自动安装脚本
- 优化登录/注册/忘记密码页
- 修复移动端首页课程缓存刷新
- sitemap条目增加过滤条件
### [v1.5.7](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.7)(2022-08-18)
- 清理群组残留
- 升级腾讯云存储SDK到v2.5.6
- GuzzleHttp升级到v6.5.7
- 优化HtmlPurifier缓存目录自动创建
- 优化问题回答排序问题
- 优化腾讯云短信错误日志
- 整理查询构建语句
- 整理优化CSS
### [v1.5.6](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.6)(2022-08-08)
- 增加应用内命令行migrations
- 移除群组和微聊模块
- kindeditor替换vditor
- markdown转html
### [v1.5.5](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.5)(2022-07-27)
- 修正获分类查询条件
- 修正锁定账户还能登录的问题
- 发货增加noMatchedHandler
- 增加demo数据清理脚本
- 用户课程列表增加角色限定条件
- 精简模块加载和路由扫描
- 优化CsrfToken
- 去除无实质作用的数据表优化
### [v1.5.4](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.4)(2022-06-15)
- 增加migration助手SettingTrait
- 增加积分兑换会员
- 增加ISP备案和电子执照配置
- 增加获取视频时长补偿机制
- 优化课程和套餐发货
- 优化验证码
- 优化视频点播回调处理任务
- 优化章节排序初始值和步长
- 优化后台视频上传和转码
- 修正获取子分类查询条件
### [v1.5.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.3)(2022-05-30)
- 优化章节排序初始值和步长
- 修复删除群组前台列表仍然显示问题
- 设置360浏览器的默认模式为webkit
- 修复首页简单模式课程项顶部缺少空白
- vditor本地化彻底弃用cdn.jsdelivr.net
- 调整markdown样式
### [v1.5.2](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.2)(2022-04-17)
- 补充话题列表课程数据结构
- 调整发送验证码相关样式
- 优化套餐和话题下拉课程数据显示
- 去除礼物详情中多出来的"}}"标签
- 修正关闭秒杀订单时没有回填库存的问题
- vditor编辑器切换为七牛cdn加速
### [v1.5.1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.1)(2022-03-17)
- 推荐课程等列表补充属性
- 修正后台评价列表中的搜索链接
- 修正后台点播设置视频码率后500错误问题
- 修正多码率远程播放地址部分为空播放问题
- 修正更新套餐课程缓存传参数据类型问题
- 修正第三方登录解除绑定失败问题
- 使用ServiceTrait归纳获取服务代码
- 优化anonymous隐藏部分字符函数
- 调整积分兑换相关定义命名
- 去除js_vars中关于IM客服的配置
- 增加验证码开关
### [v1.5.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.5.0)(2022-02-17)
- 调整对内部人员通知任务类型的前缀
- 调整微信和短信通知发送判断逻辑
- 清理后台实用工具的无用文件内容
- 支付后解除秒杀商品锁定
- 加强支付流程数据验证
- 加强退款流程数据验证
- 优化账户创建数据流
- 优化课程创建数据流
- 优化章节创建数据流
- 优化积分商品兑换
- 优化发货逻辑
### [v1.4.9](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.9)(2022-01-01)
- 修正订单消费未奖励积分问题
- 修正前台课程分类排序无效问题
- 修正后台点播防盗链配置显隐藏状态问题
- 修正分享链接非h5环境也会跳转到h5问题
- 修正后台钉钉配置调用错误
- 使用腾讯云新SDK发送短信
- 优化show400错误输出页
- 优化下单时产品检查逻辑
- 优化上传文件筛选限制
- 优化后台配置更新
### [v1.4.8](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.8)(2021-11-28)
- 修正后台下载课程附件问题
- 修正后台登录检查跳转地址
- 修正公众号关注二维码样式问题
- 优化发货失败自动退款逻辑
- 创建交易时增加订单支付检查
- H5增加底部tab图标
### [v1.4.7](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.7)(2021-10-28)
- 更新README.md
- 优化分页查询参数过滤
- 优化后台学员添加和搜索
- 优化后台学员课程过期管理
- 增加编辑会员特权功能
- 增加清空用户头像功能
- 增加编辑器内站外图片自动保存到本地
- 增加CSRF放行白名单
- 完善订单|交易|退款序号
### [v1.4.6](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.6)(2021-10-18)
- 完善首页文章缓存的获取条件
- 完善热门专题的获取条件
- 优化课程章节列表逻辑
- 更新教学中心我的课程获取逻辑
- 修正后台点播和面授类型课时列表宽度未100%铺满问题
- 完善添加积分礼品的逻辑
- 修正编辑课程类型礼品时编辑器初始化js报错
- 修正非root用户后台添加用户时报错
- 修正微信等第三方登录code被重用问题
- 手机端访问web端地址自动跳转到手机端
- 增加锁定用户逻辑(会自动登出锁定用户)
- 增加虚假课程订阅数(用于营销效果)
### [v1.4.5](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.5)(2021-09-27)
- 修正点击内容分享到微信会额外出现公众号二维码的问题
- 修正后台首页提问和回答的数量统计
- 调整登录限制(邮箱|手机)为注册限制
- 调整订单发货为每一分钟执行一次
- 增强课时安全性,无权限时不返回播放地址或内容
- 抽离出文章关闭,仅我可见操作
- 增加退出群组和解除好友接口
- 增加删除文章和提问接口
- 增加首页推荐教师接口
- 增加微信公众号支付处理
- 增加取消订单功能
- 优化订单API结构
- 优化计划任务
### [v1.4.4](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.4)(2021-09-17)
- 后台增加邮件手机登录选择配置
- 增加移动端支付选项配置
- 首页增加秒杀,直播,提问,文章接口
- 增加秒杀列表列表接口
- 调整markdown解析安全级别
- 精简取消点赞以及取消收藏逻辑
- 修复浮点转整型精度丢失造成的支付回调失败
- 修复竖屏直播时造成的位置错乱
- 修复视频清晰度配置序列化问题
- 修复评论取消点赞数量不变问题
- 修复章节资源数量问题
- 修复删除课程后引发的用户课程列表错误问题
- 修正课程咨询列表查询条件
- 修正回答,兑换礼品说明重复转译的问题
- 资源下载查询主键由md5改为加密的ID
- 去除上传文件md5唯一索引
- 去除课程发布对章节的要求
- 去除点播回调中的处理数量限制
- 优化文章,课程,提问,群组全文搜索
- 优化直播列表数据结构
- 优化章节目录交互呈现
- 优化后台添加学员重复检查
- 优化订单发货逻辑
- 优化公众号订阅逻辑
### [v1.4.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.3)(2021-08-23)
- 优化邮件验证码
- 优化logo和favicon上传
- 优化api验证码中使用的ticket和rand
- 优化点播和直播地址获取
- 修复部分清晰度外链播放地址为空时切换卡死问题
- 增加QQ微信微博邮件电话等联系配置
- 用户控制台文章和提问列表增加删除过滤
- 去除layim在线客服
- 提高视频转码分辨率
### [v1.4.2](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.2)(2021-08-13)
- 后台增加转码码率配置选项
- 后台增加微聊配置开关
- 优化外链播放地址逻辑
- 访问课程文章等未发布资源404处理
- 优化课件上传不返回md5值的处理
- 优化用户中心内容数据展示
- 前台暂时屏蔽文章仅我可见和关闭评论功能
- 直播增加极速码率选项
- 调整码率标签对应
- 修复后台数据统计中心Hash缓存问题
- 修复未发布的课程仍然可购买问题
- 修复购买课程后学员人数未增加问题
- 增加同步课程数据统计脚本
### [v1.4.1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.1)(2021-08-08)
- AnswerInfo结构补充遗漏的comment_count字段
- AnswerList结构去除deleted字段
- ChapterList结构补充published字段
- 使用开源的播放器DPlayer替换腾讯TcPlayer
- 修正第三方登录开关的判断
- 修正课程方向过滤问题
- 修正教师主页会显示未发布课程问题
- 修正评论删除点击无效问题
- 优化课时列表可点击权限判断
- 优化来源检查域名带端口问题
- 优化微信公众号业务处理类
### [v1.4.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.0)(2021-08-03)
### 更新
- 单页增加自定义别名访问
- 回答增加评论功能
- 顶部导航微聊增加开关控制
- 更新默认的ICP备案链接指向
- 更正部分model定义中字段的类型申明
- 优化章节过多导致页面过长问题
- 优化评论前端部分相关逻辑和交互
- 优化403错误页面使用forward代替redirect
- 优化播放地址中带queryString的扩展名检查
- 修正解除第三登录绑定500错误问题
- 修正教师教授课程未过滤已删除课程问题
- 修正咨询编辑500错误问题
- 修正后台列表中restore_url未定义问题
### [v1.3.9](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.9)(2021-07-24)
### 更新
- 修正分类下无课程时会查询出所有课程问题
- 修正sitemap中的部分路径问题
- 修正课程套餐相关问题
- 优化问答部分相关逻辑
- 优化评论部分相关逻辑
- 优化浏览器Title显示
- 优化审核和举报相关逻辑
- 优化命令行脚本执行输出
- 优化API的分页返回结构
- 增加文章问答评论相关API
- 增加重新统计tag中相关计数计划任务
- 增加tag的使用范围文章问题课程计数
- 站点logo和favicon使用随机文件名
- 增加评价,咨询审核
- 去除编辑器中的酷瓜云课堂标识
- 清理数据迁移文件
### [v1.3.8](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.8)(2021-07-11)
### 更新
- 更正readme中github仓库信息
- 增加清除点播地址缓存命令
- 若干缓存键名重命名 后台站点名称修改为用户站点名称
- 标签名称比较忽略大小写
- 重新设计前后台登录界面
- 更正后台存储设置中图片样式的参数描述
- 记录逻辑删除后浏览重定向到404
- 修正图文类型的章节markdown解析问题
- 优化文章和提问不必要的标签数据提交
- 图文中图片增加点击放大预览功能
- 各数据结构中增加若干业务字段
- COS存储中去除多余的年月目录结构
- 清理优化css
- 修正直播地址问题
- 修正评论审核路由问题
- 修正取消收藏问题
### [v1.3.7](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.7)(2021-06-14)
### 更新
- 升级layui到v2.6.8
- 升级腾讯云播放器到v2.4.0
- 点播增加外链支持
- 源文件增加版权信息
- 优化模块继承基类
- 优化评论审核机制
- 优化课程和群组状态协同逻辑
- 优化用户索引重建逻辑
### [v1.3.6](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.6)(2021-06-04)
### 更新
- 清理没有用到的引用
- 优化界面和CSS样式
- 优化视频无法获取时长处理逻辑
- 优化视频无法转码处理逻辑
- 优化评论审核机制
- 优化评论相关数据更新逻辑
- 优化文章,问答,评论数据更新
- 优化内容标签的更新逻辑
- 优化首页H5的跳转判断
- 优化单页的浏览权限
- 优化Model中的事件方法
- 优化kg_parse_summary函数
- 用户主页加入问答列表
- 修复无法关闭问题讨论
- 修复编辑群组的路由
- 直播去除FLV方式拉流
- xs.question.ini加入忽略列表
- kg_user表增加comment_count字段
### [v1.3.5](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.5)(2021-05-20)
### 更新
- 更新演示数据
- 优化安装脚本install.sh
- 升级脚本upgrade.sh中加入更新导航缓存
- 撰写文章和提问markdown编辑器通栏显示
- 完善文章和问题的浏览权限
- 优化通用ajax表单提交
- 文章,提问,回答点赞作者有提醒和积分奖励
- 前台增加针对回答的预览访问地址
- 前台增加文章,问题,回答,评论加入举报功能
- 后台增加文章,问题,回答,评论的举报审核功能
- 后台首页增加审核队列统计
### [v1.3.4](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.4)(2021-05-13)
### 更新
- 增加问答功能
- 增加标签关注功能
- 优化标签功能
- 优化文章功能以及全文搜索
- 优化课程评价,咨询,文章等相关统计
- 优化前台界面
- 后台增加提问和回答审核功能
- 后台增加查看用户在线记录
- 修正后台编辑角色权限错误
### [v1.3.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.3)(2021-04-30)
### 更新
- 前台增加文章发布功能
- 增加文章,咨询,评价,评论相关事件站内提醒
- 增加文章,咨询,评价,评论事件埋点
- 后台首页增加若干统计项目
- 后台增加文章审核功能
- 重构积分历史记录
- 优化在线统计方式
- 优化前台界面
### [v1.3.2](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.2)(2021-04-20)
### 更新
- 前台增文章和章节评论功能
- 后台增加评论相关管理功能
- 优化课程,章节,文章等前台界面
- 优化分享链接的生成和跳转方式
- 优化课程章节文章相关js
- 优化后台数据展示
- 修正后台分类二级分类错位问题
- 修正文章命名空间问题
- 修正后台轮播没有保存问题
### [v1.3.1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.1)(2021-04-09)
### 更新
- 后台增加文章功能
- 后台增加标签功能
- 增加文章全文检索
- 整理命名空间别名
- 更新部分链接打开方式
- xm-select搜索忽略大小写
- 补充遗漏的面授模型章节相关迁移文件
- 修正上次字段整理导致的字段不存在问题
- 修正上次整理发布字段导致的添加单页和帮助错误
- 增加开启/关闭站点终端命令
### [v1.3.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.0)(2021-03-26)
### 更新
- 课程增加面授模型
- 重构前台群组成员管理
- 后台增加群组成员管理
- 重构订单存储商品详情数据结构
- 调整用户和群组列表等UI
### [v1.2.9](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.9)(2021-03-22)
### 更新
- 增加限时秒杀功能
- 更新phinx默认环境配置项
- 优化存储相关命名以及逻辑
- 重构轮播图表结构
- 重构套餐数表结构
- 重构会员表结构
- 重构xm-select插件选取内容方式
- 整理UI展现形式
### [v1.2.8](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.8)(2021-03-08)
### 更新
- 数据库迁移脚本整理
- 数据表软删除字段整理
- 微信公众号路由整理
- 退款增加手续费逻辑
- 课程增加不支持退款逻辑
- 会员价格和期限可通过后台配置
- 修复IM通知中字段重命名导致的问题
- 修复购买会员会员标识未改变的问题
- 会员中心订单列表样式调整
### [v1.2.7](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.7)(2021-02-26)
### 新增
- 钉钉机器人群消息通知
- demo分支重置演示帐号计划任务
- 添加学员自动加入相关课程群组
- 后台查看积分记录
### 更新
- 路由重命名 admin.group -> admin.im_group
- 路由重命名 home.group -> home.im_group
- 样式重命名 sidebar-teacher-card -> sidebar-user-card
- 去除顶部积分导航
- 用户中心部分样式调整
- 后台部分导航调整
- 不能删除课程教师问题
- 积分模块可通过后台控制是否启用
- 解除好友关系后好友数量未递减
### [v1.2.6](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.6)(2021-02-20)
### 新增
- 积分兑换机制
- 课程增加原价属性
- gitee提交webhooks自动化部署脚本
### 更新
- course和chapter数据迁移文件中遗漏了recourse_count字段
- app/Caches/TopicCourseList不存在
- Model文件属性定义默认值
- 隐藏非付费课程的咨询服务
- 教学中心教师直播推流按钮无反应
- 用户中心部分样式调整
- 播放器清晰度标签和实际的清晰度不对应
- CNZZ统计代码会显示出站长统计图标
- 自动安装后访问站点500错误
- 自动更新脚本可更新css和js版本号
### [v1.2.5](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.5)(2021-01-20)
### 新增
- 自动化安装脚本
- 自动化更新脚本
- 自动化备份脚本
### 更新
- 更新ip2region包
- 更新php-cron-scheduler包
- 替换aferrandini/phpqrcode为endroid/qr-code
- 替换joyqi/hyper-down为league/commonmark
- 移除lcobucci/jwt包
- 相关连接指向官网
### [v1.2.4](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.4)(2021-01-10)
#### 增加
- 后台增加上传logo和favicon图标
- 后台增加公众号自定义菜单配置
- 课程页增加咨询
### 优化
- oauth中state参数为安全base64加解码
- 判断是否api请求逻辑
- findById参数类型不对时抛出异常
- task表增加索引加快数据查找
- markdown内容解析改由后端完成
- 公众号应答处理逻辑
### [v1.2.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.3)(2021-01-03)
#### 增加
- 多人使用同一帐号防范机制
- 首页缓存刷新工具
- 课程综合评分
- 课程推荐
#### 修复
- phinx-migration-generator 无符号问题
- online表并发写入重复记录问题
- 计划任务生成sitemap.xml失败
### [v1.2.2](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.2)(2020-12-24)
#### 增加
- 登录账户微信提醒
- 购买成功微信提醒
- 退款成功微信提醒
- 开始直播微信提醒
- 咨询回复微信提醒
- 咨询回复短信提醒
#### 修复
- 创建章节,关联表数据没有生成
- 创建群组没有生成max_im_group_id缓存
- 课程分类列表没有过滤掉帮助分类的内容
- 创建角色字段routes MySQL text 类型报错
- 低品质视频无法播放
- 后台遗漏的权限
### [v1.2.1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.1)(2020-12-10)
- 增加QQ微信微博第三方登录
- 代码优化以及问题修复
### [v1.2.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.0)(2020-11-25)
- 增加客户端api
- 代码优化以及问题修复
### [v1.1.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.1.0)(2020-10-08)
- 增加运营统计功能
- 增加课程资料功能
- 增加changelog
- 忽略schema
- 账户安全页面调整
- 简化部分路由
- 修复相关课程列表undefined问题
### [v1.0.0-beta1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.0.0-beta1)(2020-09-26)
前台功能:
- 注册、登录、忘记密码
- 首页:轮播、新上课程、免费课程、会员课程
- 课程列表:多维度筛选,多维度排序
- 课程详情:章节,咨询,评价,相关课程,推荐课程,课程套餐
- 课时详情:点播,直播,图文
- 购买支付:课程,套餐,赞赏,会员
- 教师列表
- 群组列表
- 即时通讯
- 在线客服
- 全文检索:课程、群组、用户
- 个人主页:我的课程,我的收藏,我的好友,我的群组
- 会员中心:我的课程,我的收藏,我的咨询,我的评价,我的好友,我的群组,我的订单,我的退款,个人信息,账户安全
- 教学中心 :我的课程,我的直播,我的咨询
后台功能:
- 课程管理:课程列表,课程搜索,添加课程,编辑课程,删除课程,课程分类
- 套餐管理:套餐列表,添加套餐,编辑套餐,删除套餐
- 话题管理:话题列表,添加话题,编辑话题,删除话题
- 单页管理:单页列表,添加单页,编辑单页,删除单页
- 帮助管理:帮助列表,添加帮助,编辑帮助,删除帮助,帮助分类
- 学员管理:学员列表,搜索学员,添加学员,编辑学员,学习记录
- 咨询管理:咨询列表,搜索咨询,编辑咨询,删除咨询
- 评价管理:评价列表,搜索评价,编辑评价,删除评价
- 群组管理:群组列表,搜索群组,编辑群组,删除群组
- 轮播管理:轮播列表,编辑轮播,删除轮播
- 导航管理:导航列表,编辑导航,删除导航
- 订单管理:订单列表,搜索订单,订单详情
- 交易管理:交易列表,搜索交易,交易详情
- 退款管理:退款列表,搜索退款,退款详情,退款审核
- 用户管理:用户列表,编辑用户,添加用户
- 角色管理:角色列表,编辑角色,删除角色
- 操作记录:记录列表,搜索记录,记录详情
- 系统配置:网站,密钥,存储,点播,直播,短信,邮件,验证码,支付,会员,微聊

339
LICENSE
View File

@ -1,339 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

112
README.md
View File

@ -1,66 +1,82 @@
## 酷瓜云课堂
[![酷瓜云课堂-开源知识付费解决方案](https://portal-1255691183.file.myqcloud.com/img/content/63ec392618bd5.png)](https://www.koogua.com)
#### 项目介绍
### 系统介绍
酷瓜云课堂,依托腾讯云基础服务架构,采用 C 扩展框架 Phalcon 开发,致力网络教育软件。
酷瓜云课堂依托腾讯云基础服务架构采用C扩展框架Phalcon开发GPL-2.0开源协议,致力开源网课系统,开源网校系统,开源知识付费系统,开源在线教育系统。
#### 系统功能
![star](https://www.koogua.com/gitee/badge?user=koogua&project=course-tencent-cloud&type=star)
![fork](https://www.koogua.com/gitee/badge?user=koogua&project=course-tencent-cloud&type=fork)
![license](https://www.koogua.com/gitee/badge?user=koogua&project=course-tencent-cloud&type=license)
![release](https://www.koogua.com/gitee/badge?user=koogua&project=course-tencent-cloud&type=release)
### 系统功能
实现了点播、直播、专栏、问答、会员、积分等。
友情提示:
- 演示系统配置低2核2G1M 跑多个容器)切莫压测
- 课程数据来源于网络(无实质内容)切莫购买
- 管理后台已禁止数据提交,私密配置已过滤
桌面端演示:
实现了点播、直播、专栏、会员、微聊等,是一个完整的产品,具体功能我也不想写一大堆,自己体验吧!
- [前台演示](https://ctc.koogua.com)
- [后台演示](https://ctc.koogua.com/admin)
演示账号100015@163.com / 123456 (前后台通用)
帐号100015@163.com / 123456 (前后台通用)
H5手机端演示:
友情提示:
![H5二维码](https://portal-1255691183.file.myqcloud.com/img/content/616fc238895b7.png)
- 系统配置低1核 1G 1M 跑多个容器),切莫压测
- 课程数据来源于网络(无实质内容),切莫购买
- 管理后台已禁止数据提交,私密配置已过滤
演示账号13507083515 / 123456
#### 项目组件
支付流程演示:
- [MySQL提升课程全面讲解MySQL架构设计0.01元)](https://ctc.koogua.com/order/confirm?item_id=1390&item_type=1)
- [Nginx入门到实践Nginx中间件0.01元)](https://ctc.koogua.com/order/confirm?item_id=1286&item_type=1)
- [数据库与中间件的基础必修课0.02元)](https://ctc.koogua.com/order/confirm?item_id=80&item_type=2)
Tips: 测试支付请用手机号注册一个新账户,以便接收订单通知,以及避免课程无法购买
微信推送演示:
Tips: 请用手机注册一个新账号,用户中心 -> 关注订阅,扫码关注公众号。之后的登录、购买、退款、直播、咨询等会有消息推送。
### 项目组件
- 后台框架:[phalcon 3.4](https://phalcon.io)
- 前端框架:[layui 2.9](https://layui.dev)
- 全文检索:[xunsearch 1.4](http://www.xunsearch.com)
- 后台框架:[phalcon 3.4.5](https://phalcon.io)
- 前端框架:[layui 2.5.6](https://layui.com) [layim 3.9.5](https://www.layui.com/layim)(已授权)
- 全文检索:[xunsearch 1.4.9](http://www.xunsearch.com)
- 即时通讯:[workerman 3.5.22](https://workerman.net)
- 基础依赖:[php7.3](https://php.net) [mysql5.7](https://mysql.com) [redis5.0](https://redis.io)
### 项目文档
#### 使用协议
- [运行环境搭建](https://www.koogua.com/page/wiki)
- [系统服务配置](https://www.koogua.com/page/wiki)
- [客户终端配置](https://www.koogua.com/page/wiki)
虽然尝试了解过开源协议,但是理解的模棱两可,干脆用自己的协议吧。
### 意见反馈
1. 本系统属于强业务类型,非通用类库框架,不适合再次衍生发布。
2. 在保留我们版权标识的前提下,用户可以修改以满足自己的需求,可以用于商业用途。
3. 有限社区支持,用户对自己的行为负责。
#### 安装指南
- [运行环境搭建](https://gitee.com/koogua/course-tencent-cloud-docker)
- [系统服务配置](https://gitee.com/koogua/course-tencent-cloud/wikis)
#### 开发计划
- 桌面端:进行中
- 移动端:待启动
- 小程序:待启动
#### 意见反馈
- [在线反馈](https://gitee.com/koogua/course-tencent-cloud/issues)(推荐)
- QQ邮箱: 76632555@qq.com
- QQ群组: 787363898
#### 加入我们
这是一个创业项目,个人能力和精力有限,要兼顾产品规划以及开发,还要处理很多琐碎事情。目前在南山科技园某个众创空间,希望有 **深圳前端同学** 加入我们。
联系邮箱76632555@qq.com
#### 通过这个项目能学到什么?
- 项目规划phalcon缓存JWT即时通讯全文检索
- dockersupervisordevops
- gitlinuxphpmysqlredisnginx
#### 有阿里云版吗?
阿里云版规划中,之前阿里云服务过期未续费,所以腾讯云版本先出。
#### 代码有加密吗?
所有代码都公开授权代码除外例如layim没有所谓的商业版和付费插件。
#### 有商业服务吗?
生存才能发展,我们目前提供的服务包括:
- 系统安装
- 系统定制
- 企业授权
- [码云平台](https://gitee.com/koogua/course-tencent-cloud/issues)
- [官方社区](https://www.koogua.com/community)
- QQ交流群: 787363898

View File

@ -1,5 +0,0 @@
# Security Policy
## Reporting a Vulnerability
Please report security issues to `xiaochong0302@gmail.com`

View File

@ -1,61 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Question as QuestionRepo;
class AnswerList extends Builder
{
public function handleQuestions(array $answers)
{
$questions = $this->getQuestions($answers);
foreach ($answers as $key => $answer) {
$answers[$key]['question'] = $questions[$answer['question_id']] ?? null;
}
return $answers;
}
public function handleUsers(array $answers)
{
$users = $this->getUsers($answers);
foreach ($answers as $key => $answer) {
$answers[$key]['owner'] = $users[$answer['owner_id']] ?? null;
}
return $answers;
}
public function getQuestions(array $answers)
{
$ids = kg_array_column($answers, 'question_id');
$questionRepo = new QuestionRepo();
$questions = $questionRepo->findByIds($ids, ['id', 'title']);
$result = [];
foreach ($questions->toArray() as $question) {
$result[$question['id']] = $question;
}
return $result;
}
public function getUsers(array $answers)
{
$ids = kg_array_column($answers, 'owner_id');
return $this->getShallowUserByIds($ids);
}
}

View File

@ -1,75 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Article as ArticleRepo;
use Phalcon\Text;
class ArticleFavoriteList extends Builder
{
public function handleArticles(array $relations)
{
$articles = $this->getArticles($relations);
foreach ($relations as $key => $value) {
$relations[$key]['article'] = $articles[$value['article_id']] ?? null;
}
return $relations;
}
public function handleUsers(array $relations)
{
$users = $this->getUsers($relations);
foreach ($relations as $key => $value) {
$relations[$key]['user'] = $users[$value['user_id']] ?? null;
}
return $relations;
}
public function getArticles(array $relations)
{
$ids = kg_array_column($relations, 'article_id');
$articleRepo = new ArticleRepo();
$columns = [
'id', 'title', 'cover',
'view_count', 'like_count',
'comment_count', 'favorite_count',
];
$articles = $articleRepo->findByIds($ids, $columns);
$baseUrl = kg_cos_url();
$result = [];
foreach ($articles->toArray() as $article) {
if (!empty($article['cover']) && !Text::startsWith($article['cover'], 'http')) {
$article['cover'] = $baseUrl . $article['cover'];
}
$result[$article['id']] = $article;
}
return $result;
}
public function getUsers(array $relations)
{
$ids = kg_array_column($relations, 'user_id');
return $this->getShallowUserByIds($ids);
}
}

View File

@ -1,74 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Caches\CategoryAllList as CategoryAllListCache;
use App\Models\Category as CategoryModel;
class ArticleList extends Builder
{
public function handleArticles(array $articles)
{
foreach ($articles as $key => $article) {
$articles[$key]['tags'] = json_decode($article['tags'], true);
}
return $articles;
}
public function handleCategories(array $articles)
{
$categories = $this->getCategories();
foreach ($articles as $key => $article) {
$articles[$key]['category'] = $categories[$article['category_id']] ?? null;
}
return $articles;
}
public function handleUsers(array $articles)
{
$users = $this->getUsers($articles);
foreach ($articles as $key => $article) {
$articles[$key]['owner'] = $users[$article['owner_id']] ?? null;
}
return $articles;
}
public function getCategories()
{
$cache = new CategoryAllListCache();
$items = $cache->get(CategoryModel::TYPE_ARTICLE);
if (empty($items)) return [];
$result = [];
foreach ($items as $item) {
$result[$item['id']] = [
'id' => $item['id'],
'name' => $item['name'],
];
}
return $result;
}
public function getUsers($articles)
{
$ids = kg_array_column($articles, 'owner_id');
return $this->getShallowUserByIds($ids);
}
}

View File

@ -1,16 +1,10 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\User as UserRepo;
use Phalcon\Di\Injectable;
use Phalcon\Mvc\User\Component;
class Builder extends Injectable
class Builder extends Component
{
public function objects(array $items)
@ -18,22 +12,4 @@ class Builder extends Injectable
return kg_array_object($items);
}
protected function getShallowUserByIds(array $ids)
{
$userRepo = new UserRepo();
$users = $userRepo->findShallowUserByIds($ids);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,23 +1,17 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Models\Category as CategoryModel;
use App\Repos\Category as CategoryRepo;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class CategoryTreeList extends Builder
{
public function handle($type)
{
$categoryRepo = new CategoryRepo();
$topCategories = $categoryRepo->findTopCategories($type);
$topCategories = $this->findChildCategories($type, 0);
if ($topCategories->count() == 0) {
return [];
@ -29,8 +23,6 @@ class CategoryTreeList extends Builder
$list[] = [
'id' => $category->id,
'name' => $category->name,
'alias' => $category->alias,
'icon' => $category->icon,
'children' => $this->handleChildren($category),
];
}
@ -40,9 +32,7 @@ class CategoryTreeList extends Builder
protected function handleChildren(CategoryModel $category)
{
$categoryRepo = new CategoryRepo();
$subCategories = $categoryRepo->findChildCategories($category->id);
$subCategories = $this->findChildCategories($category->type, $category->id);
if ($subCategories->count() == 0) {
return [];
@ -54,12 +44,34 @@ class CategoryTreeList extends Builder
$list[] = [
'id' => $category->id,
'name' => $category->name,
'alias' => $category->alias,
'icon' => $category->icon,
];
}
return $list;
}
/**
* @param string $type
* @param int $parentId
* @return ResultsetInterface|Resultset|CategoryModel[]
*/
protected function findChildCategories($type = 'course', $parentId = 0)
{
$query = CategoryModel::query();
$query->where('published = 1');
if ($type) {
$query->andWhere('type = :type:', ['type' => $type]);
}
if ($parentId) {
$query->andWhere('parent_id = :parent_id:', ['parent_id' => $parentId]);
}
$query->orderBy('priority ASC');
return $query->execute();
}
}

View File

@ -1,34 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
class CommentList extends Builder
{
public function handleUsers(array $comments)
{
$users = $this->getUsers($comments);
foreach ($comments as $key => $comment) {
$comments[$key]['owner'] = $users[$comment['owner_id']] ?? null;
$comments[$key]['to_user'] = $users[$comment['to_user_id']] ?? null;
}
return $comments;
}
public function getUsers(array $comments)
{
$ownerIds = kg_array_column($comments, 'owner_id');
$toUserIds = kg_array_column($comments, 'to_user_id');
$ids = array_merge($ownerIds, $toUserIds);
return $this->getShallowUserByIds($ids);
}
}

View File

@ -1,13 +1,10 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Chapter as ChapterRepo;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class ConsultList extends Builder
{
@ -17,7 +14,7 @@ class ConsultList extends Builder
$courses = $this->getCourses($consults);
foreach ($consults as $key => $consult) {
$consults[$key]['course'] = $courses[$consult['course_id']] ?? null;
$consults[$key]['course'] = $courses[$consult['course_id']] ?? new \stdClass();
}
return $consults;
@ -28,8 +25,7 @@ class ConsultList extends Builder
$users = $this->getUsers($consults);
foreach ($consults as $key => $consult) {
$consults[$key]['owner'] = $users[$consult['owner_id']] ?? null;
$consults[$key]['replier'] = $users[$consult['replier_id']] ?? null;
$consults[$key]['owner'] = $users[$consult['owner_id']] ?? new \stdClass();
}
return $consults;
@ -52,13 +48,41 @@ class ConsultList extends Builder
return $result;
}
public function getChapters(array $consults)
{
$ids = kg_array_column($consults, 'chapter_id');
$chapterRepo = new ChapterRepo();
$chapters = $chapterRepo->findByIds($ids, ['id', 'title']);
$result = [];
foreach ($chapters->toArray() as $chapter) {
$result[$chapter['id']] = $chapter;
}
return $result;
}
public function getUsers(array $consults)
{
$ownerIds = kg_array_column($consults, 'owner_id');
$replierIds = kg_array_column($consults, 'replier_id');
$ids = array_merge($ownerIds, $replierIds);
$ids = kg_array_column($consults, 'owner_id');
return $this->getShallowUserByIds($ids);
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
@ -34,7 +29,6 @@ class CourseChapterList extends Builder
'id' => $chapter->id,
'title' => $chapter->title,
'model' => $chapter->model,
'published' => $chapter->published,
'children' => $this->handleChildren($chapter),
];
}
@ -71,7 +65,6 @@ class CourseChapterList extends Builder
'id' => $lesson->id,
'title' => $lesson->title,
'model' => $lesson->model,
'published' => $lesson->published,
'free' => $lesson->free,
'attrs' => $attrs,
];

View File

@ -1,13 +1,9 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class CourseFavoriteList extends Builder
{
@ -17,7 +13,7 @@ class CourseFavoriteList extends Builder
$courses = $this->getCourses($relations);
foreach ($relations as $key => $value) {
$relations[$key]['course'] = $courses[$value['course_id']] ?? null;
$relations[$key]['course'] = $courses[$value['course_id']] ?? new \stdClass();
}
return $relations;
@ -28,7 +24,7 @@ class CourseFavoriteList extends Builder
$users = $this->getUsers($relations);
foreach ($relations as $key => $value) {
$relations[$key]['user'] = $users[$value['user_id']] ?? null;
$relations[$key]['user'] = $users[$value['user_id']] ?? new \stdClass();
}
return $relations;
@ -69,7 +65,20 @@ class CourseFavoriteList extends Builder
{
$ids = kg_array_column($relations, 'user_id');
return $this->getShallowUserByIds($ids);
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,14 +1,10 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Caches\CategoryAllList as CategoryAllListCache;
use App\Caches\CategoryList as CategoryListCache;
use App\Models\Category as CategoryModel;
use App\Repos\User as UserRepo;
class CourseList extends Builder
{
@ -18,7 +14,7 @@ class CourseList extends Builder
$categories = $this->getCategories();
foreach ($courses as $key => $course) {
$courses[$key]['category'] = $categories[$course['category_id']] ?? null;
$courses[$key]['category'] = $categories[$course['category_id']] ?? new \stdClass();
}
return $courses;
@ -29,7 +25,7 @@ class CourseList extends Builder
$teachers = $this->getTeachers($courses);
foreach ($courses as $key => $course) {
$courses[$key]['teacher'] = $teachers[$course['teacher_id']] ?? null;
$courses[$key]['teacher'] = $teachers[$course['teacher_id']] ?? new \stdClass();
}
return $courses;
@ -37,11 +33,13 @@ class CourseList extends Builder
public function getCategories()
{
$cache = new CategoryAllListCache();
$cache = new CategoryListCache();
$items = $cache->get(CategoryModel::TYPE_COURSE);
if (empty($items)) return [];
if (empty($items)) {
return [];
}
$result = [];
@ -59,7 +57,20 @@ class CourseList extends Builder
{
$ids = kg_array_column($courses, 'teacher_id');
return $this->getShallowUserByIds($ids);
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
@ -18,7 +13,7 @@ class CourseTopicList extends Builder
$courses = $this->getCourses($relations);
foreach ($relations as $key => $value) {
$relations[$key]['course'] = $courses[$value['course_id']] ?? null;
$relations[$key]['course'] = $courses[$value['course_id']] ?? new \stdClass();
}
return $relations;
@ -29,7 +24,7 @@ class CourseTopicList extends Builder
$topics = $this->getTopics($relations);
foreach ($relations as $key => $value) {
$relations[$key]['topic'] = $topics[$value['topic_id']] ?? null;
$relations[$key]['topic'] = $topics[$value['topic_id']] ?? new \stdClass();
}
return $relations;
@ -45,7 +40,7 @@ class CourseTopicList extends Builder
'id', 'title', 'cover',
'market_price', 'vip_price',
'rating', 'model', 'level', 'attrs',
'user_count', 'lesson_count', 'review_count', 'favorite_count',
'user_count', 'lesson_count',
];
$courses = $courseRepo->findByIds($ids, $columns);

View File

@ -1,13 +1,9 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class CourseUserList extends Builder
{
@ -17,7 +13,7 @@ class CourseUserList extends Builder
$courses = $this->getCourses($relations);
foreach ($relations as $key => $value) {
$relations[$key]['course'] = $courses[$value['course_id']] ?? null;
$relations[$key]['course'] = $courses[$value['course_id']] ?? new \stdClass();
}
return $relations;
@ -28,7 +24,7 @@ class CourseUserList extends Builder
$users = $this->getUsers($relations);
foreach ($relations as $key => $value) {
$relations[$key]['user'] = $users[$value['user_id']] ?? null;
$relations[$key]['user'] = $users[$value['user_id']] ?? new \stdClass();
}
return $relations;
@ -43,8 +39,8 @@ class CourseUserList extends Builder
$columns = [
'id', 'title', 'cover',
'market_price', 'vip_price',
'rating', 'model', 'level', 'attrs', 'published', 'deleted',
'user_count', 'fake_user_count', 'lesson_count', 'review_count', 'favorite_count',
'rating', 'model', 'level', 'attrs',
'user_count', 'lesson_count', 'review_count', 'favorite_count',
];
$courses = $courseRepo->findByIds($ids, $columns);
@ -54,15 +50,8 @@ class CourseUserList extends Builder
$result = [];
foreach ($courses->toArray() as $course) {
if ($course['fake_user_count'] > $course['user_count']) {
$course['user_count'] = $course['fake_user_count'];
}
$course['cover'] = $baseUrl . $course['cover'];
$course['attrs'] = json_decode($course['attrs'], true);
$result[$course['id']] = $course;
}
@ -73,7 +62,20 @@ class CourseUserList extends Builder
{
$ids = kg_array_column($relations, 'user_id');
return $this->getShallowUserByIds($ids);
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace App\Builders;
use App\Repos\Chapter as ChapterRepo;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class DanmuList extends Builder
{
public function handleCourses(array $danmus)
{
$courses = $this->getCourses($danmus);
foreach ($danmus as $key => $danmu) {
$danmus[$key]['course'] = $courses[$danmu['course_id']] ?? new \stdClass();
}
return $danmus;
}
public function handleChapters(array $danmus)
{
$chapters = $this->getChapters($danmus);
foreach ($danmus as $key => $danmu) {
$danmus[$key]['chapter'] = $chapters[$danmu['chapter_id']] ?? new \stdClass();
}
return $danmus;
}
public function handleUsers(array $danmus)
{
$users = $this->getUsers($danmus);
foreach ($danmus as $key => $danmu) {
$danmus[$key]['owner'] = $users[$danmu['owner_id']] ?? new \stdClass();
}
return $danmus;
}
public function getCourses(array $danmus)
{
$ids = kg_array_column($danmus, 'course_id');
$courseRepo = new CourseRepo();
$courses = $courseRepo->findByIds($ids, ['id', 'title']);
$result = [];
foreach ($courses->toArray() as $course) {
$result[$course['id']] = $course;
}
return $result;
}
public function getChapters(array $danmus)
{
$ids = kg_array_column($danmus, 'chapter_id');
$chapterRepo = new ChapterRepo();
$chapters = $chapterRepo->findByIds($ids, ['id', 'title']);
$result = [];
foreach ($chapters->toArray() as $chapter) {
$result[$chapter['id']] = $chapter;
}
return $result;
}
public function getUsers(array $danmus)
{
$ids = kg_array_column($danmus, 'owner_id');
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,13 +1,8 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Caches\CategoryAllList as CategoryAllListCache;
use App\Caches\CategoryList as CategoryListCache;
use App\Models\Category as CategoryModel;
class HelpList extends Builder
@ -18,7 +13,7 @@ class HelpList extends Builder
$categories = $this->getCategories();
foreach ($helps as $key => $help) {
$helps[$key]['category'] = $categories[$help['category_id']] ?? null;
$helps[$key]['category'] = $categories[$help['category_id']] ?? new \stdClass();
}
return $helps;
@ -26,7 +21,7 @@ class HelpList extends Builder
public function getCategories()
{
$cache = new CategoryAllListCache();
$cache = new CategoryListCache();
$items = $cache->get(CategoryModel::TYPE_HELP);

View File

@ -0,0 +1,46 @@
<?php
namespace App\Builders;
use App\Repos\User as UserRepo;
class ImFriendUserList extends Builder
{
public function handleFriends(array $relations)
{
$users = $this->getFriends($relations);
foreach ($relations as $key => $value) {
$relations[$key]['friend'] = $users[$value['friend_id']] ?? new \stdClass();
}
return $relations;
}
public function getFriends(array $relations)
{
$ids = kg_array_column($relations, 'friend_id');
$userRepo = new UserRepo();
$columns = [
'id', 'name', 'avatar', 'title', 'about', 'vip',
'gender', 'area', 'active_time',
];
$users = $userRepo->findByIds($ids, $columns);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -0,0 +1,70 @@
<?php
namespace App\Builders;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class ImGroupList extends Builder
{
public function handleCourses(array $groups)
{
$courses = $this->getCourses($groups);
foreach ($groups as $key => $group) {
$groups[$key]['course'] = $courses[$group['course_id']] ?? new \stdClass();
}
return $groups;
}
public function handleUsers(array $groups)
{
$users = $this->getUsers($groups);
foreach ($groups as $key => $group) {
$groups[$key]['owner'] = $users[$group['owner_id']] ?? new \stdClass();
}
return $groups;
}
public function getCourses(array $groups)
{
$ids = kg_array_column($groups, 'course_id');
$courseRepo = new CourseRepo();
$courses = $courseRepo->findByIds($ids, ['id', 'title']);
$result = [];
foreach ($courses->toArray() as $course) {
$result[$course['id']] = $course;
}
return $result;
}
public function getUsers(array $groups)
{
$ids = kg_array_column($groups, 'owner_id');
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -0,0 +1,100 @@
<?php
namespace App\Builders;
use App\Repos\ImGroup as ImGroupRepo;
use App\Repos\User as UserRepo;
class ImGroupUserList extends Builder
{
public function handleGroups(array $relations)
{
$groups = $this->getGroups($relations);
foreach ($relations as $key => $value) {
$relations[$key]['group'] = $groups[$value['group_id']] ?? new \stdClass();
}
return $relations;
}
public function handleUsers(array $relations)
{
$users = $this->getUsers($relations);
foreach ($relations as $key => $value) {
$relations[$key]['user'] = $users[$value['user_id']] ?? new \stdClass();
}
return $relations;
}
public function getUsers(array $relations)
{
$ids = kg_array_column($relations, 'user_id');
$userRepo = new UserRepo();
$columns = ['id', 'name', 'avatar', 'title', 'about', 'vip'];
$users = $userRepo->findByIds($ids, $columns);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
public function getGroups(array $relations)
{
$ids = kg_array_column($relations, 'group_id');
$groupRepo = new ImGroupRepo();
$columns = ['id', 'type', 'name', 'avatar', 'about', 'owner_id', 'user_count', 'msg_count'];
$groups = $groupRepo->findByIds($ids, $columns);
$users = $this->getGroupOwners($groups->toArray());
$baseUrl = kg_cos_url();
$result = [];
foreach ($groups->toArray() as $group) {
$group['avatar'] = $baseUrl . $group['avatar'];
$group['owner'] = $users[$group['owner_id']] ?? new \stdClass();
unset($group['owner_id']);
$result[$group['id']] = $group;
}
return $result;
}
protected function getGroupOwners(array $groups)
{
$ids = kg_array_column($groups, 'owner_id');
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name']);
$result = [];
if ($users->count() > 0) {
foreach ($users->toArray() as $user) {
$result[$user['id']] = $user;
}
}
return $result;
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Builders;
use App\Repos\User as UserRepo;
class ImMessageList extends Builder
{
public function handleSenders(array $messages)
{
$users = $this->getSenders($messages);
foreach ($messages as $key => $message) {
$messages[$key]['sender'] = $users[$message['sender_id']] ?? new \stdClass();
}
return $messages;
}
public function getSenders(array $messages)
{
$ids = kg_array_column($messages, 'sender_id');
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,14 +1,10 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Chapter as ChapterRepo;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class LearningList extends Builder
{
@ -18,7 +14,7 @@ class LearningList extends Builder
$courses = $this->getCourses($relations);
foreach ($relations as $key => $value) {
$relations[$key]['course'] = $courses[$value['course_id']] ?? null;
$relations[$key]['course'] = $courses[$value['course_id']] ?? new \stdClass();
}
return $relations;
@ -29,7 +25,7 @@ class LearningList extends Builder
$chapters = $this->getChapters($relations);
foreach ($relations as $key => $value) {
$relations[$key]['chapter'] = $chapters[$value['chapter_id']] ?? null;
$relations[$key]['chapter'] = $chapters[$value['chapter_id']] ?? new \stdClass();
}
return $relations;
@ -40,7 +36,7 @@ class LearningList extends Builder
$users = $this->getUsers($relations);
foreach ($relations as $key => $value) {
$relations[$key]['user'] = $users[$value['user_id']] ?? null;
$relations[$key]['user'] = $users[$value['user_id']] ?? new \stdClass();
}
return $relations;
@ -84,7 +80,17 @@ class LearningList extends Builder
{
$ids = kg_array_column($relations, 'user_id');
return $this->getShallowUserByIds($ids);
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name']);
$result = [];
foreach ($users->toArray() as $user) {
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,95 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Chapter as ChapterRepo;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class LiveList extends Builder
{
public function handleCourses(array $lives)
{
$courses = $this->getCourses($lives);
foreach ($lives as $key => $live) {
$lives[$key]['course'] = $courses[$live['course_id']] ?? null;
}
return $lives;
}
public function handleChapters(array $lives)
{
$chapters = $this->getChapters($lives);
foreach ($lives as $key => $live) {
$lives[$key]['chapter'] = $chapters[$live['chapter_id']] ?? null;
}
return $lives;
}
public function getCourses(array $lives)
{
$courseIds = kg_array_column($lives, 'course_id');
$courseRepo = new CourseRepo();
$courses = $courseRepo->findByIds($courseIds, ['id', 'title', 'cover', 'teacher_id']);
$teacherIds = kg_array_column($courses->toArray(), 'teacher_id');
$userRepo = new UserRepo();
$users = $userRepo->findShallowUserByIds($teacherIds);
$baseUrl = kg_cos_url();
$teachers = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$teachers[$user['id']] = $user;
}
$result = [];
foreach ($courses->toArray() as $course) {
$course['cover'] = $baseUrl . $course['cover'];
$course['teacher'] = $teachers[$course['teacher_id']] ?? null;
$result[$course['id']] = [
'id' => $course['id'],
'title' => $course['title'],
'cover' => $course['cover'],
'teacher' => $course['teacher'],
];
}
return $result;
}
public function getChapters(array $lives)
{
$ids = kg_array_column($lives, 'chapter_id');
$chapterRepo = new ChapterRepo();
$chapters = $chapterRepo->findByIds($ids, ['id', 'title']);
$result = [];
foreach ($chapters->toArray() as $chapter) {
$result[$chapter['id']] = $chapter;
}
return $result;
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
@ -68,7 +63,6 @@ class NavTreeList extends Builder
return NavModel::query()
->where('parent_id = :parent_id:', ['parent_id' => $navId])
->andWhere('published = 1')
->andWhere('deleted = 0')
->orderBy('priority ASC')
->execute();
}
@ -81,9 +75,7 @@ class NavTreeList extends Builder
{
return NavModel::query()
->where('position = :position:', ['position' => $position])
->andWhere('level = 1')
->andWhere('published = 1')
->andWhere('deleted = 0')
->andWhere('level = 1 AND published = 1')
->orderBy('priority ASC')
->execute();
}

View File

@ -1,34 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
class NotificationList extends Builder
{
public function handleUsers(array $notifications)
{
$users = $this->getUsers($notifications);
foreach ($notifications as $key => $notification) {
$notifications[$key]['sender'] = $users[$notification['sender_id']] ?? null;
$notifications[$key]['receiver'] = $users[$notification['receiver_id']] ?? null;
}
return $notifications;
}
public function getUsers(array $notifications)
{
$senderIds = kg_array_column($notifications, 'sender_id');
$receiverIds = kg_array_column($notifications, 'receiver_id');
$ids = array_merge($senderIds, $receiverIds);
return $this->getShallowUserByIds($ids);
}
}

View File

@ -1,14 +1,9 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Models\Course as CourseModel;
use App\Models\Order as OrderModel;
use App\Repos\User as UserRepo;
class OrderList extends Builder
{
@ -29,7 +24,7 @@ class OrderList extends Builder
$users = $this->getUsers($orders);
foreach ($orders as $key => $order) {
$orders[$key]['owner'] = $users[$order['owner_id']] ?? null;
$orders[$key]['owner'] = $users[$order['owner_id']] ?? new \stdClass();
}
return $orders;
@ -42,7 +37,7 @@ class OrderList extends Builder
public function handleItems(array $orders)
{
foreach ($orders as $key => $order) {
$itemInfo = $this->handleItemInfo($order);
$itemInfo = $this->handleItem($order);
$orders[$key]['item_info'] = $itemInfo;
}
@ -53,7 +48,7 @@ class OrderList extends Builder
* @param array $order
* @return array|mixed
*/
public function handleItemInfo(array $order)
public function handleItem(array $order)
{
$itemInfo = [];
@ -72,57 +67,6 @@ class OrderList extends Builder
return $itemInfo;
}
/**
* @param array $order
* @return array|mixed
*/
public function handleMeInfo(array $order)
{
$me = [
'allow_pay' => 0,
'allow_cancel' => 0,
'allow_refund' => 0,
];
$payStatusOk = $order['status'] == OrderModel::STATUS_PENDING ? 1 : 0;
$cancelStatusOk = $order['status'] == OrderModel::STATUS_PENDING ? 1 : 0;
$refundStatusOk = $order['status'] == OrderModel::STATUS_FINISHED ? 1 : 0;
if ($order['item_type'] == OrderModel::ITEM_COURSE) {
$course = $order['item_info']['course'];
$courseModelOk = $course['model'] != CourseModel::MODEL_OFFLINE;
$refundTimeOk = $course['refund_expiry_time'] > time();
$me['allow_refund'] = $courseModelOk && $refundStatusOk && $refundTimeOk ? 1 : 0;
} elseif ($order['item_type'] == OrderModel::ITEM_PACKAGE) {
$courses = $order['item_info']['courses'];
$refundTimeOk = false;
foreach ($courses as $course) {
if ($course['refund_expiry_time'] > time()) {
$refundTimeOk = true;
}
}
$me['allow_refund'] = $refundStatusOk && $refundTimeOk ? 1 : 0;
}
if ($payStatusOk == 1) {
$me['allow_pay'] = 1;
}
if ($cancelStatusOk == 1) {
$me['allow_cancel'] = 1;
}
return $me;
}
/**
* @param string $itemInfo
* @return mixed
@ -153,6 +97,20 @@ class OrderList extends Builder
return $itemInfo;
}
/**
* @param string $itemInfo
* @return mixed
*/
protected function handleRewardInfo($itemInfo)
{
if (!empty($itemInfo) && is_string($itemInfo)) {
$itemInfo = json_decode($itemInfo, true);
$itemInfo['course']['cover'] = $this->imgBaseUrl . $itemInfo['course']['cover'];
}
return $itemInfo;
}
/**
* @param string $itemInfo
* @return mixed
@ -174,7 +132,17 @@ class OrderList extends Builder
{
$ids = kg_array_column($orders, 'owner_id');
return $this->getShallowUserByIds($ids);
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name']);
$result = [];
foreach ($users->toArray() as $user) {
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,75 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Question as QuestionRepo;
use Phalcon\Text;
class QuestionFavoriteList extends Builder
{
public function handleQuestions(array $relations)
{
$questions = $this->getQuestions($relations);
foreach ($relations as $key => $value) {
$relations[$key]['question'] = $questions[$value['question_id']] ?? null;
}
return $relations;
}
public function handleUsers(array $relations)
{
$users = $this->getUsers($relations);
foreach ($relations as $key => $value) {
$relations[$key]['user'] = $users[$value['user_id']] ?? null;
}
return $relations;
}
public function getQuestions(array $relations)
{
$ids = kg_array_column($relations, 'question_id');
$questionRepo = new QuestionRepo();
$columns = [
'id', 'title', 'cover',
'view_count', 'like_count',
'answer_count', 'favorite_count',
];
$questions = $questionRepo->findByIds($ids, $columns);
$baseUrl = kg_cos_url();
$result = [];
foreach ($questions->toArray() as $question) {
if (!empty($question['cover']) && !Text::startsWith($question['cover'], 'http')) {
$question['cover'] = $baseUrl . $question['cover'];
}
$result[$question['id']] = $question;
}
return $result;
}
public function getUsers(array $relations)
{
$ids = kg_array_column($relations, 'user_id');
return $this->getShallowUserByIds($ids);
}
}

View File

@ -1,77 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Caches\CategoryAllList as CategoryAllListCache;
use App\Models\Category as CategoryModel;
class QuestionList extends Builder
{
public function handleQuestions(array $questions)
{
foreach ($questions as $key => $question) {
$questions[$key]['tags'] = json_decode($question['tags'], true);
}
return $questions;
}
public function handleCategories(array $questions)
{
$categories = $this->getCategories();
foreach ($questions as $key => $question) {
$questions[$key]['category'] = $categories[$question['category_id']] ?? null;
}
return $questions;
}
public function handleUsers(array $questions)
{
$users = $this->getUsers($questions);
foreach ($questions as $key => $question) {
$questions[$key]['owner'] = $users[$question['owner_id']] ?? null;
$questions[$key]['last_replier'] = $users[$question['last_replier_id']] ?? null;
}
return $questions;
}
public function getCategories()
{
$cache = new CategoryAllListCache();
$items = $cache->get(CategoryModel::TYPE_QUESTION);
if (empty($items)) return [];
$result = [];
foreach ($items as $item) {
$result[$item['id']] = [
'id' => $item['id'],
'name' => $item['name'],
];
}
return $result;
}
public function getUsers($questions)
{
$ownerIds = kg_array_column($questions, 'owner_id');
$lastReplierIds = kg_array_column($questions, 'last_replier_id');
$ids = array_merge($ownerIds, $lastReplierIds);
return $this->getShallowUserByIds($ids);
}
}

View File

@ -1,14 +1,9 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Models\Refund as RefundModel;
use App\Repos\Order as OrderRepo;
use App\Repos\User as UserRepo;
class RefundList extends Builder
{
@ -18,7 +13,7 @@ class RefundList extends Builder
$orders = $this->getOrders($trades);
foreach ($trades as $key => $trade) {
$trades[$key]['order'] = $orders[$trade['order_id']] ?? null;
$trades[$key]['order'] = $orders[$trade['order_id']] ?? new \stdClass();
}
return $trades;
@ -29,30 +24,12 @@ class RefundList extends Builder
$users = $this->getUsers($refunds);
foreach ($refunds as $key => $refund) {
$refunds[$key]['owner'] = $users[$refund['owner_id']] ?? null;
$refunds[$key]['owner'] = $users[$refund['owner_id']] ?? new \stdClass();
}
return $refunds;
}
public function handleMeInfo(array $refund)
{
$me = [
'allow_cancel' => 0,
];
$statusTypes = [
RefundModel::STATUS_PENDING,
RefundModel::STATUS_APPROVED,
];
if (in_array($refund['status'], $statusTypes)) {
$me['allow_cancel'] = 1;
}
return $me;
}
public function getOrders(array $trades)
{
$ids = kg_array_column($trades, 'order_id');
@ -74,7 +51,17 @@ class RefundList extends Builder
{
$ids = kg_array_column($refunds, 'owner_id');
return $this->getShallowUserByIds($ids);
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name']);
$result = [];
foreach ($users->toArray() as $user) {
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,31 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
class ReportList extends Builder
{
public function handleUsers(array $reports)
{
$users = $this->getUsers($reports);
foreach ($reports as $key => $report) {
$reports[$key]['owner'] = $users[$report['owner_id']] ?? null;
}
return $reports;
}
public function getUsers(array $reports)
{
$ids = kg_array_column($reports, 'owner_id');
return $this->getShallowUserByIds($ids);
}
}

View File

@ -1,50 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Upload as UploadRepo;
class ResourceList extends Builder
{
public function handleUploads($relations)
{
$uploads = $this->getUploads($relations);
foreach ($relations as $key => $value) {
$relations[$key]['upload'] = $uploads[$value['upload_id']] ?? null;
}
return $relations;
}
public function getUploads($relations)
{
$ids = kg_array_column($relations, 'upload_id');
$uploadRepo = new UploadRepo();
$columns = ['id', 'name', 'path', 'mime', 'md5', 'size'];
$uploads = $uploadRepo->findByIds($ids, $columns);
$result = [];
foreach ($uploads->toArray() as $upload) {
$id = $this->crypt->encryptBase64($upload['id'], null, true);
$upload['url'] = $this->url->get(['for' => 'home.download', 'id' => $id]);
$result[$upload['id']] = $upload;
}
return $result;
}
}

View File

@ -1,13 +1,9 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
class ReviewList extends Builder
{
@ -17,7 +13,7 @@ class ReviewList extends Builder
$courses = $this->getCourses($reviews);
foreach ($reviews as $key => $review) {
$reviews[$key]['course'] = $courses[$review['course_id']] ?? null;
$reviews[$key]['course'] = $courses[$review['course_id']] ?? new \stdClass();
}
return $reviews;
@ -28,7 +24,7 @@ class ReviewList extends Builder
$users = $this->getUsers($reviews);
foreach ($reviews as $key => $review) {
$reviews[$key]['owner'] = $users[$review['owner_id']] ?? null;
$reviews[$key]['owner'] = $users[$review['owner_id']] ?? new \stdClass();
}
return $reviews;
@ -55,7 +51,20 @@ class ReviewList extends Builder
{
$ids = kg_array_column($reviews, 'owner_id');
return $this->getShallowUserByIds($ids);
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
$baseUrl = kg_cos_url();
$result = [];
foreach ($users->toArray() as $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,66 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Tag as TagRepo;
class TagFollowList extends Builder
{
public function handleTags(array $relations)
{
$tags = $this->getTags($relations);
foreach ($relations as $key => $value) {
$relations[$key]['tag'] = $tags[$value['tag_id']] ?? null;
}
return $relations;
}
public function handleUsers(array $relations)
{
$users = $this->getUsers($relations);
foreach ($relations as $key => $value) {
$relations[$key]['user'] = $users[$value['user_id']] ?? null;
}
return $relations;
}
public function getTags(array $relations)
{
$ids = kg_array_column($relations, 'tag_id');
$tagRepo = new TagRepo();
$columns = ['id', 'name', 'alias', 'icon', 'follow_count'];
$tags = $tagRepo->findByIds($ids, $columns);
$baseUrl = kg_cos_url();
$result = [];
foreach ($tags->toArray() as $tag) {
$tag['icon'] = $baseUrl . $tag['icon'];
$result[$tag['id']] = $tag;
}
return $result;
}
public function getUsers(array $relations)
{
$ids = kg_array_column($relations, 'user_id');
return $this->getShallowUserByIds($ids);
}
}

View File

@ -1,13 +1,9 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Repos\Order as OrderRepo;
use App\Repos\User as UserRepo;
class TradeList extends Builder
{
@ -17,7 +13,7 @@ class TradeList extends Builder
$orders = $this->getOrders($trades);
foreach ($trades as $key => $trade) {
$trades[$key]['order'] = $orders[$trade['order_id']] ?? null;
$trades[$key]['order'] = $orders[$trade['order_id']] ?? new \stdClass();
}
return $trades;
@ -28,7 +24,7 @@ class TradeList extends Builder
$users = $this->getUsers($trades);
foreach ($trades as $key => $trade) {
$trades[$key]['owner'] = $users[$trade['owner_id']] ?? null;
$trades[$key]['owner'] = $users[$trade['owner_id']] ?? new \stdClass();
}
return $trades;
@ -55,7 +51,17 @@ class TradeList extends Builder
{
$ids = kg_array_column($trades, 'owner_id');
return $this->getShallowUserByIds($ids);
$userRepo = new UserRepo();
$users = $userRepo->findByIds($ids, ['id', 'name']);
$result = [];
foreach ($users->toArray() as $user) {
$result[$user['id']] = $user;
}
return $result;
}
}

View File

@ -1,14 +1,8 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Builders;
use App\Models\User as UserModel;
use App\Repos\Account as AccountRepo;
use App\Repos\Role as RoleRepo;
class UserList extends Builder
@ -25,17 +19,6 @@ class UserList extends Builder
return $users;
}
public function handleAccounts(array $users)
{
$accounts = $this->getAccounts($users);
foreach ($users as $key => $user) {
$users[$key]['account'] = $accounts[$user['id']] ?? null;
}
return $users;
}
public function handleAdminRoles(array $users)
{
$roles = $this->getAdminRoles($users);
@ -58,26 +41,6 @@ class UserList extends Builder
return $users;
}
protected function getAccounts(array $users)
{
$ids = kg_array_column($users, 'id');
$accountRepo = new AccountRepo();
$accounts = $accountRepo->findByIds($ids);
$result = [];
foreach ($accounts as $account) {
$result[$account->id] = [
'phone' => $account->phone,
'email' => $account->email,
];
}
return $result;
}
protected function getAdminRoles(array $users)
{
$ids = kg_array_column($users, 'admin_role');

View File

@ -1,37 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
class AppInfo extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "_APP_INFO_";
}
public function getContent($id = null)
{
$appInfo = new \App\Library\AppInfo();
return [
'name' => $appInfo->get('name'),
'alias' => $appInfo->get('alias'),
'link' => $appInfo->get('link'),
'version' => $appInfo->get('version'),
];
}
}

View File

@ -1,16 +1,11 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use Phalcon\Cache\Backend\Redis as RedisCache;
use Phalcon\Di\Injectable;
use Phalcon\Mvc\User\Component;
abstract class Cache extends Injectable
abstract class Cache extends Component
{
/**

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;

View File

@ -1,46 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Category as CategoryModel;
use Phalcon\Mvc\Model\Resultset;
class CategoryAllList extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "category_all_list:{$id}";
}
public function getContent($id = null)
{
/**
* @var Resultset $categories
*/
$categories = CategoryModel::query()
->columns(['id', 'parent_id', 'name', 'priority', 'level', 'path'])
->where('type = :type:', ['type' => $id])
->orderBy('level ASC, priority ASC')
->execute();
if ($categories->count() == 0) {
return [];
}
return $categories->toArray();
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -20,22 +15,24 @@ class CategoryList extends Cache
return $this->lifetime;
}
public function getKey($id = null)
public function getKey($type = null)
{
return "category_list:{$id}";
return "category_list:{$type}";
}
public function getContent($id = null)
/**
* @param null $type
* @return array
*/
public function getContent($type = null)
{
/**
* @var Resultset $categories
*/
$categories = CategoryModel::query()
->columns(['id', 'parent_id', 'name', 'priority', 'level', 'path'])
->where('type = :type:', ['type' => $id])
->where('type = :type:', ['type' => $type])
->andWhere('published = 1')
->andWhere('deleted = 0')
->orderBy('level ASC, priority ASC')
->execute();
if ($categories->count() == 0) {

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -19,16 +14,16 @@ class CategoryTreeList extends Cache
return $this->lifetime;
}
public function getKey($id = null)
public function getKey($type = null)
{
return "category_tree_list:{$id}";
return "category_tree_list:{$type}";
}
public function getContent($id = null)
public function getContent($type = null)
{
$builder = new CategoryTreeListBuilder();
$list = $builder->handle($id);
$list = $builder->handle($type);
return $list ?: [];
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -12,7 +7,7 @@ use App\Repos\Chapter as ChapterRepo;
class Chapter extends Cache
{
protected $lifetime = 86400;
protected $lifetime = 1 * 86400;
public function getLifetime()
{

View File

@ -1,17 +1,11 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Library\Cache\Backend\Redis as RedisCache;
use Phalcon\Di\Injectable;
use Redis;
use Phalcon\Mvc\User\Component;
abstract class Counter extends Injectable
abstract class Counter extends Component
{
/**
@ -20,7 +14,7 @@ abstract class Counter extends Injectable
protected $cache;
/**
* @var Redis
* @var \Redis
*/
protected $redis;
@ -31,13 +25,19 @@ abstract class Counter extends Injectable
$this->redis = $this->cache->getRedis();
}
/**
* 获取缓存内容
*
* @param mixed $id
* @return array
*/
public function get($id = null)
{
$key = $this->getKey($id);
$content = $this->redis->hGetAll($key);
if (!$this->redis->exists($key)) {
if (!$this->cache->exists($key)) {
$content = $this->getContent($id);
$lifetime = $this->getLifetime();
@ -49,13 +49,23 @@ abstract class Counter extends Injectable
return $content;
}
/**
* 删除缓存内容
*
* @param mixed $id
*/
public function delete($id = null)
{
$key = $this->getKey($id);
$this->redis->del($key);
$this->cache->delete($key);
}
/**
* 重建缓存内容
*
* @param mixed $id
*/
public function rebuild($id = null)
{
$this->delete($id);

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -12,7 +7,7 @@ use App\Repos\Course as CourseRepo;
class Course extends Cache
{
protected $lifetime = 86400;
protected $lifetime = 1 * 86400;
public function getLifetime()
{

View File

@ -0,0 +1,54 @@
<?php
namespace App\Caches;
use App\Models\Category as CategoryModel;
use App\Repos\Course as CourseRepo;
class CourseCategoryList extends Cache
{
protected $lifetime = 1 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "course_category_list:{$id}";
}
public function getContent($id = null)
{
$courseRepo = new CourseRepo();
$categories = $courseRepo->findCategories($id);
if ($categories->count() == 0) {
return [];
}
return $this->handleContent($categories);
}
/**
* @param CategoryModel[] $categories
* @return array
*/
public function handleContent($categories)
{
$result = [];
foreach ($categories as $category) {
$result[] = [
'id' => $category->id,
'name' => $category->name,
];
}
return $result;
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -13,7 +8,7 @@ use App\Repos\Course as CourseRepo;
class CoursePackageList extends Cache
{
protected $lifetime = 86400;
protected $lifetime = 1 * 86400;
public function getLifetime()
{

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2023 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -11,12 +6,10 @@ use App\Models\Course as CourseModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class FeaturedCourseList extends Cache
class CourseRecommendedList extends Cache
{
protected $lifetime = 3600;
protected $limit = 5;
protected $lifetime = 1 * 86400;
public function getLifetime()
{
@ -25,40 +18,40 @@ class FeaturedCourseList extends Cache
public function getKey($id = null)
{
return 'featured_course_list';
return "course_recommended_list:{$id}";
}
public function getContent($id = null)
{
$courses = $this->findCourses($this->limit);
$courses = $this->findCourses(5);
if ($courses->count() == 0) {
return [];
}
return $this->handleContent($courses);
}
/**
* @param CourseModel[] $courses
* @return array
*/
public function handleContent($courses)
{
$result = [];
foreach ($courses as $course) {
$userCount = $course->user_count;
if ($course->fake_user_count > $course->user_count) {
$userCount = $course->fake_user_count;
}
$result[] = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'market_price' => $course->market_price,
'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
'rating' => round($course->rating, 1),
'market_price' => (float)$course->market_price,
'vip_price' => (float)$course->vip_price,
'user_count' => $userCount,
'user_count' => $course->user_count,
'lesson_count' => $course->lesson_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
}
@ -69,12 +62,10 @@ class FeaturedCourseList extends Cache
* @param int $limit
* @return ResultsetInterface|Resultset|CourseModel[]
*/
protected function findCourses($limit = 5)
public function findCourses($limit = 5)
{
return CourseModel::query()
->where('featured = 1')
->andWhere('published = 1')
->andWhere('deleted = 0')
->where('published = 1 AND market_price > 0')
->orderBy('RAND()')
->limit($limit)
->execute();

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -13,7 +8,7 @@ use App\Repos\Course as CourseRepo;
class CourseRelatedList extends Cache
{
protected $lifetime = 86400;
protected $lifetime = 1 * 86400;
public function getLifetime()
{
@ -48,25 +43,16 @@ class CourseRelatedList extends Cache
foreach ($courses as $course) {
$userCount = $course->user_count;
if ($course->fake_user_count > $course->user_count) {
$userCount = $course->fake_user_count;
}
$result[] = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'market_price' => $course->market_price,
'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
'rating' => round($course->rating, 1),
'market_price' => (float)$course->market_price,
'vip_price' => (float)$course->vip_price,
'user_count' => $userCount,
'user_count' => $course->user_count,
'lesson_count' => $course->lesson_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
}

View File

@ -0,0 +1,57 @@
<?php
namespace App\Caches;
use App\Models\User as UserModel;
use App\Repos\Course as CourseRepo;
class CourseTeacherList extends Cache
{
protected $lifetime = 1 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "course_teacher_list:{$id}";
}
public function getContent($id = null)
{
$courseRepo = new CourseRepo();
$users = $courseRepo->findTeachers($id);
if ($users->count() == 0) {
return [];
}
return $this->handleContent($users);
}
/**
* @param UserModel[] $users
* @return array
*/
public function handleContent($users)
{
$result = [];
foreach ($users as $user) {
$result[] = [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
'title' => $user->title,
'about' => $user->about,
];
}
return $result;
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -14,7 +9,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class CourseTopicList extends Cache
{
protected $lifetime = 86400;
protected $lifetime = 1 * 86400;
public function getLifetime()
{
@ -64,7 +59,6 @@ class CourseTopicList extends Cache
{
return TopicModel::query()
->where('published = 1')
->andWhere('deleted = 0')
->orderBy('RAND()')
->limit($limit)
->execute();

View File

@ -1,71 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2023 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Article as ArticleModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class FeaturedArticleList extends Cache
{
protected $lifetime = 3600;
protected $limit = 5;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'featured_article_list';
}
public function getContent($id = null)
{
$articles = $this->findArticles($this->limit);
if ($articles->count() == 0) {
return [];
}
$result = [];
foreach ($articles as $article) {
$result[] = [
'id' => $article->id,
'title' => $article->title,
'cover' => $article->cover,
'favorite_count' => $article->favorite_count,
'comment_count' => $article->comment_count,
'view_count' => $article->view_count,
'like_count' => $article->like_count,
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|ArticleModel[]
*/
protected function findArticles($limit = 5)
{
return ArticleModel::query()
->where('featured = 1')
->andWhere('published = :published:', ['published' => ArticleModel::PUBLISH_APPROVED])
->andWhere('deleted = 0')
->orderBy('RAND()')
->limit($limit)
->execute();
}
}

View File

@ -1,72 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2023 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Question as QuestionModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class FeaturedQuestionList extends Cache
{
protected $lifetime = 3600;
protected $limit = 5;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'featured_question_list';
}
public function getContent($id = null)
{
$questions = $this->findQuestions($this->limit);
if ($questions->count() == 0) {
return [];
}
$result = [];
foreach ($questions as $question) {
$result[] = [
'id' => $question->id,
'title' => $question->title,
'cover' => $question->cover,
'favorite_count' => $question->favorite_count,
'answer_count' => $question->answer_count,
'view_count' => $question->view_count,
'like_count' => $question->like_count,
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|QuestionModel[]
*/
protected function findQuestions($limit = 5)
{
return QuestionModel::query()
->where('featured = 1')
->andWhere('published = :published:', ['published' => QuestionModel::PUBLISH_APPROVED])
->andWhere('deleted = 0')
->orderBy('RAND()')
->limit($limit)
->execute();
}
}

31
app/Caches/Help.php Normal file
View File

@ -0,0 +1,31 @@
<?php
namespace App\Caches;
use App\Repos\Help as HelpRepo;
class Help extends Cache
{
protected $lifetime = 7 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "help:{$id}";
}
public function getContent($id = null)
{
$helpRepo = new HelpRepo();
$help = $helpRepo->findById($id);
return $help ?: null;
}
}

89
app/Caches/HelpList.php Normal file
View File

@ -0,0 +1,89 @@
<?php
namespace App\Caches;
use App\Models\Category as CategoryModel;
use App\Models\Help as HelpModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class HelpList extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'help_list';
}
public function getContent($id = null)
{
$categories = $this->findCategories();
if ($categories->count() == 0) {
return [];
}
$result = [];
foreach ($categories as $category) {
$item = [];
$item['category'] = [
'id' => $category->id,
'name' => $category->name,
];
$item['helps'] = [];
$helps = $this->findHelps($category->id);
if ($helps->count() > 0) {
foreach ($helps as $help) {
$item['helps'][] = [
'id' => $help->id,
'title' => $help->title,
];
}
}
$result[] = $item;
}
return $result;
}
/**
* @return ResultsetInterface|Resultset|CategoryModel[]
*/
protected function findCategories()
{
return CategoryModel::query()
->where('type = :type:', ['type' => CategoryModel::TYPE_HELP])
->andWhere('level = 1 AND published = 1')
->orderBy('priority ASC')
->execute();
}
/**
* @param int $categoryId
* @return ResultsetInterface|Resultset|CategoryModel[]
*/
protected function findHelps($categoryId)
{
return HelpModel::query()
->where('category_id = :category_id:', ['category_id' => $categoryId])
->andWhere('published = 1')
->orderBy('priority ASC')
->execute();
}
}

View File

@ -1,136 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Question as QuestionModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class HotQuestionList extends Cache
{
protected $lifetime = 3600;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'hot_question_list';
}
public function getContent($id = null)
{
$questions = $this->findWeeklyHotQuestions();
if ($questions->count() > 0) {
return $this->handleQuestions($questions);
}
$questions = $this->findMonthlyHotQuestions();
if ($questions->count() > 0) {
return $this->handleQuestions($questions);
}
$questions = $this->findYearlyHotQuestions();
if ($questions->count() > 0) {
return $this->handleQuestions($questions);
}
$questions = $this->findFullyHotQuestions();
if ($questions->count() > 0) {
return $this->handleQuestions($questions);
}
return [];
}
/**
* @param QuestionModel[] $questions
* @return array
*/
protected function handleQuestions($questions)
{
$result = [];
foreach ($questions as $question) {
$result[] = [
'id' => $question->id,
'title' => $question->title,
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|QuestionModel[]
*/
protected function findWeeklyHotQuestions($limit = 10)
{
$createTime = strtotime('monday this week');
return $this->findHotQuestions($createTime, $limit);
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|QuestionModel[]
*/
protected function findMonthlyHotQuestions($limit = 10)
{
$createTime = strtotime(date('Y-m-01'));
return $this->findHotQuestions($createTime, $limit);
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|QuestionModel[]
*/
protected function findYearlyHotQuestions($limit = 10)
{
$createTime = strtotime(date('Y-01-01'));
return $this->findHotQuestions($createTime, $limit);
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|QuestionModel[]
*/
protected function findFullyHotQuestions($limit = 10)
{
$createTime = 0;
return $this->findHotQuestions($createTime, $limit);
}
/**
* @param int $createTime
* @param int $limit
* @return ResultsetInterface|Resultset|QuestionModel[]
*/
protected function findHotQuestions($createTime, $limit = 10)
{
return QuestionModel::query()
->where('create_time > :create_time:', ['create_time' => $createTime])
->andWhere('published = :published:', ['published' => QuestionModel::PUBLISH_APPROVED])
->andWhere('deleted = 0')
->orderBy('score DESC')
->limit($limit)
->execute();
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace App\Caches;
use App\Models\ImMessage;
use App\Models\ImMessage as ImMessageModel;
use App\Models\User as UserModel;
use App\Repos\ImGroup as ImGroupRepo;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ImActiveGroupList extends Cache
{
protected $lifetime = 1 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'im_active_group_list';
}
public function getContent($id = null)
{
$groups = $this->findGroups();
if (empty($groups)) {
return [];
}
$result = [];
foreach ($groups as $group) {
$result[] = [
'id' => $group->id,
'type' => $group->type,
'name' => $group->name,
'avatar' => $group->avatar,
'about' => $group->about,
'user_count' => $group->user_count,
'msg_count' => $group->msg_count,
];
}
return $result;
}
/**
* @param int $days
* @param int $limit
* @return ResultsetInterface|Resultset|UserModel[]
*/
protected function findGroups($days = 7, $limit = 12)
{
$result = [];
$startTime = strtotime("-{$days} days");
$endTime = time();
$rows = ImMessageModel::query()
->columns(['receiver_id', 'total_count' => 'count(receiver_id)'])
->groupBy('receiver_id')
->orderBy('total_count DESC')
->where('receiver_type = :type:', ['type' => ImMessageModel::TYPE_GROUP])
->betweenWhere('create_time', $startTime, $endTime)
->limit($limit)
->execute();
if ($rows->count() > 0) {
$ids = kg_array_column($rows->toArray(), 'receiver_id');
$groupRepo = new ImGroupRepo();
$result = $groupRepo->findByIds($ids);
}
return $result;
}
}

View File

@ -0,0 +1,82 @@
<?php
namespace App\Caches;
use App\Models\ImMessage as ImMessageModel;
use App\Models\User as UserModel;
use App\Repos\User as UserRepo;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ImActiveUserList extends Cache
{
protected $lifetime = 1 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'im_active_user_list';
}
public function getContent($id = null)
{
$users = $this->findUsers($id);
if (empty($users)) {
return [];
}
$result = [];
foreach ($users as $user) {
$result[] = [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
'title' => $user->title,
'about' => $user->about,
'vip' => $user->vip,
];
}
return $result;
}
/**
* @param int $days
* @param int $limit
* @return ResultsetInterface|Resultset|UserModel[]
*/
protected function findUsers($days = 7, $limit = 12)
{
$result = [];
$startTime = strtotime("-{$days} days");
$endTime = time();
$rows = ImMessageModel::query()
->columns(['sender_id', 'total_count' => 'count(sender_id)'])
->groupBy('sender_id')
->orderBy('total_count DESC')
->betweenWhere('create_time', $startTime, $endTime)
->limit($limit)
->execute();
if ($rows->count() > 0) {
$ids = kg_array_column($rows->toArray(), 'sender_id');
$userRepo = new UserRepo();
$result = $userRepo->findByIds($ids);
}
return $result;
}
}

31
app/Caches/ImGroup.php Normal file
View File

@ -0,0 +1,31 @@
<?php
namespace App\Caches;
use App\Repos\ImGroup as ImGroupRepo;
class ImGroup extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "im_group:{$id}";
}
public function getContent($id = null)
{
$groupRepo = new ImGroupRepo();
$group = $groupRepo->findById($id);
return $group ?: null;
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace App\Caches;
use App\Models\ImMessage as ImMessageModel;
use App\Models\User as UserModel;
use App\Repos\User as UserRepo;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ImGroupActiveUserList extends Cache
{
protected $lifetime = 1 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "im_group_active_user_list:{$id}";
}
public function getContent($id = null)
{
$users = $this->findUsers($id);
if (empty($users)) {
return [];
}
$result = [];
foreach ($users as $user) {
$result[] = [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
'title' => $user->title,
'about' => $user->about,
'vip' => $user->vip,
];
}
return $result;
}
/**
* @param int $groupId
* @param int $days
* @param int $limit
* @return ResultsetInterface|Resultset|UserModel[]
*/
protected function findUsers($groupId, $days = 7, $limit = 5)
{
$result = [];
$startTime = strtotime("-{$days} days");
$endTime = time();
$rows = ImMessageModel::query()
->columns(['sender_id', 'total_count' => 'count(sender_id)'])
->groupBy('sender_id')
->orderBy('total_count DESC')
->where('receiver_id = :group_id:', ['group_id' => $groupId])
->andWhere('receiver_type = :type:', ['type' => ImMessageModel::TYPE_GROUP])
->betweenWhere('create_time', $startTime, $endTime)
->limit($limit)
->execute();
if ($rows->count() > 0) {
$ids = kg_array_column($rows->toArray(), 'sender_id');
$userRepo = new UserRepo();
$result = $userRepo->findByIds($ids);
}
return $result;
}
}

View File

@ -0,0 +1,73 @@
<?php
namespace App\Caches;
use App\Models\ImGroup as ImGroupModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ImNewGroupList extends Cache
{
protected $lifetime = 1 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'im_new_group_list';
}
public function getContent($id = null)
{
$limit = 12;
$groups = $this->findGroups($limit);
if ($groups->count() == 0) {
return [];
}
return $this->handleContent($groups);
}
/**
* @param ImGroupModel[] $groups
* @return array
*/
protected function handleContent($groups)
{
$result = [];
foreach ($groups as $group) {
$result[] = [
'id' => $group->id,
'type' => $group->type,
'name' => $group->name,
'avatar' => $group->avatar,
'about' => $group->about,
'user_count' => $group->user_count,
'msg_count' => $group->msg_count,
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|ImGroupModel[]
*/
public function findGroups($limit = 12)
{
return ImGroupModel::query()
->where('published = 1')
->orderBy('id DESC')
->limit($limit)
->execute();
}
}

View File

@ -0,0 +1,72 @@
<?php
namespace App\Caches;
use App\Models\User as UserModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ImNewUserList extends Cache
{
protected $lifetime = 1 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'im_new_user_list';
}
public function getContent($id = null)
{
$limit = 12;
$users = $this->findUsers($limit);
if ($users->count() == 0) {
return [];
}
return $this->handleContent($users);
}
/**
* @param UserModel[] $users
* @return array
*/
protected function handleContent($users)
{
$result = [];
foreach ($users as $user) {
$result[] = [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
'title' => $user->title,
'about' => $user->about,
'vip' => $user->vip,
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|UserModel[]
*/
public function findUsers($limit = 12)
{
return UserModel::query()
->where('deleted = 0')
->orderBy('id DESC')
->limit($limit)
->execute();
}
}

View File

@ -1,48 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Article as ArticleModel;
use App\Repos\Article as ArticleRepo;
use App\Services\Logic\Article\ArticleList as ArticleListService;
class IndexArticleList extends Cache
{
protected $lifetime = 3600;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'index_article_list';
}
public function getContent($id = null)
{
$articleRepo = new ArticleRepo();
$where = [
'published' => ArticleModel::PUBLISH_APPROVED,
'private' => 0,
'deleted' => 0,
];
$pager = $articleRepo->paginate($where, 'latest', 1, 10);
$service = new ArticleListService();
$pager = $service->handleArticles($pager);
return $pager->items ?: [];
}
}

View File

@ -1,136 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Category as CategoryModel;
use App\Models\Course as CourseModel;
use App\Services\Category as CategoryService;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
/**
* 推荐课程
*/
class IndexFeaturedCourseList extends Cache
{
protected $lifetime = 3600;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'index_featured_course_list';
}
public function getContent($id = null)
{
$categoryLimit = 5;
$courseLimit = 8;
$categories = $this->findCategories($categoryLimit);
if ($categories->count() == 0) {
return [];
}
$result = [];
foreach ($categories as $category) {
$item = [];
$item['category'] = [
'id' => $category->id,
'name' => $category->name,
];
$item['courses'] = [];
$courses = $this->findCategoryCourses($category->id, $courseLimit);
if ($courses->count() == 0) {
continue;
}
$categoryCourses = [];
foreach ($courses as $course) {
$userCount = $course->user_count;
if ($course->fake_user_count > $course->user_count) {
$userCount = $course->fake_user_count;
}
$categoryCourses[] = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'model' => $course->model,
'level' => $course->level,
'rating' => round($course->rating, 1),
'market_price' => (float)$course->market_price,
'vip_price' => (float)$course->vip_price,
'user_count' => $userCount,
'lesson_count' => $course->lesson_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
}
$item['courses'] = $categoryCourses;
$result[] = $item;
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|CategoryModel[]
*/
protected function findCategories($limit = 5)
{
return CategoryModel::query()
->where('type = :type:', ['type' => CategoryModel::TYPE_COURSE])
->andWhere('level = 1')
->andWhere('published = 1')
->andWhere('deleted = 0')
->orderBy('priority ASC')
->limit($limit)
->execute();
}
/**
* @param int $categoryId
* @param int $limit
* @return ResultsetInterface|Resultset|CourseModel[]
*/
protected function findCategoryCourses($categoryId, $limit = 8)
{
$categoryService = new CategoryService();
$categoryIds = $categoryService->getChildCategoryIds($categoryId);
return CourseModel::query()
->inWhere('category_id', $categoryIds)
->andWhere('featured = 1')
->andWhere('published = 1')
->andWhere('deleted = 0')
->orderBy('id DESC')
->limit($limit)
->execute();
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -19,7 +14,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexFreeCourseList extends Cache
{
protected $lifetime = 3600;
protected $lifetime = 1 * 86400;
public function getLifetime()
{
@ -65,26 +60,16 @@ class IndexFreeCourseList extends Cache
$categoryCourses = [];
foreach ($courses as $course) {
$userCount = $course->user_count;
if ($course->fake_user_count > $course->user_count) {
$userCount = $course->fake_user_count;
}
$categoryCourses[] = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'market_price' => $course->market_price,
'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
'rating' => round($course->rating, 1),
'market_price' => (float)$course->market_price,
'vip_price' => (float)$course->vip_price,
'user_count' => $userCount,
'user_count' => $course->user_count,
'lesson_count' => $course->lesson_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
}
@ -104,9 +89,7 @@ class IndexFreeCourseList extends Cache
{
return CategoryModel::query()
->where('type = :type:', ['type' => CategoryModel::TYPE_COURSE])
->andWhere('level = 1')
->andWhere('published = 1')
->andWhere('deleted = 0')
->andWhere('level = 1 AND published = 1')
->orderBy('priority ASC')
->limit($limit)
->execute();
@ -125,9 +108,8 @@ class IndexFreeCourseList extends Cache
return CourseModel::query()
->inWhere('category_id', $categoryIds)
->andWhere('market_price = 0')
->andWhere('published = 1')
->andWhere('deleted = 0')
->andWhere('market_price = 0')
->orderBy('score DESC')
->limit($limit)
->execute();

View File

@ -1,24 +1,19 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Chapter as ChapterModel;
use App\Models\ChapterLive as ChapterLiveModel;
use App\Repos\Chapter as ChapterRepo;
use App\Repos\Course as CourseRepo;
use App\Repos\User as UserRepo;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
/**
* 直播课程
*/
class IndexLiveList extends Cache
{
protected $lifetime = 3600;
protected $lifetime = 1 * 86400;
public function getLifetime()
{
@ -32,11 +27,32 @@ class IndexLiveList extends Cache
public function getContent($id = null)
{
$limit = 8;
/**
* 限制输出多少天数(一维限额)
*/
$dayLimit = 3;
$lives = $this->findChapterLives();
/**
* 限制每天维度下的输出数(二维限额)
*/
$perDayLimit = 10;
if ($lives->count() == 0) return [];
$beginTime = strtotime('today');
$endTime = strtotime("+30 days");
/**
* @var Resultset|ChapterLiveModel[] $lives
*/
$lives = ChapterLiveModel::query()
->betweenWhere('start_time', $beginTime, $endTime)
->orderBy('start_time ASC')
->execute();
if ($lives->count() == 0) {
return [];
}
$result = [];
$chapterIds = kg_array_column($lives->toArray(), 'chapter_id');
@ -56,87 +72,53 @@ class IndexLiveList extends Cache
$courses = $courseRepo->findByIds($courseIds);
$teacherIds = kg_array_column($courses->toArray(), 'teacher_id');
$userRepo = new UserRepo();
$users = $userRepo->findByIds($teacherIds);
$courseMapping = [];
foreach ($courses as $course) {
$courseMapping[$course->id] = $course;
}
$userMapping = [];
foreach ($users as $user) {
$userMapping[$user->id] = $user;
}
$result = [];
$flag = [];
foreach ($lives as $live) {
if (count($result) >= $dayLimit) {
break;
}
$day = date('y-m-d', $live->start_time);
if (isset($result[$day]) && count($result[$day]) >= $perDayLimit) {
continue;
}
$chapter = $chapterMapping[$live->chapter_id];
$course = $courseMapping[$chapter->course_id];
$teacher = $userMapping[$course->teacher_id];
$teacherInfo = [
'id' => $teacher->id,
'name' => $teacher->name,
'title' => $teacher->title,
'avatar' => $teacher->avatar,
];
$chapterInfo = [
'id' => $chapter->id,
'title' => $chapter->title,
'start_time' => $live->start_time,
'end_time' => $live->end_time,
];
$courseInfo = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'teacher' => $teacherInfo,
'market_price' => $course->market_price,
'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
'user_count' => $course->user_count,
'lesson_count' => $course->lesson_count,
];
if (!isset($flag[$course->id]) && count($flag) < $limit) {
$flag[$course->id] = 1;
$result[] = [
'id' => $live->id,
'status' => $live->status,
'start_time' => $live->start_time,
'end_time' => $live->end_time,
'course' => $courseInfo,
'chapter' => $chapterInfo,
];
}
$result[$day][] = [
'course' => $courseInfo,
'chapter' => $chapterInfo,
];
}
return $result;
}
/**
* @return ResultsetInterface|Resultset|ChapterLiveModel[]
*/
protected function findChapterLives()
{
$startTime = strtotime('today');
$endTime = strtotime('+30 days');
return $this->modelsManager->createBuilder()
->columns('cl.*')
->addFrom(ChapterLiveModel::class, 'cl')
->join(ChapterModel::class, 'cl.chapter_id = c.id', 'c')
->betweenWhere('start_time', $startTime, $endTime)
->andWhere('published = 1')
->andWhere('deleted = 0')
->orderBy('start_time ASC')
->getQuery()
->execute();
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -19,7 +14,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexNewCourseList extends Cache
{
protected $lifetime = 3600;
protected $lifetime = 1 * 86400;
public function getLifetime()
{
@ -65,26 +60,16 @@ class IndexNewCourseList extends Cache
$categoryCourses = [];
foreach ($courses as $course) {
$userCount = $course->user_count;
if ($course->fake_user_count > $course->user_count) {
$userCount = $course->fake_user_count;
}
$categoryCourses[] = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'market_price' => $course->market_price,
'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
'rating' => round($course->rating, 1),
'market_price' => (float)$course->market_price,
'vip_price' => (float)$course->vip_price,
'user_count' => $userCount,
'user_count' => $course->user_count,
'lesson_count' => $course->lesson_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
}
@ -104,9 +89,7 @@ class IndexNewCourseList extends Cache
{
return CategoryModel::query()
->where('type = :type:', ['type' => CategoryModel::TYPE_COURSE])
->andWhere('level = 1')
->andWhere('published = 1')
->andWhere('deleted = 0')
->andWhere('level = 1 AND published = 1')
->orderBy('priority ASC')
->limit($limit)
->execute();
@ -126,7 +109,6 @@ class IndexNewCourseList extends Cache
return CourseModel::query()
->inWhere('category_id', $categoryIds)
->andWhere('published = 1')
->andWhere('deleted = 0')
->orderBy('id DESC')
->limit($limit)
->execute();

View File

@ -1,47 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Question as QuestionModel;
use App\Repos\Question as QuestionRepo;
use App\Services\Logic\Question\QuestionList as QuestionListService;
class IndexQuestionList extends Cache
{
protected $lifetime = 3600;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'index_question_list';
}
public function getContent($id = null)
{
$questionRepo = new QuestionRepo();
$where = [
'published' => QuestionModel::PUBLISH_APPROVED,
'deleted' => 0,
];
$pager = $questionRepo->paginate($where, 'latest', 1, 10);
$service = new QuestionListService();
$pager = $service->handleQuestions($pager);
return $pager->items ?: [];
}
}

View File

@ -1,86 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Course as CourseModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
/**
* 简版推荐课程
*/
class IndexSimpleFeaturedCourseList extends Cache
{
protected $lifetime = 3600;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'index_simple_featured_course_list';
}
public function getContent($id = null)
{
$limit = 8;
$courses = $this->findCourses($limit);
if ($courses->count() == 0) {
return [];
}
$result = [];
foreach ($courses as $course) {
$userCount = $course->user_count;
if ($course->fake_user_count > $course->user_count) {
$userCount = $course->fake_user_count;
}
$result[] = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'model' => $course->model,
'level' => $course->level,
'rating' => round($course->rating, 1),
'market_price' => (float)$course->market_price,
'vip_price' => (float)$course->vip_price,
'user_count' => $userCount,
'lesson_count' => $course->lesson_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|CourseModel[]
*/
protected function findCourses($limit = 8)
{
return CourseModel::query()
->where('featured = 1')
->andWhere('published = 1')
->andWhere('deleted = 0')
->orderBy('id DESC')
->limit($limit)
->execute();
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -17,7 +12,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexSimpleFreeCourseList extends Cache
{
protected $lifetime = 3600;
protected $lifetime = 1 * 86400;
public function getLifetime()
{
@ -42,26 +37,16 @@ class IndexSimpleFreeCourseList extends Cache
$result = [];
foreach ($courses as $course) {
$userCount = $course->user_count;
if ($course->fake_user_count > $course->user_count) {
$userCount = $course->fake_user_count;
}
$result[] = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'market_price' => $course->market_price,
'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
'rating' => round($course->rating, 1),
'market_price' => (float)$course->market_price,
'vip_price' => (float)$course->vip_price,
'user_count' => $userCount,
'user_count' => $course->user_count,
'lesson_count' => $course->lesson_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
}
@ -75,9 +60,8 @@ class IndexSimpleFreeCourseList extends Cache
protected function findCourses($limit = 8)
{
return CourseModel::query()
->where('market_price = 0')
->andWhere('published = 1')
->andWhere('deleted = 0')
->where('published = 1')
->andWhere('market_price = 0')
->orderBy('score DESC')
->limit($limit)
->execute();

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -17,7 +12,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexSimpleNewCourseList extends Cache
{
protected $lifetime = 3600;
protected $lifetime = 1 * 86400;
public function getLifetime()
{
@ -42,26 +37,16 @@ class IndexSimpleNewCourseList extends Cache
$result = [];
foreach ($courses as $course) {
$userCount = $course->user_count;
if ($course->fake_user_count > $course->user_count) {
$userCount = $course->fake_user_count;
}
$result[] = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'market_price' => $course->market_price,
'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
'rating' => round($course->rating, 1),
'market_price' => (float)$course->market_price,
'vip_price' => (float)$course->vip_price,
'user_count' => $userCount,
'user_count' => $course->user_count,
'lesson_count' => $course->lesson_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
}
@ -76,7 +61,6 @@ class IndexSimpleNewCourseList extends Cache
{
return CourseModel::query()
->where('published = 1')
->andWhere('deleted = 0')
->orderBy('id DESC')
->limit($limit)
->execute();

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -17,7 +12,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexSimpleVipCourseList extends Cache
{
protected $lifetime = 3600;
protected $lifetime = 1 * 86400;
public function getLifetime()
{
@ -42,26 +37,16 @@ class IndexSimpleVipCourseList extends Cache
$result = [];
foreach ($courses as $course) {
$userCount = $course->user_count;
if ($course->fake_user_count > $course->user_count) {
$userCount = $course->fake_user_count;
}
$result[] = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'market_price' => $course->market_price,
'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
'rating' => round($course->rating, 1),
'market_price' => (float)$course->market_price,
'vip_price' => (float)$course->vip_price,
'user_count' => $userCount,
'user_count' => $course->user_count,
'lesson_count' => $course->lesson_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
}
@ -75,10 +60,8 @@ class IndexSimpleVipCourseList extends Cache
protected function findCourses($limit = 8)
{
return CourseModel::query()
->where('market_price > vip_price')
->where('published = 1')
->andWhere('vip_price >= 0')
->andWhere('published = 1')
->andWhere('deleted = 0')
->orderBy('score DESC')
->limit($limit)
->execute();

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -14,7 +9,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexSlideList extends Cache
{
protected $lifetime = 3600;
protected $lifetime = 365 * 86400;
public function getLifetime()
{
@ -68,7 +63,6 @@ class IndexSlideList extends Cache
{
return SlideModel::query()
->where('published = 1')
->andWhere('deleted = 0')
->orderBy('priority ASC')
->limit($limit)
->execute();

View File

@ -1,69 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\User as UserModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class IndexTeacherList extends Cache
{
protected $lifetime = 3600;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'index_teacher_list';
}
public function getContent($id = null)
{
$teachers = $this->findTeachers();
if ($teachers->count() == 0) return [];
$result = [];
$baseUrl = kg_cos_url();
foreach ($teachers->toArray() as $teacher) {
$teacher['avatar'] = $baseUrl . $teacher['avatar'];
$result[] = [
'id' => $teacher['id'],
'name' => $teacher['name'],
'title' => $teacher['title'],
'avatar' => $teacher['avatar'],
'about' => $teacher['about'],
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|UserModel[]
*/
protected function findTeachers($limit = 8)
{
return UserModel::query()
->where('edu_role = :edu_role:', ['edu_role' => UserModel::EDU_ROLE_TEACHER])
->andWhere('deleted = 0')
->orderBy('RAND()')
->limit($limit)
->execute();
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -19,7 +14,7 @@ use Phalcon\Mvc\Model\ResultsetInterface;
class IndexVipCourseList extends Cache
{
protected $lifetime = 3600;
protected $lifetime = 1 * 86400;
public function getLifetime()
{
@ -65,26 +60,16 @@ class IndexVipCourseList extends Cache
$categoryCourses = [];
foreach ($courses as $course) {
$userCount = $course->user_count;
if ($course->fake_user_count > $course->user_count) {
$userCount = $course->fake_user_count;
}
$categoryCourses[] = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'market_price' => $course->market_price,
'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
'rating' => round($course->rating, 1),
'market_price' => (float)$course->market_price,
'vip_price' => (float)$course->vip_price,
'user_count' => $userCount,
'user_count' => $course->user_count,
'lesson_count' => $course->lesson_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
}
@ -104,9 +89,7 @@ class IndexVipCourseList extends Cache
{
return CategoryModel::query()
->where('type = :type:', ['type' => CategoryModel::TYPE_COURSE])
->andWhere('level = 1')
->andWhere('published = 1')
->andWhere('deleted = 0')
->andWhere('level = 1 AND published = 1')
->orderBy('priority ASC')
->limit($limit)
->execute();
@ -125,10 +108,8 @@ class IndexVipCourseList extends Cache
return CourseModel::query()
->inWhere('category_id', $categoryIds)
->andWhere('market_price > vip_price')
->andWhere('vip_price >= 0')
->andWhere('published = 1')
->andWhere('deleted = 0')
->andWhere('vip_price >= 0')
->orderBy('score DESC')
->limit($limit)
->execute();

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;

29
app/Caches/MaxHelpId.php Normal file
View File

@ -0,0 +1,29 @@
<?php
namespace App\Caches;
use App\Models\Help as HelpModel;
class MaxHelpId extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'max_help_id';
}
public function getContent($id = null)
{
$help = HelpModel::findFirst(['order' => 'id DESC']);
return $help->id ?? 0;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Caches;
use App\Models\ImGroup as ImGroupModel;
class MaxImGroupId extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'max_im_group_id';
}
public function getContent($id = null)
{
$group = ImGroupModel::findFirst(['order' => 'id DESC']);
return $group->id ?? 0;
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;

29
app/Caches/MaxPageId.php Normal file
View File

@ -0,0 +1,29 @@
<?php
namespace App\Caches;
use App\Models\Page as PageModel;
class MaxPageId extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'max_page_id';
}
public function getContent($id = null)
{
$page = PageModel::findFirst(['order' => 'id DESC']);
return $page->id ?? 0;
}
}

View File

@ -1,34 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Tag as TagModel;
class MaxTagId extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'max_tag_id';
}
public function getContent($id = null)
{
$tag = TagModel::findFirst(['order' => 'id DESC']);
return $tag->id ?? 0;
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
@ -13,7 +8,7 @@ use App\Repos\Package as PackageRepo;
class PackageCourseList extends Cache
{
protected $lifetime = 86400;
protected $lifetime = 1 * 86400;
public function getLifetime()
{
@ -47,26 +42,16 @@ class PackageCourseList extends Cache
$result = [];
foreach ($courses as $course) {
$userCount = $course->user_count;
if ($course->fake_user_count > $course->user_count) {
$userCount = $course->fake_user_count;
}
$result[] = [
'id' => $course->id,
'title' => $course->title,
'cover' => $course->cover,
'market_price' => $course->market_price,
'vip_price' => $course->vip_price,
'model' => $course->model,
'level' => $course->level,
'rating' => round($course->rating, 1),
'market_price' => (float)$course->market_price,
'vip_price' => (float)$course->vip_price,
'user_count' => $userCount,
'user_count' => $course->user_count,
'lesson_count' => $course->lesson_count,
'review_count' => $course->review_count,
'favorite_count' => $course->favorite_count,
];
}

31
app/Caches/Page.php Normal file
View File

@ -0,0 +1,31 @@
<?php
namespace App\Caches;
use App\Repos\Page as PageRepo;
class Page extends Cache
{
protected $lifetime = 7 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "page:{$id}";
}
public function getContent($id = null)
{
$pageRepo = new PageRepo();
$page = $pageRepo->findById($id);
return $page ?: null;
}
}

View File

@ -1,85 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\PointGift as PointGiftModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class PointHotGiftList extends Cache
{
/**
* 过期时间
*
* @var int
*/
protected $lifetime = 86400;
/**
* 显示个数
*
* @var int
*/
protected $limit = 5;
public function setLimit($limit)
{
$this->limit = $limit;
}
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'point_hot_gift_list';
}
public function getContent($id = null)
{
$gifts = $this->findGifts($this->limit);
if (count($gifts) == 0) {
return [];
}
$result = [];
foreach ($gifts as $gift) {
$result[] = [
'id' => $gift->id,
'name' => $gift->name,
'cover' => $gift->cover,
'details' => $gift->details,
'type' => $gift->type,
'point' => $gift->point,
'redeem_count' => $gift->redeem_count,
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset|PointGiftModel[]
*/
protected function findGifts($limit = 5)
{
return PointGiftModel::query()
->where('published = 1')
->andWhere('deleted = 0')
->orderBy('redeem_count DESC')
->limit($limit)
->execute();
}
}

65
app/Caches/SaleTrend.php Normal file
View File

@ -0,0 +1,65 @@
<?php
namespace App\Caches;
use App\Models\Order as OrderModel;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class SaleTrend extends Cache
{
protected $lifetime = 2 * 3600;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'sale_trend';
}
public function getContent($id = null)
{
}
/**
* @param OrderModel[] $sales
* @param int $days
* @return array
*/
protected function handleSales($sales, $days = 7)
{
$result = [];
foreach (array_reverse(range(1, $days)) as $num) {
$date = date('Y-m-d', strtotime("-{$num} days"));
$result[$date] = 0;
}
foreach ($sales as $sale) {
$date = date('Y-m-d', $sale->create_time);
$result[$date] += $sale->amount;
}
return $result;
}
/**
* @param int $days
* @return ResultsetInterface|Resultset|OrderModel[]
*/
protected function findSales($days = 7)
{
$time = strtotime("-{$days} days");
return OrderModel::query()
->where('status = :status:', ['status' => OrderModel::STATUS_FINISHED])
->andWhere('create_time > :time:', ['time' => $time])
->execute();
}
}

View File

@ -1,9 +1,4 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;

View File

@ -1,19 +1,12 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Repos\Answer as AnswerRepo;
use App\Repos\Article as ArticleRepo;
use App\Repos\Comment as CommentRepo;
use App\Repos\Consult as ConsultRepo;
use App\Repos\Course as CourseRepo;
use App\Repos\ImGroup as GroupRepo;
use App\Repos\Order as OrderRepo;
use App\Repos\Package as PackageRepo;
use App\Repos\Question as QuestionRepo;
use App\Repos\Review as ReviewRepo;
use App\Repos\Topic as TopicRepo;
use App\Repos\User as UserRepo;
@ -21,7 +14,7 @@ use App\Repos\User as UserRepo;
class SiteGlobalStat extends Cache
{
protected $lifetime = 15 * 60;
protected $lifetime = 2 * 3600;
public function getLifetime()
{
@ -36,11 +29,9 @@ class SiteGlobalStat extends Cache
public function getContent($id = null)
{
$courseRepo = new CourseRepo();
$articleRepo = new ArticleRepo();
$questionRepo = new QuestionRepo();
$answerRepo = new AnswerRepo();
$commentRepo = new CommentRepo();
$consultRepo = new ConsultRepo();
$groupRepo = new GroupRepo();
$orderRepo = new OrderRepo();
$packageRepo = new PackageRepo();
$reviewRepo = new ReviewRepo();
$topicRepo = new TopicRepo();
@ -48,12 +39,9 @@ class SiteGlobalStat extends Cache
return [
'course_count' => $courseRepo->countCourses(),
'article_count' => $articleRepo->countArticles(),
'question_count' => $questionRepo->countQuestions(),
'answer_count' => $answerRepo->countAnswers(),
'comment_count' => $commentRepo->countComments(),
'consult_count' => $consultRepo->countConsults(),
'vip_count' => $userRepo->countVipUsers(),
'group_count' => $groupRepo->countGroups(),
'order_count' => $orderRepo->countOrders(),
'package_count' => $packageRepo->countPackages(),
'review_count' => $reviewRepo->countReviews(),
'topic_count' => $topicRepo->countTopics(),

View File

@ -1,18 +1,14 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Repos\Stat as StatRepo;
use App\Models\Order as OrderModel;
use App\Models\User as UserModel;
class SiteTodayStat extends Cache
{
protected $lifetime = 15 * 60;
protected $lifetime = 1 * 3600;
public function getLifetime()
{
@ -26,25 +22,42 @@ class SiteTodayStat extends Cache
public function getContent($id = null)
{
$statRepo = new StatRepo();
$date = date('Y-m-d');
$saleCount = $statRepo->countDailySales($date);
$refundCount = $statRepo->countDailyRefunds($date);
$saleAmount = $statRepo->sumDailySales($date);
$refundAmount = $statRepo->sumDailyRefunds($date);
$registerCount = $statRepo->countDailyRegisteredUsers($date);
$pointRedeemCount = $statRepo->countDailyPointGiftRedeems($date);
return [
'sale_count' => $saleCount,
'refund_count' => $refundCount,
'sale_amount' => $saleAmount,
'refund_amount' => $refundAmount,
'register_count' => $registerCount,
'point_redeem_count' => $pointRedeemCount,
'user_count' => $this->countUsers(),
'order_count' => $this->countOrders(),
'sale_amount' => $this->sumSales(),
];
}
protected function countUsers()
{
return (int)UserModel::count([
'conditions' => 'create_time > :time:',
'bind' => ['time' => strtotime('today')],
]);
}
protected function countOrders()
{
return (int)OrderModel::count([
'conditions' => 'create_time > :time: AND status = :status:',
'bind' => [
'time' => strtotime('today'),
'status' => OrderModel::STATUS_FINISHED,
],
]);
}
protected function sumSales()
{
return (float)OrderModel::sum([
'column' => 'amount',
'conditions' => 'create_time > :time: AND status = :status:',
'bind' => [
'time' => strtotime('today'),
'status' => OrderModel::STATUS_FINISHED,
],
]);
}
}

View File

@ -1,36 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Repos\Tag as TagRepo;
class Tag extends Cache
{
protected $lifetime = 365 * 86400;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "tag:{$id}";
}
public function getContent($id = null)
{
$tagRepo = new TagRepo();
$tag = $tagRepo->findById($id);
return $tag ?: null;
}
}

View File

@ -1,75 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Article as ArticleModel;
use App\Repos\Article as ArticleRepo;
class TaggedArticleList extends Cache
{
protected $limit = 15;
protected $lifetime = 3600;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "tagged_article_list:{$id}";
}
public function getContent($id = null)
{
$articleRepo = new ArticleRepo();
$where = [
'tag_id' => $id,
'published' => ArticleModel::PUBLISH_APPROVED,
'deleted' => 0,
];
$pager = $articleRepo->paginate($where);
if ($pager->total_items == 0) return [];
return $this->handleContent($pager->items);
}
/**
* @param ArticleModel[] $articles
* @return array
*/
public function handleContent($articles)
{
$result = [];
$count = 0;
foreach ($articles as $article) {
if ($count < $this->limit) {
$result[] = [
'id' => $article->id,
'title' => $article->title,
'cover' => $article->cover,
'view_count' => $article->view_count,
'like_count' => $article->like_count,
'comment_count' => $article->comment_count,
'favorite_count' => $article->favorite_count,
];
$count++;
}
}
return $result;
}
}

View File

@ -1,75 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Question as QuestionModel;
use App\Repos\Question as QuestionRepo;
class TaggedQuestionList extends Cache
{
protected $limit = 15;
protected $lifetime = 3600;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return "tagged_question_list:{$id}";
}
public function getContent($id = null)
{
$questionRepo = new QuestionRepo();
$where = [
'tag_id' => $id,
'published' => QuestionModel::PUBLISH_APPROVED,
'deleted' => 0,
];
$pager = $questionRepo->paginate($where);
if ($pager->total_items == 0) return [];
return $this->handleContent($pager->items);
}
/**
* @param QuestionModel[] $questions
* @return array
*/
public function handleContent($questions)
{
$result = [];
$count = 0;
foreach ($questions as $question) {
if ($count < $this->limit) {
$result[] = [
'id' => $question->id,
'title' => $question->title,
'view_count' => $question->view_count,
'like_count' => $question->like_count,
'answer_count' => $question->answer_count,
'favorite_count' => $question->favorite_count,
];
$count++;
}
}
return $result;
}
}

View File

@ -1,152 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
namespace App\Caches;
use App\Models\Answer as AnswerModel;
use App\Models\AnswerLike as AnswerLikeModel;
use App\Repos\User as UserRepo;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class TopAnswererList extends Cache
{
protected $lifetime = 3600;
public function getLifetime()
{
return $this->lifetime;
}
public function getKey($id = null)
{
return 'top_answerer_list';
}
public function getContent($id = null)
{
$rankings = $this->findWeeklyAuthorRankings();
if ($rankings->count() > 0) {
$userIds = kg_array_column($rankings->toArray(), 'author_id');
return $this->handleUsers($userIds);
}
$rankings = $this->findMonthlyAuthorRankings();
if ($rankings->count() > 0) {
$userIds = kg_array_column($rankings->toArray(), 'author_id');
return $this->handleUsers($userIds);
}
$rankings = $this->findYearlyAuthorRankings();
if ($rankings->count() > 0) {
$userIds = kg_array_column($rankings->toArray(), 'author_id');
return $this->handleUsers($userIds);
}
$rankings = $this->findFullyAuthorRankings();
if ($rankings->count() > 0) {
$userIds = kg_array_column($rankings->toArray(), 'author_id');
return $this->handleUsers($userIds);
}
return [];
}
protected function handleUsers($userIds)
{
$userRepo = new UserRepo();
$users = $userRepo->findByIds($userIds);
$result = [];
foreach ($users as $user) {
$result[] = [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
'title' => $user->title,
'about' => $user->about,
'vip' => $user->vip,
];
}
return $result;
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset
*/
protected function findWeeklyAuthorRankings($limit = 10)
{
$createTime = strtotime('monday this week');
return $this->findAuthorRankings($createTime, $limit);
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset
*/
protected function findMonthlyAuthorRankings($limit = 10)
{
$createTime = strtotime(date('Y-m-01'));
return $this->findAuthorRankings($createTime, $limit);
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset
*/
protected function findYearlyAuthorRankings($limit = 10)
{
$createTime = strtotime(date('Y-01-01'));
return $this->findAuthorRankings($createTime, $limit);
}
/**
* @param int $limit
* @return ResultsetInterface|Resultset
*/
protected function findFullyAuthorRankings($limit = 10)
{
$createTime = 0;
return $this->findAuthorRankings($createTime, $limit);
}
/**
* @param int $createTime
* @param int $limit
* @return ResultsetInterface|Resultset
*/
protected function findAuthorRankings($createTime, $limit = 10)
{
$columns = [
'author_id' => 'a.owner_id',
'like_count' => 'count(al.user_id)',
];
return $this->modelsManager->createBuilder()
->columns($columns)
->addFrom(AnswerLikeModel::class, 'al')
->join(AnswerModel::class, 'al.answer_id = a.id', 'a')
->where('al.create_time > :create_time:', ['create_time' => $createTime])
->groupBy('author_id')
->orderBy('like_count DESC')
->limit($limit)->getQuery()->execute();
}
}

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