rename com.imyeyu.server to com.imyeyu.api
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
package com.imyeyu.api.modules.bill.controller;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.bill.entity.Bill;
|
||||
import com.imyeyu.api.modules.bill.service.BillService;
|
||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||
import com.imyeyu.api.modules.common.service.SettingService;
|
||||
import com.imyeyu.spring.TimiSpring;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 收支帐单接口
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2023-02-04 01:02
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/bill")
|
||||
public class BillController {
|
||||
|
||||
private final BillService service;
|
||||
private final SettingService settingService;
|
||||
|
||||
/**
|
||||
* 创建收支帐单
|
||||
*
|
||||
* @param bill 账单
|
||||
*/
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@PostMapping("/create")
|
||||
public void createREBill(@Valid @RequestBody Bill bill) {
|
||||
if (!settingService.getAsString(SettingKey.BILL_API_TOKEN).equals(TimiSpring.getToken())) {
|
||||
throw new TimiException(TimiCode.REQUEST_BAD).msgKey("token.illegal");
|
||||
}
|
||||
service.create(bill);
|
||||
}
|
||||
}
|
||||
120
src/main/java/com/imyeyu/api/modules/bill/entity/Bill.java
Normal file
120
src/main/java/com/imyeyu/api/modules/bill/entity/Bill.java
Normal file
@@ -0,0 +1,120 @@
|
||||
package com.imyeyu.api.modules.bill.entity;
|
||||
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
|
||||
/**
|
||||
* 收支账单
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-03-29 11:28
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Bill extends Entity {
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-03-29 11:28
|
||||
*/
|
||||
@Getter
|
||||
public enum Type {
|
||||
|
||||
/** 收入 */
|
||||
REVENUE,
|
||||
|
||||
/** 支出 */
|
||||
EXPENDITURE
|
||||
}
|
||||
|
||||
/**
|
||||
* 收入类型
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-03-29 11:28
|
||||
*/
|
||||
@Getter
|
||||
public enum RevenueType {
|
||||
|
||||
/** 工作 */
|
||||
WORK,
|
||||
|
||||
/** 退款 */
|
||||
REFUND,
|
||||
|
||||
/** 其他 */
|
||||
OTHER
|
||||
}
|
||||
|
||||
/**
|
||||
* 支出类型
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-03-29 11:28
|
||||
*/
|
||||
@Getter
|
||||
public enum ExpenditureType {
|
||||
|
||||
/** 饮食 */
|
||||
FOOD,
|
||||
|
||||
/** 生活 */
|
||||
LIFE,
|
||||
|
||||
/** 通信 */
|
||||
COMMUNICATION,
|
||||
|
||||
/** 交通 */
|
||||
TRAFFIC,
|
||||
|
||||
/** 娱乐 */
|
||||
GAME,
|
||||
|
||||
/** 工作 */
|
||||
WORK,
|
||||
|
||||
/** 服饰 */
|
||||
CLOTHES,
|
||||
|
||||
/** 医疗 */
|
||||
HEALTH,
|
||||
|
||||
/** 其他 */
|
||||
OTHER
|
||||
}
|
||||
|
||||
/** 收入类型 */
|
||||
private RevenueType revenueType;
|
||||
|
||||
/** 支出类型 */
|
||||
private ExpenditureType expenditureType;
|
||||
|
||||
/** 描述 */
|
||||
@NotBlank(message = "bill.description.empty")
|
||||
private String description;
|
||||
|
||||
/** 金额(未确保计算精度,放大了 100 倍) */
|
||||
@NotNull(message = "bill.decimal.empty")
|
||||
@DecimalMin(value = "0", message = "bill.decimal.limit")
|
||||
private Long decimal;
|
||||
|
||||
/** 备注 */
|
||||
private String remarks;
|
||||
|
||||
/** @return true 为收入账单 */
|
||||
public boolean isRevenue() {
|
||||
return revenueType != null;
|
||||
}
|
||||
|
||||
/** @return true 为支出账单 */
|
||||
public boolean isExpenditure() {
|
||||
return expenditureType != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.imyeyu.api.modules.bill.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.bill.entity.Bill;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* 收支帐单表
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-04-01 16:26
|
||||
*/
|
||||
public interface BillMapper extends BaseMapper<Bill, Long> {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.imyeyu.api.modules.bill.service;
|
||||
|
||||
import com.imyeyu.api.modules.bill.entity.Bill;
|
||||
import com.imyeyu.spring.service.CreatableService;
|
||||
|
||||
/**
|
||||
* 收支帐单服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-04-01 16:24
|
||||
*/
|
||||
public interface BillService extends CreatableService<Bill> {
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.imyeyu.api.modules.bill.service.implement;
|
||||
|
||||
import com.imyeyu.api.modules.bill.entity.Bill;
|
||||
import com.imyeyu.api.modules.bill.mapper.BillMapper;
|
||||
import com.imyeyu.api.modules.bill.service.BillService;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 收支账单服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-04-01 16:25
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class BillServiceImplement extends AbstractEntityService<Bill, Long> implements BillService {
|
||||
|
||||
private final BillMapper mapper;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<Bill, Long> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.imyeyu.api.modules.blog.controller;
|
||||
|
||||
import com.imyeyu.api.modules.blog.entity.Article;
|
||||
import com.imyeyu.api.modules.blog.entity.ArticleRanking;
|
||||
import com.imyeyu.api.modules.blog.service.ArticleService;
|
||||
import com.imyeyu.api.modules.blog.vo.article.ArticleView;
|
||||
import com.imyeyu.api.modules.blog.vo.article.KeywordPage;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文章接口
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-17 17:47
|
||||
*/
|
||||
@Validated
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/article")
|
||||
public class ArticleController {
|
||||
|
||||
private final ArticleService service;
|
||||
|
||||
/**
|
||||
* 查看
|
||||
*
|
||||
* @param id 文章 ID
|
||||
* @return 文章
|
||||
*/
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequestMapping("/{id}")
|
||||
public ArticleView view(@Min(1) @NotNull @PathVariable Long id) {
|
||||
return service.view(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 喜欢文章
|
||||
*
|
||||
* @param id 文章 ID
|
||||
* @return 最新喜欢数量
|
||||
*/
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@RequestMapping("/like/{id}")
|
||||
public int like(@Min(1) @NotNull @PathVariable Long id) {
|
||||
return service.like(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 主列表
|
||||
*
|
||||
* @param page 页面参数
|
||||
* @return 文章列表
|
||||
*/
|
||||
@RequestRateLimit
|
||||
@RequestMapping("/list")
|
||||
public PageResult<Article> list(@Valid @RequestBody Page page) {
|
||||
return service.page(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关键字获取列表
|
||||
*
|
||||
* @param page 关键字页面参数
|
||||
* @return 文章列表
|
||||
*/
|
||||
@RequestRateLimit
|
||||
@RequestMapping("/list/search")
|
||||
public PageResult<Article> listByKeyword(@Valid @RequestBody KeywordPage page) {
|
||||
return service.pageByKeyword(page);
|
||||
}
|
||||
|
||||
/** @return 每周访问排位 */
|
||||
@RequestMapping("/list/ranking")
|
||||
public List<ArticleRanking> ranking() {
|
||||
return service.listRanking();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.imyeyu.api.modules.blog.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.imyeyu.api.modules.blog.entity.Friend;
|
||||
import com.imyeyu.api.modules.blog.service.FriendService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 主控
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2023-02-04 10:28
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/")
|
||||
public class BlogController {
|
||||
|
||||
private final FriendService friendService;
|
||||
|
||||
/** @return 所有友链列表 */
|
||||
@GetMapping("/friend")
|
||||
public List<Friend> friend() {
|
||||
return friendService.listAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.imyeyu.api.modules.blog.entity;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.imyeyu.api.modules.common.bean.CommentSupport;
|
||||
import com.imyeyu.spring.entity.Destroyable;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 文章
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-03-01 17:10
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Article extends Entity implements CommentSupport, Destroyable {
|
||||
|
||||
/**
|
||||
* 文章渲染类型,对应前端模板
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-04 09:23
|
||||
*/
|
||||
public enum Type {
|
||||
|
||||
/** 公版 */
|
||||
PUBLIC,
|
||||
|
||||
/** 音乐 */
|
||||
MUSIC,
|
||||
|
||||
/** 软件 */
|
||||
SOFTWARE
|
||||
}
|
||||
|
||||
/** 标题 */
|
||||
protected String title;
|
||||
|
||||
/** 类型 */
|
||||
protected Type type;
|
||||
|
||||
/** 摘要 */
|
||||
protected String digest;
|
||||
|
||||
/** 数据 */
|
||||
protected String data;
|
||||
|
||||
/** 扩展数据 */
|
||||
protected JsonElement extendData;
|
||||
|
||||
/** 阅读数量 */
|
||||
protected int reads;
|
||||
|
||||
/** 喜欢数量 */
|
||||
protected int likes;
|
||||
|
||||
/** true 为显示评论 */
|
||||
protected boolean showComment;
|
||||
|
||||
/** true 为可评论 */
|
||||
protected boolean canComment;
|
||||
|
||||
/** true 为可排位 */
|
||||
protected boolean canRanking;
|
||||
|
||||
/** true 为可通过列表查询 */
|
||||
protected boolean canList;
|
||||
|
||||
/** @return true 为可评论 */
|
||||
@Override
|
||||
public boolean canComment() {
|
||||
return canComment;
|
||||
}
|
||||
|
||||
/** @return true 为不可评论 */
|
||||
@Override
|
||||
public boolean canNotComment() {
|
||||
return !canComment;
|
||||
}
|
||||
|
||||
/** @return true 为可排位 */
|
||||
public boolean canRanking() {
|
||||
return canRanking;
|
||||
}
|
||||
|
||||
/** @return true 为不可排位 */
|
||||
public boolean canNotRanking() {
|
||||
return !canRanking;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.imyeyu.api.modules.blog.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
|
||||
/**
|
||||
* 访问排行(每周)
|
||||
* 只记录访问次数、标题和最近访问,具体文章由 Redis key 记录
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-03-01 17:10
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ArticleRanking extends Entity {
|
||||
|
||||
private String title;
|
||||
private Article.Type type;
|
||||
private int count = 1;
|
||||
private Long recentAt; // 最近访问
|
||||
|
||||
public ArticleRanking(Long id, String title, Article.Type type) {
|
||||
setId(id);
|
||||
this.title = title;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/** 访问计数 + 1 */
|
||||
public void read() {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.imyeyu.api.modules.blog.entity;
|
||||
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
||||
import com.imyeyu.spring.annotation.table.AutoUUID;
|
||||
import com.imyeyu.spring.annotation.table.Id;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 评论回复提醒队列,和 CommentReplyRecord 不一样,本队列在推送消息后就删除,而后者会持久保存
|
||||
*
|
||||
* <p>基本逻辑:
|
||||
* <pre>
|
||||
* 触发:用户回复一条评论
|
||||
* 条件:被回复者是注册用户 && 不是回复自己 && 邮箱已验证 && 接收回复提醒邮件
|
||||
* 事件:添加本对象到队列列表,等待邮件推送服务调度,邮件推送服务
|
||||
* </pre>
|
||||
* 会针对用户收集本队列消息组合成邮件再一并推送
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-25 00:00
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class CommentRemindQueue {
|
||||
|
||||
@Id
|
||||
@AutoUUID
|
||||
private String UUID;
|
||||
|
||||
private Long userId;
|
||||
|
||||
private Long replyId;
|
||||
|
||||
private CommentReplyView reply;
|
||||
}
|
||||
21
src/main/java/com/imyeyu/api/modules/blog/entity/Friend.java
Normal file
21
src/main/java/com/imyeyu/api/modules/blog/entity/Friend.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.imyeyu.api.modules.blog.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
|
||||
/**
|
||||
* 夜雨 创建于 2021-07-15 15:59
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Friend extends Entity {
|
||||
|
||||
private String icon;
|
||||
private String name;
|
||||
private String link;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.imyeyu.api.modules.blog.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.blog.entity.Article;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文章
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-23 21:34
|
||||
*/
|
||||
public interface ArticleMapper extends BaseMapper<Article, Long> {
|
||||
|
||||
long countByKeyword(String keyword);
|
||||
|
||||
List<Article> selectByKeyword(String keyword, Long offset, int limit);
|
||||
|
||||
@Select("UPDATE `article` SET `likes` = `likes` + 1 WHERE `id` = #{articleId}")
|
||||
void like(Long articleId);
|
||||
|
||||
@Select("UPDATE `article` SET `reads` = `reads` + 1 WHERE `id` = #{articleId}")
|
||||
void read(Long articleId);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.imyeyu.api.modules.blog.mapper;
|
||||
|
||||
|
||||
import com.imyeyu.api.modules.blog.entity.Friend;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 友链
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-15 16:11
|
||||
*/
|
||||
public interface FriendMapper extends BaseMapper<Friend, Long> {
|
||||
|
||||
@Select("SELECT * FROM friend WHERE 1 = 1" + NOT_DELETE)
|
||||
List<Friend> listAll();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.imyeyu.api.modules.blog.service;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.blog.entity.Article;
|
||||
import com.imyeyu.api.modules.blog.entity.ArticleRanking;
|
||||
import com.imyeyu.api.modules.blog.vo.article.ArticleView;
|
||||
import com.imyeyu.api.modules.blog.vo.article.KeywordPage;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.GettableService;
|
||||
import com.imyeyu.spring.service.PageableService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文章服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-23 21:33
|
||||
*/
|
||||
public interface ArticleService extends GettableService<Article, Long>, PageableService<Article> {
|
||||
|
||||
/**
|
||||
* 获取文章,此方法触发阅读计数,包括触发每周热门排行统计,同一 IP 3 小时内访问多次的文章只计一次
|
||||
*
|
||||
* @param id 文章 ID
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
ArticleView view(long id);
|
||||
|
||||
PageResult<Article> pageByKeyword(KeywordPage page);
|
||||
|
||||
/**
|
||||
* 获取每周阅读排行
|
||||
*
|
||||
* @return 热门文章列表
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
List<ArticleRanking> listRanking();
|
||||
|
||||
/**
|
||||
* 喜欢文章
|
||||
*
|
||||
* @param articleId 文章 ID
|
||||
* @return 最新喜欢数量
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
int like(Long articleId);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.imyeyu.api.modules.blog.service;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.blog.entity.CommentRemindQueue;
|
||||
import com.imyeyu.spring.service.CreatableService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论回复队列服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-25 00:11
|
||||
*/
|
||||
public interface CommentRemindQueueService extends CreatableService<CommentRemindQueue> {
|
||||
|
||||
/**
|
||||
* 根据用户 ID 获取
|
||||
*
|
||||
* @param userId 用户 ID
|
||||
* @return 回复提醒列表
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
List<CommentRemindQueue> listByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 根据用户 ID 移出队列
|
||||
*
|
||||
* @param uid 用户 ID
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
void destroyByUserId(Long uid);
|
||||
|
||||
/**
|
||||
* 根据回复 ID 移出队列
|
||||
*
|
||||
* @param rid 回复 ID
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
void destroyByReplyId(Long rid);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.imyeyu.api.modules.blog.service;
|
||||
|
||||
import com.imyeyu.api.modules.blog.entity.Friend;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 友链服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-15 16:04
|
||||
*/
|
||||
public interface FriendService {
|
||||
|
||||
List<Friend> listAll();
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.imyeyu.api.modules.blog.service.implement;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.blog.entity.Article;
|
||||
import com.imyeyu.api.modules.blog.entity.ArticleRanking;
|
||||
import com.imyeyu.api.modules.blog.mapper.ArticleMapper;
|
||||
import com.imyeyu.api.modules.blog.service.ArticleService;
|
||||
import com.imyeyu.api.modules.blog.vo.article.ArticleView;
|
||||
import com.imyeyu.api.modules.blog.vo.article.KeywordPage;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.entity.Comment;
|
||||
import com.imyeyu.api.modules.common.entity.Tag;
|
||||
import com.imyeyu.api.modules.common.mapper.CommentMapper;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.common.service.TagService;
|
||||
import com.imyeyu.spring.TimiSpring;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import com.imyeyu.spring.util.Redis;
|
||||
import com.imyeyu.utils.Time;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文章服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-17 17:48
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ArticleServiceImplement extends AbstractEntityService<Article, Long> implements ArticleService {
|
||||
|
||||
private final TagService tagService;
|
||||
private final AttachmentService attachmentService;
|
||||
|
||||
private final ArticleMapper mapper;
|
||||
private final CommentMapper commentMapper;
|
||||
|
||||
private final Redis<String, Long> redisArticleRead;
|
||||
private final Redis<Long, ArticleRanking> redisArticleRanking;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<Article, Long> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public ArticleView view(long id) {
|
||||
String ip = TimiSpring.getRequestIP();
|
||||
Article article = get(id);
|
||||
TimiException.required(article, "article.not_found");
|
||||
// 计数
|
||||
if (!redisArticleRead.contains(ip, article.getId())) {
|
||||
// 3 小时内访问记录
|
||||
redisArticleRead.add(ip, article.getId());
|
||||
redisArticleRead.setExpire(ip, Time.H * 3);
|
||||
mapper.read(article.getId());
|
||||
// 每周访问计数
|
||||
if (article.canRanking()) {
|
||||
ArticleRanking ranking = redisArticleRanking.get(article.getId());
|
||||
if (ranking == null) {
|
||||
ranking = new ArticleRanking(article.getId(), article.getTitle(), article.getType());
|
||||
ranking.setRecentAt(Time.now());
|
||||
redisArticleRanking.set(article.getId(), ranking, Time.D * 7);
|
||||
} else {
|
||||
ranking.read();
|
||||
ranking.setRecentAt(Time.now());
|
||||
redisArticleRanking.setAndKeepTTL(article.getId(), ranking);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArticleView view = new ArticleView();
|
||||
BeanUtils.copyProperties(article, view);
|
||||
view.setComments(commentMapper.countAll(Comment.BizType.ARTICLE, article.getId()));
|
||||
view.setTagList(tagService.listByBizID(Tag.BizType.ARTICLE, String.valueOf(article.getId())));
|
||||
view.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.ARTICLE, article.getId()));
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<Article> pageByKeyword(KeywordPage page) {
|
||||
PageResult<Article> result = new PageResult<>();
|
||||
result.setList(mapper.selectByKeyword(page.getKeyword(), page.getOffset(), page.getLimit()));
|
||||
result.setTotal(mapper.countByKeyword(page.getKeyword()));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ArticleRanking> listRanking() {
|
||||
List<ArticleRanking> list = redisArticleRanking.values();
|
||||
list.sort(Comparator.comparing(ArticleRanking::getCount).reversed());
|
||||
return list.subList(0, Math.min(10, list.size()));
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public int like(Long articleId) {
|
||||
mapper.like(articleId);
|
||||
return get(articleId).getLikes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.imyeyu.api.modules.blog.service.implement;
|
||||
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.blog.entity.CommentRemindQueue;
|
||||
import com.imyeyu.api.modules.blog.service.CommentRemindQueueService;
|
||||
import com.imyeyu.api.modules.common.mapper.CommentRemindQueueMapper;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论回复队列服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-25 00:11
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CommentRemindQueueServiceImplement extends AbstractEntityService<CommentRemindQueue, String> implements CommentRemindQueueService {
|
||||
|
||||
private final CommentRemindQueueMapper mapper;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<CommentRemindQueue, String> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommentRemindQueue> listByUserId(Long userId) {
|
||||
return mapper.listByUserId(userId);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void destroyByUserId(Long userId) {
|
||||
mapper.destroyByUserId(userId);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void destroyByReplyId(Long replyId) {
|
||||
mapper.destroyByReplyId(replyId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.imyeyu.api.modules.blog.service.implement;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import com.imyeyu.api.modules.blog.entity.Friend;
|
||||
import com.imyeyu.api.modules.blog.mapper.FriendMapper;
|
||||
import com.imyeyu.api.modules.blog.service.FriendService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 友链服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-15 16:05
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FriendServiceImplement implements FriendService {
|
||||
|
||||
private final FriendMapper mapper;
|
||||
|
||||
@Override
|
||||
public List<Friend> listAll() {
|
||||
return mapper.listAll();
|
||||
}
|
||||
}
|
||||
147
src/main/java/com/imyeyu/api/modules/blog/util/UserToken.java
Normal file
147
src/main/java/com/imyeyu/api/modules/blog/util/UserToken.java
Normal file
@@ -0,0 +1,147 @@
|
||||
package com.imyeyu.api.modules.blog.util;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.config.RedisConfig;
|
||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||
import com.imyeyu.api.modules.common.entity.User;
|
||||
import com.imyeyu.api.modules.common.service.SettingService;
|
||||
import com.imyeyu.api.modules.common.service.UserService;
|
||||
import com.imyeyu.spring.TimiSpring;
|
||||
import com.imyeyu.spring.util.Redis;
|
||||
import com.imyeyu.spring.util.RedisSerializers;
|
||||
import com.imyeyu.utils.Time;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Redis 令牌缓存
|
||||
*
|
||||
* <p>一级缓存 Session,二级缓存 Redis,有效期为 {@link SettingKey#TTL_USER_TOKEN} 天,每次触发
|
||||
* 二级缓存获取时会刷新这个时间,即指定天数内不再访问则视为登出
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2023-07-17 16:58
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
|
||||
public class UserToken {
|
||||
|
||||
private final RedisConfig redisConfig;
|
||||
|
||||
private final UserService userService;
|
||||
private final SettingService settingService;
|
||||
|
||||
private Redis<String, Long> redis;
|
||||
|
||||
@PostConstruct
|
||||
private void postConstruct() {
|
||||
redis = redisConfig.getRedis(redisConfig.getDatabase().getUserToken(), RedisSerializers.STRING, RedisSerializers.LONG);
|
||||
}
|
||||
|
||||
public Long set(String token, Long userId) {
|
||||
long ttl = Time.D * settingService.getAsInt(SettingKey.TTL_USER_TOKEN);
|
||||
// 会话
|
||||
TimiSpring.setSessionAttr(token, userId);
|
||||
// 跨站 Cookie
|
||||
Cookie cookie = Objects.requireNonNullElse(TimiSpring.getCookie("Token"), new Cookie("Token", token));
|
||||
cookie.setDomain(settingService.getAsString(SettingKey.DOMAIN_ROOT));
|
||||
cookie.setPath("/");
|
||||
cookie.setSecure(true);
|
||||
cookie.setMaxAge((int) (ttl / 1000));
|
||||
TimiSpring.addCookie(cookie);
|
||||
// Redis
|
||||
redis.set(token, userId, ttl);
|
||||
return Time.now() + ttl;
|
||||
}
|
||||
|
||||
public Long getExpireAt(String token) {
|
||||
return Time.now() + redis.getExpire(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取令牌是否有效
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return true 为有效
|
||||
*/
|
||||
public boolean isValid(String token) {
|
||||
return getUserId(token) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取令牌是否无效({@link #isValid(String)} 取反)
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return true 为无效
|
||||
*/
|
||||
public boolean isInvalid(String token) {
|
||||
return !isValid(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户 ID
|
||||
* <p>一级缓存 Session,二级缓存 Redis,每次触发二级缓存获取时会刷新这个时间
|
||||
* <p>Session 存键为 token,值为 UserId,Redis 也相同
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 用户 ID
|
||||
* @throws TimiException 无效 token
|
||||
*/
|
||||
public @Nullable Long getRequiredUserId(String token) throws TimiException {
|
||||
return TimiException.required(getUserId(token), "invalid token");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户 ID
|
||||
* <p>一级缓存 Session,二级缓存 Redis,每次触发二级缓存获取时会刷新这个时间
|
||||
* <p>Session 存键为 token,值为 UserId,Redis 也相同
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 用户 ID,token 无效时为 null
|
||||
*/
|
||||
public @Nullable Long getUserId(String token) {
|
||||
if (TimiJava.isEmpty(token)) {
|
||||
return null;
|
||||
}
|
||||
Long userId;
|
||||
// Session
|
||||
if (TimiSpring.getSessionAttr(token) instanceof Long sessionUserId) {
|
||||
userId = sessionUserId;
|
||||
} else {
|
||||
// Redis
|
||||
userId = redis.get(token);
|
||||
}
|
||||
if (TimiJava.isEmpty(userId)) {
|
||||
return null;
|
||||
}
|
||||
// 刷新
|
||||
set(token, userId);
|
||||
return userId;
|
||||
}
|
||||
|
||||
public @NotNull User getUser(String token) {
|
||||
return userService.get(getRequiredUserId(token));
|
||||
}
|
||||
|
||||
public void clear(String token) {
|
||||
// 会话
|
||||
TimiSpring.removeSessionAttr(token);
|
||||
// 清除跨站 Cookie
|
||||
Cookie cookie = new Cookie("Token", "DIED");
|
||||
cookie.setDomain(settingService.getAsString(SettingKey.DOMAIN_ROOT));
|
||||
cookie.setPath("/");
|
||||
cookie.setSecure(true);
|
||||
cookie.setMaxAge(0);
|
||||
TimiSpring.addCookie(cookie);
|
||||
// Redis
|
||||
redis.destroy(token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.imyeyu.api.modules.blog.vo.article;
|
||||
|
||||
import com.imyeyu.api.modules.blog.entity.Article;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.entity.Tag;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-08-07 17:19
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ArticleView extends Article {
|
||||
|
||||
private long comments;
|
||||
|
||||
private List<Tag> tagList;
|
||||
|
||||
private List<Attachment> attachmentList;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.imyeyu.api.modules.blog.vo.article;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-07-14 17:52
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ClassPage extends Page {
|
||||
|
||||
private long classId;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.imyeyu.api.modules.blog.vo.article;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-07-14 17:53
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class KeywordPage extends Page {
|
||||
|
||||
@NotBlank(message = "article.keyword.empty")
|
||||
private String keyword;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.imyeyu.api.modules.blog.vo.article;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-07-14 17:53
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class LabelPage extends Page {
|
||||
|
||||
private long labelId;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.imyeyu.api.modules.common.bean;
|
||||
|
||||
/**
|
||||
* 支持评论的实体
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2023-10-10 11:39
|
||||
*/
|
||||
public interface CommentSupport {
|
||||
|
||||
/** @return true 为可评论 */
|
||||
boolean canComment();
|
||||
|
||||
/** @return true 为不可评论 */
|
||||
boolean canNotComment();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.imyeyu.api.modules.common.bean;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
|
||||
/**
|
||||
* 邮件服务异常
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-10-03 11:14
|
||||
*/
|
||||
public class EmailException extends TimiException {
|
||||
|
||||
/** 邮箱 */
|
||||
@Setter
|
||||
@Getter
|
||||
private String email;
|
||||
|
||||
public EmailException(TimiCode code, String email) {
|
||||
super(code);
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public EmailException(TimiCode code, String msg, String email) {
|
||||
super(code, msg);
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.imyeyu.api.modules.common.bean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-09-20 11:49
|
||||
*/
|
||||
public enum ImageType {
|
||||
|
||||
/** 双线性 */
|
||||
AUTO,
|
||||
|
||||
/** 模糊 */
|
||||
SMOOTH,
|
||||
|
||||
/** 像素 */
|
||||
PIXELATED
|
||||
}
|
||||
160
src/main/java/com/imyeyu/api/modules/common/bean/SettingKey.java
Normal file
160
src/main/java/com/imyeyu/api/modules/common/bean/SettingKey.java
Normal file
@@ -0,0 +1,160 @@
|
||||
package com.imyeyu.api.modules.common.bean;
|
||||
|
||||
/**
|
||||
* 系统设置
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-20 21:46
|
||||
*/
|
||||
public enum SettingKey {
|
||||
|
||||
// ---------- 通用 ----------
|
||||
|
||||
RUN_ENV,
|
||||
|
||||
PUBLIC_RESOURCES,
|
||||
|
||||
/** 启用注册 */
|
||||
ENABLE_REGISTER,
|
||||
|
||||
/** 启用登录 */
|
||||
ENABLE_LOGIN,
|
||||
|
||||
/** 启用评论 */
|
||||
ENABLE_COMMENT,
|
||||
|
||||
/** 启用测试 */
|
||||
ENABLE_DEBUG,
|
||||
|
||||
/** 启用账号数据更新(User 和 UserProfile) */
|
||||
ENABLE_USER_UPDATE,
|
||||
|
||||
/** 启用灰色滤镜 */
|
||||
ENABLE_GRAY_FILTER,
|
||||
|
||||
// ---------- ICP 备案号 ----------
|
||||
|
||||
ICP_IMYEYU_COM,
|
||||
|
||||
// ---------- 域名 ----------
|
||||
|
||||
DOMAIN_ROOT,
|
||||
|
||||
DOMAIN_API,
|
||||
|
||||
DOMAIN_GIT,
|
||||
|
||||
DOMAIN_BLOG,
|
||||
|
||||
DOMAIN_SPACE,
|
||||
|
||||
DOMAIN_RESOURCE,
|
||||
|
||||
DOMAIN_DOWNLOAD,
|
||||
|
||||
DOMAIN_FOREVER_MC,
|
||||
|
||||
// ---------- ForeverMC ----------
|
||||
|
||||
/** 启用登录服务 */
|
||||
FMC_PLAYER_LOGIN_ENABLE,
|
||||
|
||||
/** 最多绑定玩家数量 */
|
||||
FMC_MAX_BIND,
|
||||
|
||||
/** 闪烁标语 */
|
||||
FMC_SPLASHES,
|
||||
|
||||
/** 启动器背景 */
|
||||
FMC_BG,
|
||||
|
||||
FMC_BGM,
|
||||
|
||||
FMC_BG_SWIPER,
|
||||
|
||||
/** JRE 列表 */
|
||||
FMC_JRE,
|
||||
|
||||
/** 辅助登录模组 */
|
||||
FMC_LOGIN_FABRIC,
|
||||
|
||||
/** 启用图片地图上传 */
|
||||
FMC_ENABLE_IMAGE_MAP_UPLOAD,
|
||||
|
||||
/** 玩家登录令牌有效期(天) */
|
||||
FMC_PLAYER_LOGIN_TOKEN_TTL,
|
||||
|
||||
/** 服务器与数据中心的通信令牌 */
|
||||
FMC_SERVER_TOKEN,
|
||||
|
||||
// ---------- 生存时间 ----------
|
||||
|
||||
TTL_USER_TOKEN,
|
||||
|
||||
TTL_SETTING,
|
||||
|
||||
TTL_MULTILINGUAL,
|
||||
|
||||
// ---------- 多语言翻译 ----------
|
||||
|
||||
MULTILINGUAL_TRANSLATE_API,
|
||||
|
||||
MULTILINGUAL_TRANSLATE_APP_ID,
|
||||
|
||||
MULTILINGUAL_TRANSLATE_KEY,
|
||||
|
||||
// ---------- 账单 ----------
|
||||
|
||||
BILL_API_TOKEN,
|
||||
|
||||
// ---------- Git ----------
|
||||
|
||||
GIT_API,
|
||||
|
||||
GIT_ABOUT_ARTICLE,
|
||||
|
||||
GIT_REPO_PATH,
|
||||
|
||||
// ---------- 远程音乐 ----------
|
||||
|
||||
MUSIC_MAX_FRAME_LENGTH,
|
||||
|
||||
MUSIC_PLAYER_PORT,
|
||||
|
||||
MUSIC_PLAYER_IP,
|
||||
|
||||
MUSIC_CONTROLLER_PORT,
|
||||
|
||||
MUSIC_CONTROLLER_IP,
|
||||
|
||||
MUSIC_CONTROLLER_URI,
|
||||
|
||||
// ---------- 系统 ----------
|
||||
|
||||
SYSTEM_FILE_BASE,
|
||||
|
||||
SYSTEM_FILE_TYPE,
|
||||
|
||||
SYSTEM_FILE_SYNC,
|
||||
|
||||
/** 文件过滤(通过密钥类型) */
|
||||
SYSTEM_FILE_FILTER,
|
||||
|
||||
SYSTEM_STATUS_RATE,
|
||||
|
||||
SYSTEM_STATUS_LIMIT,
|
||||
|
||||
SYSTEM_STATUS_NETWORK_MAC,
|
||||
|
||||
SYSTEM_TERMINAL_TTL,
|
||||
|
||||
SYSTEM_TERMINAL_FILTERS,
|
||||
|
||||
/** 一般密钥 */
|
||||
SYSTEM_API_KEY,
|
||||
|
||||
/** 超级密钥 */
|
||||
SYSTEM_API_SUPER_KEY,
|
||||
|
||||
SYSTEM_REBOOT_COMMAND,
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.imyeyu.api.modules.common.controller;
|
||||
|
||||
import com.imyeyu.api.annotation.CaptchaValid;
|
||||
import com.imyeyu.api.annotation.EnableSetting;
|
||||
import com.imyeyu.api.bean.CaptchaFrom;
|
||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||
import com.imyeyu.api.modules.common.entity.Comment;
|
||||
import com.imyeyu.api.modules.common.entity.CommentReply;
|
||||
import com.imyeyu.api.modules.common.service.CommentReplyService;
|
||||
import com.imyeyu.api.modules.common.service.CommentService;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyPage;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentView;
|
||||
import com.imyeyu.api.modules.git.vo.issue.CommentPage;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.CaptchaData;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 评论操作接口
|
||||
* <p>*评论回复只依赖评论而不含业务关联,评论有关联业务,所以此接口是通用接口
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-23 21:36
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/comment")
|
||||
public class CommentController {
|
||||
|
||||
private final CommentService service;
|
||||
private final CommentReplyService replyService;
|
||||
|
||||
@AOPLog
|
||||
@CaptchaValid(CaptchaFrom.COMMENT)
|
||||
@EnableSetting(value = SettingKey.ENABLE_COMMENT, message = "comment.off_service")
|
||||
@RequestRateLimit
|
||||
@PostMapping("/create")
|
||||
public void create(@Valid @RequestBody CaptchaData<Comment> captchaData) {
|
||||
service.create(captchaData.getData());
|
||||
}
|
||||
|
||||
@RequestRateLimit
|
||||
@PostMapping("/list")
|
||||
public PageResult<CommentView> list(@Valid @RequestBody CommentPage commentPage) {
|
||||
return service.pageByBizId(commentPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建评论回复
|
||||
*
|
||||
* @param request 回复数据
|
||||
*/
|
||||
@AOPLog
|
||||
@CaptchaValid(CaptchaFrom.COMMENT_REPLY)
|
||||
@EnableSetting(value = SettingKey.ENABLE_COMMENT, message = "comment.off_service")
|
||||
@RequestRateLimit
|
||||
@PostMapping("/reply/create")
|
||||
public void createReply(@Valid @RequestBody CaptchaData<CommentReply> request) {
|
||||
replyService.create(request.getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取回复列表
|
||||
*
|
||||
* @param page 页面参数
|
||||
* @return 回复列表
|
||||
*/
|
||||
@RequestRateLimit
|
||||
@RequestMapping("/reply/list")
|
||||
public PageResult<CommentReplyView> pageCommentReplies(@Valid @RequestBody CommentReplyPage page) {
|
||||
// 通用接口,只允许查询评论的回复
|
||||
page.setBizType(CommentReplyPage.BizType.COMMENT);
|
||||
return replyService.pageByBizType(page);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
package com.imyeyu.api.modules.common.controller;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.imyeyu.io.IO;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.java.ref.Ref;
|
||||
import com.imyeyu.network.Network;
|
||||
import com.imyeyu.api.bean.CaptchaFrom;
|
||||
import com.imyeyu.api.modules.common.bean.ImageType;
|
||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.entity.Setting;
|
||||
import com.imyeyu.api.modules.common.entity.Task;
|
||||
import com.imyeyu.api.modules.common.entity.Template;
|
||||
import com.imyeyu.api.modules.common.entity.Version;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.common.service.FeedbackService;
|
||||
import com.imyeyu.api.modules.common.service.SettingService;
|
||||
import com.imyeyu.api.modules.common.service.TaskService;
|
||||
import com.imyeyu.api.modules.common.service.TemplateService;
|
||||
import com.imyeyu.api.modules.common.service.VersionService;
|
||||
import com.imyeyu.api.modules.common.vo.FeedbackRequest;
|
||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentView;
|
||||
import com.imyeyu.api.modules.system.util.ResourceHandler;
|
||||
import com.imyeyu.api.util.CaptchaManager;
|
||||
import com.imyeyu.spring.TimiSpring;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.IgnoreGlobalReturn;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.CaptchaData;
|
||||
import com.mongodb.client.gridfs.GridFSBucket;
|
||||
import com.mongodb.client.gridfs.GridFSDownloadStream;
|
||||
import com.mongodb.client.gridfs.model.GridFSFile;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.tika.Tika;
|
||||
import org.springframework.data.mongodb.gridfs.GridFsResource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 系统接口
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-23 21:38
|
||||
*/
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/")
|
||||
public class CommonController {
|
||||
|
||||
private final TaskService taskService;
|
||||
private final VersionService versionService;
|
||||
private final SettingService settingService;
|
||||
private final FeedbackService feedbackService;
|
||||
private final TemplateService templateService;
|
||||
private final AttachmentService attachmentService;
|
||||
|
||||
private final Gson gson;
|
||||
private final Yaml yaml;
|
||||
private final GridFSBucket gridFSBucket;
|
||||
private final CaptchaManager captchaManager;
|
||||
private final ResourceHandler resourceHandler;
|
||||
|
||||
@AOPLog
|
||||
@RequestMapping("")
|
||||
public String root() {
|
||||
return "IT WORKING! " + TimiSpring.getRequestIP();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param from 来源
|
||||
* @param response 返回对象
|
||||
*/
|
||||
@IgnoreGlobalReturn
|
||||
@GetMapping("/captcha")
|
||||
public void captcha(int width, int height, CaptchaFrom from, HttpServletResponse response) {
|
||||
// 返回图像流
|
||||
response.setHeader("Pragma", "no-cache");
|
||||
response.setHeader("Cache-Control", "no-cache"); // 禁止缓存
|
||||
response.setDateHeader("Expires", -1);
|
||||
response.setContentType("image/jpg");
|
||||
try {
|
||||
// 宽度
|
||||
if (width < 64) {
|
||||
ImageIO.write(captchaManager.error(TimiCode.ARG_BAD), "jpg", response.getOutputStream());
|
||||
return;
|
||||
}
|
||||
// 高度
|
||||
if (height < 19) {
|
||||
ImageIO.write(captchaManager.error(TimiCode.ARG_BAD), "jpg", response.getOutputStream());
|
||||
return;
|
||||
}
|
||||
// 来自
|
||||
if (TimiJava.isEmpty(from)) {
|
||||
ImageIO.write(captchaManager.error(TimiCode.ARG_MISS), "jpg", response.getOutputStream());
|
||||
return;
|
||||
}
|
||||
// 输出图像流
|
||||
ImageIO.write(captchaManager.generate(from, width, height), "jpg", response.getOutputStream());
|
||||
} catch (Exception e) {
|
||||
log.error("CommonController.getCaptcha", e);
|
||||
try {
|
||||
ImageIO.write(captchaManager.error(TimiCode.ERROR), "jpg", response.getOutputStream());
|
||||
} catch (IOException subE) {
|
||||
log.error("write error image fail", subE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取软件最新版本状态
|
||||
*
|
||||
* @param name 软件名称
|
||||
* @return 最新版本状态
|
||||
* @deprecated 兼容旧程序
|
||||
*/
|
||||
@AOPLog
|
||||
@GetMapping("/versions/{name}")
|
||||
@Deprecated
|
||||
public Version versions(@Valid @NotBlank @PathVariable("name") String name) {
|
||||
return versionService.getByName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取软件最新版本状态
|
||||
*
|
||||
* @param name 软件名称
|
||||
* @return 最新版本状态
|
||||
*/
|
||||
@AOPLog
|
||||
@GetMapping("/version/{name}")
|
||||
public Version version(@Valid @NotBlank @PathVariable("name") String name) {
|
||||
return versionService.getByName(name);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@PostMapping("/feedback")
|
||||
public void createFeedback(@Valid @NotNull @RequestBody CaptchaData<FeedbackRequest> request) {
|
||||
captchaManager.test(request.getCaptcha(), request.getFrom());
|
||||
feedbackService.create(request.getData());
|
||||
}
|
||||
|
||||
/** @return 公开任务信息 */
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@GetMapping("/tasklist")
|
||||
public List<Task> getTasks() {
|
||||
return taskService.listAll4Public();
|
||||
}
|
||||
|
||||
@RequestRateLimit
|
||||
@GetMapping("/template")
|
||||
public String viewTemplate(@RequestParam Template.BizType bizType, @RequestParam String bizCode) {
|
||||
return templateService.get(bizType, bizCode).getData();
|
||||
}
|
||||
|
||||
@RequestRateLimit
|
||||
@GetMapping("/setting/{key}")
|
||||
public String settingByKey(@PathVariable("key") String key, @RequestParam(value = "as", required = false) Setting.Type asType) {
|
||||
Setting setting = settingService.getByKey(SettingKey.valueOf(key.toUpperCase()));
|
||||
if (setting.isPrivate()) {
|
||||
throw new TimiException(TimiCode.PERMISSION_ERROR);
|
||||
}
|
||||
String result = setting.getValue();
|
||||
if (asType == null) {
|
||||
return result;
|
||||
}
|
||||
switch (asType) {
|
||||
case JSON -> {
|
||||
if (setting.getType() == Setting.Type.YAML) {
|
||||
Map<String, Object> obj = yaml.load(setting.getValue());
|
||||
result = gson.toJson(obj);
|
||||
}
|
||||
}
|
||||
case YAML -> {
|
||||
if (setting.getType() == Setting.Type.JSON) {
|
||||
Map<String, Object> obj = gson.fromJson(setting.getValue(), new TypeToken<Map<String, Object>>() {}.getType());
|
||||
result = yaml.dump(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@RequestRateLimit
|
||||
@PostMapping("/setting/map")
|
||||
public Map<SettingKey, String> mapSettingByKeys(@RequestBody Map<SettingKey, Map<String, Object>> settingMap) {
|
||||
List<Setting> result = settingService.listByKeys(new ArrayList<>(settingMap.keySet()));
|
||||
for (int i = 0; i < result.size(); i++) {
|
||||
Setting setting = result.get(i);
|
||||
if (setting.isPrivate()) {
|
||||
throw new TimiException(TimiCode.PERMISSION_ERROR);
|
||||
}
|
||||
Map<String, Object> args = settingMap.get(setting.getKey());
|
||||
if (args == null) {
|
||||
continue;
|
||||
}
|
||||
if (args.containsKey("as")) {
|
||||
switch (Ref.toType(Setting.Type.class, args.get("as").toString())) {
|
||||
case JSON -> {
|
||||
if (setting.getType() == Setting.Type.YAML) {
|
||||
Map<String, Object> obj = new Yaml().load(setting.getValue());
|
||||
setting.setValue(gson.toJson(obj));
|
||||
}
|
||||
}
|
||||
case YAML -> {
|
||||
if (setting.getType() == Setting.Type.JSON) {
|
||||
Map<String, Object> obj = gson.fromJson(setting.getValue(), new TypeToken<Map<String, Object>>() {}.getType());
|
||||
setting.setValue(new Yaml().dump(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.stream().collect(Collectors.toMap(Setting::getKey, Setting::getValue));
|
||||
}
|
||||
|
||||
@RequestRateLimit
|
||||
@GetMapping("/setting/flushCache")
|
||||
public void settingFlushCache() {
|
||||
settingService.flushCache();
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@GetMapping("/attachment/{mongoId}")
|
||||
public AttachmentView getAttachment(@PathVariable String mongoId) {
|
||||
return attachmentService.viewByMongoId(mongoId);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@IgnoreGlobalReturn
|
||||
@GetMapping("/attachment/read/{mongoId}")
|
||||
public void readAttachment(
|
||||
@PathVariable String mongoId,
|
||||
@RequestParam(name = "size", required = false) Integer size,
|
||||
@RequestParam(name = "type", required = false) String type,
|
||||
HttpServletRequest req,
|
||||
HttpServletResponse resp
|
||||
) {
|
||||
try {
|
||||
GridFSFile file = attachmentService.readByMongoId(mongoId);
|
||||
if (file == null) {
|
||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
resp.setCharacterEncoding(StandardCharsets.UTF_8.toString());
|
||||
return;
|
||||
}
|
||||
GridFSDownloadStream mimeReadStream = gridFSBucket.openDownloadStream(file.getObjectId());
|
||||
String mimeType = new Tika().detect(mimeReadStream);
|
||||
if (TimiJava.isNotEmpty(mimeType)) {
|
||||
resp.setContentType(mimeType);
|
||||
}
|
||||
if (size != null) {
|
||||
String fileType = switch (mimeType) {
|
||||
case "image/png" -> "png";
|
||||
case "image/jpeg" -> "jpg";
|
||||
default -> throw new TimiException(TimiCode.ARG_BAD).msgKey("TODO not support Re render mineType");
|
||||
};
|
||||
|
||||
switch (mimeType) {
|
||||
case "image/png", "image/jpeg" -> {
|
||||
// 图片缩放
|
||||
GridFSDownloadStream stream = gridFSBucket.openDownloadStream(file.getObjectId());
|
||||
byte[] bytes = IO.toBytes(stream);
|
||||
BufferedImage imgSrc = ImageIO.read(new ByteArrayInputStream(bytes));
|
||||
|
||||
double scale;
|
||||
if (imgSrc.getHeight() < imgSrc.getWidth()) {
|
||||
// 横向
|
||||
scale = 1D * size / imgSrc.getWidth();
|
||||
} else {
|
||||
scale = 1D * size / imgSrc.getHeight();
|
||||
}
|
||||
int width = (int) (imgSrc.getWidth() * scale);
|
||||
int height = (int) (imgSrc.getHeight() * scale);
|
||||
BufferedImage imgResult = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = imgResult.createGraphics();
|
||||
if (ImageType.PIXELATED == Ref.toType(ImageType.class, type)) {
|
||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
|
||||
} else {
|
||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
|
||||
g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
|
||||
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
}
|
||||
g.drawImage(imgSrc, 0, 0, width, height, null);
|
||||
ImageIO.write(imgResult, fileType, resp.getOutputStream());
|
||||
}
|
||||
default -> throw new TimiException(TimiCode.ARG_BAD).msgKey("TODO not support Re render mineType");
|
||||
}
|
||||
} else {
|
||||
GridFSDownloadStream downloadStream = gridFSBucket.openDownloadStream(file.getObjectId());
|
||||
GridFsResource gridFsResource = new GridFsResource(file, downloadStream);
|
||||
req.setAttribute(ResourceHandler.ATTR_TYPE, ResourceHandler.Type.MONGO);
|
||||
req.setAttribute(ResourceHandler.ATTR_VALUE, gridFsResource);
|
||||
resourceHandler.handleRequest(req, resp);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("read attachment error", e);
|
||||
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
resp.setCharacterEncoding(StandardCharsets.UTF_8.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@IgnoreGlobalReturn
|
||||
@GetMapping("/attachment/download/{mongoId}")
|
||||
public void downloadAttachment(@PathVariable String mongoId, HttpServletResponse resp) {
|
||||
try {
|
||||
Attachment attachment = attachmentService.getByMongoId(mongoId);
|
||||
GridFSFile file = attachmentService.readByMongoId(mongoId);
|
||||
if (file == null) {
|
||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
resp.setCharacterEncoding(StandardCharsets.UTF_8.toString());
|
||||
return;
|
||||
}
|
||||
{
|
||||
GridFSDownloadStream downloadStream = gridFSBucket.openDownloadStream(file.getObjectId());
|
||||
String mimeType = new Tika().detect(downloadStream);
|
||||
if (TimiJava.isNotEmpty(mimeType)) {
|
||||
resp.setContentType(mimeType);
|
||||
}
|
||||
}
|
||||
GridFSDownloadStream downloadStream = gridFSBucket.openDownloadStream(file.getObjectId());
|
||||
resp.setHeader("Content-Disposition", Network.getFileDownloadHeader(file.getFilename()));
|
||||
resp.setHeader("Content-Range", String.valueOf(attachment.getSize()));
|
||||
resp.setHeader("Accept-Ranges", "bytes");
|
||||
resp.setContentLengthLong(attachment.getSize());
|
||||
IO.toOutputStream(downloadStream, resp.getOutputStream());
|
||||
resp.flushBuffer();
|
||||
} catch (Exception e) {
|
||||
log.error("read attachment error", e);
|
||||
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
resp.setCharacterEncoding(StandardCharsets.UTF_8.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.imyeyu.api.modules.common.controller;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Icon;
|
||||
import com.imyeyu.api.modules.common.service.IconService;
|
||||
import com.imyeyu.api.modules.common.vo.icon.AllResponse;
|
||||
import com.imyeyu.api.modules.common.vo.icon.NamePage;
|
||||
import com.imyeyu.api.modules.common.vo.icon.UnicodePage;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.bean.Page;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* timi-icon 前端接口
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-09-14 23:59
|
||||
*/
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/icon")
|
||||
public class IconController {
|
||||
|
||||
private final IconService service;
|
||||
|
||||
/**
|
||||
* 获取图标列表
|
||||
*
|
||||
* @param page 查询列表参数
|
||||
* @return 图标列表
|
||||
*/
|
||||
@RequestRateLimit
|
||||
@PostMapping("/list")
|
||||
public PageResult<Icon> list(@RequestBody Page page) {
|
||||
return service.page(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有图标,为了减小传输数据,此接口只返回 name 名称、Unicode 代码和 SVG 路径
|
||||
*
|
||||
* @param latest 请求缓存的最新数据时间
|
||||
* @return 所有图标,如果请求缓存的最新时间等于数据库的最新数据时间,不返回任何数据
|
||||
*/
|
||||
@RequestRateLimit
|
||||
@GetMapping("/list/all")
|
||||
public AllResponse listAll(@Valid @RequestParam Long latest) {
|
||||
AllResponse resp = service.listAll(latest);
|
||||
List<Icon> icons = resp.getIcons();
|
||||
for (int i = 0; i < icons.size(); i++) {
|
||||
Icon icon = icons.get(i);
|
||||
icon.setId(null);
|
||||
icon.setCreatedAt(null);
|
||||
icon.setUpdatedAt(null);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称获取查询列表参数列表
|
||||
*
|
||||
* @param page 查询列表参数
|
||||
* @return 图标列表
|
||||
*/
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@PostMapping("/list/name")
|
||||
public PageResult<Icon> listByName(@Valid @RequestBody NamePage page) {
|
||||
return service.pageByName(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 Unicode 获取图标列表
|
||||
*
|
||||
* @param page 查询列表参数
|
||||
* @return 图标列表
|
||||
*/
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@PostMapping("/list/unicode")
|
||||
public PageResult<Icon> listByUnicode(@Valid @RequestBody UnicodePage page) {
|
||||
return service.pageByUnicode(page);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
package com.imyeyu.api.modules.common.controller;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.annotation.CaptchaValid;
|
||||
import com.imyeyu.api.annotation.EnableSetting;
|
||||
import com.imyeyu.api.bean.CaptchaFrom;
|
||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||
import com.imyeyu.api.modules.common.entity.CommentReply;
|
||||
import com.imyeyu.api.modules.common.entity.UserConfig;
|
||||
import com.imyeyu.api.modules.common.entity.UserPrivacy;
|
||||
import com.imyeyu.api.modules.common.service.CommentReplyService;
|
||||
import com.imyeyu.api.modules.common.service.CommentService;
|
||||
import com.imyeyu.api.modules.common.service.UserConfigService;
|
||||
import com.imyeyu.api.modules.common.service.UserPrivacyService;
|
||||
import com.imyeyu.api.modules.common.service.UserProfileService;
|
||||
import com.imyeyu.api.modules.common.service.UserService;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyPage;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentView;
|
||||
import com.imyeyu.api.modules.common.vo.comment.UserCommentPage;
|
||||
import com.imyeyu.api.modules.common.vo.user.EmailVerifyCallbackRequest;
|
||||
import com.imyeyu.api.modules.common.vo.user.LoginRequest;
|
||||
import com.imyeyu.api.modules.common.vo.user.LoginResponse;
|
||||
import com.imyeyu.api.modules.common.vo.user.RegisterRequest;
|
||||
import com.imyeyu.api.modules.common.vo.user.UpdatePasswordByKeyRequest;
|
||||
import com.imyeyu.api.modules.common.vo.user.UpdatePasswordRequest;
|
||||
import com.imyeyu.api.modules.common.vo.user.UserRequest;
|
||||
import com.imyeyu.api.modules.common.vo.user.UserView;
|
||||
import com.imyeyu.spring.annotation.AOPLog;
|
||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||
import com.imyeyu.spring.annotation.RequestSingleParam;
|
||||
import com.imyeyu.spring.annotation.RequiredToken;
|
||||
import com.imyeyu.spring.bean.CaptchaData;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.utils.Time;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 用户接口
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-23 21:38
|
||||
*/
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/user")
|
||||
public class UserController implements TimiJava {
|
||||
|
||||
private final UserService service;
|
||||
private final CommentService commentService;
|
||||
private final UserConfigService configService;
|
||||
private final UserProfileService profileService;
|
||||
private final UserPrivacyService privacyService;
|
||||
private final CommentReplyService commentReplyService;
|
||||
|
||||
/**
|
||||
* 注册。执行成功会自动登录
|
||||
*
|
||||
* @param request 注册请求
|
||||
* @return 登录数据
|
||||
*/
|
||||
@AOPLog
|
||||
@CaptchaValid(CaptchaFrom.REGISTER)
|
||||
@EnableSetting(value = SettingKey.ENABLE_REGISTER, message = "user.register.off_service")
|
||||
@RequestRateLimit(value = 1, lifeCycle = 60)
|
||||
@PostMapping("/register")
|
||||
public LoginResponse register(@Valid @RequestBody CaptchaData<RegisterRequest> request) {
|
||||
return service.register(request.getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*
|
||||
* @param request 登录请求
|
||||
* @return 登录数据
|
||||
*/
|
||||
@AOPLog
|
||||
@CaptchaValid(CaptchaFrom.LOGIN)
|
||||
@EnableSetting(value = SettingKey.ENABLE_LOGIN, message = "user.login.off_service")
|
||||
@RequestRateLimit
|
||||
@PostMapping("/login")
|
||||
public LoginResponse login(@Valid @RequestBody CaptchaData<LoginRequest> request) {
|
||||
return service.login(request.getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据令牌登录,请求头携带 Token 参数,通常用于延续登录令牌
|
||||
*
|
||||
* @return 登录数据
|
||||
*/
|
||||
@AOPLog
|
||||
@EnableSetting(value = SettingKey.ENABLE_LOGIN, message = "user.login.off_service")
|
||||
@RequestRateLimit
|
||||
@PostMapping("/login/token")
|
||||
public LoginResponse login4Token() {
|
||||
return service.login4Token();
|
||||
}
|
||||
|
||||
/** 登出 */
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@PostMapping("/logout")
|
||||
public void logout() {
|
||||
service.logout();
|
||||
}
|
||||
|
||||
/** 发送邮箱验证邮件 */
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit(value = 1, lifeCycle = 50)
|
||||
@PostMapping("/email/verify")
|
||||
public void sendEmailVerify() {
|
||||
service.sendEmailVerify();
|
||||
}
|
||||
|
||||
/**
|
||||
* 邮箱验证邮件回调,验证请求的密钥来源于 {@link #sendEmailVerify()} 接口发送的邮件
|
||||
*
|
||||
* @param request 邮箱验证请求
|
||||
*/
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit(value = 1, lifeCycle = 50)
|
||||
@PostMapping("/email/verify/callback")
|
||||
public void emailVerifyCallback(@Valid @RequestBody EmailVerifyCallbackRequest request) {
|
||||
service.emailVerifyCallback(request.getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码,需要已登录状态,使用旧密码修改
|
||||
*
|
||||
* @param request 修改密码请求
|
||||
*/
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@PostMapping("/password/update")
|
||||
public void updatePassword(@Valid @RequestBody UpdatePasswordRequest request) {
|
||||
service.updatePassword(request.getOldValue(), request.getNewValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送用于重置密码的忘记密码邮件,入参数据可能是 UID、邮箱或用户名,该数据目标用户的邮箱需要通过验证
|
||||
*
|
||||
* @param request 忘记密码邮件请求
|
||||
*/
|
||||
@AOPLog
|
||||
@CaptchaValid(CaptchaFrom.RESET_PASSWORD)
|
||||
@RequestRateLimit(value = 1, lifeCycle = 50)
|
||||
@PostMapping("/password/forget")
|
||||
public void sendPasswordForgetVerify(@Valid @RequestBody CaptchaData<String> request) {
|
||||
service.sendPasswordForgetVerify(request.getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码,不需要登录状态,入参数据的密钥来源于 {@link #sendPasswordForgetVerify(CaptchaData)} 接口发送的邮件
|
||||
*
|
||||
* @param request 重置密码请求
|
||||
*/
|
||||
@AOPLog
|
||||
@RequestRateLimit(value = 1, lifeCycle = 50)
|
||||
@PostMapping("/password/reset")
|
||||
public void resetPasswordByKey(@Valid @RequestBody UpdatePasswordByKeyRequest request) {
|
||||
service.resetPasswordByKey(request.getKey(), request.getNewPassword());
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销账号,此操作将会标记此用户的所有数据为删除状态
|
||||
*
|
||||
* @param password 密码
|
||||
*/
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@PostMapping("/cancel")
|
||||
public void cancel(@RequestSingleParam String password) {
|
||||
service.cancel(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户资料
|
||||
*
|
||||
* @param userId 目标用户 ID
|
||||
* @return 用户资料
|
||||
*/
|
||||
@AOPLog
|
||||
@RequestRateLimit
|
||||
@PostMapping("/view/{userId}")
|
||||
public UserView view(@Min(1) @NotNull @PathVariable Long userId) throws Exception {
|
||||
return service.view(userId).doFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户数据
|
||||
*
|
||||
* @param data 用户数据(包括账号数据)
|
||||
*/
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@EnableSetting(value = SettingKey.ENABLE_USER_UPDATE, message = "user.data.off_service")
|
||||
@RequestRateLimit
|
||||
@PostMapping("/profile/update")
|
||||
public void updateProfile(@Valid UserRequest data) {
|
||||
profileService.update(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户隐私控制
|
||||
*
|
||||
* @return 用户资料
|
||||
*/
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@PostMapping("/privacy")
|
||||
public UserPrivacy privacy() {
|
||||
return privacyService.get(service.getLoginUser().getId());
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@PostMapping("/privacy/update")
|
||||
public void updatePrivacy(@Valid @RequestBody UserPrivacy privacy) {
|
||||
privacyService.update(privacy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户设置
|
||||
*
|
||||
* @return 用户设置
|
||||
*/
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@PostMapping("/config")
|
||||
public UserConfig config() {
|
||||
return configService.get(service.getLoginUser().getId());
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@PostMapping("/config/update")
|
||||
public void updateConfig(@Valid @RequestBody UserConfig config) {
|
||||
configService.update(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户评论
|
||||
*/
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@PostMapping("/comment/list")
|
||||
public PageResult<CommentView> listComment(@Valid @RequestBody UserCommentPage page) {
|
||||
page.setUserId(service.getLoginUser().getId());
|
||||
return commentService.pageByUserId(page);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@PostMapping("/comment/delete")
|
||||
public void deleteComment(@RequestSingleParam Long commentId) {
|
||||
commentService.get(commentId);
|
||||
commentService.delete(commentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户被回复的评论
|
||||
*/
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@PostMapping("/comment/reply/list")
|
||||
public PageResult<CommentReplyView> listCommentReply(@Valid @RequestBody CommentReplyPage page) {
|
||||
page.setBizId(service.getLoginUser().getId());
|
||||
return commentReplyService.pageByBizType(page);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@PostMapping("/comment/reply/delete")
|
||||
public void deleteCommentReply(@RequestSingleParam Long replyId) {
|
||||
CommentReply reply = commentReplyService.get(replyId);
|
||||
TimiException.requiredTrue(reply.getSenderId().equals(service.getLoginUser().getId()), "user.comment.reply.delete.not_owner");
|
||||
commentReplyService.delete(replyId);
|
||||
}
|
||||
|
||||
@AOPLog
|
||||
@RequiredToken
|
||||
@RequestRateLimit
|
||||
@PostMapping("/comment/reply/ignore")
|
||||
public void ignoreCommentReply(@RequestSingleParam Long replyId) {
|
||||
CommentReply reply = commentReplyService.get(replyId);
|
||||
TimiException.requiredTrue(reply.getReceiverId().equals(service.getLoginUser().getId()), "user.comment.reply.ignore.not_owner");
|
||||
reply.setIgnoredAt(Time.now());
|
||||
commentReplyService.update(reply);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import com.imyeyu.java.ref.Ref;
|
||||
import com.imyeyu.api.bean.MultilingualHandler;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-08-15 10:17
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Attachment extends Entity implements MultilingualHandler {
|
||||
|
||||
/**
|
||||
* 附件类型
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2023-08-21 16:32
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum BizType {
|
||||
|
||||
/** 用户 */
|
||||
USER,
|
||||
|
||||
/** 文章 */
|
||||
ARTICLE,
|
||||
|
||||
/** Git */
|
||||
GIT,
|
||||
|
||||
/** 歌词 */
|
||||
LYRIC,
|
||||
|
||||
/** ForeverMC */
|
||||
FMC,
|
||||
|
||||
/** 镜像 */
|
||||
MIRROR,
|
||||
|
||||
/** 系统 */
|
||||
SYSTEM
|
||||
}
|
||||
|
||||
private BizType bizType;
|
||||
|
||||
private Long bizId;
|
||||
|
||||
private String attachType;
|
||||
|
||||
private String mongoId;
|
||||
|
||||
@MultilingualField
|
||||
private String title;
|
||||
|
||||
private String name;
|
||||
|
||||
private Long size;
|
||||
|
||||
public void setAttachTypeValue(Enum<?> attachType) {
|
||||
this.attachType = attachType.toString();
|
||||
}
|
||||
|
||||
public <T extends Enum<T>> T getAttachTypeValue(Class<T> attachTypeClass) {
|
||||
return Ref.toType(attachTypeClass, attachType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import com.imyeyu.spring.service.GettableService;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import com.imyeyu.api.modules.blog.service.implement.ArticleServiceImplement;
|
||||
import com.imyeyu.api.modules.common.bean.CommentSupport;
|
||||
import com.imyeyu.api.modules.git.service.implement.IssueServiceImplement;
|
||||
import com.imyeyu.api.modules.git.service.implement.MergeServiceImplement;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
|
||||
/**
|
||||
* 评论
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-25 14:46
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Comment extends Entity {
|
||||
|
||||
|
||||
/**
|
||||
* 关联业务类型
|
||||
* <p>
|
||||
* TODO 添加模块名称以便区别邮件通知推送来源,使用多语言键
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2023-08-06 23:42
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum BizType {
|
||||
|
||||
ARTICLE(ArticleServiceImplement.class),
|
||||
|
||||
GIT_ISSUE(IssueServiceImplement.class),
|
||||
|
||||
GIT_MERGE(MergeServiceImplement.class);
|
||||
|
||||
final Class<? extends GettableService<? extends CommentSupport, Long>> serviceClass;
|
||||
}
|
||||
|
||||
/** 关联业务类型 */
|
||||
private BizType bizType;
|
||||
|
||||
/** 关联业务 ID */
|
||||
private Long bizId;
|
||||
|
||||
/** 发送用户 ID,登录用户评论有值,游客无 */
|
||||
private Long userId;
|
||||
|
||||
/** 发送用户昵称,游客评论有值,登录用户无 */
|
||||
private String nick;
|
||||
|
||||
/** 评论数据 */
|
||||
@NotBlank
|
||||
private String content;
|
||||
|
||||
/** 发送用户 IP */
|
||||
private String ip;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
|
||||
/**
|
||||
* 评论回复
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-03-01 17:11
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CommentReply extends Entity {
|
||||
|
||||
/** 所属评论 ID */
|
||||
private Long commentId;
|
||||
|
||||
/** 被回复的回复,回复主评论时为 NULL */
|
||||
private Long replyId;
|
||||
|
||||
/** 发送用户 ID,登录用户回复有值,游客无 */
|
||||
private Long senderId;
|
||||
|
||||
/** 回复用户 ID,系统用户回复有值,游客无 */
|
||||
private Long receiverId;
|
||||
|
||||
/** 发送用户昵称,游客回复有值,登录用户无 */
|
||||
private String senderNick;
|
||||
|
||||
/** 回复用户昵称,游客回复有值,系统用户无 */
|
||||
private String receiverNick;
|
||||
|
||||
/** 回复数据 */
|
||||
private String content;
|
||||
|
||||
/** 发送用户 IP */
|
||||
private String ip;
|
||||
|
||||
/** 被回复用户忽略该回复的时间 */
|
||||
private Long ignoredAt;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import com.imyeyu.spring.annotation.table.AutoUUID;
|
||||
import com.imyeyu.spring.annotation.table.Id;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 邮件队列
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-24 14:59
|
||||
*/
|
||||
@Data
|
||||
public class EmailQueue {
|
||||
|
||||
/**
|
||||
* 业务类型
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-24 15:54
|
||||
*/
|
||||
public enum BizType {
|
||||
|
||||
/** 回复提醒 */
|
||||
REPLY_REMINAD,
|
||||
|
||||
/** 邮箱验证 */
|
||||
EMAIL_VERIFY,
|
||||
|
||||
/** 重置密码 */
|
||||
RESET_PASSWORD
|
||||
}
|
||||
|
||||
@Id
|
||||
@AutoUUID
|
||||
private String UUID;
|
||||
|
||||
private BizType bizType;
|
||||
|
||||
private Long bizId;
|
||||
|
||||
private Long sendAt;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2021-08-24 18:00
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EmailQueueLog extends Entity {
|
||||
|
||||
private String UUID;
|
||||
private EmailQueue.BizType bizType;
|
||||
private Long bizId;
|
||||
private String sendTo;
|
||||
private Long sendAt;
|
||||
|
||||
private Boolean isSent;
|
||||
private String exceptionMsg;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
|
||||
/**
|
||||
* 反馈
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-11-16 22:22
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Feedback extends Entity {
|
||||
|
||||
private String from;
|
||||
private String email;
|
||||
private String data;
|
||||
private String ip;
|
||||
}
|
||||
25
src/main/java/com/imyeyu/api/modules/common/entity/Icon.java
Normal file
25
src/main/java/com/imyeyu/api/modules/common/entity/Icon.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
|
||||
/**
|
||||
* 字体图标
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-09-09 10:54
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Icon extends Entity {
|
||||
|
||||
/** 名称 */
|
||||
private String name;
|
||||
|
||||
/** Unicode */
|
||||
private String unicode;
|
||||
|
||||
/** SVG 路径 */
|
||||
private String svg;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.java.ref.Ref;
|
||||
import com.imyeyu.api.TimiServerAPI;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-10-24 16:41
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Multilingual extends Entity {
|
||||
|
||||
protected String key;
|
||||
|
||||
protected String zhCN;
|
||||
|
||||
protected String zhTW;
|
||||
|
||||
protected String enUS;
|
||||
|
||||
protected String ruRU;
|
||||
|
||||
protected String koKR;
|
||||
|
||||
protected String jaJP;
|
||||
|
||||
protected String deDE;
|
||||
|
||||
/** @return 根据用户环境获取语言值 */
|
||||
public String getValue() {
|
||||
try {
|
||||
Field field = Ref.getField(getClass(), TimiServerAPI.getUserLanguage().toString().replace("_", ""));
|
||||
if (field == null) {
|
||||
throw new TimiException(TimiCode.RESULT_NULL).msgKey("TODO not support language");
|
||||
}
|
||||
return Ref.getFieldValue(this, field, String.class);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定语言值
|
||||
*
|
||||
* @param language 指定语言
|
||||
* @return 值
|
||||
*/
|
||||
public String getValue(com.imyeyu.java.bean.Language language) {
|
||||
try {
|
||||
Field field = Ref.getField(getClass(), language.toString().replace("_", ""));
|
||||
if (field == null) {
|
||||
throw new TimiException(TimiCode.RESULT_NULL).msgKey("TODO not support language");
|
||||
}
|
||||
return Ref.getFieldValue(this, field, String.class);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||
import com.imyeyu.spring.annotation.table.Id;
|
||||
import com.imyeyu.spring.entity.Creatable;
|
||||
import com.imyeyu.spring.entity.Updatable;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 系统配置
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-20 21:46
|
||||
*/
|
||||
@Data
|
||||
public class Setting implements Creatable, Updatable {
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2025-01-10 17:08
|
||||
*/
|
||||
public enum Type {
|
||||
|
||||
INTEGER,
|
||||
|
||||
STRING,
|
||||
|
||||
JSON,
|
||||
|
||||
YAML,
|
||||
}
|
||||
|
||||
@Id
|
||||
private SettingKey key;
|
||||
|
||||
private String value;
|
||||
|
||||
private Type type;
|
||||
|
||||
private boolean isPrivate;
|
||||
|
||||
private Long createdAt;
|
||||
|
||||
private Long updatedAt;
|
||||
|
||||
public boolean isPublic() {
|
||||
return !isPrivate;
|
||||
}
|
||||
}
|
||||
39
src/main/java/com/imyeyu/api/modules/common/entity/Tag.java
Normal file
39
src/main/java/com/imyeyu/api/modules/common/entity/Tag.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import com.imyeyu.api.bean.MultilingualHandler;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2024-08-28 14:26
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Tag extends Entity implements MultilingualHandler {
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2024-08-28 14:26
|
||||
*/
|
||||
public enum BizType {
|
||||
|
||||
ARTICLE,
|
||||
|
||||
MUSIC,
|
||||
|
||||
SERVER_FILE,
|
||||
|
||||
WALLPAPER
|
||||
}
|
||||
|
||||
protected BizType bizType;
|
||||
|
||||
protected String bizID;
|
||||
|
||||
@MultilingualHandler.MultilingualField
|
||||
protected String value;
|
||||
}
|
||||
40
src/main/java/com/imyeyu/api/modules/common/entity/Task.java
Normal file
40
src/main/java/com/imyeyu/api/modules/common/entity/Task.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 开发任务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-02-26 11:12
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Task extends Entity {
|
||||
|
||||
/**
|
||||
* 任务状态
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-02-26 11:53
|
||||
*/
|
||||
@Getter
|
||||
public enum Status {
|
||||
|
||||
UPDATE, WITH, WAIT, KEEP, DIE;
|
||||
|
||||
final int sort = ordinal();
|
||||
}
|
||||
|
||||
private String name;
|
||||
private Status status;
|
||||
private String digest;
|
||||
|
||||
// 关联数据
|
||||
private List<TaskDetail> details;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
|
||||
/**
|
||||
* 开发任务详细信息
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-02-27 17:58
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TaskDetail extends Entity {
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-02-27 18:03
|
||||
*/
|
||||
@Getter
|
||||
public enum Type {
|
||||
|
||||
BUG, FEATURE
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-02-27 18:06
|
||||
*/
|
||||
@Getter
|
||||
public enum Status {
|
||||
|
||||
UPDATE, WAIT, FINISH, CLOSE
|
||||
}
|
||||
|
||||
private Long taskId;
|
||||
private Type type;
|
||||
private Status status;
|
||||
private String digest;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-09-21 00:53
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Template extends Entity {
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2023-09-22 16:38
|
||||
*/
|
||||
public enum BizType {
|
||||
|
||||
GIT,
|
||||
|
||||
FOREVER_MC
|
||||
}
|
||||
|
||||
private BizType bizType;
|
||||
|
||||
private String bizCode;
|
||||
|
||||
private String data;
|
||||
}
|
||||
68
src/main/java/com/imyeyu/api/modules/common/entity/User.java
Normal file
68
src/main/java/com/imyeyu/api/modules/common/entity/User.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
import com.imyeyu.utils.Time;
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-03-01 17:11
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class User extends Entity {
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2024-02-21 14:48
|
||||
*/
|
||||
public enum AttachType {
|
||||
|
||||
AVATAR,
|
||||
|
||||
WRAPPER,
|
||||
|
||||
LICENSE,
|
||||
|
||||
DEFAULT_AVATAR,
|
||||
|
||||
DEFAULT_WRAPPER
|
||||
}
|
||||
|
||||
/** 用户名 */
|
||||
protected String name;
|
||||
|
||||
/** 密码 */
|
||||
protected String password;
|
||||
|
||||
/** 邮箱 */
|
||||
protected String email;
|
||||
|
||||
/** 邮箱验证时间 */
|
||||
protected Long emailVerifyAt;
|
||||
|
||||
/** 解除禁言时间 */
|
||||
protected Long unmuteAt;
|
||||
|
||||
/** 解除封禁时间 */
|
||||
protected Long unbanAt;
|
||||
|
||||
/** @return true 为禁言中 */
|
||||
public boolean isMuting() {
|
||||
return unmuteAt != null && Time.now() < unmuteAt;
|
||||
}
|
||||
|
||||
/** @return true 为封禁中 */
|
||||
public boolean isBanning() {
|
||||
return unbanAt != null && Time.now() < unbanAt;
|
||||
}
|
||||
|
||||
public boolean emailVerified() {
|
||||
return emailVerifyAt != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import com.imyeyu.spring.annotation.table.Id;
|
||||
import com.imyeyu.spring.entity.Updatable;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户设置
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-12 15:06
|
||||
*/
|
||||
@Data
|
||||
public class UserConfig implements Updatable {
|
||||
|
||||
@Min(1)
|
||||
@Id
|
||||
private Long userId;
|
||||
|
||||
private Boolean emailReplyRemind;
|
||||
|
||||
private Long updatedAt;
|
||||
|
||||
public UserConfig(Long uid) {
|
||||
this.userId = uid;
|
||||
emailReplyRemind = true;
|
||||
}
|
||||
|
||||
public Boolean isEmailReplyRemind() {
|
||||
return emailReplyRemind;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import com.imyeyu.java.ref.Ref;
|
||||
import com.imyeyu.spring.annotation.table.Id;
|
||||
import com.imyeyu.spring.entity.Updatable;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户隐私控制
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-27 16:51
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class UserPrivacy implements Updatable {
|
||||
|
||||
@Min(1)
|
||||
@Id
|
||||
private Long userId;
|
||||
|
||||
private boolean email;
|
||||
private boolean sex;
|
||||
private boolean birthdate;
|
||||
private boolean qq;
|
||||
private boolean lastLoginAt;
|
||||
private boolean createdAt;
|
||||
|
||||
private Long updatedAt;
|
||||
|
||||
public UserPrivacy(Long uid) {
|
||||
this.userId = uid;
|
||||
}
|
||||
|
||||
/** @return 过滤字段列表 */
|
||||
public List<String> listFilterFields() {
|
||||
return Ref.listFields(getClass()).stream().filter(f -> {
|
||||
try {
|
||||
f.setAccessible(true);
|
||||
return boolean.class.isAssignableFrom(f.getType()) && !(boolean) f.get(this);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).map(Field::getName).toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import com.imyeyu.api.modules.common.bean.ImageType;
|
||||
import com.imyeyu.spring.annotation.table.Id;
|
||||
import com.imyeyu.spring.entity.Updatable;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户数据
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-05-29 15:58
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class UserProfile implements Updatable {
|
||||
|
||||
/** 用户 ID */
|
||||
@Min(1)
|
||||
@Id
|
||||
protected Long userId;
|
||||
|
||||
/** 封面类型 */
|
||||
protected ImageType wrapperType;
|
||||
|
||||
/** 头像类型 */
|
||||
protected ImageType avatarType;
|
||||
|
||||
/** 经验值 */
|
||||
protected Integer exp;
|
||||
|
||||
/** 性别 */
|
||||
@Max(1)
|
||||
@Min(0)
|
||||
protected Byte sex;
|
||||
|
||||
/** 出生日期 */
|
||||
@Min(0)
|
||||
protected Long birthdate;
|
||||
|
||||
/** QQ */
|
||||
@Pattern(regexp = "[1-9]\\d{4,14}")
|
||||
protected String qq;
|
||||
|
||||
/** 说明 */
|
||||
@Size(max = 240)
|
||||
protected String description;
|
||||
|
||||
/** 最近登录 IP */
|
||||
protected String lastLoginIP;
|
||||
|
||||
/** 最近登录时间 */
|
||||
protected Long lastLoginAt;
|
||||
|
||||
/** 修改时间 */
|
||||
protected Long updatedAt;
|
||||
|
||||
public UserProfile(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.imyeyu.api.modules.common.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.imyeyu.spring.entity.Entity;
|
||||
|
||||
/**
|
||||
* 版本管理
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-06-10 16:01
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Version extends Entity {
|
||||
|
||||
private String name;
|
||||
private String version;
|
||||
private String content;
|
||||
private String url;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-08-15 10:22
|
||||
*/
|
||||
public interface AttachmentMapper extends BaseMapper<Attachment, Long> {
|
||||
|
||||
/** 有效条件,非删除和销毁 */
|
||||
String VALID = NOT_DELETE + " AND destroy_at IS NULL";
|
||||
|
||||
@Select("SELECT * FROM attachment WHERE biz_type = #{bizType} AND biz_id = #{bizId} " + VALID + LIMIT_1)
|
||||
Attachment selectByBizId(Attachment.BizType bizType, long bizId);
|
||||
|
||||
@Select("SELECT * FROM attachment WHERE mongo_id = #{mongoId} " + VALID + LIMIT_1)
|
||||
Attachment selectByMongoId(String mongoId);
|
||||
|
||||
@Select("SELECT * FROM attachment WHERE biz_type = #{bizType} AND biz_id = #{bizId} AND attach_type = #{attachType} " + VALID + LIMIT_1)
|
||||
Attachment selectByAttachType(Attachment.BizType bizType, long bizId, Enum<?> attachType);
|
||||
|
||||
@Select("SELECT * FROM attachment WHERE biz_type = #{bizType} AND biz_id = #{bizId} AND " + VALID + PAGE)
|
||||
List<Attachment> listByBizId(Attachment.BizType bizType, long bizId, long offset, int limit);
|
||||
|
||||
List<Attachment> listByAttachType(Attachment.BizType bizType, long bizId, Enum<?> ...attachTypes);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Comment;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentView;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-2-23 21:33
|
||||
*/
|
||||
public interface CommentMapper extends BaseMapper<Comment, Long> {
|
||||
|
||||
@Select("SELECT * FROM comment WHERE id = #{id}" + NOT_DELETE)
|
||||
Comment select(Long id);
|
||||
|
||||
@Update("UPDATE comment SET deleted_at = FLOOR(UNIX_TIMESTAMP(NOW(3)) * 1000) WHERE id = #{id}")
|
||||
@Override
|
||||
void delete(Long id);
|
||||
|
||||
@Select("SELECT COUNT(1) FROM comment WHERE biz_type = #{bizType} AND biz_id = #{bizId}" + NOT_DELETE)
|
||||
long count(Comment.BizType bizType, Long bizId);
|
||||
|
||||
long countAll(Comment.BizType bizType, Long bizId);
|
||||
|
||||
List<CommentView> list(Comment.BizType bizType, Long bizId, Long offset, int limit, LinkedHashMap<String, OrderType> orderMap);
|
||||
|
||||
long countByUserId(Long userId);
|
||||
|
||||
List<CommentView> listByUserId(Long userId, Long offset, int limit, LinkedHashMap<String, OrderType> orderMap);
|
||||
|
||||
@Update("UPDATE comment SET deleted_at = FLOOR(UNIX_TIMESTAMP(NOW(3)) * 1000) WHERE user_id = #{userId} ")
|
||||
void deleteByUserId(Long userId);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.blog.entity.CommentRemindQueue;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Delete;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论回复提醒队列
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-25 00:15
|
||||
*/
|
||||
public interface CommentRemindQueueMapper extends BaseMapper<CommentRemindQueue, String> {
|
||||
|
||||
@Select("SELECT * FROM comment_remind_queue WHERE user_id = #{userId}")
|
||||
List<CommentRemindQueue> listByUserId(Long userId);
|
||||
|
||||
@Delete("DELETE FROM comment_remind_queue WHERE user_id = #{userId}")
|
||||
void destroyByUserId(Long userId);
|
||||
|
||||
@Delete("DELETE FROM comment_remind_queue WHERE reply_id = #{replyId}")
|
||||
void destroyByReplyId(Long replyId);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.CommentReply;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyPage;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论回复
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-24 10:36
|
||||
*/
|
||||
public interface CommentReplyMapper extends BaseMapper<CommentReply, Long> {
|
||||
|
||||
@Select("SELECT * FROM comment_reply WHERE sender_id = #{senderId}" + NOT_DELETE)
|
||||
List<CommentReply> listAllBySenderId(Long senderId);
|
||||
|
||||
@Select("SELECT COUNT(1) FROM comment_reply WHERE ${bizType.column} = #{bizId}" + NOT_DELETE)
|
||||
long countByBizType(CommentReplyPage.BizType bizType, Long bizId);
|
||||
|
||||
@Select("SELECT * FROM comment_reply WHERE ${bizType.column} = #{bizId} AND ignored_at IS NULL" + NOT_DELETE + PAGE)
|
||||
List<CommentReplyView> listByBizType(CommentReplyPage.BizType bizType, Long bizId, Long offset, int limit);
|
||||
|
||||
@Update("UPDATE comment_reply SET deleted_at = " + UNIX_TIME + " WHERE sender_id = #{userId} OR receiver_id = #{userId}")
|
||||
void deleteByUserId(Long userId);
|
||||
|
||||
@Update("UPDATE comment_reply SET deleted_at = " + UNIX_TIME + " WHERE comment_id = #{commentId}")
|
||||
void deleteByCommentId(Long commentId);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.EmailQueueLog;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-08-10 10:38
|
||||
*/
|
||||
public interface EmailQueueLogMapper extends BaseMapper<EmailQueueLog, Long> {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.EmailQueue;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 邮件推送队列
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-24 16:22
|
||||
*/
|
||||
public interface EmailQueueMapper extends BaseMapper<EmailQueue, String> {
|
||||
|
||||
@Select("SELECT * FROM email_queue WHERE biz_type = #{bizType} AND biz_id = #{bizId}")
|
||||
EmailQueue query(EmailQueue.BizType bizType, Long bizId);
|
||||
|
||||
@Select("SELECT * FROM email_queue")
|
||||
List<EmailQueue> listAll();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Feedback;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* 反馈
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-11-16 22:28
|
||||
*/
|
||||
public interface FeedbackMapper extends BaseMapper<Feedback, Long> {
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Icon;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 图标
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-09-15 00:02
|
||||
*/
|
||||
public interface IconMapper extends BaseMapper<Icon, Long> {
|
||||
|
||||
@Select("SELECT COUNT(1) FROM icon" + NOT_DELETE)
|
||||
@Override
|
||||
long count();
|
||||
|
||||
@Select("SELECT * FROM icon LIMIT #{offset}, #{limit}" + NOT_DELETE)
|
||||
@Override
|
||||
List<Icon> list(long offset, int limit);
|
||||
|
||||
@Select("SELECT * FROM icon WHERE 1 = 1" + NOT_DELETE)
|
||||
List<Icon> listAll();
|
||||
|
||||
@Select("SELECT COUNT(1) FROM icon WHERE name LIKE CONCAT('%', #{name}, '%')" + NOT_DELETE)
|
||||
long countByName(String name);
|
||||
|
||||
@Select("SELECT * FROM icon WHERE name LIKE CONCAT('%', #{name}, '%')" + PAGE)
|
||||
List<Icon> listByName(String name, long offset, int limit);
|
||||
|
||||
long countByLabel(String lang, String label);
|
||||
|
||||
List<Icon> listByLabel(String lang, String label, long offset, int limit);
|
||||
|
||||
@Select("SELECT COUNT(1) FROM icon WHERE unicode = #{unicode}" + NOT_DELETE)
|
||||
long countByUnicode(String unicode);
|
||||
|
||||
@Select("SELECT * FROM icon WHERE unicode = #{unicode}" + PAGE)
|
||||
List<Icon> listByUnicode(String unicode, long offset, int limit);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Multilingual;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-10-25 10:47
|
||||
*/
|
||||
public interface MultilingualMapper extends BaseMapper<Multilingual, Long> {
|
||||
|
||||
// 以下临时
|
||||
|
||||
@Select("SELECT * FROM multilingual WHERE key LIKE CONCAT('%', #{key}, '%')" + NOT_DELETE)
|
||||
List<Multilingual> selectByKeyLike(String key);
|
||||
|
||||
List<Multilingual> selectByKeyList(List<String> keys);
|
||||
|
||||
// 以上临时
|
||||
|
||||
@Select("SELECT * FROM multilingual WHERE zh_cn = #{zhCN}" + NOT_DELETE + LIMIT_1)
|
||||
Multilingual selectByZhCN(String zhCN);
|
||||
|
||||
@Select("SELECT * FROM multilingual WHERE en_US IS NULL OR en_US = ''" + NOT_DELETE)
|
||||
List<Multilingual> selectByNotTranslate();
|
||||
|
||||
@Select("SELECT * FROM multilingual WHERE `key` = #{key}" + NOT_DELETE + LIMIT_1)
|
||||
Multilingual selectByKey(String key);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||
import com.imyeyu.api.modules.common.entity.Setting;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统配置
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-20 22:26
|
||||
*/
|
||||
public interface SettingMapper extends BaseMapper<Setting, String> {
|
||||
|
||||
@Select("SELECT * FROM `setting` WHERE `key` = #{key}")
|
||||
Setting selectByKey(SettingKey key);
|
||||
|
||||
@Select("SELECT * FROM `setting`")
|
||||
List<Setting> listAll();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Tag;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2025-05-30 22:48
|
||||
*/
|
||||
public interface TagMapper extends BaseMapper<Tag, Long> {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Task;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 任务服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-04-03 15:37
|
||||
*/
|
||||
public interface TaskMapper {
|
||||
|
||||
List<Task> listAll4Public();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Template;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-09-22 16:41
|
||||
*/
|
||||
public interface TemplateMapper extends BaseMapper<Template, String> {
|
||||
|
||||
@Select("SELECT * FROM template WHERE biz_type = #{bizType} AND biz_code = #{bizCode}" + NOT_DELETE)
|
||||
Template query(Template.BizType bizType, String bizCode);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.UserConfig;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* 用户设置
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-12 16:36
|
||||
*/
|
||||
public interface UserConfigMapper extends BaseMapper<UserConfig, Long> {
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.User;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-23 21:33
|
||||
*/
|
||||
public interface UserMapper extends BaseMapper<User, Long> {
|
||||
|
||||
@Select("SELECT * FROM user WHERE BINARY name = #{name}" + NOT_DELETE + LIMIT_1)
|
||||
User selectByName(String name);
|
||||
|
||||
@Select("SELECT * FROM user WHERE BINARY email = #{email} AND email_verify_at IS NOT NULL" + NOT_DELETE + LIMIT_1)
|
||||
User selectByEmail(String email);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.UserPrivacy;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* 用户隐私控制
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-27 17:18
|
||||
*/
|
||||
public interface UserPrivacyMapper extends BaseMapper<UserPrivacy, Long> {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.UserProfile;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* 用户数据
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-27 17:04
|
||||
*/
|
||||
public interface UserProfileMapper extends BaseMapper<UserProfile, Long> {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.imyeyu.api.modules.common.mapper;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Version;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
/**
|
||||
* 版本管理
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-06-10 16:08
|
||||
*/
|
||||
public interface VersionMapper extends BaseMapper<Version, String> {
|
||||
|
||||
@Select("SELECT * FROM version WHERE name = #{name}" + NOT_DELETE)
|
||||
Version queryByName(String name);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
|
||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentView;
|
||||
import com.imyeyu.spring.service.DeletableService;
|
||||
import com.imyeyu.spring.service.DestroyableService;
|
||||
import com.imyeyu.spring.service.GettableService;
|
||||
import com.mongodb.client.gridfs.model.GridFSFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 附件服务
|
||||
*
|
||||
* <p>删除和销毁都为数据库软删除,但删除不删除 MongoDB 文件,而销毁则删除 MongoDB 文件
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2023-08-15 10:21
|
||||
*/
|
||||
public interface AttachmentService extends GettableService<Attachment, Long>, DeletableService<Long>, DestroyableService<Long> {
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
void create(AttachmentRequest request);
|
||||
|
||||
Attachment getByBizId(Attachment.BizType bizType, long bizId);
|
||||
|
||||
Attachment getByAttachType(Attachment.BizType bizType, long bizId, Enum<?> attachType);
|
||||
|
||||
Attachment getByMongoId(String mongoId);
|
||||
|
||||
AttachmentView viewByMongoId(String mongoId);
|
||||
|
||||
GridFSFile readByMongoId(String mongoId);
|
||||
|
||||
InputStream getInputStreamByMongoId(String mongoId);
|
||||
|
||||
byte[] readAllByMongoId(String mongoId);
|
||||
|
||||
/**
|
||||
* 根据业务获取所有附件
|
||||
*
|
||||
* @param bizType 业务类型
|
||||
* @param bizId 业务 ID
|
||||
* @param attachTypes
|
||||
* @return 所有附件
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
List<Attachment> listByBizId(Attachment.BizType bizType, long bizId, Enum<?> ...attachTypes);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.CommentReply;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyPage;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.CreatableService;
|
||||
import com.imyeyu.spring.service.DeletableService;
|
||||
import com.imyeyu.spring.service.GettableService;
|
||||
import com.imyeyu.spring.service.UpdatableService;
|
||||
|
||||
/**
|
||||
* 评论回复服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-24 10:33
|
||||
*/
|
||||
public interface CommentReplyService extends CreatableService<CommentReply>, GettableService<CommentReply, Long>, UpdatableService<CommentReply>, DeletableService<Long> {
|
||||
|
||||
PageResult<CommentReplyView> pageByBizType(CommentReplyPage page);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.common.entity.Comment;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentView;
|
||||
import com.imyeyu.api.modules.common.vo.comment.UserCommentPage;
|
||||
import com.imyeyu.api.modules.git.vo.issue.CommentPage;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.CreatableService;
|
||||
import com.imyeyu.spring.service.DeletableService;
|
||||
import com.imyeyu.spring.service.GettableService;
|
||||
|
||||
/**
|
||||
* 评论服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-23 21:32
|
||||
*/
|
||||
public interface CommentService extends CreatableService<Comment>, GettableService<Comment, Long>, DeletableService<Long> {
|
||||
|
||||
PageResult<CommentView> pageByBizId(CommentPage page);
|
||||
|
||||
/**
|
||||
* 获取用户评论页面
|
||||
*
|
||||
* @param userCommentPage 页面参数
|
||||
* @return 页面列表
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
PageResult<CommentView> pageByUserId(UserCommentPage userCommentPage);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.common.entity.EmailQueue;
|
||||
import com.imyeyu.api.modules.common.entity.EmailQueueLog;
|
||||
import com.imyeyu.spring.service.CreatableService;
|
||||
import com.imyeyu.spring.service.DestroyableService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 邮件推送队列服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-24 16:20
|
||||
*/
|
||||
public interface EmailQueueService extends CreatableService<EmailQueue>, DestroyableService<String> {
|
||||
|
||||
/**
|
||||
* 根据推送类型和数据 ID 获取推送对象
|
||||
*
|
||||
* @param type 推送类型
|
||||
* @param dataId 数据 ID
|
||||
* @return 推送对象
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
EmailQueue get(EmailQueue.BizType type, Long dataId);
|
||||
|
||||
/**
|
||||
* 移出队列
|
||||
*
|
||||
* @param UUID UUID
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
void destroy(String UUID);
|
||||
|
||||
/**
|
||||
* 添加推送日志
|
||||
*
|
||||
* @param log 推送日志
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
void addLog(EmailQueueLog log);
|
||||
|
||||
/**
|
||||
* 遍历推送队列
|
||||
*
|
||||
* @return 推送队列
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
List<EmailQueue> listAll();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.api.modules.common.vo.FeedbackRequest;
|
||||
|
||||
/**
|
||||
* 反馈服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-11-16 22:27
|
||||
*/
|
||||
public interface FeedbackService {
|
||||
|
||||
void create(FeedbackRequest request);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Icon;
|
||||
import com.imyeyu.api.modules.common.vo.icon.AllResponse;
|
||||
import com.imyeyu.api.modules.common.vo.icon.NamePage;
|
||||
import com.imyeyu.api.modules.common.vo.icon.UnicodePage;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.service.PageableService;
|
||||
|
||||
/**
|
||||
* 图标服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-09-09 16:47
|
||||
*/
|
||||
public interface IconService extends PageableService<Icon> {
|
||||
|
||||
AllResponse listAll(Long latest);
|
||||
|
||||
PageResult<Icon> pageByName(NamePage page);
|
||||
|
||||
PageResult<Icon> pageByUnicode(UnicodePage page);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.java.bean.Language;
|
||||
import com.imyeyu.api.modules.common.entity.Multilingual;
|
||||
import com.imyeyu.spring.service.UpdatableService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-10-25 10:47
|
||||
*/
|
||||
public interface MultilingualService extends UpdatableService<Multilingual> {
|
||||
|
||||
Long create(String key, String zhCN);
|
||||
|
||||
Long createIfNotExist(String key, String zhCN);
|
||||
|
||||
String get(Language language, Long id);
|
||||
|
||||
String getByKey(Language language, String key);
|
||||
|
||||
List<Multilingual> listByNotTranslate();
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||
import com.imyeyu.api.modules.common.entity.Setting;
|
||||
import com.imyeyu.spring.service.UpdatableService;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统配置服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-20 22:06
|
||||
*/
|
||||
public interface SettingService extends UpdatableService<Setting> {
|
||||
|
||||
default List<Setting> listByKeys(SettingKey... keys) {
|
||||
return listByKeys(Arrays.asList(keys));
|
||||
}
|
||||
|
||||
List<Setting> listByKeys(List<SettingKey> keyList);
|
||||
|
||||
Setting getByKey(SettingKey key);
|
||||
|
||||
/**
|
||||
* 获取指定类型配置值字符串
|
||||
*
|
||||
* @param key 键
|
||||
* @return 配置值
|
||||
*/
|
||||
String getAsString(SettingKey key);
|
||||
|
||||
int getAsInt(SettingKey key);
|
||||
|
||||
long getAsLong(SettingKey key);
|
||||
|
||||
double getAsDouble(SettingKey key);
|
||||
|
||||
/**
|
||||
* 获取为布尔值
|
||||
*
|
||||
* @param key 键
|
||||
* @return 配置值
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
boolean is(SettingKey key);
|
||||
|
||||
/**
|
||||
* 获取为布尔值,并取反
|
||||
*
|
||||
* @param key 键
|
||||
* @return 配置值
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
boolean not(SettingKey key);
|
||||
|
||||
JsonElement getAsJsonElement(SettingKey key);
|
||||
|
||||
JsonObject getAsJsonObject(SettingKey key);
|
||||
|
||||
JsonArray getAsJsonArray(SettingKey key);
|
||||
|
||||
<T> T fromJson(SettingKey key, Class<T> clazz);
|
||||
|
||||
<T> T fromJson(SettingKey key, TypeToken<T> typeToken);
|
||||
|
||||
<T> T fromYaml(SettingKey key, Class<T> clazz);
|
||||
|
||||
List<Setting> listAll();
|
||||
|
||||
void flushCache();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.common.entity.Tag;
|
||||
import com.imyeyu.spring.service.CreatableService;
|
||||
import com.imyeyu.spring.service.DeletableService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2025-05-30 22:46
|
||||
*/
|
||||
public interface TagService extends CreatableService<Tag>, DeletableService<Long> {
|
||||
|
||||
List<Tag> listByBizID(Tag.BizType bizType, String bizID) throws TimiException;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.common.entity.Task;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 任务服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-04-03 15:36
|
||||
*/
|
||||
public interface TaskService {
|
||||
|
||||
/**
|
||||
* 查询所有公开任务
|
||||
*
|
||||
* @return 公开任务列表
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
List<Task> listAll4Public();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Template;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-09-22 16:38
|
||||
*/
|
||||
public interface TemplateService {
|
||||
|
||||
Template get(Template.BizType bizType, String bizCode);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.UserConfig;
|
||||
import com.imyeyu.spring.service.CreatableService;
|
||||
import com.imyeyu.spring.service.GettableService;
|
||||
import com.imyeyu.spring.service.UpdatableService;
|
||||
|
||||
/**
|
||||
* 用户设置服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-12 16:23
|
||||
*/
|
||||
public interface UserConfigService extends GettableService<UserConfig, Long>, CreatableService<UserConfig>, UpdatableService<UserConfig> {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.UserPrivacy;
|
||||
import com.imyeyu.spring.service.CreatableService;
|
||||
import com.imyeyu.spring.service.GettableService;
|
||||
import com.imyeyu.spring.service.UpdatableService;
|
||||
|
||||
/**
|
||||
* 用户隐私控制服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-27 17:18
|
||||
*/
|
||||
public interface UserPrivacyService extends GettableService<UserPrivacy, Long>, CreatableService<UserPrivacy>, UpdatableService<UserPrivacy> {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.UserProfile;
|
||||
import com.imyeyu.api.modules.common.vo.user.UserRequest;
|
||||
import com.imyeyu.spring.service.CreatableService;
|
||||
import com.imyeyu.spring.service.GettableService;
|
||||
import com.imyeyu.spring.service.UpdatableService;
|
||||
|
||||
/**
|
||||
* 用户数据服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-27 17:05
|
||||
*/
|
||||
public interface UserProfileService extends GettableService<UserProfile, Long>, CreatableService<UserProfile>, UpdatableService<UserProfile> {
|
||||
|
||||
void update(UserRequest request);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.common.entity.User;
|
||||
import com.imyeyu.api.modules.common.vo.user.LoginRequest;
|
||||
import com.imyeyu.api.modules.common.vo.user.LoginResponse;
|
||||
import com.imyeyu.api.modules.common.vo.user.RegisterRequest;
|
||||
import com.imyeyu.api.modules.common.vo.user.UserView;
|
||||
import com.imyeyu.spring.service.GettableService;
|
||||
|
||||
/**
|
||||
* 用户管理服务
|
||||
* <p>操作任何用户数据前应保证调用 find,以确保用户存在且没有注销(不需要判断,不存在时 find 会抛异常),如果不需要返回数据,可以调用
|
||||
* 一次 exist(),也不需要判断
|
||||
* <p>经过令牌验证的操作也不需要检验用户是否存在,不存在的用户无法登录也无法生成正确的令牌
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-23 21:32
|
||||
*/
|
||||
public interface UserService extends GettableService<User, Long> {
|
||||
|
||||
/**
|
||||
* 注册用户,传入参 User.password 是明文
|
||||
*
|
||||
* @param request 注册数据
|
||||
* @return 登录返回数据
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
LoginResponse register(RegisterRequest request);
|
||||
|
||||
/**
|
||||
* 执行登录
|
||||
*
|
||||
* @param request 登录数据
|
||||
* @return 登录返回数据
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
LoginResponse login(LoginRequest request);
|
||||
|
||||
/**
|
||||
* 用户视图,未经过权限过滤和隐私过滤数据
|
||||
*
|
||||
* @param userId
|
||||
* @return
|
||||
* @throws TimiException
|
||||
*/
|
||||
UserView view(Long userId);
|
||||
|
||||
/**
|
||||
* 密码验证
|
||||
*
|
||||
* @param digestPassword 摘要密码
|
||||
* @param password 明文密码
|
||||
* @return true 为无效密码
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
boolean isInvalidPassword(String digestPassword, String password);
|
||||
|
||||
/**
|
||||
* 令牌登录
|
||||
*
|
||||
* @return 登录结果
|
||||
*/
|
||||
LoginResponse login4Token();
|
||||
|
||||
/** 退出登录 */
|
||||
void logout();
|
||||
|
||||
/**
|
||||
* 查找用户
|
||||
*
|
||||
* @param user UID、邮箱和用户名
|
||||
* @return 用户
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
User get(String user);
|
||||
|
||||
boolean isLogged();
|
||||
|
||||
User getLoginUser();
|
||||
|
||||
/**
|
||||
* 根据用户名查找
|
||||
*
|
||||
* @param name 用户名
|
||||
* @return 账号数据
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
User getByName(String name);
|
||||
|
||||
/**
|
||||
* 根据邮箱查找
|
||||
*
|
||||
* @param email 邮箱
|
||||
* @return 账号数据
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
User getByEmail(String email);
|
||||
|
||||
/**
|
||||
* 发送邮箱验证邮件
|
||||
*
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
void sendEmailVerify();
|
||||
|
||||
/**
|
||||
* 邮箱验证回调
|
||||
*
|
||||
* @param key 邮件密钥(非登录令牌)
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
void emailVerifyCallback(String key);
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*
|
||||
* @param oldValue 旧密码
|
||||
* @param newValue 新密码
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
void updatePassword(String oldValue, String newValue);
|
||||
|
||||
|
||||
/**
|
||||
* 发送找回密码验证邮件
|
||||
*
|
||||
* @param user UID、用户名或邮箱
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
void sendPasswordForgetVerify(String user);
|
||||
|
||||
/**
|
||||
* 重置密码(需要密钥,由找回密码 sendPasswordForgetVerify 接口通过发送邮件分发)
|
||||
*
|
||||
* @param key 密钥
|
||||
* @param password 明文密码
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
void resetPasswordByKey(String key, String password);
|
||||
|
||||
/**
|
||||
* 注销
|
||||
*
|
||||
* @param password 密码校验
|
||||
* @throws TimiException 服务异常
|
||||
*/
|
||||
void cancel(String password);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.imyeyu.api.modules.common.service;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Version;
|
||||
|
||||
/**
|
||||
* 版本管理服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-06-10 16:06
|
||||
*/
|
||||
public interface VersionService {
|
||||
|
||||
Version getByName(String name);
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.io.IO;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.mapper.AttachmentMapper;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
|
||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentView;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import com.imyeyu.utils.Time;
|
||||
import com.mongodb.client.gridfs.GridFSBucket;
|
||||
import com.mongodb.client.gridfs.model.GridFSFile;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-08-15 10:21
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AttachmentServiceImplement extends AbstractEntityService<Attachment, Long> implements AttachmentService {
|
||||
|
||||
private final AttachmentMapper mapper;
|
||||
|
||||
private final GridFSBucket gridFSBucket;
|
||||
private final GridFsTemplate gridFsTemplate;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<Attachment, Long> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void destroy(Long id) {
|
||||
try {
|
||||
Attachment attachment = get(id);
|
||||
if (!attachment.isDeleted()) {
|
||||
delete(id);
|
||||
}
|
||||
mapper.destroy(attachment.getId());
|
||||
gridFsTemplate.delete(Query.query(Criteria.where("_id").is(attachment.getMongoId())));
|
||||
} catch (Exception e) {
|
||||
log.error("delete mongo file error", e);
|
||||
throw new TimiException(TimiCode.ERROR).msgKey("TODO delete mongo file error");
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void create(AttachmentRequest request) {
|
||||
TimiException.required(request.getBizType(), "not found request.bizType");
|
||||
TimiException.required(request.getBizId(), "not found request.bizId");
|
||||
TimiException.required(request.getName(), "not found request.name");
|
||||
String mongoId = null;
|
||||
try {
|
||||
InputStream is = request.getInputStream();
|
||||
TimiException.required(is, "not found request.inputStream");
|
||||
TimiException.requiredTrue(is.available() != 0, "empty request.inputStream");
|
||||
|
||||
StringBuilder mongoName = new StringBuilder(request.getBizType().toString());
|
||||
if (TimiJava.isNotEmpty(request.getAttachType())) {
|
||||
mongoName.append("_").append(request.getAttachType().toUpperCase()).append("_");
|
||||
}
|
||||
mongoName.append(request.getName());
|
||||
|
||||
mongoId = gridFsTemplate.store(is, mongoName.toString()).toString();
|
||||
Attachment attachment = new Attachment();
|
||||
BeanUtils.copyProperties(request, attachment);
|
||||
attachment.setMongoId(mongoId);
|
||||
attachment.setCreatedAt(Time.now());
|
||||
mapper.insert(attachment);
|
||||
} catch (Exception e) {
|
||||
if (mongoId != null) {
|
||||
gridFsTemplate.delete(Query.query(Criteria.where("_id").is(mongoId)));
|
||||
}
|
||||
log.error("create attachment error", e);
|
||||
throw new TimiException(TimiCode.ARG_BAD).msgKey("TODO read attachment input stream error");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attachment getByBizId(Attachment.BizType bizType, long bizId) {
|
||||
return mapper.selectByBizId(bizType, bizId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attachment getByAttachType(Attachment.BizType bizType, long bizId, Enum<?> attachType) {
|
||||
return mapper.selectByAttachType(bizType, bizId, attachType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attachment getByMongoId(String mongoId) {
|
||||
return mapper.selectByMongoId(mongoId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttachmentView viewByMongoId(String mongoId) {
|
||||
Attachment attachment = getByMongoId(mongoId);
|
||||
if (attachment == null) {
|
||||
throw new TimiException(TimiCode.RESULT_NULL).msgKey("TODO not found attachment");
|
||||
}
|
||||
AttachmentView view = new AttachmentView();
|
||||
BeanUtils.copyProperties(attachment, view);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GridFSFile readByMongoId(String mongoId) {
|
||||
Attachment view = mapper.selectByMongoId(mongoId);
|
||||
TimiException.required(view, "not found attachment: %s".formatted(mongoId));
|
||||
return gridFsTemplate.findOne(new Query(Criteria.where("_id").is(view.getMongoId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStreamByMongoId(String mongoId) {
|
||||
return gridFSBucket.openDownloadStream(readByMongoId(mongoId).getObjectId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readAllByMongoId(String mongoId) {
|
||||
try {
|
||||
return IO.toBytes(getInputStreamByMongoId(mongoId));
|
||||
} catch (IOException e) {
|
||||
throw new TimiException(TimiCode.ERROR, "TODO 读取失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Attachment> listByBizId(Attachment.BizType bizType, long bizId, Enum<?>... attachTypes) {
|
||||
return mapper.listByAttachType(bizType, bizId, attachTypes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.TimiServerAPI;
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.blog.entity.Article;
|
||||
import com.imyeyu.api.modules.blog.entity.CommentRemindQueue;
|
||||
import com.imyeyu.api.modules.blog.service.ArticleService;
|
||||
import com.imyeyu.api.modules.blog.service.CommentRemindQueueService;
|
||||
import com.imyeyu.api.modules.common.bean.CommentSupport;
|
||||
import com.imyeyu.api.modules.common.entity.Comment;
|
||||
import com.imyeyu.api.modules.common.entity.CommentReply;
|
||||
import com.imyeyu.api.modules.common.entity.EmailQueue;
|
||||
import com.imyeyu.api.modules.common.entity.User;
|
||||
import com.imyeyu.api.modules.common.entity.UserConfig;
|
||||
import com.imyeyu.api.modules.common.mapper.CommentReplyMapper;
|
||||
import com.imyeyu.api.modules.common.service.CommentReplyService;
|
||||
import com.imyeyu.api.modules.common.service.CommentService;
|
||||
import com.imyeyu.api.modules.common.service.EmailQueueService;
|
||||
import com.imyeyu.api.modules.common.service.UserConfigService;
|
||||
import com.imyeyu.api.modules.common.service.UserService;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyPage;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentView;
|
||||
import com.imyeyu.api.modules.git.service.RepositoryService;
|
||||
import com.imyeyu.spring.TimiSpring;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import com.imyeyu.spring.service.GettableService;
|
||||
import com.imyeyu.utils.Time;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 评论回复服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-24 10:34
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
|
||||
public class CommentReplyServiceImplement extends AbstractEntityService<CommentReply, Long> implements CommentReplyService {
|
||||
|
||||
private final UserService userService;
|
||||
private final CommentService commentService;
|
||||
private final ArticleService articleService;
|
||||
private final RepositoryService repositoryService;
|
||||
private final UserConfigService userConfigService;
|
||||
private final EmailQueueService emailQueueService;
|
||||
private final CommentRemindQueueService commentRemindQueueService;
|
||||
|
||||
private final CommentReplyMapper mapper;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<CommentReply, Long> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void delete(Long crId) {
|
||||
super.delete(crId);
|
||||
commentRemindQueueService.destroyByReplyId(crId);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void create(CommentReply commentReply) {
|
||||
Comment comment = commentService.get(commentReply.getCommentId());
|
||||
|
||||
Class<? extends GettableService<? extends CommentSupport, Long>> serviceClass = comment.getBizType().getServiceClass();
|
||||
GettableService<? extends CommentSupport, Long> service = TimiServerAPI.applicationContext.getBean(serviceClass);
|
||||
CommentSupport commentSupport = service.get(comment.getBizId());
|
||||
if (commentSupport.canNotComment()) {
|
||||
throw new TimiException(TimiCode.PERMISSION_ERROR).msgKey("评论已关闭");
|
||||
}
|
||||
|
||||
CommentReply dbReply = new CommentReply();
|
||||
dbReply.setCommentId(comment.getId());
|
||||
|
||||
// 发送者
|
||||
String token = TimiSpring.getToken();
|
||||
if (TimiJava.isNotEmpty(token)) {
|
||||
User senderUser = userService.getLoginUser();
|
||||
if (senderUser.isBanning() || senderUser.isMuting()) {
|
||||
throw new TimiException(TimiCode.RESULT_BAN).msgKey("comment.banded");
|
||||
}
|
||||
dbReply.setSenderId(senderUser.getId());
|
||||
dbReply.setSenderNick(null);
|
||||
} else {
|
||||
if (TimiJava.isEmpty(commentReply.getSenderNick())) {
|
||||
throw new TimiException(TimiCode.ARG_MISS).msgKey("comment.nick.empty");
|
||||
}
|
||||
dbReply.setSenderNick(commentReply.getSenderNick());
|
||||
}
|
||||
// 被回复
|
||||
dbReply.setReplyId(commentReply.getReplyId());
|
||||
if (commentReply.getReplyId() == null) {
|
||||
// 回复主评论
|
||||
dbReply.setReceiverId(comment.getUserId());
|
||||
if (TimiJava.isEmpty(comment.getUserId())) {
|
||||
dbReply.setReceiverNick(comment.getNick());
|
||||
}
|
||||
} else {
|
||||
// 回复其他回复
|
||||
CommentReply targetReply = get(commentReply.getReplyId());
|
||||
dbReply.setReceiverId(targetReply.getSenderId());
|
||||
if (TimiJava.isEmpty(targetReply.getSenderId())) {
|
||||
dbReply.setReceiverNick(targetReply.getSenderNick());
|
||||
}
|
||||
}
|
||||
// 创建回复
|
||||
dbReply.setContent(commentReply.getContent());
|
||||
dbReply.setIp(TimiSpring.getRequestIP());
|
||||
super.create(dbReply);
|
||||
// 被回复为注册用户时处理通知和邮件
|
||||
{
|
||||
if (TimiJava.isEmpty(dbReply.getReceiverId())) {
|
||||
return;
|
||||
}
|
||||
if (TimiJava.isNotEmpty(dbReply.getSenderId()) && dbReply.getSenderId().equals(dbReply.getReceiverId())) {
|
||||
return;
|
||||
}
|
||||
// 被回复账号
|
||||
User receiverUser = userService.get(dbReply.getReceiverId());
|
||||
UserConfig userConfig = userConfigService.get(dbReply.getReceiverId());
|
||||
if (!receiverUser.emailVerified() || !userConfig.isEmailReplyRemind()) {
|
||||
return;
|
||||
}
|
||||
// 添加提醒队列
|
||||
CommentRemindQueue remindQueue = new CommentRemindQueue();
|
||||
remindQueue.setUUID(UUID.randomUUID().toString());
|
||||
remindQueue.setUserId(receiverUser.getId());
|
||||
remindQueue.setReplyId(dbReply.getId());
|
||||
commentRemindQueueService.create(remindQueue);
|
||||
|
||||
// 邮件队列
|
||||
EmailQueue emailQueue = emailQueueService.get(EmailQueue.BizType.REPLY_REMINAD, receiverUser.getId());
|
||||
if (emailQueue == null) {
|
||||
emailQueue = new EmailQueue();
|
||||
emailQueue.setBizType(EmailQueue.BizType.REPLY_REMINAD);
|
||||
emailQueue.setBizId(receiverUser.getId());
|
||||
long H10 = Time.H * 10;
|
||||
if (Time.now() < Time.today() + H10) {
|
||||
emailQueue.setSendAt(Time.today() + H10);
|
||||
} else {
|
||||
emailQueue.setSendAt(Time.tomorrow() + H10);
|
||||
}
|
||||
emailQueueService.create(emailQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CommentReplyView> pageByBizType(CommentReplyPage page) {
|
||||
PageResult<CommentReplyView> result = new PageResult<>();
|
||||
List<CommentReplyView> list = mapper.listByBizType(page.getBizType(), page.getBizId(), page.getOffset(), page.getLimit());
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
CommentReplyView reply = list.get(i);
|
||||
CommentView comment = new CommentView();
|
||||
BeanUtils.copyProperties(commentService.get(reply.getCommentId()), comment);
|
||||
if (TimiJava.isNotEmpty(comment.getUserId())) {
|
||||
comment.setUser(userService.view(comment.getUserId()));
|
||||
}
|
||||
switch (comment.getBizType()) {
|
||||
case ARTICLE -> {
|
||||
Article article = articleService.get(comment.getBizId());
|
||||
article.setData(null);
|
||||
article.setExtendData(null);
|
||||
comment.setArticle(article);
|
||||
}
|
||||
// case GIT_ISSUE, GIT_MERGE -> comment.setRepository(repositoryService.get(comment.getBizId()));
|
||||
}
|
||||
reply.setComment(comment);
|
||||
|
||||
if (TimiJava.isNotEmpty(reply.getSenderId())) {
|
||||
reply.setSender(userService.view(reply.getSenderId()));
|
||||
}
|
||||
if (TimiJava.isNotEmpty(reply.getReceiverId())) {
|
||||
reply.setReceiver(userService.view(reply.getReceiverId()));
|
||||
}
|
||||
}
|
||||
result.setList(list);
|
||||
result.setTotal(mapper.countByBizType(page.getBizType(), page.getBizId()));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.TimiServerAPI;
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.blog.entity.Article;
|
||||
import com.imyeyu.api.modules.blog.service.ArticleService;
|
||||
import com.imyeyu.api.modules.common.bean.CommentSupport;
|
||||
import com.imyeyu.api.modules.common.entity.Comment;
|
||||
import com.imyeyu.api.modules.common.entity.CommentReply;
|
||||
import com.imyeyu.api.modules.common.entity.User;
|
||||
import com.imyeyu.api.modules.common.mapper.CommentMapper;
|
||||
import com.imyeyu.api.modules.common.mapper.CommentRemindQueueMapper;
|
||||
import com.imyeyu.api.modules.common.mapper.CommentReplyMapper;
|
||||
import com.imyeyu.api.modules.common.service.CommentService;
|
||||
import com.imyeyu.api.modules.common.service.UserService;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
||||
import com.imyeyu.api.modules.common.vo.comment.CommentView;
|
||||
import com.imyeyu.api.modules.common.vo.comment.UserCommentPage;
|
||||
import com.imyeyu.api.modules.git.service.RepositoryService;
|
||||
import com.imyeyu.api.modules.git.vo.issue.CommentPage;
|
||||
import com.imyeyu.spring.TimiSpring;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import com.imyeyu.spring.service.GettableService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评论操作服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-23 21:41
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
|
||||
public class CommentServiceImplement extends AbstractEntityService<Comment, Long> implements CommentService {
|
||||
|
||||
private final UserService userService;
|
||||
private final ArticleService articleService;
|
||||
private final RepositoryService repositoryService;
|
||||
|
||||
private final CommentMapper mapper;
|
||||
private final CommentReplyMapper replyMapper;
|
||||
private final CommentRemindQueueMapper remindQueueMapper;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<Comment, Long> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void create(Comment comment) {
|
||||
Class<? extends GettableService<? extends CommentSupport, Long>> serviceClass = comment.getBizType().getServiceClass();
|
||||
GettableService<? extends CommentSupport, Long> service = TimiServerAPI.applicationContext.getBean(serviceClass);
|
||||
CommentSupport commentSupport = service.get(comment.getBizId());
|
||||
TimiException.requiredTrue(commentSupport.canComment(), "comment.not.can.comment");
|
||||
|
||||
// 令牌和账号验证
|
||||
if (TimiJava.isNotEmpty(TimiSpring.getToken())) {
|
||||
User commentUser = userService.getLoginUser();
|
||||
TimiException.requiredTrue(!commentUser.isBanning(), "comment.user.banned");
|
||||
TimiException.requiredTrue(!commentUser.isMuting(), "comment.user.muting");
|
||||
comment.setUserId(commentUser.getId());
|
||||
comment.setNick(null);
|
||||
} else {
|
||||
TimiException.required(comment.getNick(), "comment.nick.empty");
|
||||
}
|
||||
// 内容
|
||||
TimiException.required(comment.getContent(), "comment.data.empty");
|
||||
comment.setIp(TimiSpring.getRequestIP());
|
||||
super.create(comment);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void delete(Long cId) {
|
||||
User user = userService.getLoginUser();
|
||||
|
||||
Comment comment = get(cId);
|
||||
if (!comment.getUserId().equals(user.getId())) {
|
||||
throw new TimiException(TimiCode.PERMISSION_ERROR).msgKey("token.illegal");
|
||||
}
|
||||
List<CommentReply> replies = replyMapper.listAllBySenderId(user.getId());
|
||||
for (int i = 0; i < replies.size(); i++) {
|
||||
// 移出被回复者的回复提醒队列
|
||||
remindQueueMapper.destroyByReplyId(replies.get(i).getId());
|
||||
}
|
||||
replyMapper.deleteByCommentId(cId);
|
||||
super.delete(cId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CommentView> pageByBizId(CommentPage page) {
|
||||
if (page.getOrderMap() == null) {
|
||||
page.setOrderMap(new LinkedHashMap<>());
|
||||
}
|
||||
if (page.getOrderMap().isEmpty()) {
|
||||
page.getOrderMap().put("createdAt", BaseMapper.OrderType.DESC);
|
||||
}
|
||||
List<CommentView> list = mapper.list(page.getBizType(), page.getBizId(), page.getOffset(), page.getLimit(), page.getOrderMap());
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
CommentView comment = list.get(i);
|
||||
if (TimiJava.isNotEmpty(comment.getUserId())) {
|
||||
comment.setUser(userService.view(comment.getUserId()));
|
||||
}
|
||||
List<CommentReplyView> replies = comment.getReplies();
|
||||
for (int j = 0; j < replies.size(); j++) {
|
||||
CommentReplyView reply = replies.get(j);
|
||||
if (TimiJava.isNotEmpty(reply.getSenderId())) {
|
||||
reply.setSender(userService.view(reply.getSenderId()));
|
||||
}
|
||||
if (TimiJava.isNotEmpty(reply.getReceiverId())) {
|
||||
reply.setReceiver(userService.view(reply.getReceiverId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
PageResult<CommentView> result = new PageResult<>();
|
||||
result.setList(list);
|
||||
result.setTotal(mapper.count(page.getBizType(), page.getBizId()));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CommentView> pageByUserId(UserCommentPage page) {
|
||||
if (page.getOrderMap() == null) {
|
||||
page.setOrderMap(new LinkedHashMap<>());
|
||||
}
|
||||
if (page.getOrderMap().isEmpty()) {
|
||||
page.getOrderMap().put("createdAt", BaseMapper.OrderType.DESC);
|
||||
}
|
||||
PageResult<CommentView> result = new PageResult<>();
|
||||
result.setList(mapper.listByUserId(page.getUserId(), page.getOffset(), page.getLimit(), page.getOrderMap()));
|
||||
result.setTotal(mapper.countByUserId(page.getUserId()));
|
||||
|
||||
for (int i = 0; i < result.getList().size(); i++) {
|
||||
CommentView view = result.getList().get(i);
|
||||
switch (view.getBizType()) {
|
||||
case ARTICLE -> {
|
||||
Article article = articleService.get(view.getBizId());
|
||||
article.setData(null);
|
||||
article.setExtendData(null);
|
||||
view.setArticle(article);
|
||||
}
|
||||
// case GIT_ISSUE, GIT_MERGE -> view.setRepository(repositoryService.get(view.getBizId()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.common.entity.EmailQueue;
|
||||
import com.imyeyu.api.modules.common.entity.EmailQueueLog;
|
||||
import com.imyeyu.api.modules.common.mapper.EmailQueueLogMapper;
|
||||
import com.imyeyu.api.modules.common.mapper.EmailQueueMapper;
|
||||
import com.imyeyu.api.modules.common.service.EmailQueueService;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 邮件推送队列服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-24 16:21
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class EmailQueueServiceImplement extends AbstractEntityService<EmailQueue, String> implements EmailQueueService {
|
||||
|
||||
private final EmailQueueMapper mapper;
|
||||
private final EmailQueueLogMapper logMapper;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<EmailQueue, String> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailQueue get(EmailQueue.BizType type, Long dataId) {
|
||||
return mapper.query(type, dataId);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void addLog(EmailQueueLog log) {
|
||||
logMapper.insert(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EmailQueue> listAll() {
|
||||
return mapper.listAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.common.entity.Feedback;
|
||||
import com.imyeyu.api.modules.common.mapper.FeedbackMapper;
|
||||
import com.imyeyu.api.modules.common.service.FeedbackService;
|
||||
import com.imyeyu.api.modules.common.vo.FeedbackRequest;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 反馈服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-11-16 22:27
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FeedbackServiceImplement extends AbstractEntityService<Feedback, Long> implements FeedbackService {
|
||||
|
||||
private final FeedbackMapper mapper;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<Feedback, Long> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void create(FeedbackRequest request) {
|
||||
Feedback feedback = new Feedback();
|
||||
feedback.setFrom(request.getFrom());
|
||||
feedback.setEmail(request.getEmail());
|
||||
feedback.setData(request.getData());
|
||||
super.create(feedback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Icon;
|
||||
import com.imyeyu.api.modules.common.mapper.IconMapper;
|
||||
import com.imyeyu.api.modules.common.service.IconService;
|
||||
import com.imyeyu.api.modules.common.vo.icon.AllResponse;
|
||||
import com.imyeyu.api.modules.common.vo.icon.NamePage;
|
||||
import com.imyeyu.api.modules.common.vo.icon.UnicodePage;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 图标服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-09-09 16:48
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class IconServiceImplement extends AbstractEntityService<Icon, Long> implements IconService {
|
||||
|
||||
private final IconMapper mapper;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<Icon, Long> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AllResponse listAll(Long latest) {
|
||||
List<Icon> list = mapper.listAll();
|
||||
long dbLatest = -1;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (dbLatest < list.get(i).getCreatedAt()) {
|
||||
dbLatest = list.get(i).getCreatedAt();
|
||||
}
|
||||
if (list.get(i).getUpdatedAt() != null && dbLatest < list.get(i).getUpdatedAt()) {
|
||||
dbLatest = list.get(i).getUpdatedAt();
|
||||
}
|
||||
}
|
||||
AllResponse response = new AllResponse();
|
||||
response.setLatest(dbLatest);
|
||||
if (latest < dbLatest) {
|
||||
// 存在更新
|
||||
response.setIcons(list);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<Icon> pageByName(NamePage page) {
|
||||
PageResult<Icon> result = new PageResult<>();
|
||||
result.setList(mapper.listByName(page.getName(), page.getOffset(), page.getLimit()));
|
||||
result.setTotal(mapper.countByName(page.getName()));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<Icon> pageByUnicode(UnicodePage page) {
|
||||
String unicode = page.getUnicode().toLowerCase();
|
||||
if (unicode.startsWith("0x") || unicode.startsWith("&#") || unicode.startsWith("\\u")) {
|
||||
unicode = unicode.substring(2);
|
||||
}
|
||||
PageResult<Icon> result = new PageResult<>();
|
||||
result.setList(mapper.listByUnicode(unicode, page.getOffset(), page.getLimit()));
|
||||
result.setTotal(mapper.countByUnicode(unicode));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.Language;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||
import com.imyeyu.api.modules.common.entity.Multilingual;
|
||||
import com.imyeyu.api.modules.common.mapper.MultilingualMapper;
|
||||
import com.imyeyu.api.modules.common.service.MultilingualService;
|
||||
import com.imyeyu.api.modules.common.service.SettingService;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import com.imyeyu.spring.util.Redis;
|
||||
import com.imyeyu.utils.Time;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-10-25 10:48
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MultilingualServiceImplement extends AbstractEntityService<Multilingual, Long> implements MultilingualService {
|
||||
|
||||
private final SettingService settingService;
|
||||
|
||||
private final MultilingualMapper mapper;
|
||||
|
||||
private final Redis<String, Long> redisLanguageMap;
|
||||
private final Redis<Long, Multilingual> redisLanguage;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<Multilingual, Long> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long create(String key, String zhCN) {
|
||||
Multilingual multilingual = new Multilingual();
|
||||
multilingual.setKey(key);
|
||||
multilingual.setZhCN(zhCN);
|
||||
mapper.insert(multilingual);
|
||||
return multilingual.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createIfNotExist(String key, String zhCN) {
|
||||
Multilingual existItem = mapper.selectByZhCN(zhCN);
|
||||
if (TimiJava.isNotEmpty(existItem)) {
|
||||
return existItem.getId();
|
||||
}
|
||||
return create(key, zhCN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Language language, Long id) {
|
||||
Multilingual result = redisLanguage.get(id);
|
||||
if (result == null) {
|
||||
result = mapper.select(id);
|
||||
if (result == null) {
|
||||
throw new TimiException(TimiCode.RESULT_NULL).msgKey("TODO not found language");
|
||||
}
|
||||
redisLanguage.set(id, result, Time.D * settingService.getAsInt(SettingKey.TTL_MULTILINGUAL));
|
||||
}
|
||||
return result.getValue(language);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getByKey(Language language, String key) {
|
||||
Long languageId = redisLanguageMap.get(key);
|
||||
if (languageId == null) {
|
||||
Multilingual result = mapper.selectByKey(key);
|
||||
if (result == null) {
|
||||
log.warn("not found language for key: {}", key);
|
||||
return key;
|
||||
}
|
||||
long ttl = Time.D * settingService.getAsInt(SettingKey.TTL_MULTILINGUAL);
|
||||
redisLanguage.set(result.getId(), result, ttl);
|
||||
redisLanguageMap.set(result.getKey(), result.getId(), ttl);
|
||||
return result.getValue(language);
|
||||
}
|
||||
return get(language, languageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Multilingual> listByNotTranslate() {
|
||||
return mapper.selectByNotTranslate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||
import com.imyeyu.api.modules.common.entity.Setting;
|
||||
import com.imyeyu.api.modules.common.mapper.SettingMapper;
|
||||
import com.imyeyu.api.modules.common.service.SettingService;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import com.imyeyu.spring.util.Redis;
|
||||
import com.imyeyu.utils.Time;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统配置服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-20 22:11
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SettingServiceImplement extends AbstractEntityService<Setting, String> implements SettingService {
|
||||
|
||||
private final SettingMapper mapper;
|
||||
private final Redis<String, String> redisSetting;
|
||||
|
||||
private final Gson gson;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<Setting, String> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
public Setting getByKey(SettingKey key) {
|
||||
if (key == null) {
|
||||
throw new TimiException(TimiCode.ARG_MISS).msgKey("key can not be null");
|
||||
}
|
||||
|
||||
String cacheValue = redisSetting.get(key.toString());
|
||||
if (TimiJava.isNotEmpty(cacheValue)) {
|
||||
return gson.fromJson(cacheValue, Setting.class);
|
||||
}
|
||||
Setting setting = mapper.selectByKey(key);
|
||||
if (TimiJava.isEmpty(setting.getValue())) {
|
||||
return setting;
|
||||
}
|
||||
int settingTTL;
|
||||
if (key == SettingKey.TTL_SETTING) {
|
||||
settingTTL = Integer.parseInt(setting.getValue());
|
||||
} else {
|
||||
settingTTL = Integer.parseInt(getByKey(SettingKey.TTL_SETTING).getValue());
|
||||
}
|
||||
if (0 < settingTTL) {
|
||||
redisSetting.set(key.toString(), gson.toJson(setting), Time.M * settingTTL);
|
||||
}
|
||||
return setting;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Setting> listByKeys(List<SettingKey> keyList) {
|
||||
List<Setting> result = new ArrayList<>();
|
||||
for (int i = 0; i < keyList.size(); i++) {
|
||||
result.add(getByKey(keyList.get(i)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsString(SettingKey key) {
|
||||
return getByKey(key).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAsInt(SettingKey key) {
|
||||
return Integer.parseInt(getAsString(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAsLong(SettingKey key) {
|
||||
return Long.parseLong(getAsString(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAsDouble(SettingKey key) {
|
||||
return Double.parseDouble(getAsString(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean is(SettingKey key) {
|
||||
return Boolean.parseBoolean(getAsString(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean not(SettingKey key) {
|
||||
return !is(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement getAsJsonElement(SettingKey key) {
|
||||
return JsonParser.parseString(getAsString(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject getAsJsonObject(SettingKey key) {
|
||||
return getAsJsonElement(key).getAsJsonObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArray getAsJsonArray(SettingKey key) {
|
||||
return getAsJsonElement(key).getAsJsonArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T fromJson(SettingKey key, Class<T> clazz) {
|
||||
return gson.fromJson(getAsJsonElement(key), clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T fromJson(SettingKey key, TypeToken<T> typeToken) {
|
||||
return gson.fromJson(getAsJsonElement(key), typeToken);
|
||||
}
|
||||
|
||||
public <T> T fromYaml(SettingKey key, Class<T> clazz) {
|
||||
return new Yaml().loadAs(getAsString(key), clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Setting> listAll() {
|
||||
return mapper.listAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushCache() {
|
||||
redisSetting.flushAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.common.entity.Tag;
|
||||
import com.imyeyu.api.modules.common.mapper.TagMapper;
|
||||
import com.imyeyu.api.modules.common.service.MultilingualService;
|
||||
import com.imyeyu.api.modules.common.service.TagService;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2025-05-30 22:47
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TagServiceImplement extends AbstractEntityService<Tag, Long> implements TagService {
|
||||
|
||||
private final MultilingualService multilingualService;
|
||||
|
||||
private final TagMapper mapper;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<Tag, Long> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(Tag tag) {
|
||||
Long langId = multilingualService.createIfNotExist(UUID.randomUUID().toString(), tag.getValue());
|
||||
tag.setValue(String.valueOf(langId));
|
||||
super.create(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tag> listByBizID(Tag.BizType bizType, String bizID) throws TimiException {
|
||||
Tag example = new Tag();
|
||||
example.setBizType(bizType);
|
||||
example.setBizID(bizID);
|
||||
return mapper.selectAllByExample(example);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.api.modules.common.entity.Task;
|
||||
import com.imyeyu.api.modules.common.mapper.TaskMapper;
|
||||
import com.imyeyu.api.modules.common.service.TaskService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 任务服务
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2022-04-03 15:37
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TaskServiceImplement implements TaskService {
|
||||
|
||||
private final TaskMapper mapper;
|
||||
|
||||
@Override
|
||||
public List<Task> listAll4Public() {
|
||||
return mapper.listAll4Public().stream().sorted(Comparator.comparingInt(c -> c.getStatus().getSort())).toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.modules.common.entity.Template;
|
||||
import com.imyeyu.api.modules.common.mapper.TemplateMapper;
|
||||
import com.imyeyu.api.modules.common.service.TemplateService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2023-09-22 16:40
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TemplateServiceImplement implements TemplateService {
|
||||
|
||||
private final TemplateMapper mapper;
|
||||
|
||||
@Override
|
||||
public Template get(Template.BizType bizType, String bizCode) {
|
||||
Template template = mapper.query(bizType, bizCode);
|
||||
if (template == null) {
|
||||
throw new TimiException(TimiCode.RESULT_NULL).msgKey("not found template");
|
||||
}
|
||||
return template;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.common.entity.User;
|
||||
import com.imyeyu.api.modules.common.entity.UserConfig;
|
||||
import com.imyeyu.api.modules.common.mapper.UserConfigMapper;
|
||||
import com.imyeyu.api.modules.common.service.UserConfigService;
|
||||
import com.imyeyu.api.modules.common.service.UserService;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import com.imyeyu.utils.Time;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 用户设置服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-08-12 16:24
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
|
||||
public class UserConfigServiceImplement extends AbstractEntityService<UserConfig, Long> implements UserConfigService {
|
||||
|
||||
private final UserService userService;
|
||||
private final UserConfigMapper mapper;
|
||||
|
||||
@Override
|
||||
protected UserConfigMapper mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void update(UserConfig config) {
|
||||
User user = userService.getLoginUser();
|
||||
|
||||
UserConfig dbConfig = get(user.getId());
|
||||
dbConfig.setUserId(user.getId());
|
||||
dbConfig.setEmailReplyRemind(config.isEmailReplyRemind());
|
||||
dbConfig.setUpdatedAt(Time.now());
|
||||
super.update(dbConfig);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.common.entity.User;
|
||||
import com.imyeyu.api.modules.common.entity.UserPrivacy;
|
||||
import com.imyeyu.api.modules.common.mapper.UserPrivacyMapper;
|
||||
import com.imyeyu.api.modules.common.service.UserPrivacyService;
|
||||
import com.imyeyu.api.modules.common.service.UserService;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import com.imyeyu.utils.Time;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 用户隐私控制服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-27 17:19
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserPrivacyServiceImplement extends AbstractEntityService<UserPrivacy, Long> implements UserPrivacyService {
|
||||
|
||||
private final UserService userService;
|
||||
private final UserPrivacyMapper mapper;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<UserPrivacy, Long> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void update(UserPrivacy privacy) {
|
||||
User user = userService.getLoginUser();
|
||||
|
||||
UserPrivacy dbPrivacy = get(user.getId());
|
||||
dbPrivacy.setEmail(privacy.isEmail());
|
||||
dbPrivacy.setSex(privacy.isSex());
|
||||
dbPrivacy.setBirthdate(privacy.isBirthdate());
|
||||
dbPrivacy.setQq(privacy.isQq());
|
||||
dbPrivacy.setLastLoginAt(privacy.isLastLoginAt());
|
||||
dbPrivacy.setCreatedAt(privacy.isCreatedAt());
|
||||
dbPrivacy.setUserId(user.getId());
|
||||
dbPrivacy.setUpdatedAt(Time.now());
|
||||
super.update(dbPrivacy);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.io.IOSize;
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.entity.User;
|
||||
import com.imyeyu.api.modules.common.entity.UserProfile;
|
||||
import com.imyeyu.api.modules.common.mapper.UserProfileMapper;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.common.service.UserProfileService;
|
||||
import com.imyeyu.api.modules.common.service.UserService;
|
||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
|
||||
import com.imyeyu.api.modules.common.vo.user.UserRequest;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 用户数据服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-07-27 17:08
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
|
||||
public class UserProfileServiceImplement extends AbstractEntityService<UserProfile, Long> implements UserProfileService {
|
||||
|
||||
private final UserService userService;
|
||||
private final AttachmentService attachmentService;
|
||||
|
||||
private final UserProfileMapper mapper;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<UserProfile, Long> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(UserProfile profile) {
|
||||
UserProfile dbProfile = get(profile.getUserId());
|
||||
dbProfile.setWrapperType(profile.getWrapperType());
|
||||
dbProfile.setAvatarType(profile.getAvatarType());
|
||||
dbProfile.setSex(profile.getSex());
|
||||
dbProfile.setBirthdate(profile.getBirthdate());
|
||||
dbProfile.setQq(profile.getQq());
|
||||
dbProfile.setDescription(profile.getDescription());
|
||||
super.update(dbProfile);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void update(UserRequest request) {
|
||||
try {
|
||||
User user = userService.getLoginUser();
|
||||
if (TimiJava.isNotEmpty(request.getProfile().getWrapper())) {
|
||||
Attachment dbWrapper = attachmentService.getByAttachType(Attachment.BizType.USER, request.getId(), User.AttachType.WRAPPER);
|
||||
// TODO 限制 PNG
|
||||
if (dbWrapper != null) {
|
||||
attachmentService.delete(dbWrapper.getId());
|
||||
}
|
||||
MultipartFile wrapper = request.getProfile().getWrapper();
|
||||
// 字节数据
|
||||
byte[] bytes = wrapper.getInputStream().readAllBytes();
|
||||
if (IOSize.MB < bytes.length) {
|
||||
throw new TimiException(TimiCode.ARG_BAD).msgKey("限制上传文件大小 1 MB");
|
||||
}
|
||||
AttachmentRequest wrapperAttach = new AttachmentRequest();
|
||||
wrapperAttach.setName(request.getId() + ".png");
|
||||
wrapperAttach.setBizType(Attachment.BizType.USER);
|
||||
wrapperAttach.setBizId(request.getId());
|
||||
wrapperAttach.setAttachTypeValue(User.AttachType.WRAPPER);
|
||||
wrapperAttach.setSize(wrapper.getSize());
|
||||
wrapperAttach.setInputStream(wrapper.getInputStream());
|
||||
attachmentService.create(wrapperAttach);
|
||||
}
|
||||
if (TimiJava.isNotEmpty(request.getProfile().getAvatar())) {
|
||||
Attachment dbAvatar = attachmentService.getByAttachType(Attachment.BizType.USER, request.getId(), User.AttachType.AVATAR);
|
||||
if (dbAvatar != null) {
|
||||
attachmentService.delete(dbAvatar.getId());
|
||||
}
|
||||
MultipartFile avatar = request.getProfile().getAvatar();
|
||||
AttachmentRequest avatarAttach = new AttachmentRequest();
|
||||
avatarAttach.setName(request.getId() + ".png");
|
||||
avatarAttach.setBizType(Attachment.BizType.USER);
|
||||
avatarAttach.setBizId(request.getId());
|
||||
avatarAttach.setAttachTypeValue(User.AttachType.AVATAR);
|
||||
avatarAttach.setSize(avatar.getSize());
|
||||
avatarAttach.setInputStream(avatar.getInputStream());
|
||||
attachmentService.create(avatarAttach);
|
||||
}
|
||||
update(request.getProfile());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||
import com.imyeyu.api.modules.blog.util.UserToken;
|
||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||
import com.imyeyu.api.modules.common.entity.EmailQueue;
|
||||
import com.imyeyu.api.modules.common.entity.User;
|
||||
import com.imyeyu.api.modules.common.entity.UserConfig;
|
||||
import com.imyeyu.api.modules.common.entity.UserPrivacy;
|
||||
import com.imyeyu.api.modules.common.entity.UserProfile;
|
||||
import com.imyeyu.api.modules.common.mapper.CommentMapper;
|
||||
import com.imyeyu.api.modules.common.mapper.CommentReplyMapper;
|
||||
import com.imyeyu.api.modules.common.mapper.UserMapper;
|
||||
import com.imyeyu.api.modules.common.mapper.UserProfileMapper;
|
||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||
import com.imyeyu.api.modules.common.service.EmailQueueService;
|
||||
import com.imyeyu.api.modules.common.service.UserConfigService;
|
||||
import com.imyeyu.api.modules.common.service.UserPrivacyService;
|
||||
import com.imyeyu.api.modules.common.service.UserProfileService;
|
||||
import com.imyeyu.api.modules.common.service.UserService;
|
||||
import com.imyeyu.api.modules.common.vo.user.LoginRequest;
|
||||
import com.imyeyu.api.modules.common.vo.user.LoginResponse;
|
||||
import com.imyeyu.api.modules.common.vo.user.RegisterRequest;
|
||||
import com.imyeyu.api.modules.common.vo.user.UserProfileView;
|
||||
import com.imyeyu.api.modules.common.vo.user.UserView;
|
||||
import com.imyeyu.api.modules.git.entity.Developer;
|
||||
import com.imyeyu.api.modules.git.service.DeveloperService;
|
||||
import com.imyeyu.api.modules.minecraft.service.PlayerService;
|
||||
import com.imyeyu.spring.TimiSpring;
|
||||
import com.imyeyu.spring.mapper.BaseMapper;
|
||||
import com.imyeyu.spring.service.AbstractEntityService;
|
||||
import com.imyeyu.spring.util.Redis;
|
||||
import com.imyeyu.utils.Calc;
|
||||
import com.imyeyu.utils.Digest;
|
||||
import com.imyeyu.utils.Text;
|
||||
import com.imyeyu.utils.Time;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 用户管理服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-23 21:43
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
|
||||
public class UserServiceImplement extends AbstractEntityService<User, Long> implements UserService, TimiJava {
|
||||
|
||||
private final PlayerService mcPlayerService;
|
||||
private final DeveloperService gitDeveloperService;
|
||||
private final EmailQueueService emailQueueService;
|
||||
private final UserConfigService configService;
|
||||
private final AttachmentService attachmentService;
|
||||
private final UserProfileService profileService;
|
||||
private final UserPrivacyService privacyService;
|
||||
|
||||
private final UserMapper mapper;
|
||||
private final CommentMapper commentMapper;
|
||||
private final UserProfileMapper userProfileMapper;
|
||||
private final CommentReplyMapper commentReplyMapper;
|
||||
|
||||
private final Redis<Long, String> redisUserExpFlag;
|
||||
private final Redis<String, Long> redisUserEmailVerify;
|
||||
private final Redis<String, Long> redisUserResetPWVerify;
|
||||
|
||||
private final UserToken userToken;
|
||||
|
||||
@Override
|
||||
protected BaseMapper<User, Long> mapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void update(User user) {
|
||||
User userByName = getByName(user.getName());
|
||||
if (userByName != null && !userByName.getId().equals(user.getId())) {
|
||||
throw new TimiException(TimiCode.DATA_EXIST).msgKey("user.name.exist");
|
||||
}
|
||||
User userByEmail = getByEmail(user.getEmail());
|
||||
if (userByEmail != null && !userByEmail.getId().equals(user.getId())) {
|
||||
throw new TimiException(TimiCode.DATA_EXIST).msgKey("user.email.exist_and_verified");
|
||||
}
|
||||
User dbUser = get(user.getId());
|
||||
if (TimiJava.isNotEmpty(user.getEmail())) {
|
||||
if (!user.getEmail().equals(dbUser.getEmail())) {
|
||||
// 重新验证
|
||||
dbUser.setEmailVerifyAt(null);
|
||||
}
|
||||
}
|
||||
dbUser.setEmail(user.getEmail());
|
||||
dbUser.setName(user.getName());
|
||||
super.update(dbUser);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public LoginResponse register(RegisterRequest request) {
|
||||
if (getByName(request.getName()) != null) {
|
||||
throw new TimiException(TimiCode.DATA_EXIST).msgKey("user.register.exist_name");
|
||||
}
|
||||
if (getByEmail(request.getEmail()) != null) {
|
||||
throw new TimiException(TimiCode.DATA_EXIST).msgKey("user.register.exist_email");
|
||||
}
|
||||
User user = new User();
|
||||
user.setEmail(request.getEmail());
|
||||
user.setName(request.getName());
|
||||
user.setPassword(digestPassword(request.getPassword(), Text.randomString(16)));
|
||||
user.setCreatedAt(Time.now());
|
||||
// 注册账号
|
||||
create(user);
|
||||
// 初始化资料
|
||||
profileService.create(new UserProfile(user.getId()));
|
||||
// 初始化隐私控制
|
||||
privacyService.create(new UserPrivacy(user.getId()));
|
||||
// 初始化设置
|
||||
configService.create(new UserConfig(user.getId()));
|
||||
// 初始化开发者
|
||||
gitDeveloperService.create(new Developer(user.getId()));
|
||||
// 自动登录
|
||||
return login(new LoginRequest(String.valueOf(user.getId()), request.getPassword()));
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public LoginResponse login(LoginRequest request) {
|
||||
if (TimiJava.isEmpty(request)) {
|
||||
throw new TimiException(TimiCode.ARG_MISS);
|
||||
}
|
||||
// 用户
|
||||
if (TimiJava.isEmpty(request.getUser())) {
|
||||
throw new TimiException(TimiCode.ARG_MISS).msgKey("user.login.user.empty");
|
||||
}
|
||||
// 密码
|
||||
if (TimiJava.isEmpty(request.getPassword())) {
|
||||
throw new TimiException(TimiCode.ARG_MISS).msgKey("user.login.password.empty");
|
||||
}
|
||||
User result = get(request.getUser());
|
||||
if (request.getUser().contains("@")) {
|
||||
if (!result.emailVerified()) {
|
||||
throw new TimiException(TimiCode.RESULT_BAD).msgKey("user.login.email.not_verify");
|
||||
}
|
||||
}
|
||||
if (TimiJava.isEmpty(result)) {
|
||||
throw new TimiException(TimiCode.RESULT_NULL).msgKey("user.login.not_found");
|
||||
}
|
||||
if (result.isBanning()) {
|
||||
throw new TimiException(TimiCode.RESULT_BAN).msgKey("user.login.baned");
|
||||
}
|
||||
if (isInvalidPassword(result.getPassword(), request.getPassword())) {
|
||||
throw new TimiException(TimiCode.RESULT_BAD).msgKey("user.login.password.mismatch");
|
||||
}
|
||||
// 用户数据
|
||||
UserProfile profile = profileService.get(result.getId());
|
||||
profile.setLastLoginIP(TimiSpring.getRequestIP());
|
||||
profile.setLastLoginAt(Time.now());
|
||||
updateExp(profile.getUserId());
|
||||
userProfileMapper.update(profile);
|
||||
// 生成并缓存 Token
|
||||
String token = UUID.randomUUID().toString();
|
||||
Long expireAt = userToken.set(token, result.getId());
|
||||
return new LoginResponse(result.getId(), token, expireAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserView view(Long userId) {
|
||||
UserProfileView profile = new UserProfileView();
|
||||
{
|
||||
// 附件
|
||||
List<Attachment> attachmentList = attachmentService.listByBizId(Attachment.BizType.USER, userId);
|
||||
boolean hasAvatar = false, hasWrapper = false;
|
||||
for (int i = 0; i < attachmentList.size(); i++) {
|
||||
if (!hasAvatar && User.AttachType.AVATAR.toString().equals(attachmentList.get(i).getAttachType())) {
|
||||
hasAvatar = true;
|
||||
}
|
||||
if (!hasWrapper && User.AttachType.WRAPPER.toString().equals(attachmentList.get(i).getAttachType())) {
|
||||
hasWrapper = true;
|
||||
}
|
||||
}
|
||||
if (!hasAvatar) {
|
||||
attachmentList.add(attachmentService.getByAttachType(Attachment.BizType.USER, 0, User.AttachType.DEFAULT_AVATAR));
|
||||
}
|
||||
if (!hasWrapper) {
|
||||
attachmentList.add(attachmentService.getByAttachType(Attachment.BizType.USER, 0, User.AttachType.DEFAULT_WRAPPER));
|
||||
}
|
||||
profile.setAttachmentList(attachmentList);
|
||||
}
|
||||
BeanUtils.copyProperties(profileService.get(userId), profile);
|
||||
|
||||
UserView view = new UserView();
|
||||
view.setProfile(profile);
|
||||
BeanUtils.copyProperties(get(userId), view);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvalidPassword(String digestPassword, String password) {
|
||||
// "$SHA$盐$摘要(摘要(明文) + 盐)
|
||||
String[] arr = digestPassword.split("\\$");
|
||||
return digestPassword(digestPassword, arr[2]).equals(password);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public LoginResponse login4Token() {
|
||||
String token = TimiSpring.getToken();
|
||||
try {
|
||||
User user = getLoginUser();
|
||||
updateExp(user.getId());
|
||||
return new LoginResponse(user.getId(), token, userToken.getExpireAt(token));
|
||||
} catch (Exception e) {
|
||||
throw new TimiException(TimiCode.IGNORE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout() {
|
||||
userToken.clear(TimiSpring.getToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public User get(String user) {
|
||||
User result;
|
||||
if (Calc.isNumber(user)) {
|
||||
result = get(Long.parseLong(user));
|
||||
} else if (user.contains("@")) {
|
||||
result = getByEmail(user);
|
||||
} else {
|
||||
result = getByName(user);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogged() {
|
||||
return userToken.isValid(TimiSpring.getToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getLoginUser() {
|
||||
String token = TimiSpring.getToken();
|
||||
if (TimiJava.isEmpty(token)) {
|
||||
return null;
|
||||
}
|
||||
return userToken.getUser(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getByName(String name) {
|
||||
return mapper.selectByName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getByEmail(String email) {
|
||||
return mapper.selectByEmail(email);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void sendEmailVerify() {
|
||||
User user = getLoginUser();
|
||||
if (TimiJava.isEmpty(user.getEmail())) {
|
||||
throw new TimiException(TimiCode.RESULT_BAD).msgKey("user.email.empty");
|
||||
}
|
||||
if (user.emailVerified()) {
|
||||
throw new TimiException(TimiCode.RESULT_BAD).msgKey("user.email.verified");
|
||||
}
|
||||
EmailQueue emailQueue = new EmailQueue();
|
||||
emailQueue.setBizType(EmailQueue.BizType.EMAIL_VERIFY);
|
||||
emailQueue.setBizId(user.getId());
|
||||
emailQueue.setSendAt(Time.now());
|
||||
emailQueueService.create(emailQueue);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void emailVerifyCallback(String key) {
|
||||
User user = getLoginUser();
|
||||
if (user.emailVerified()) {
|
||||
throw new TimiException(TimiCode.RESULT_BAD).msgKey("user.email.verified");
|
||||
}
|
||||
Long userId = redisUserEmailVerify.get(key);
|
||||
if (userId == null) {
|
||||
throw new TimiException(TimiCode.ARG_BAD).msgKey("user.email.illegal_key");
|
||||
}
|
||||
if (!userId.equals(user.getId())) {
|
||||
throw new TimiException(TimiCode.ARG_BAD).msgKey("user.email.illegal_key");
|
||||
}
|
||||
redisUserEmailVerify.destroy(key);
|
||||
user.setEmailVerifyAt(null);
|
||||
update(user);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void updatePassword(String oldPassword, String newPassword) {
|
||||
String token = TimiSpring.getToken();
|
||||
User user = getLoginUser();
|
||||
if (isInvalidPassword(user.getPassword(), oldPassword)) {
|
||||
throw new TimiException(TimiCode.RESULT_BAD).msgKey("user.password.mismatch");
|
||||
}
|
||||
// 更新密码
|
||||
user.setPassword(digestPassword(newPassword, Text.randomString(16)));
|
||||
user.setUpdatedAt(Time.now());
|
||||
mapper.update(user);
|
||||
// 清除登录会话
|
||||
userToken.clear(token);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void sendPasswordForgetVerify(String user) {
|
||||
User result = get(user);
|
||||
if (result == null) {
|
||||
throw new TimiException(TimiCode.RESULT_NULL).msgKey("user.password.forget.not_found");
|
||||
}
|
||||
if (TimiJava.isEmpty(result.getEmail()) || !result.emailVerified()) {
|
||||
throw new TimiException(TimiCode.RESULT_NULL).msgKey("user.password.forget.not_valid_email");
|
||||
}
|
||||
EmailQueue emailQueue = new EmailQueue();
|
||||
emailQueue.setBizType(EmailQueue.BizType.RESET_PASSWORD);
|
||||
emailQueue.setBizId(result.getId());
|
||||
emailQueue.setSendAt(Time.now());
|
||||
emailQueueService.create(emailQueue);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void resetPasswordByKey(String key, String password) {
|
||||
Long userId = redisUserResetPWVerify.get(key);
|
||||
if (userId == null) {
|
||||
throw new TimiException(TimiCode.ARG_BAD).msgKey("user.password.forget.illegal_key");
|
||||
}
|
||||
User user = get(userId);
|
||||
redisUserResetPWVerify.destroy(key);
|
||||
user.setPassword(digestPassword(password, Text.randomString(16)));
|
||||
user.setUpdatedAt(Time.now());
|
||||
mapper.update(user);
|
||||
}
|
||||
|
||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||
@Override
|
||||
public void cancel(String password) {
|
||||
String token = TimiSpring.getToken();
|
||||
User user = getLoginUser();
|
||||
if (isInvalidPassword(user.getPassword(), password)) {
|
||||
throw new TimiException(TimiCode.ARG_BAD).msgKey("user.cancel.password_error");
|
||||
}
|
||||
// 删除评论
|
||||
commentMapper.deleteByUserId(user.getId());
|
||||
// 删除回复
|
||||
commentReplyMapper.deleteByUserId(user.getId());
|
||||
// 删除账号
|
||||
delete(user.getId());
|
||||
// 清除登录会话
|
||||
userToken.clear(token);
|
||||
}
|
||||
|
||||
private void updateExp(Long userId) {
|
||||
if (!redisUserExpFlag.has(userId)) {
|
||||
// 当天无登录标记,加经验
|
||||
UserProfile profile = profileService.get(userId);
|
||||
profile.setExp(profile.getExp() + 2);
|
||||
redisUserExpFlag.set(profile.getUserId(), "", Time.tomorrow() - Time.now());
|
||||
profileService.update(profile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成密码摘要
|
||||
*
|
||||
* @param password 原始密码
|
||||
* @param salt 盐值
|
||||
* @return 密码摘要
|
||||
*/
|
||||
private String digestPassword(String password, String salt) {
|
||||
try {
|
||||
return "$SHA$%s$%s".formatted(salt, Digest.sha256(Digest.sha256(password) + salt));
|
||||
} catch (Exception e) {
|
||||
log.error("digest password error", e);
|
||||
throw new TimiException(TimiCode.ERROR).msgKey("TODO digest password error");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.imyeyu.api.modules.common.service.implement;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import com.imyeyu.api.modules.common.entity.Version;
|
||||
import com.imyeyu.api.modules.common.mapper.VersionMapper;
|
||||
import com.imyeyu.api.modules.common.service.VersionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 版本服务实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-06-10 16:07
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class VersionServiceImplement implements VersionService {
|
||||
|
||||
private final VersionMapper mapper;
|
||||
|
||||
@Override
|
||||
public Version getByName(String name) {
|
||||
return mapper.queryByName(name);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user