rename com.imyeyu.server to com.imyeyu.api

This commit is contained in:
Timi
2025-07-22 15:26:14 +08:00
parent e816b885b2
commit 323e038e86
340 changed files with 1174 additions and 1175 deletions
@@ -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);
}
}