u
This commit is contained in:
1 parent
f98c280ac4
commit
d4b25b13ac
73 files changed
+13112
-1003
No files matched your search
@@ -136,4 +136,24 @@
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.5.15</version>
|
||||
<configuration>
|
||||
<fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+10
@@ -1,5 +1,6 @@
|
||||
package cn.link56.auth.identity.controller;
|
||||
|
||||
import cn.link56.auth.identity.model.dto.AuthForgetDTO;
|
||||
import cn.link56.auth.identity.model.dto.AuthLoginEmailCodeDTO;
|
||||
import cn.link56.auth.identity.model.dto.AuthLoginPasswordDTO;
|
||||
import cn.link56.auth.identity.model.dto.AuthRegisterDTO;
|
||||
@@ -36,6 +37,15 @@ public class AuthIdentityController {
|
||||
@PostMapping("/login/emailCode")
|
||||
public Result<AuthLoginVO> loginByEmailCode(@RequestBody @Validated AuthLoginEmailCodeDTO loginEmailCodeDTO){
|
||||
AuthLoginVO authLoginVO = authIdentityService.loginByEmailCode(loginEmailCodeDTO);
|
||||
|
||||
return Result.success(authLoginVO);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/forget")
|
||||
public Result<Void> loginByEmailCode(@RequestBody @Validated AuthForgetDTO dto){
|
||||
authIdentityService.forgetPassword(dto.getEmail());
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package cn.link56.auth.identity.model.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class AuthForgetDTO {
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式有误")
|
||||
private String email;
|
||||
}
|
||||
+22
@@ -9,6 +9,7 @@ import cn.link56.auth.identity.model.vo.AuthLoginVO;
|
||||
import cn.link56.auth.token.model.AuthToken;
|
||||
import cn.link56.auth.token.model.TokenPayload;
|
||||
import cn.link56.auth.token.service.AuthTokenService;
|
||||
import cn.link56.common.utils.EmailUtils;
|
||||
import cn.link56.company.model.po.Company;
|
||||
import cn.link56.company.model.po.CompanyUser;
|
||||
import cn.link56.auth.identity.model.dto.AuthRegisterDTO;
|
||||
@@ -16,11 +17,16 @@ import cn.link56.company.service.CompanyService;
|
||||
import cn.link56.company.service.CompanyUserService;
|
||||
import cn.link56.common.exception.BusinessException;
|
||||
import cn.link56.common.utils.snowflake.idgen.IdGenerator;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class AuthIdentityService {
|
||||
@@ -32,6 +38,8 @@ public class AuthIdentityService {
|
||||
private AuthTokenService tokenService;
|
||||
@Resource
|
||||
private AuthVerificationCodeService authVerificationCodeService;
|
||||
@Resource
|
||||
private EmailUtils emailUtils;
|
||||
|
||||
@Transactional
|
||||
public void register(AuthRegisterDTO registerDTO) {
|
||||
@@ -145,4 +153,18 @@ public class AuthIdentityService {
|
||||
}
|
||||
|
||||
|
||||
public void forgetPassword(String email) {
|
||||
CompanyUser companyUser = companyUserService.getByEmail(email);
|
||||
|
||||
if (ObjectUtils.isEmpty(companyUser)) {
|
||||
throw new BusinessException("该邮箱地址未注册,请检查后再试");
|
||||
}
|
||||
|
||||
Map<String,Object> data = new HashMap<String,Object>();
|
||||
data.put("Password",companyUser.getPassword());
|
||||
|
||||
boolean flag = emailUtils.sendSync("ForgetPassword.html", "找回密码", data, email);
|
||||
|
||||
System.out.println(flag);
|
||||
}
|
||||
}
|
||||
+21
-22
@@ -1,41 +1,40 @@
|
||||
package cn.link56.common.config;
|
||||
|
||||
import cn.link56.common.interceptor.AuthInterceptor;
|
||||
import cn.link56.common.interceptor.TokenParsingInterceptor;
|
||||
import cn.link56.common.interceptor.TokenValidationInterceptor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
private final AuthInterceptor authInterceptor;
|
||||
private final TokenParsingInterceptor tokenParsingInterceptor;
|
||||
private final TokenValidationInterceptor tokenValidationInterceptor;
|
||||
|
||||
public WebMvcConfig(AuthInterceptor authInterceptor) {
|
||||
this.authInterceptor = authInterceptor;
|
||||
public WebMvcConfig(TokenParsingInterceptor tokenParsingInterceptor,
|
||||
TokenValidationInterceptor tokenValidationInterceptor) {
|
||||
this.tokenParsingInterceptor = tokenParsingInterceptor;
|
||||
this.tokenValidationInterceptor = tokenValidationInterceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(authInterceptor)
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns(
|
||||
"/auth/**",
|
||||
"/company/query",
|
||||
"/company/detail/**",
|
||||
"/company/recommend",
|
||||
"/company/recommend/hot",
|
||||
registry.addInterceptor(tokenParsingInterceptor)
|
||||
.addPathPatterns("/**");
|
||||
|
||||
"/post/tag",
|
||||
registry.addInterceptor(tokenValidationInterceptor)
|
||||
.addPathPatterns(
|
||||
"/im/**",
|
||||
|
||||
"/forum/post/page",
|
||||
"/forum/post/detail",
|
||||
"/forum/post/getRecommendList",
|
||||
"/forum/post/getHotList",
|
||||
"/forum/post/comment/list",
|
||||
"/company/update",
|
||||
"/company/user/add",
|
||||
"/company/user/delete",
|
||||
"/company/user/update",
|
||||
"/company/user/query",
|
||||
|
||||
"/base/**",
|
||||
|
||||
"/error/**",
|
||||
"/favicon.ico"
|
||||
"/post/editor/**",
|
||||
"/post/comment/create",
|
||||
"/post/comment/delete"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package cn.link56.common.context;
|
||||
|
||||
/**
|
||||
* 用户信息上下文(ThreadLocal)
|
||||
* 独立,不依赖 JwtUtils 或拦截器
|
||||
*/
|
||||
public class UserContext {
|
||||
|
||||
@@ -37,4 +36,8 @@ public class UserContext {
|
||||
removeUserId();
|
||||
removeCompanyId();
|
||||
}
|
||||
|
||||
public static boolean isValid() {
|
||||
return USER_ID.get() != null && COMPANY_ID.get() != null;
|
||||
}
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
package cn.link56.common.interceptor;
|
||||
|
||||
import cn.link56.auth.token.model.TokenPayload;
|
||||
import cn.link56.auth.token.service.AuthTokenService;
|
||||
import cn.link56.common.Result;
|
||||
import cn.link56.common.constants.ResponseStatusConstants;
|
||||
import cn.link56.common.context.UserContext;
|
||||
import cn.link56.common.utils.JsonUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@Component
|
||||
public class AuthInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final AuthTokenService authTokenService;
|
||||
|
||||
public AuthInterceptor(AuthTokenService authTokenService) {
|
||||
this.authTokenService = authTokenService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
|
||||
// 预检请求放行
|
||||
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
String token = request.getHeader("Authorization");
|
||||
if (StringUtils.isBlank(token)) {
|
||||
return writeUnauthorized(response);
|
||||
}
|
||||
|
||||
if (token.startsWith("Bearer ")) {
|
||||
token = token.substring(7);
|
||||
}else{
|
||||
return writeUnauthorized(response);
|
||||
}
|
||||
|
||||
boolean flag = authTokenService.validateToken(token);
|
||||
|
||||
if (!flag){
|
||||
return writeUnauthorized(response);
|
||||
}
|
||||
|
||||
// 解析 token
|
||||
TokenPayload tokenPayload = authTokenService.parsePayload(token);
|
||||
|
||||
// 对比信息
|
||||
String companyIdStr = request.getHeader("X-Company-Id");
|
||||
String userIdStr = request.getHeader("X-User-Id");
|
||||
|
||||
if ((StringUtils.isNotBlank(companyIdStr) && !companyIdStr.equals(tokenPayload.getCompanyId().toString()))
|
||||
|| (StringUtils.isNotBlank(userIdStr) && !userIdStr.equals(tokenPayload.getUserId().toString()))) {
|
||||
return writeUnauthorized(response);
|
||||
}
|
||||
|
||||
// 注入到 UserContext
|
||||
UserContext.setUserId(tokenPayload.getUserId());
|
||||
UserContext.setCompanyId(tokenPayload.getCompanyId());
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
return writeUnauthorized(response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||
UserContext.clear(); // 清理 ThreadLocal
|
||||
}
|
||||
|
||||
private boolean writeUnauthorized(HttpServletResponse response) throws Exception {
|
||||
Result<Void> result = Result.error(ResponseStatusConstants.UNAUTHORIZED, "登录过期或未授权");
|
||||
String jsonString = JsonUtils.toJson(result);
|
||||
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
|
||||
try (PrintWriter out = response.getWriter()) {
|
||||
out.println(jsonString);
|
||||
out.flush();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package cn.link56.common.interceptor;
|
||||
|
||||
import cn.link56.auth.token.model.TokenPayload;
|
||||
import cn.link56.auth.token.service.AuthTokenService;
|
||||
import cn.link56.common.context.UserContext;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@Component
|
||||
public class TokenParsingInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final AuthTokenService authTokenService;
|
||||
|
||||
public TokenParsingInterceptor(AuthTokenService authTokenService) {
|
||||
this.authTokenService = authTokenService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
// 预检请求放行
|
||||
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String token = request.getHeader("Authorization");
|
||||
if (StringUtils.isBlank(token)) {
|
||||
return true; // 如果没有 token,不做处理,交给下游处理
|
||||
}
|
||||
|
||||
try {
|
||||
if (token.startsWith("Bearer ")) {
|
||||
token = token.substring(7); // 去掉 "Bearer " 前缀
|
||||
} else {
|
||||
return true; // 如果不是 Bearer token,交给下游处理
|
||||
}
|
||||
|
||||
// 解析 token
|
||||
TokenPayload tokenPayload = authTokenService.parsePayload(token);
|
||||
|
||||
// 将解析后的用户信息存入 UserContext(或其他类似上下文)
|
||||
UserContext.setUserId(tokenPayload.getUserId());
|
||||
UserContext.setCompanyId(tokenPayload.getCompanyId());
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||
UserContext.clear();
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package cn.link56.common.interceptor;
|
||||
|
||||
import cn.link56.auth.token.model.TokenPayload;
|
||||
import cn.link56.auth.token.service.AuthTokenService;
|
||||
import cn.link56.common.Result;
|
||||
import cn.link56.common.constants.ResponseStatusConstants;
|
||||
import cn.link56.common.context.UserContext;
|
||||
import cn.link56.common.utils.JsonUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@Component
|
||||
public class TokenValidationInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
boolean valid = UserContext.isValid(); // 校验是否有效(是否登录)
|
||||
|
||||
if (!valid) {
|
||||
// 用户未登录或信息无效,返回 401 Unauthorized
|
||||
return writeUnauthorized(response);
|
||||
}
|
||||
|
||||
// 如果有效,允许请求继续
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||
}
|
||||
|
||||
private boolean writeUnauthorized(HttpServletResponse response) throws Exception {
|
||||
Result<Void> result = Result.error(ResponseStatusConstants.UNAUTHORIZED, "登录过期或未授权");
|
||||
String jsonString = JsonUtils.toJson(result);
|
||||
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
response.setContentType("application/json;charset=utf-8");
|
||||
|
||||
try (PrintWriter out = response.getWriter()) {
|
||||
out.println(jsonString);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
return false; // 返回 false,阻止请求继续处理
|
||||
}
|
||||
}
|
||||
+12
-9
@@ -20,6 +20,18 @@ public class CompanyController {
|
||||
@Resource
|
||||
private CompanyService companyService;
|
||||
|
||||
// 地区
|
||||
@GetMapping("/area")
|
||||
public Result<List<AreaVO>> listAreas(){
|
||||
return Result.success(companyService.listAreas());
|
||||
}
|
||||
|
||||
// 行业
|
||||
@GetMapping("/industry")
|
||||
public Result<List<IndustryVO>> listIndustries(){
|
||||
return Result.success(companyService.listIndustries());
|
||||
}
|
||||
|
||||
// 公司分页
|
||||
@PostMapping("/query")
|
||||
public Result<BasePageVO<QueryCompanyVO>> query(@RequestBody QueryCompanyDTO queryCompanyDTO){
|
||||
@@ -58,13 +70,4 @@ public class CompanyController {
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@GetMapping("/area")
|
||||
public Result<List<AreaVO>> listAreas(){
|
||||
return Result.success(companyService.listAreas());
|
||||
}
|
||||
|
||||
@GetMapping("/industry")
|
||||
public Result<List<IndustryVO>> listIndustries(){
|
||||
return Result.success(companyService.listIndustries());
|
||||
}
|
||||
}
|
||||
+14
@@ -19,6 +19,8 @@ import cn.link56.im.mapper.ImFriendMapper;
|
||||
import cn.link56.im.mapper.ImGroupMapper;
|
||||
import cn.link56.im.mapper.ImGroupUserMapper;
|
||||
import cn.link56.im.storage.SessionManager;
|
||||
import cn.link56.post.mapper.PostMapper;
|
||||
import cn.link56.post.service.PostService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@@ -44,6 +46,9 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
|
||||
@Resource
|
||||
private CompanyMapper companyMapper;
|
||||
|
||||
@Resource
|
||||
private PostMapper postMapper;
|
||||
|
||||
/**
|
||||
* 判断邮箱是否重复
|
||||
* @param email 用户邮箱
|
||||
@@ -74,6 +79,13 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
|
||||
}
|
||||
|
||||
|
||||
public CompanyUser getByEmail(String email){
|
||||
LambdaQueryWrapper<CompanyUser> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(CompanyUser::getEmail,email);
|
||||
|
||||
return companyUserMapper.selectOne(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验当前用户是否有权限操作指定的公司
|
||||
* @param companyId 当前公司ID
|
||||
@@ -309,6 +321,8 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
|
||||
|
||||
// 执行删除操作
|
||||
companyUserMapper.delete(updateWrapper);
|
||||
// 删除该用户的帖子
|
||||
postMapper.deleteUserPosts(ids,UserContext.getUserId());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -44,6 +44,6 @@ public interface ImFriendMapper extends BaseMapper<ImFriend> {
|
||||
);
|
||||
|
||||
|
||||
@Select("SELECT SUM(b_unread_count) FROM b_im_friend WHERE b_user_id = #{userId}")
|
||||
@Select("SELECT ISNULL(SUM(b_unread_count), 0) FROM b_im_friend WHERE b_user_id = #{userId}")
|
||||
Long selectAllUnreadCount(@Param("userId") Long userId);
|
||||
}
|
||||
@@ -17,6 +17,6 @@ public interface ImGroupUserMapper extends BaseMapper<ImGroupUser> {
|
||||
);
|
||||
|
||||
|
||||
@Select("SELECT SUM(b_unread_count) FROM b_im_group_user WHERE b_user_id = #{userId}")
|
||||
@Select("SELECT ISNULL(SUM(b_unread_count), 0) FROM b_im_group_user WHERE b_user_id = #{userId}")
|
||||
Long selectAllUnreadCount(@Param("userId") Long userId);
|
||||
}
|
||||
+5
-10
@@ -11,9 +11,10 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/editor/post")
|
||||
@RequestMapping("/post/editor")
|
||||
public class EditorPostController {
|
||||
@Resource
|
||||
private PostService postService;
|
||||
@@ -43,9 +44,9 @@ public class EditorPostController {
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
public Result<Void> deletePost(@RequestBody @Validated DeletePostDTO deletePostDTO){
|
||||
postService.deletePost(deletePostDTO);
|
||||
@PostMapping("/delete/{postId}")
|
||||
public Result<Void> deletePost(@PathVariable @NotNull(message = "帖子Id不能为空") Long postId) {
|
||||
postService.deletePost(postId);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@@ -55,10 +56,4 @@ public class EditorPostController {
|
||||
return Result.success(data);
|
||||
}
|
||||
|
||||
@GetMapping("/dashboard")
|
||||
public Result<EditorDashboardVO> getEditorDashboard(){
|
||||
EditorDashboardVO editorDashboardVO = postService.getEditorDashboard();
|
||||
|
||||
return Result.success(editorDashboardVO);
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -40,11 +40,11 @@ public class PostController {
|
||||
return Result.success(publicPostVO);
|
||||
}
|
||||
|
||||
@GetMapping("/company/{id}")
|
||||
public Result<PostCompanyVO> getForumCompany(@PathVariable("id") @NotNull(message = "公司ID不能为空") Long companyId){
|
||||
PostCompanyVO postCompanyVO = postService.getPostCompanyById(companyId);
|
||||
@GetMapping("/author/{id}")
|
||||
public Result<PostAuthorProfileVO> getAuthorProfile(@PathVariable("id") @NotNull(message = "文章ID不能为空") Long id){
|
||||
PostAuthorProfileVO postAuthorProfileVO = postService.getAuthorProfileByPostId(id);
|
||||
|
||||
return Result.success(postCompanyVO);
|
||||
return Result.success(postAuthorProfileVO);
|
||||
}
|
||||
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
package cn.link56.post.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/post/view")
|
||||
public class PostViewController {
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import cn.link56.post.model.vo.PublicPostVO;
|
||||
import cn.link56.post.model.vo.QueryEditorPostVO;
|
||||
import cn.link56.post.model.vo.QueryPublicPostVO;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Delete;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
@@ -32,10 +33,6 @@ public interface PostMapper extends BaseMapper<Post> {
|
||||
@Param("month") String month
|
||||
);
|
||||
|
||||
EditorDashboardVO.Count selectDashboardOverview(@Param("userId") Long userId);
|
||||
|
||||
List<EditorDashboardVO.Chart> selectDashboardViewStats(@Param("userId") Long userId);
|
||||
List<EditorDashboardVO.Chart> selectDashboardNumberStats(@Param("userId") Long userId);
|
||||
|
||||
List<QueryPublicPostVO> selectPublicPostPage(
|
||||
@Param("offset") int offset,
|
||||
@@ -51,4 +48,6 @@ public interface PostMapper extends BaseMapper<Post> {
|
||||
|
||||
@Update("UPDATE b_post SET b_active_time = GETDATE() WHERE b_id = #{postId}")
|
||||
int updateActiveTime(@Param("postId") Long postId);
|
||||
|
||||
void deleteUserPosts(@Param("userIds") List<Long> userIds,Long updateBy);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package cn.link56.post.model.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class CreatePostViewDTO {
|
||||
@NotNull(message = "帖子ID不能为空")
|
||||
private Long postId;
|
||||
|
||||
private Long userId;
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
package cn.link56.post.model.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -64,4 +61,11 @@ public class Post {
|
||||
value = "b_active_time"
|
||||
)
|
||||
private Date activeTime;
|
||||
|
||||
@TableLogic
|
||||
@TableField(
|
||||
value = "b_deleted",
|
||||
fill = FieldFill.INSERT
|
||||
)
|
||||
private Integer deleted;
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@@ -26,5 +28,5 @@ public class PostStat {
|
||||
value = "b_update_time",
|
||||
fill = FieldFill.UPDATE
|
||||
)
|
||||
private Long createTime;
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@@ -26,5 +28,5 @@ public class PostView {
|
||||
value = "b_create_time",
|
||||
fill = FieldFill.INSERT
|
||||
)
|
||||
private Long createTime;
|
||||
private Date createTime;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package cn.link56.post.model.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PostAuthorProfileVO {
|
||||
private Company company;
|
||||
private User user;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class User{
|
||||
private Long id;
|
||||
private String avatar;
|
||||
private String name;
|
||||
private String position;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Company {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String logo;
|
||||
private String profile;
|
||||
private Date establishmentDate;
|
||||
private List<String> areas;
|
||||
private List<String> industries;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package cn.link56.post.model.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PostCompanyVO {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String logo;
|
||||
private String profile;
|
||||
private Date establishmentDate;
|
||||
private List<String> areas;
|
||||
private List<String> industries;
|
||||
}
|
||||
@@ -17,8 +17,8 @@ public class PublicPostVO {
|
||||
private List<String> tags;
|
||||
private Date activeTime;
|
||||
|
||||
private Long viewCount;
|
||||
private Long commentCount;
|
||||
private Integer viewCount;
|
||||
private Integer commentCount;
|
||||
|
||||
private Long companyId;
|
||||
}
|
||||
+3
-2
@@ -22,7 +22,8 @@ public class QueryEditorPostVO {
|
||||
private Integer status;
|
||||
|
||||
private Integer viewCount;
|
||||
private Integer likeCount;
|
||||
private Integer favoriteCount;
|
||||
private Integer commentCount;
|
||||
|
||||
private Long editorId;
|
||||
private String editorName;
|
||||
}
|
||||
@@ -4,13 +4,16 @@ import cn.link56.common.BasePageVO;
|
||||
import cn.link56.common.context.UserContext;
|
||||
import cn.link56.common.exception.BusinessException;
|
||||
import cn.link56.common.utils.snowflake.idgen.IdGenerator;
|
||||
import cn.link56.company.mapper.CompanyMapper;
|
||||
import cn.link56.company.mapper.CompanyUserMapper;
|
||||
import cn.link56.company.model.po.Company;
|
||||
import cn.link56.company.model.po.CompanyUser;
|
||||
import cn.link56.company.service.CompanyService;
|
||||
import cn.link56.company.service.CompanyUserService;
|
||||
import cn.link56.post.mapper.PostMapper;
|
||||
import cn.link56.post.model.dto.*;
|
||||
import cn.link56.post.model.enums.PostStatusEnum;
|
||||
import cn.link56.post.model.po.Post;
|
||||
import cn.link56.post.model.po.PostComment;
|
||||
import cn.link56.post.model.vo.*;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
@@ -34,11 +37,11 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
|
||||
@Resource
|
||||
private PostViewService postViewService;
|
||||
@Resource
|
||||
private PostCommentService postCommentService;
|
||||
@Resource
|
||||
private CompanyService companyService;
|
||||
@Resource
|
||||
private PostStatService postStatService;
|
||||
@Resource
|
||||
private CompanyMapper companyMapper;
|
||||
@Resource
|
||||
private CompanyUserMapper companyUserMapper;
|
||||
|
||||
@Transactional
|
||||
public EditorPostVO createPost(CreatePostDTO dto) {
|
||||
@@ -145,17 +148,9 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deletePost(DeletePostDTO deletePostDTO) {
|
||||
Long postId = deletePostDTO.getId();
|
||||
|
||||
public void deletePost(Long postId) {
|
||||
// 删除帖子
|
||||
postMapper.deleteById(postId);
|
||||
// 删除帖子统计
|
||||
postStatService.removeById(postId);
|
||||
// 删除帖子浏览
|
||||
postViewService.deleteByPostId(postId);
|
||||
// 删除帖子评论
|
||||
postCommentService.deleteByPostId(postId);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -172,27 +167,6 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
|
||||
postMapper.updateById(post);
|
||||
}
|
||||
|
||||
public EditorDashboardVO getEditorDashboard() {
|
||||
Long userId = UserContext.getUserId();
|
||||
|
||||
EditorDashboardVO editorDashboardVO = new EditorDashboardVO();
|
||||
|
||||
EditorDashboardVO.Count count = postMapper.selectDashboardOverview(userId);
|
||||
|
||||
// 原始数据
|
||||
List<EditorDashboardVO.Chart> viewCharts = postMapper.selectDashboardViewStats(userId);
|
||||
List<EditorDashboardVO.Chart> numberCharts = postMapper.selectDashboardNumberStats(userId);
|
||||
List<EditorDashboardVO.Chart> typeCharts = getDashboardTypeStats(userId);
|
||||
|
||||
// 统一处理最近 7 天,补齐缺失值
|
||||
editorDashboardVO.setViewChart(fillLast7Days(viewCharts));
|
||||
editorDashboardVO.setNumberChart(fillLast7Days(numberCharts));
|
||||
editorDashboardVO.setTypeChart(typeCharts);
|
||||
|
||||
editorDashboardVO.setAllCount(count);
|
||||
return editorDashboardVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充最近7天的 chart 数据,如果 map 里没有,则补 0
|
||||
*/
|
||||
@@ -275,27 +249,46 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
|
||||
BeanUtils.copyProperties(post,postVO);
|
||||
postVO.setTags(Arrays.stream(StringUtils.split(post.getTag(),",")).collect(Collectors.toList()));
|
||||
|
||||
Long viewTotal = postViewService.getTotalByPostId(id);
|
||||
Long commentTotal = postCommentService.getTotalByPostId(id);
|
||||
Integer viewTotal = postStatService.getPostViewCount(id);
|
||||
Integer commentTotal = postStatService.getPostCommentCount(id);
|
||||
|
||||
postVO.setViewCount(viewTotal);
|
||||
postVO.setCommentCount(commentTotal);
|
||||
|
||||
// 新增view数量
|
||||
postViewService.create(id,UserContext.getUserId());
|
||||
|
||||
return postVO;
|
||||
}
|
||||
|
||||
public PostCompanyVO getPostCompanyById(Long id) {
|
||||
Company company = companyService.getById(id);
|
||||
public PostAuthorProfileVO getAuthorProfileByPostId(Long id) {
|
||||
Post post = postMapper.selectById(id);
|
||||
|
||||
Company company = companyMapper.selectById(post.getCompanyId());
|
||||
if (company == null){
|
||||
throw new BusinessException("该公司不存在或已注销");
|
||||
}
|
||||
|
||||
PostCompanyVO postCompanyVO = new PostCompanyVO();
|
||||
BeanUtils.copyProperties(company,postCompanyVO);
|
||||
// company
|
||||
PostAuthorProfileVO.Company companyVO = new PostAuthorProfileVO.Company();
|
||||
|
||||
postCompanyVO.setAreas(Arrays.stream(StringUtils.split(company.getArea(),",")).collect(Collectors.toList()));
|
||||
postCompanyVO.setIndustries(Arrays.stream(StringUtils.split(company.getIndustry(),",")).collect(Collectors.toList()));
|
||||
BeanUtils.copyProperties(company, companyVO);
|
||||
|
||||
return postCompanyVO;
|
||||
companyVO.setAreas(Arrays.stream(StringUtils.split(company.getArea(),",")).collect(Collectors.toList()));
|
||||
companyVO.setIndustries(Arrays.stream(StringUtils.split(company.getIndustry(),",")).collect(Collectors.toList()));
|
||||
|
||||
// user
|
||||
CompanyUser companyUser = companyUserMapper.selectById(post.getCreateBy());
|
||||
|
||||
PostAuthorProfileVO.User userVO =new PostAuthorProfileVO.User();
|
||||
|
||||
BeanUtils.copyProperties(companyUser,userVO);
|
||||
|
||||
PostAuthorProfileVO postAuthorProfileVO = new PostAuthorProfileVO();
|
||||
postAuthorProfileVO.setCompany(companyVO);
|
||||
postAuthorProfileVO.setUser(userVO);
|
||||
|
||||
return postAuthorProfileVO;
|
||||
}
|
||||
|
||||
}
|
||||
+21
-2
@@ -2,9 +2,8 @@ package cn.link56.post.service;
|
||||
|
||||
import cn.link56.post.mapper.PostStatMapper;
|
||||
import cn.link56.post.model.po.PostStat;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -39,4 +38,24 @@ public class PostStatService extends ServiceImpl<PostStatMapper, PostStat> {
|
||||
this.save(stat);
|
||||
}
|
||||
|
||||
|
||||
public Integer getPostViewCount(Long postId){
|
||||
PostStat postStat = postStatMapper.selectById(postId);
|
||||
|
||||
if (ObjectUtils.isEmpty(postStat)){
|
||||
return 0;
|
||||
}
|
||||
|
||||
return postStat.getViewCount();
|
||||
}
|
||||
|
||||
public Integer getPostCommentCount(Long postId){
|
||||
PostStat postStat = postStatMapper.selectById(postId);
|
||||
|
||||
if (ObjectUtils.isEmpty(postStat)){
|
||||
return 0;
|
||||
}
|
||||
|
||||
return postStat.getCommentCount();
|
||||
}
|
||||
}
|
||||
+19
-1
@@ -1,9 +1,13 @@
|
||||
package cn.link56.post.service;
|
||||
|
||||
import cn.link56.common.utils.snowflake.idgen.IdGenerator;
|
||||
import cn.link56.post.mapper.PostViewMapper;
|
||||
import cn.link56.post.model.dto.CreatePostViewDTO;
|
||||
import cn.link56.post.model.po.PostView;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@@ -11,7 +15,8 @@ import javax.annotation.Resource;
|
||||
public class PostViewService extends ServiceImpl<PostViewMapper, PostView> {
|
||||
@Resource
|
||||
private PostViewMapper postViewMapper;
|
||||
|
||||
@Resource
|
||||
private PostStatService postStatService;
|
||||
|
||||
/**
|
||||
* 根据文章 id 获取浏览总数
|
||||
@@ -30,4 +35,17 @@ public class PostViewService extends ServiceImpl<PostViewMapper, PostView> {
|
||||
.eq(PostView::getPostId, postId)
|
||||
.remove();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void create(Long postId,Long userId) {
|
||||
if (ObjectUtils.isNotEmpty(userId)){
|
||||
PostView postView = new PostView();
|
||||
postView.setId(IdGenerator.nextId());
|
||||
postView.setPostId(postId);
|
||||
postView.setUserId(userId);
|
||||
postViewMapper.insert(postView);
|
||||
}
|
||||
|
||||
postStatService.increaseViewCount(postId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
server:
|
||||
port: 8090
|
||||
netty:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driver-class-name: net.sourceforge.jtds.jdbc.Driver
|
||||
url: jdbc:jtds:sqlserver://127.0.0.1:1433;databaseName=G3-Link56
|
||||
username: sa
|
||||
password: "123456@aaa"
|
||||
# druid 连接池管理
|
||||
druid:
|
||||
# 初始化时建立物理连接的个数
|
||||
initial-size: 5
|
||||
# 连接池的最小空闲数量
|
||||
min-idle: 5
|
||||
# 连接池最大连接数量
|
||||
max-active: 20
|
||||
# 获取连接时最大等待时间,单位毫秒
|
||||
max-wait: 60000
|
||||
# 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
|
||||
test-while-idle: true
|
||||
# 既作为检测的间隔时间又作为testWhileIdel执行的依据
|
||||
time-between-eviction-runs-millis: 60000
|
||||
# 销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接(配置连接在池中的最小生存时间)
|
||||
min-evictable-idle-time-millis: 30000
|
||||
# 用来检测数据库连接是否有效的sql 必须是一个查询语句(oracle中为 select 1 from dual) validation-query: select 'x'
|
||||
# 申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
|
||||
test-on-borrow: false
|
||||
# 归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
|
||||
test-on-return: false
|
||||
# 是否缓存preparedStatement, 也就是PSCache,PSCache对支持游标的数据库性能提升巨大,比如说oracle,在mysql下建议关闭。
|
||||
pool-prepared-statements: false
|
||||
# 置监控统计拦截的filters,去掉后监控界面sql无法统计,stat: 监控统计、Slf4j:日志记录、waLL: 防御sqL注入
|
||||
filters: stat,wall,slf4j
|
||||
# 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
|
||||
max-pool-prepared-statement-per-connection-size: -1
|
||||
# 合并多个DruidDataSource的监控数据
|
||||
use-global-data-source-stat: true
|
||||
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
|
||||
connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
|
||||
|
||||
stat-view-servlet:
|
||||
# 是否启用StatViewServlet默认值true
|
||||
enabled: true
|
||||
# 访问路径为/druid时,跳转到StatViewServlet
|
||||
url-pattern: /druid/*
|
||||
# 是否能够重置数据
|
||||
reset-enable: false
|
||||
# 需要账号密码才能访问控制台,默认为root
|
||||
login-username: root
|
||||
login-password: root
|
||||
# IP白名单
|
||||
allow: 127.0.0.1
|
||||
# IP黑名单(共同存在时,deny优先于allow)
|
||||
deny:
|
||||
@@ -15,21 +15,19 @@ logging:
|
||||
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
active: prod
|
||||
jackson:
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
time-zone: GMT+8
|
||||
mail:
|
||||
host: smtp.163.com #邮箱服务器地址
|
||||
username: hi_itao@163.com #邮箱账号
|
||||
password: GVdEzKAee9MByXdy #邮箱授权码
|
||||
password: RSchPDWrKwVcyMG7 #邮箱授权码
|
||||
default-encoding: utf-8 #默认编码
|
||||
|
||||
mybatis-plus:
|
||||
# 对应的 XML 文件位置
|
||||
mapperLocations: classpath*:mapper/**/*Mapper.xml
|
||||
# 实体扫描,多个package用逗号或者分号分隔
|
||||
typeAliasesPackage: cn.link56.domain
|
||||
# configLocation: classpath:mybatis/mybatis-config.xml
|
||||
configuration:
|
||||
cache-enabled: true
|
||||
|
||||
+20
-39
@@ -72,7 +72,7 @@
|
||||
</if>
|
||||
|
||||
<if test="title != null and title != ''">
|
||||
AND p.b_title LIKE CONCAT('%', #{title}, '%')
|
||||
AND p.b_title LIKE '%' + #{title} + '%'
|
||||
</if>
|
||||
|
||||
<if test="year != null and year != ''">
|
||||
@@ -82,6 +82,8 @@
|
||||
<if test="month != null and month != ''">
|
||||
AND MONTH(p.b_update_time) = #{month}
|
||||
</if>
|
||||
|
||||
AND p.b_deleted = 0
|
||||
</where>
|
||||
) t
|
||||
WHERE t.rn BETWEEN #{offset} AND #{limit}
|
||||
@@ -111,47 +113,10 @@
|
||||
<if test="month != null and month != ''">
|
||||
AND MONTH(p.b_update_time) = #{month}
|
||||
</if>
|
||||
AND p.b_deleted = 0
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="selectDashboardOverview"
|
||||
resultType="cn.link56.post.model.vo.EditorDashboardVO$Count">
|
||||
SELECT
|
||||
COUNT(p.b_id) AS postCount,
|
||||
ISNULL(SUM(ps.b_view_count), 0) AS viewCount,
|
||||
ISNULL(SUM(ps.b_like_count), 0) AS likeCount
|
||||
FROM b_post p
|
||||
LEFT JOIN b_post_stat ps ON ps.b_post_id = p.b_id
|
||||
WHERE p.b_create_by = #{userId}
|
||||
</select>
|
||||
|
||||
|
||||
<select id="selectDashboardViewStats" resultType="cn.link56.post.model.vo.EditorDashboardVO$Chart">
|
||||
SELECT
|
||||
CONVERT(VARCHAR(10), v.b_create_time, 120) AS label,
|
||||
COUNT(*) AS value
|
||||
FROM b_post_view v
|
||||
JOIN b_post p ON v.b_post_id = p.b_id
|
||||
WHERE p.b_create_by = #{userId}
|
||||
AND v.b_create_time >= CONVERT(DATE, DATEADD(DAY, -6, GETDATE()))
|
||||
AND v.b_create_time < CONVERT(DATE, DATEADD(DAY, 1, GETDATE()))
|
||||
GROUP BY CONVERT(VARCHAR(10), v.b_create_time, 120)
|
||||
ORDER BY label
|
||||
</select>
|
||||
|
||||
<select id="selectDashboardNumberStats" resultType="cn.link56.post.model.vo.EditorDashboardVO$Chart">
|
||||
SELECT
|
||||
CONVERT(VARCHAR(10), p.b_create_time, 120) AS label,
|
||||
COUNT(*) AS value
|
||||
FROM b_post p
|
||||
WHERE p.b_create_by = #{userId}
|
||||
AND p.b_create_time >= CONVERT(DATE, DATEADD(DAY, -6, GETDATE()))
|
||||
AND p.b_create_time < CONVERT(DATE, DATEADD(DAY, 1, GETDATE()))
|
||||
GROUP BY CONVERT(VARCHAR(10), p.b_create_time, 120)
|
||||
ORDER BY label
|
||||
</select>
|
||||
|
||||
|
||||
<select id="selectPublicPostPage" resultMap="QueryPublicPostVOMap">
|
||||
SELECT
|
||||
t.id,
|
||||
@@ -191,6 +156,7 @@
|
||||
<if test="tag != null and tag != ''">
|
||||
AND p.b_tag LIKE '%' + #{tag} + '%'
|
||||
</if>
|
||||
AND p.b_deleted = 0
|
||||
</where>
|
||||
) t
|
||||
WHERE t.rn BETWEEN #{offset} AND #{limit}
|
||||
@@ -209,7 +175,22 @@
|
||||
<if test="tag != null and tag != ''">
|
||||
AND p.b_tag LIKE '%' + #{tag} + '%'
|
||||
</if>
|
||||
AND p.b_deleted = 0
|
||||
</where>
|
||||
</select>
|
||||
|
||||
|
||||
<!-- 批量软删除用户的帖子 -->
|
||||
<delete id="deleteUserPosts">
|
||||
UPDATE b_post
|
||||
SET b_deleted = 1,
|
||||
update_by = #{updateBy},
|
||||
update_time = GETDATE()
|
||||
WHERE b_create_by IN
|
||||
<foreach item="userId" collection="userIds" open="(" separator="," close=")">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND b_deleted = 0
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
@@ -11,7 +11,6 @@
|
||||
<title></title>
|
||||
<style type="text/css" rel="stylesheet" media="all">
|
||||
/* Base ------------------------------ */
|
||||
|
||||
@import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&display=swap");
|
||||
|
||||
body {
|
||||
@@ -381,6 +380,7 @@
|
||||
width: 100%;
|
||||
margin: 30px auto;
|
||||
padding: 0;
|
||||
font-size: 42px;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
@@ -464,7 +464,7 @@
|
||||
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="email-masthead">
|
||||
<a href="https://example.com" class="f-fallback email-masthead_name">
|
||||
<a href="http://link56.net/" class="f-fallback email-masthead_name">
|
||||
link56
|
||||
</a>
|
||||
</td>
|
||||
@@ -478,29 +478,17 @@
|
||||
<tr>
|
||||
<td class="content-cell">
|
||||
<div class="f-fallback">
|
||||
<h1>您好 [[${Name}]],</h1>
|
||||
<p>您最近请求重置 link56 账户的密码。请点击下面的按钮进行密码重置。<strong>该重置链接仅在 10 分钟内有效。</strong></p>
|
||||
<h1>您好</h1>
|
||||
<p>
|
||||
您正在 <strong>link56</strong> 平台进行<strong>找回密码</strong>操作,
|
||||
密码如下,请注意保存。
|
||||
</p>
|
||||
|
||||
<!-- Action -->
|
||||
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0"
|
||||
role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<!-- Border based button
|
||||
https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design -->
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="[[${ResetUrl}]]" class="f-fallback button button--green"
|
||||
target="_blank">重置您的密码</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p class="body-action">
|
||||
[[${Password}]]
|
||||
</p>
|
||||
<p>如果不是您本人操作,请忽略此邮件,或如有疑问,请 <a
|
||||
href="[[${SupportUrl}]]">联系客服</a>。</p>
|
||||
href="{{SupportUrl}}">联系客服</a>。</p>
|
||||
|
||||
<p>
|
||||
感谢,<br>
|
||||
|
||||
@@ -0,0 +1,524 @@
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="x-apple-disable-message-reformatting" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<meta name="supported-color-schemes" content="light dark" />
|
||||
<title></title>
|
||||
<style type="text/css" rel="stylesheet" media="all">
|
||||
/* Base ------------------------------ */
|
||||
@import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&display=swap");
|
||||
|
||||
body {
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3869D4;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
td {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.preheader {
|
||||
display: none !important;
|
||||
visibility: hidden;
|
||||
mso-hide: all;
|
||||
font-size: 1px;
|
||||
line-height: 1px;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Type ------------------------------ */
|
||||
|
||||
body,
|
||||
td,
|
||||
th {
|
||||
font-family: "Nunito Sans", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
color: #333333;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
color: #333333;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
p,
|
||||
ul,
|
||||
ol,
|
||||
blockquote {
|
||||
margin: .4em 0 1.1875em;
|
||||
font-size: 16px;
|
||||
line-height: 1.625;
|
||||
}
|
||||
|
||||
p.sub {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Utilities ------------------------------ */
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.u-margin-bottom-none {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Buttons ------------------------------ */
|
||||
|
||||
.button {
|
||||
background-color: #3869D4;
|
||||
border-top: 10px solid #3869D4;
|
||||
border-right: 18px solid #3869D4;
|
||||
border-bottom: 10px solid #3869D4;
|
||||
border-left: 18px solid #3869D4;
|
||||
display: inline-block;
|
||||
color: #FFF;
|
||||
text-decoration: none;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
|
||||
-webkit-text-size-adjust: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.button--green {
|
||||
background-color: #22BC66;
|
||||
border-top: 10px solid #22BC66;
|
||||
border-right: 18px solid #22BC66;
|
||||
border-bottom: 10px solid #22BC66;
|
||||
border-left: 18px solid #22BC66;
|
||||
}
|
||||
|
||||
.button--red {
|
||||
background-color: #FF6136;
|
||||
border-top: 10px solid #FF6136;
|
||||
border-right: 18px solid #FF6136;
|
||||
border-bottom: 10px solid #FF6136;
|
||||
border-left: 18px solid #FF6136;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
.button {
|
||||
width: 100% !important;
|
||||
text-align: center !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Attribute list ------------------------------ */
|
||||
|
||||
.attributes {
|
||||
margin: 0 0 21px;
|
||||
}
|
||||
|
||||
.attributes_content {
|
||||
background-color: #F4F4F7;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.attributes_item {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Related Items ------------------------------ */
|
||||
|
||||
.related {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 25px 0 0 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
|
||||
.related_item {
|
||||
padding: 10px 0;
|
||||
color: #CBCCCF;
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.related_item-title {
|
||||
display: block;
|
||||
margin: .5em 0 0;
|
||||
}
|
||||
|
||||
.related_item-thumb {
|
||||
display: block;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.related_heading {
|
||||
border-top: 1px solid #CBCCCF;
|
||||
text-align: center;
|
||||
padding: 25px 0 10px;
|
||||
}
|
||||
|
||||
/* Discount Code ------------------------------ */
|
||||
|
||||
.discount {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 24px;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
background-color: #F4F4F7;
|
||||
border: 2px dashed #CBCCCF;
|
||||
}
|
||||
|
||||
.discount_heading {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.discount_body {
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* Social Icons ------------------------------ */
|
||||
|
||||
.social {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.social td {
|
||||
padding: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.social_icon {
|
||||
height: 20px;
|
||||
margin: 0 8px 10px 8px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Data table ------------------------------ */
|
||||
|
||||
.purchase {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 35px 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
|
||||
.purchase_content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 25px 0 0 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
|
||||
.purchase_item {
|
||||
padding: 10px 0;
|
||||
color: #51545E;
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.purchase_heading {
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #EAEAEC;
|
||||
}
|
||||
|
||||
.purchase_heading p {
|
||||
margin: 0;
|
||||
color: #85878E;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.purchase_footer {
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #EAEAEC;
|
||||
}
|
||||
|
||||
.purchase_total {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.purchase_total--label {
|
||||
padding: 0 15px 0 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #F2F4F6;
|
||||
color: #51545E;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #51545E;
|
||||
}
|
||||
|
||||
.email-wrapper {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
background-color: #F2F4F6;
|
||||
}
|
||||
|
||||
.email-content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
|
||||
/* Masthead ----------------------- */
|
||||
|
||||
.email-masthead {
|
||||
padding: 25px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.email-masthead_logo {
|
||||
width: 94px;
|
||||
}
|
||||
|
||||
.email-masthead_name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #A8AAAF;
|
||||
text-decoration: none;
|
||||
text-shadow: 0 1px 0 white;
|
||||
}
|
||||
|
||||
/* Body ------------------------------ */
|
||||
|
||||
.email-body {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
|
||||
.email-body_inner {
|
||||
width: 570px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
-premailer-width: 570px;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.email-footer {
|
||||
width: 570px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
-premailer-width: 570px;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.email-footer p {
|
||||
color: #A8AAAF;
|
||||
}
|
||||
|
||||
.body-action {
|
||||
width: 100%;
|
||||
margin: 30px auto;
|
||||
padding: 0;
|
||||
font-size: 42px;
|
||||
-premailer-width: 100%;
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.body-sub {
|
||||
margin-top: 25px;
|
||||
padding-top: 25px;
|
||||
border-top: 1px solid #EAEAEC;
|
||||
}
|
||||
|
||||
.content-cell {
|
||||
padding: 45px;
|
||||
}
|
||||
|
||||
/*Media Queries ------------------------------ */
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
|
||||
.email-body_inner,
|
||||
.email-footer {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
body,
|
||||
.email-body,
|
||||
.email-body_inner,
|
||||
.email-content,
|
||||
.email-wrapper,
|
||||
.email-masthead,
|
||||
.email-footer {
|
||||
background-color: #333333 !important;
|
||||
color: #FFF !important;
|
||||
}
|
||||
|
||||
p,
|
||||
ul,
|
||||
ol,
|
||||
blockquote,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
span,
|
||||
.purchase_item {
|
||||
color: #FFF !important;
|
||||
}
|
||||
|
||||
.attributes_content,
|
||||
.discount {
|
||||
background-color: #222 !important;
|
||||
}
|
||||
|
||||
.email-masthead_name {
|
||||
text-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
supported-color-schemes: light dark;
|
||||
}
|
||||
</style>
|
||||
<!--[if mso]>
|
||||
<style type="text/css">
|
||||
.f-fallback {
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<span class="preheader">Use this link to reset your password. The link is only valid for 24 hours.</span>
|
||||
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="email-masthead">
|
||||
<a href="http://link56.net" class="f-fallback email-masthead_name">
|
||||
link56
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Email Body -->
|
||||
<tr>
|
||||
<td class="email-body" width="570" cellpadding="0" cellspacing="0">
|
||||
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0"
|
||||
role="presentation">
|
||||
<!-- Body content -->
|
||||
<tr>
|
||||
<td class="content-cell">
|
||||
<div class="f-fallback">
|
||||
<h1>您好</h1>
|
||||
<p>
|
||||
您正在 <strong>link56</strong> 平台进行<strong>找回密码</strong>操作,
|
||||
密码如下,请注意保存。
|
||||
</p>
|
||||
|
||||
<p class="body-action">
|
||||
[[${Password}]]
|
||||
</p>
|
||||
<p>如果不是您本人操作,请忽略此邮件,或如有疑问,请 <a
|
||||
href="{{SupportUrl}}">联系客服</a>。</p>
|
||||
|
||||
<p>
|
||||
感谢,<br>
|
||||
link56 团队
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table class="email-footer" align="center" width="570" cellpadding="0" cellspacing="0"
|
||||
role="presentation">
|
||||
<tr>
|
||||
<td class="content-cell" align="center">
|
||||
<p class="f-fallback sub align-center">
|
||||
宁波江东远通计算机有限公司
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -11,7 +11,6 @@
|
||||
<title></title>
|
||||
<style type="text/css" rel="stylesheet" media="all">
|
||||
/* Base ------------------------------ */
|
||||
|
||||
@import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&display=swap");
|
||||
|
||||
body {
|
||||
@@ -465,7 +464,7 @@
|
||||
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="email-masthead">
|
||||
<a href="https://example.com" class="f-fallback email-masthead_name">
|
||||
<a href="http://link56.net/" class="f-fallback email-masthead_name">
|
||||
link56
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@@ -465,7 +465,7 @@
|
||||
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="email-masthead">
|
||||
<a href="https://example.com" class="f-fallback email-masthead_name">
|
||||
<a href="http://link56.net/" class="f-fallback email-masthead_name">
|
||||
link56
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@@ -465,7 +465,7 @@
|
||||
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="email-masthead">
|
||||
<a href="https://example.com" class="f-fallback email-masthead_name">
|
||||
<a href="http://link56.net/" class="f-fallback email-masthead_name">
|
||||
link56
|
||||
</a>
|
||||
</td>
|
||||
|
||||
Reference in new issue
Block a user