This commit is contained in:
oneao committed 2025-12-27 17:21:38 +08:00
1 parent 2f8a815c62
commit a09fdf0a66
127 files changed
+4518 -1159

No files matched your search

@@ -5,17 +5,32 @@ import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@AllArgsConstructor
public class AuthLoginVO {
private Long companyId;
private String companyLogo;
private String companyName;
private Long userId;
private Integer userRole;
private String userName;
private String userPosition;
private Company company;
private User user;
private String token;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Company {
private Long id;
private String logo;
private String name;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class User {
private Long id;
private Integer role;
private String name;
private String position;
private String avatar;
}
}
@@ -9,8 +9,8 @@ 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.company.model.po.CompanyPO;
import cn.link56.company.model.po.CompanyUserPO;
import cn.link56.company.model.po.Company;
import cn.link56.company.model.po.CompanyUser;
import cn.link56.auth.identity.model.dto.AuthRegisterDTO;
import cn.link56.company.service.CompanyService;
import cn.link56.company.service.CompanyUserService;
@@ -61,7 +61,7 @@ public class AuthIdentityService {
long companyId = IdGenerator.nextId();
companyService.save(
new CompanyPO()
new Company()
.setId(companyId)
.setName(company.getName())
.setIndustry(StringUtils.join(company.getIndustries(),","))
@@ -72,7 +72,7 @@ public class AuthIdentityService {
);
companyUserService.save(
new CompanyUserPO()
new CompanyUser()
.setId(IdGenerator.nextId())
.setCompanyId(companyId)
.setName(admin.getName())
@@ -85,7 +85,7 @@ public class AuthIdentityService {
}
public AuthLoginVO loginByPassword(AuthLoginPasswordDTO dto) {
CompanyUserPO user = companyUserService.loginByEmailAndPassword(dto.getEmail(), dto.getPassword());
CompanyUser user = companyUserService.loginByEmailAndPassword(dto.getEmail(), dto.getPassword());
if (user == null) {
throw new BusinessException("邮箱或密码错误");
}
@@ -107,7 +107,7 @@ public class AuthIdentityService {
throw new BusinessException("邮箱或验证码失效");
}
CompanyUserPO user = companyUserService.loginByEmail(email);
CompanyUser user = companyUserService.loginByEmail(email);
if (user == null) {
throw new BusinessException("邮箱不存在");
}
@@ -118,8 +118,8 @@ public class AuthIdentityService {
/**
* 公共逻辑:根据用户生成 LoginVO
*/
private AuthLoginVO buildLoginVO(CompanyUserPO user) {
CompanyPO company = companyService.getById(user.getCompanyId());
private AuthLoginVO buildLoginVO(CompanyUser user) {
Company company = companyService.getById(user.getCompanyId());
TokenPayload tokenPayload = new TokenPayload();
tokenPayload.setCompanyId(user.getCompanyId());
@@ -127,18 +127,21 @@ public class AuthIdentityService {
AuthToken authToken = tokenService.generateToken(tokenPayload);
return new AuthLoginVO(
AuthLoginVO.Company companyVO = new AuthLoginVO.Company(
company.getId(),
company.getLogo(),
company.getName(),
company.getName()
);
AuthLoginVO.User userVO = new AuthLoginVO.User(
user.getId(),
user.getRole(),
user.getName(),
user.getPosition(),
authToken.getToken()
user.getAvatar()
);
return new AuthLoginVO(companyVO, userVO, authToken.getToken());
}
}
@@ -1,6 +1,6 @@
package cn.link56.base.mapper;
import cn.link56.base.model.po.AreaPO;
import cn.link56.base.model.po.Area;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@@ -11,5 +11,5 @@ public interface AreaMapper {
* 获取所有区域信息
* @return 区域集合
*/
List<AreaPO> listAllArea();
List<Area> listAllArea();
}
@@ -1,9 +1,9 @@
package cn.link56.base.mapper;
import cn.link56.base.model.po.FilePO;
import cn.link56.base.model.po.File;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface FileMapper extends BaseMapper<FilePO> {
public interface FileMapper extends BaseMapper<File> {
}
@@ -1,9 +1,9 @@
package cn.link56.base.mapper;
import cn.link56.base.model.po.IndustryPO;
import cn.link56.base.model.po.Industry;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface IndustryMapper extends BaseMapper<IndustryPO> {
public interface IndustryMapper extends BaseMapper<Industry> {
}
@@ -7,7 +7,7 @@ import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AreaPO {
public class Area {
private String id;
private Integer xh;
private String name;
@@ -11,7 +11,7 @@ import lombok.experimental.Accessors;
@NoArgsConstructor
@TableName("b_file")
@Accessors(chain = true)
public class FilePO {
public class File {
@TableId(value = "b_id")
private Long id;
@@ -34,7 +34,7 @@ public class FilePO {
private String createTime;
@TableField(
value = "b_create_user_id",
value = "b_create_by",
fill = FieldFill.INSERT
)
private String createBy;
@@ -13,7 +13,7 @@ import lombok.experimental.Accessors;
@NoArgsConstructor
@TableName("b_industry")
@Accessors(chain = true)
public class IndustryPO {
public class Industry {
@TableId(value = "b_id")
private Long id;
@TableField("b_xh")
@@ -2,8 +2,8 @@ package cn.link56.base.service;
import cn.link56.base.mapper.AreaMapper;
import cn.link56.base.mapper.IndustryMapper;
import cn.link56.base.model.po.AreaPO;
import cn.link56.base.model.po.IndustryPO;
import cn.link56.base.model.po.Area;
import cn.link56.base.model.po.Industry;
import cn.link56.base.model.vo.BaseDataVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
@@ -24,8 +24,8 @@ public class BaseDataService {
// 查询可用行业
List<BaseDataVO.Industry> industries = IndustryMapper.selectList(
new LambdaQueryWrapper<IndustryPO>()
.eq(IndustryPO::getCanuse, "1")
new LambdaQueryWrapper<Industry>()
.eq(Industry::getCanuse, "1")
).stream()
.map(industry -> new BaseDataVO.Industry(
industry.getId(),
@@ -37,7 +37,7 @@ public class BaseDataService {
baseDataVO.setIndustries(industries);
// 地区
List<AreaPO> allAreas = AreaMapper.listAllArea();
List<Area> allAreas = AreaMapper.listAllArea();
List<BaseDataVO.Area> areaTree = buildAreaTree(allAreas);
baseDataVO.setAreas(areaTree);
@@ -47,7 +47,7 @@ public class BaseDataService {
/**
* BArea → BaseDataVO.Area 树
*/
private List<BaseDataVO.Area> buildAreaTree(List<AreaPO> areas) {
private List<BaseDataVO.Area> buildAreaTree(List<Area> areas) {
if (areas == null || areas.isEmpty()) {
return Collections.emptyList();
}
@@ -56,7 +56,7 @@ public class BaseDataService {
Map<String, BaseDataVO.Area> map = new HashMap<>(areas.size());
// 1️⃣ 先全部转 VO
for (AreaPO a : areas) {
for (Area a : areas) {
BaseDataVO.Area vo = new BaseDataVO.Area(
a.getId(),
a.getXh(),
@@ -2,7 +2,7 @@ package cn.link56.base.service;
import cn.link56.base.mapper.FileMapper;
import cn.link56.base.model.dto.AddFileDTO;
import cn.link56.base.model.po.FilePO;
import cn.link56.base.model.po.File;
import cn.link56.common.utils.snowflake.idgen.IdGenerator;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
@@ -12,7 +12,7 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Service
public class FileService extends ServiceImpl<FileMapper, FilePO> {
public class FileService extends ServiceImpl<FileMapper, File> {
@Resource
private FileMapper fileMapper;
@@ -30,13 +30,13 @@ public class FileService extends ServiceImpl<FileMapper, FilePO> {
}
}
FilePO filePO = new FilePO();
filePO.setId(IdGenerator.nextId())
File file = new File();
file.setId(IdGenerator.nextId())
.setExt(suffix)
.setName(fileName)
.setMapName(fileMapName)
.setSize(fileSize);
fileMapper.insert(filePO);
fileMapper.insert(file);
}
}
@@ -24,6 +24,8 @@ public class AutoMetaObjectHandler implements MetaObjectHandler {
this.strictInsertFill(metaObject, "createTime", Date.class, date);
this.strictInsertFill(metaObject, "updateTime", Date.class, date);
this.strictInsertFill(metaObject, "deleted", Integer.class, 0);
}
@Override
@@ -33,6 +35,7 @@ public class AutoMetaObjectHandler implements MetaObjectHandler {
if (ObjectUtils.isNotEmpty(userId) && userId > 0){
this.strictUpdateFill(metaObject, "updateBy", Long.class, userId);
}
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
}
}
@@ -0,0 +1,13 @@
package cn.link56.common.utils;
import java.time.Instant;
public class TimeUtils {
public static long getCurrentTimestampMillis() {
return Instant.now().toEpochMilli();
}
public static long getCurrentTimestampSeconds() {
return Instant.now().getEpochSecond();
}
}
@@ -1,13 +1,13 @@
package cn.link56.company.mapper;
import cn.link56.company.model.middle.RecommendCompanyRow;
import cn.link56.company.model.po.CompanyPO;
import cn.link56.company.model.po.Company;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CompanyMapper extends BaseMapper<CompanyPO> {
public interface CompanyMapper extends BaseMapper<Company> {
List<RecommendCompanyRow> listRecommend();
}
@@ -1,9 +1,9 @@
package cn.link56.company.mapper;
import cn.link56.company.model.po.CompanyUserPO;
import cn.link56.company.model.po.CompanyUser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CompanyUserMapper extends BaseMapper<CompanyUserPO> {
public interface CompanyUserMapper extends BaseMapper<CompanyUser> {
}
@@ -24,4 +24,6 @@ public class UpdateCompanyUserDTO {
private String name;
private String phone;
private String canuse;
private String avatar;
}
@@ -17,7 +17,7 @@ import java.util.Date;
@NoArgsConstructor
@TableName("b_company")
@Accessors(chain = true)
public class CompanyPO {
public class Company {
@TableId(value = "b_id")
private Long id;
@@ -17,7 +17,7 @@ import java.util.Date;
@NoArgsConstructor
@TableName("b_company_user")
@Accessors(chain = true)
public class CompanyUserPO {
public class CompanyUser {
@TableId(value = "b_id")
private Long id;
@@ -45,11 +45,12 @@ public class CompanyUserPO {
@TableField("b_canuse")
private String canuse;
@TableField("b_avatar")
private String avatar;
@TableField(value = "b_create_time",fill = FieldFill.INSERT)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@TableField(value = "b_update_time",fill = FieldFill.INSERT_UPDATE)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
@@ -17,5 +17,6 @@ public class QueryCompanyUserVO {
private String email;
private String phone;
private String avatar;
private String canuse;
}
@@ -7,8 +7,8 @@ import cn.link56.company.mapper.CompanyMapper;
import cn.link56.company.model.dto.QueryCompanyDTO;
import cn.link56.company.model.dto.UpdateCompanyDTO;
import cn.link56.company.model.middle.RecommendCompanyRow;
import cn.link56.company.model.po.CompanyPO;
import cn.link56.company.model.po.CompanyUserPO;
import cn.link56.company.model.po.Company;
import cn.link56.company.model.po.CompanyUser;
import cn.link56.company.model.vo.CompanyDetailVO;
import cn.link56.company.model.vo.QueryCompanyVO;
import cn.link56.company.model.vo.RecommendCompanyVO;
@@ -24,12 +24,11 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class CompanyService extends ServiceImpl<CompanyMapper,CompanyPO> {
public class CompanyService extends ServiceImpl<CompanyMapper, Company> {
@Resource
private CompanyMapper companyMapper;
@Resource
@@ -67,9 +66,9 @@ public class CompanyService extends ServiceImpl<CompanyMapper,CompanyPO> {
}
public List<RecommendHotCompanyVO> listHot() {
LambdaQueryWrapper<CompanyPO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CompanyPO::getHotRecommend, "1")
.select(CompanyPO::getId, CompanyPO::getName);
LambdaQueryWrapper<Company> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Company::getHotRecommend, "1")
.select(Company::getId, Company::getName);
return companyMapper.selectList(queryWrapper)
.stream()
@@ -86,8 +85,8 @@ public class CompanyService extends ServiceImpl<CompanyMapper,CompanyPO> {
*/
public boolean isNameExists(String name) {
Long count = companyMapper.selectCount(
new LambdaQueryWrapper<CompanyPO>()
.eq(CompanyPO::getName, name)
new LambdaQueryWrapper<Company>()
.eq(Company::getName, name)
);
return count > 0;
}
@@ -97,8 +96,8 @@ public class CompanyService extends ServiceImpl<CompanyMapper,CompanyPO> {
*/
public boolean isPhoneExists(String phone) {
Long count = companyMapper.selectCount(
new LambdaQueryWrapper<CompanyPO>()
.eq(CompanyPO::getPhone, phone)
new LambdaQueryWrapper<Company>()
.eq(Company::getPhone, phone)
);
return count > 0;
}
@@ -108,8 +107,8 @@ public class CompanyService extends ServiceImpl<CompanyMapper,CompanyPO> {
*/
public boolean isEmailExists(String email) {
Long count = companyMapper.selectCount(
new LambdaQueryWrapper<CompanyPO>()
.eq(CompanyPO::getEmail, email)
new LambdaQueryWrapper<Company>()
.eq(Company::getEmail, email)
);
return count > 0;
}
@@ -123,19 +122,19 @@ public class CompanyService extends ServiceImpl<CompanyMapper,CompanyPO> {
List<String> industries = queryCompanyDTO.getIndustries();
// 分页
Page<CompanyPO> companyPage = new Page<>(currentPage,pageSize);
LambdaQueryWrapper<CompanyPO> queryWrapper = new LambdaQueryWrapper<>();
Page<Company> companyPage = new Page<>(currentPage,pageSize);
LambdaQueryWrapper<Company> queryWrapper = new LambdaQueryWrapper<>();
// 名称
if (StringUtils.isNotBlank(name)){
queryWrapper.like(CompanyPO::getName,name);
queryWrapper.like(Company::getName,name);
}
// 地区
if (areas != null && !areas.isEmpty()){
queryWrapper.and(wrapper -> {
for (String area : areas) {
wrapper.or(w -> w.like(CompanyPO::getArea, area));
wrapper.or(w -> w.like(Company::getArea, area));
}
});
}
@@ -144,12 +143,12 @@ public class CompanyService extends ServiceImpl<CompanyMapper,CompanyPO> {
if (industries != null && !industries.isEmpty()) {
queryWrapper.and(wrapper -> {
for (String industry : industries) {
wrapper.or(w -> w.like(CompanyPO::getIndustry, industry));
wrapper.or(w -> w.like(Company::getIndustry, industry));
}
});
}
queryWrapper.eq(CompanyPO::getCanuse,"1");
queryWrapper.eq(Company::getCanuse,"1");
List<QueryCompanyVO> result = companyMapper.selectList(companyPage, queryWrapper).stream().map(item -> {
QueryCompanyVO queryCompanyVO = new QueryCompanyVO();
@@ -173,18 +172,18 @@ public class CompanyService extends ServiceImpl<CompanyMapper,CompanyPO> {
}
public CompanyDetailVO getCompanyDetail(Long companyId) {
CompanyPO companyPO = companyMapper.selectById(companyId);
List<CompanyUserPO> companyUserPOS = companyUserService.listByCompanyId(companyId);
Company company = companyMapper.selectById(companyId);
List<CompanyUser> companyUsers = companyUserService.listByCompanyId(companyId);
CompanyDetailVO companyDetailVO = new CompanyDetailVO();
BeanUtils.copyProperties(companyPO,companyDetailVO);
companyDetailVO.setAreas(Arrays.stream(companyPO.getArea().split(","))
BeanUtils.copyProperties(company,companyDetailVO);
companyDetailVO.setAreas(Arrays.stream(company.getArea().split(","))
.collect(Collectors.toList()));
companyDetailVO.setIndustries(Arrays.stream(companyPO.getIndustry().split(","))
companyDetailVO.setIndustries(Arrays.stream(company.getIndustry().split(","))
.collect(Collectors.toList()));
companyDetailVO.setUsers(companyUserPOS.stream()
companyDetailVO.setUsers(companyUsers.stream()
.map(item -> {
CompanyDetailVO.User user = new CompanyDetailVO.User();
BeanUtils.copyProperties(item, user);
@@ -206,43 +205,41 @@ public class CompanyService extends ServiceImpl<CompanyMapper,CompanyPO> {
String name = updateCompanyDTO.getName();
String phone = updateCompanyDTO.getPhone();
String email = updateCompanyDTO.getEmail();
Date establishmentDate = updateCompanyDTO.getEstablishmentDate();
String profile = updateCompanyDTO.getProfile();
String description = updateCompanyDTO.getDescription();
String logo = updateCompanyDTO.getLogo();
String area = StringUtils.join(updateCompanyDTO.getAreas(), ",");
String industry = StringUtils.join(updateCompanyDTO.getIndustries(), ",");
// 检查
CompanyPO companyPO = companyMapper.selectById(id);
if (ObjectUtils.isEmpty(companyPO)){
Company company = companyMapper.selectById(id);
if (ObjectUtils.isEmpty(company)){
throw new BusinessException("更新失败,该公司不存在");
}
// 检查名称
if (!companyPO.getName().equals(name) && isNameExists(name)){
if (!company.getName().equals(name) && isNameExists(name)){
throw new BusinessException("公司名称已存在");
}
// 检查联系电话
if (!StringUtils.equals(phone, companyPO.getPhone())
if (!StringUtils.equals(phone, company.getPhone())
&& isPhoneExists(phone)) {
throw new BusinessException("公司联系电话已存在");
}
// 检查邮箱
if (!StringUtils.equals(email, companyPO.getEmail())
if (!StringUtils.equals(email, company.getEmail())
&& isEmailExists(email)) {
throw new BusinessException("公司联系邮箱已存在");
}
// 更新
BeanUtils.copyProperties(updateCompanyDTO,companyPO);
BeanUtils.copyProperties(updateCompanyDTO, company);
companyPO.setArea(area);
companyPO.setIndustry(industry);
company.setArea(area);
company.setIndustry(industry);
// 保存更新后的公司信息
companyMapper.updateById(companyPO);
companyMapper.updateById(company);
companyUserService.updateCompanyAvatar(company.getId(),company.getLogo());
}
}
@@ -4,12 +4,14 @@ 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.dto.AddCompanyUserDTO;
import cn.link56.company.model.dto.DeleteCompanyUserDTO;
import cn.link56.company.model.dto.QueryCompanyUserDTO;
import cn.link56.company.model.dto.UpdateCompanyUserDTO;
import cn.link56.company.model.po.CompanyUserPO;
import cn.link56.company.model.po.Company;
import cn.link56.company.model.po.CompanyUser;
import cn.link56.company.model.vo.QueryCompanyUserVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -26,11 +28,14 @@ import java.util.List;
import java.util.stream.Collectors;
@Service
public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUserPO> {
public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUser> {
@Resource
private CompanyUserMapper companyUserMapper;
@Resource
private CompanyMapper companyMapper;
/**
* 判断邮箱是否重复
* @param email 用户邮箱
@@ -41,8 +46,8 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
return false;
}
long count = companyUserMapper.selectCount(
new LambdaQueryWrapper<CompanyUserPO>()
.eq(CompanyUserPO::getEmail, email)
new LambdaQueryWrapper<CompanyUser>()
.eq(CompanyUser::getEmail, email)
);
return count > 0;
}
@@ -54,8 +59,8 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
*/
public boolean isPhoneExists(String phone) {
long count = companyUserMapper.selectCount(
new LambdaQueryWrapper<CompanyUserPO>()
.eq(CompanyUserPO::getPhone, phone)
new LambdaQueryWrapper<CompanyUser>()
.eq(CompanyUser::getPhone, phone)
);
return count > 0;
}
@@ -69,10 +74,10 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
*/
public boolean hasPermission(Long companyId, Long userId) {
// 根据 userId 查询公司用户信息
CompanyUserPO companyUserPO = companyUserMapper.selectById(userId);
CompanyUser companyUser = companyUserMapper.selectById(userId);
// 如果 companyUserPO 为空,表示该用户不存在,直接返回无权限
if (ObjectUtils.isEmpty(companyUserPO)) {
if (ObjectUtils.isEmpty(companyUser)) {
return false;
}
@@ -82,39 +87,55 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
}
// 判断用户是否属于该公司
if (!companyUserPO.getCompanyId().equals(companyId)) {
if (!companyUser.getCompanyId().equals(companyId)) {
return false;
}
// 检查用户角色是否为 0,只有角色为 0 才有权限
return companyUserPO.getRole() == 0;
return companyUser.getRole() == 0;
// 如果角色不是 0,表示没有权限
}
public CompanyUserPO loginByEmailAndPassword(String email, String password){
LambdaQueryWrapper<CompanyUserPO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CompanyUserPO::getEmail,email)
.eq(CompanyUserPO::getPassword,password);
public CompanyUser loginByEmailAndPassword(String email, String password){
LambdaQueryWrapper<CompanyUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CompanyUser::getEmail,email)
.eq(CompanyUser::getPassword,password);
return companyUserMapper.selectOne(queryWrapper);
}
public CompanyUserPO loginByEmail(String email){
LambdaQueryWrapper<CompanyUserPO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CompanyUserPO::getEmail,email);
public CompanyUser loginByEmail(String email){
LambdaQueryWrapper<CompanyUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CompanyUser::getEmail,email);
return companyUserMapper.selectOne(queryWrapper);
}
public List<CompanyUserPO> listByCompanyId(Long companyId){
LambdaQueryWrapper<CompanyUserPO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CompanyUserPO::getCompanyId,companyId);
public List<CompanyUser> listByCompanyId(Long companyId){
LambdaQueryWrapper<CompanyUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CompanyUser::getCompanyId,companyId);
return companyUserMapper.selectList(queryWrapper);
}
public void updateCompanyAvatar(Long companyId, String avatar) {
if (StringUtils.isBlank(avatar)) {
return;
}
LambdaUpdateWrapper<CompanyUser> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(CompanyUser::getCompanyId, companyId)
.and(w -> w.isNull(CompanyUser::getAvatar)
.or()
.eq(CompanyUser::getAvatar, ""))
.set(CompanyUser::getAvatar, avatar);
companyUserMapper.update(wrapper);
}
public BasePageVO<QueryCompanyUserVO> getUsersByCompanyId(QueryCompanyUserDTO queryCompanyUserDTO) {
boolean hasPermission = hasPermission(queryCompanyUserDTO.getCompanyId(), UserContext.getUserId());
@@ -132,34 +153,34 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
String position = queryCompanyUserDTO.getPosition();
String canuse = queryCompanyUserDTO.getCanuse();
Page<CompanyUserPO> queryPage = new Page<>(currentPage,pageSize);
LambdaQueryWrapper<CompanyUserPO> queryWrapper = new LambdaQueryWrapper<>();
Page<CompanyUser> queryPage = new Page<>(currentPage,pageSize);
LambdaQueryWrapper<CompanyUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CompanyUserPO::getCompanyId,companyId);
queryWrapper.eq(CompanyUser::getCompanyId,companyId);
if (StringUtils.isNotBlank(name)){
queryWrapper.like(CompanyUserPO::getName,name);
queryWrapper.like(CompanyUser::getName,name);
}
if (StringUtils.isNotBlank(email)){
queryWrapper.like(CompanyUserPO::getEmail,email);
queryWrapper.like(CompanyUser::getEmail,email);
}
if (StringUtils.isNotBlank(phone)){
queryWrapper.like(CompanyUserPO::getPhone,phone);
queryWrapper.like(CompanyUser::getPhone,phone);
}
if (StringUtils.isNotBlank(position)){
queryWrapper.like(CompanyUserPO::getPosition,position);
queryWrapper.like(CompanyUser::getPosition,position);
}
if (StringUtils.isNotBlank(canuse)){
queryWrapper.eq(CompanyUserPO::getCanuse,canuse);
queryWrapper.eq(CompanyUser::getCanuse,canuse);
}
List<CompanyUserPO> companyUserPOS = companyUserMapper.selectList(queryPage, queryWrapper);
List<CompanyUser> companyUsers = companyUserMapper.selectList(queryPage, queryWrapper);
List<QueryCompanyUserVO> result = companyUserPOS.stream().map(item -> {
List<QueryCompanyUserVO> result = companyUsers.stream().map(item -> {
QueryCompanyUserVO queryCompanyUserVO = new QueryCompanyUserVO();
BeanUtils.copyProperties(item, queryCompanyUserVO);
return queryCompanyUserVO;
@@ -192,14 +213,14 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
canuse = "1";
}
CompanyUserPO companyUserPO = new CompanyUserPO();
CompanyUser companyUser = new CompanyUser();
BeanUtils.copyProperties(companyUserDTO,companyUserPO);
companyUserPO.setId(IdGenerator.nextId());
companyUserPO.setRole(1);
companyUserPO.setCanuse(canuse);
BeanUtils.copyProperties(companyUserDTO, companyUser);
companyUser.setId(IdGenerator.nextId());
companyUser.setRole(1);
companyUser.setCanuse(canuse);
companyUserMapper.insert(companyUserPO);
companyUserMapper.insert(companyUser);
}
@Transactional
@@ -217,37 +238,49 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
throw new BusinessException("您没有权限访问该公司的信息,操作被拒绝");
}
CompanyUserPO companyUserPO = companyUserMapper.selectById(id);
String oldPassword = companyUserPO.getPassword();
CompanyUser companyUser = companyUserMapper.selectById(id);
String oldPassword = companyUser.getPassword();
if (ObjectUtils.isEmpty(companyUserPO)) {
if (ObjectUtils.isEmpty(companyUser)) {
throw new BusinessException("更新失败,该用户不存在");
}
// 检查邮箱是否发生变化
boolean isEmailChanged = !StringUtils.equals(email, companyUserPO.getEmail());
boolean isEmailChanged = !StringUtils.equals(email, companyUser.getEmail());
if (isEmailChanged && isEmailExists(email)) {
throw new BusinessException("邮箱已被注册");
}
// 检查手机号是否发生变化
if (!StringUtils.equals(phone, companyUserPO.getPhone()) && isPhoneExists(phone)) {
if (!StringUtils.equals(phone, companyUser.getPhone()) && isPhoneExists(phone)) {
throw new BusinessException("手机号已被注册");
}
if (StringUtils.isNotBlank(password)) {
companyUserPO.setPassword(password);
companyUser.setPassword(password);
}
BeanUtils.copyProperties(companyUserDTO, companyUserPO);
BeanUtils.copyProperties(companyUserDTO, companyUser);
if(StringUtils.isNotBlank(password)){
companyUserPO.setPassword(password);
}else{
companyUserPO.setPassword(oldPassword);
companyUser.setPassword(password);
} else {
companyUser.setPassword(oldPassword);
}
if (StringUtils.isBlank(companyUser.getAvatar())) {
LambdaQueryWrapper<Company> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Company::getId, companyUser.getCompanyId())
.select(Company::getLogo);
Company company = companyMapper.selectOne(wrapper);
if (company != null && StringUtils.isNotBlank(company.getLogo())) {
companyUser.setAvatar(company.getLogo());
}
}
// 更新用户信息
companyUserMapper.updateById(companyUserPO);
companyUserMapper.updateById(companyUser);
}
@@ -261,10 +294,10 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
throw new BusinessException("您没有权限访问该公司的信息,操作被拒绝");
}
LambdaUpdateWrapper<CompanyUserPO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(CompanyUserPO::getId, ids)
.eq(CompanyUserPO::getCompanyId, companyId)
.ne(CompanyUserPO::getRole, 0);
LambdaUpdateWrapper<CompanyUser> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(CompanyUser::getId, ids)
.eq(CompanyUser::getCompanyId, companyId)
.ne(CompanyUser::getRole, 0);
// 执行删除操作
companyUserMapper.delete(updateWrapper);
@@ -0,0 +1,78 @@
package cn.link56.im;
import cn.link56.im.config.NettyConfig;
import cn.link56.im.handler.ServerHandler;
import org.springframework.stereotype.Component;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.cors.CorsConfig;
import io.netty.handler.codec.http.cors.CorsHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
@Component
public class NettyServer {
@Resource
private NettyConfig nettyConfig;
private EventLoopGroup bossGroup;
private EventLoopGroup workerGroup;
@PostConstruct
public void start() {
new Thread(this::runServer, "Netty-ImServer-Thread").start();
}
private void runServer() {
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new CorsHandler(CorsConfig.withAnyOrigin().build()));
pipeline.addLast(new WebSocketServerProtocolHandler(nettyConfig.getPath(), null, true, 65536));
pipeline.addLast(new ServerHandler());
}
});
ChannelFuture future = bootstrap.bind(nettyConfig.getPort()).sync();
System.out.println("🚀 Netty 已启动,端口:" + nettyConfig.getPort() + ",路径:" + nettyConfig.getPath());
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
e.printStackTrace();
} finally {
stop();
}
}
@PreDestroy
public void stop() {
if (bossGroup != null) bossGroup.shutdownGracefully();
if (workerGroup != null) workerGroup.shutdownGracefully();
System.out.println("🛑 Netty 已关闭");
}
}
@@ -0,0 +1,21 @@
package cn.link56.im.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "netty")
@Data
public class NettyConfig {
/**
* Netty 服务端端口,默认 9000
*/
private Integer port = 9000;
/**
* WebSocket 路径,默认 /ws
*/
private String path = "/ws";
}
@@ -0,0 +1,18 @@
package cn.link56.im.constants;
public class MessageResponseStatusConstants {
/**
* 操作成功
*/
public static final Integer SUCCESS = 200;
/**
* 系统错误
*/
public static final Integer ERROR = 500;
/**
* 登录失效或已过期
*/
public static final Integer UNAUTHORIZED = 401;
}
@@ -0,0 +1,7 @@
package cn.link56.im.enums;
import lombok.Getter;
public enum MessageTypeEnums {
PING,PONG,ONLINE,OFFLINE,PRIVATE,GROUP
}
@@ -0,0 +1,62 @@
package cn.link56.im.handler;
import cn.link56.im.enums.MessageTypeEnums;
import cn.link56.im.message.MessageRequest;
import cn.link56.im.message.MessageResponse;
import cn.link56.im.utils.MessageWriter;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import org.springframework.stereotype.Component;
@Component
public class MessageHandler {
// 处理消息
public void handleMessage(Channel channel, MessageRequest request) {
switch (MessageTypeEnums.valueOf(request.getType())) {
case PING:
handlePing(channel, request);
break;
case ONLINE:
handleOnline(channel, request);
break;
case OFFLINE:
handleOffline(channel, request);
break;
case PRIVATE:
handlePrivate(channel, request);
break;
case GROUP:
handleGroup(channel, request);
break;
default:
System.out.println("未知消息类型: " + request.getType());
}
}
// 心跳处理
private void handlePing(Channel channel, MessageRequest request) {
MessageWriter.writeToChannel(channel, MessageResponse.success(MessageTypeEnums.PONG));
}
// 上线
private void handleOnline(Channel channel, MessageRequest request) {
// 验证
Long from = request.getFrom();
Object data = request.getData(); // companyId userId
// 账号 Token
}
private void handleOffline(Channel channel, MessageRequest request) {
// 下线逻辑
}
private void handlePrivate(Channel channel, MessageRequest request) {
// 私聊逻辑
}
private void handleGroup(Channel channel, MessageRequest request) {
// 群聊逻辑
}
}
@@ -0,0 +1,50 @@
package cn.link56.im.handler;
import cn.link56.im.message.MessageRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.timeout.IdleStateEvent;
@ChannelHandler.Sharable
public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private static final ObjectMapper MAPPER = new ObjectMapper();
private final MessageHandler messageHandler = new MessageHandler();
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
System.out.println("客户端连接: " + ctx.channel().id().asShortText());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) {
System.out.println("客户端断开: " + ctx.channel().id().asShortText());
}
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
MessageRequest request = MAPPER.readValue(textWebSocketFrame.text(), MessageRequest.class);
messageHandler.handleMessage(channelHandlerContext.channel(),request);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
System.out.println("连接超时,关闭: " + ctx.channel().id().asShortText());
ctx.close();
} else {
super.userEventTriggered(ctx, evt);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
System.out.println("Channel 异常, 关闭连接:: " + ctx.channel().id().asShortText());
cause.printStackTrace();
ctx.close();
}
}
@@ -0,0 +1,16 @@
package cn.link56.im.message;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MessageRequest {
private String type;
private Long from;
private Object to;
private Long timestamp;
private Object data;
}
@@ -0,0 +1,50 @@
package cn.link56.im.message;
import cn.link56.common.utils.TimeUtils;
import cn.link56.im.constants.MessageResponseStatusConstants;
import cn.link56.im.enums.MessageTypeEnums;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class MessageResponse {
private String type;
private Long from;
private Object data;
private Long timestamp;
private Integer code;
private String message;
public static MessageResponse success() {
return buildResponse(null, MessageResponseStatusConstants.SUCCESS, null, null);
}
public static MessageResponse success(MessageTypeEnums messageTypeEnums) {
return buildResponse(messageTypeEnums.name(), MessageResponseStatusConstants.SUCCESS, null, null);
}
public static MessageResponse error() {
return buildResponse(null, MessageResponseStatusConstants.ERROR, null, "系统异常");
}
public static MessageResponse error(String msg) {
return buildResponse(null, MessageResponseStatusConstants.ERROR, null, msg);
}
private static MessageResponse buildResponse(String type, Integer code, Object data, String message) {
MessageResponse response = new MessageResponse();
response.setType(type);
response.setCode(code);
response.setData(data);
response.setMessage(message);
response.setTimestamp(TimeUtils.getCurrentTimestampMillis());
return response;
}
}
@@ -0,0 +1,41 @@
package cn.link56.im.storage;
import io.netty.channel.Channel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Data
public class Session {
private Long companyId;
private Long userId;
private final Set<Channel> channels;
// 默认构造器,初始化 channels
public Session() {
this.channels = Collections.newSetFromMap(new ConcurrentHashMap<>());
}
// 带参数构造器(不需要传 channels,因为总是新建空 Set)
public Session(Long companyId, Long userId) {
this.companyId = companyId;
this.userId = userId;
this.channels = Collections.newSetFromMap(new ConcurrentHashMap<>());
}
public void addChannel(Channel channel) {
channels.add(channel);
}
public void removeChannel(Channel channel) {
channels.remove(channel);
}
public boolean isOnline() {
return !channels.isEmpty();
}
}
@@ -0,0 +1,51 @@
package cn.link56.im.storage;
import io.netty.channel.Channel;
import java.util.concurrent.ConcurrentHashMap;
public class SessionManager {
private static final ConcurrentHashMap<Long, Session> sessions = new ConcurrentHashMap<>();
/** 添加会话,支持同一个用户多端登录 */
public static void addSession(Long companyId, Long userId, Channel channel) {
if (companyId == null || userId == null || channel == null) return;
// 通过 compute 方法保证线程安全
sessions.compute(userId, (key, session) -> {
if (session == null) {
// 新建 Session
session = new Session(companyId, userId);
}
// 添加新的 Channel 到 Session
session.addChannel(channel);
return session;
});
}
/** 移除某个 Channel */
public static void removeChannel(Long userId, Channel channel) {
if (userId == null || channel == null) return;
Session session = sessions.get(userId);
if (session != null) {
session.removeChannel(channel);
// 如果用户所有连接都断开,删除 Session
if (!session.isOnline()) {
sessions.remove(userId);
}
}
}
/** 获取用户 Session */
public static Session getSession(Long userId) {
return sessions.get(userId);
}
/** 检查用户是否在线 */
public static boolean isOnline(Long userId) {
Session session = sessions.get(userId);
return session != null && session.isOnline();
}
}
@@ -0,0 +1,58 @@
package cn.link56.im.utils;
import cn.link56.im.storage.Session;
import cn.link56.im.storage.SessionManager;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.apache.commons.lang3.ObjectUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class MessageWriter {
private static final ObjectMapper MAPPER = new ObjectMapper();
public static void writeToUser(Long userId,Object message){
if (ObjectUtils.isEmpty(userId) || ObjectUtils.isEmpty(message)){
return;
}
Session session = SessionManager.getSession(userId);
Set<Channel> channels = session.getChannels();
if (channels.isEmpty()) return;
for (Channel ch : channels) {
writeToChannel(ch, message);
}
}
public static void writeToChannel(Channel channel, Object message) {
if (channel == null || !channel.isActive() || message == null) {
return;
}
try {
// 转为 JSON 字符串
String payload = MAPPER.writeValueAsString(message);
// 发送给客户端
channel.writeAndFlush(new TextWebSocketFrame(payload));
} catch (Exception e) {
System.out.println("===发送消息失败===");
e.printStackTrace();
}
}
public static void broadcastToUsers(List<Long> userIds, Object message) {
if (message == null || userIds == null || userIds.isEmpty()) return;
for (Long userId : userIds) {
writeToUser(userId, message);
}
}
}
@@ -0,0 +1,9 @@
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 {
}
@@ -1,9 +0,0 @@
package cn.link56.post.mapper;
import cn.link56.post.model.po.PostFavorite;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PostFavoriteMapper extends BaseMapper<PostFavorite> {
}
@@ -1,9 +0,0 @@
package cn.link56.post.mapper;
import cn.link56.post.model.po.PostLike;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PostLikeMapper extends BaseMapper<PostLike> {
}
@@ -0,0 +1,15 @@
package cn.link56.post.mapper;
import cn.link56.post.model.po.PostStat;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface PostStatMapper extends BaseMapper<PostStat> {
int increaseViewCount(@Param("postId") Long postId,@Param("count") Integer count);
int increaseCommentCount(@Param("postId") Long postId,@Param("count") Integer count);
int decreaseCommentCount(@Param("postId") Long postId,@Param("count") Integer count);
}
@@ -10,6 +10,7 @@ import lombok.NoArgsConstructor;
public class CreatePostCommentDTO {
private Long fatherId;
private Long postId;
private Long rootId;
private String content;
}
@@ -15,8 +15,6 @@ public class CreatePostDTO {
private String title;
@NotBlank(message = "帖子内容不能为空")
private String content;
@NotBlank(message = "帖子摘要不能为空")
private String summary;
private String cover;
private List<String> tags;
@@ -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;
@@ -38,4 +35,14 @@ public class PostComment {
fill = FieldFill.INSERT
)
private Date createTime;
@TableField("b_root_id")
private Long rootId;
@TableLogic
@TableField(
value = "b_deleted",
fill = FieldFill.INSERT
)
private Integer deleted;
}
@@ -1,30 +0,0 @@
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 lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("b_post_like")
public class PostLike {
@TableId(value = "b_id")
private Long id;
@TableField("b_post_id")
private Long postId;
@TableField("b_user_id")
private Long userId;
@TableField(
value = "b_create_time",
fill = FieldFill.INSERT
)
private Long createTime;
}
@@ -11,20 +11,20 @@ import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("b_post_favorite")
public class PostFavorite {
@TableId(value = "b_id")
private Long id;
@TableField("b_post_id")
@TableName("b_post_stat")
public class PostStat {
@TableId(value = "b_post_id")
private Long postId;
@TableField("b_user_id")
private Long userId;
@TableField("b_view_count")
private Integer viewCount;
@TableField("b_comment_count")
private Integer commentCount;
@TableField(
value = "b_create_time",
fill = FieldFill.INSERT
value = "b_update_time",
fill = FieldFill.UPDATE
)
private Long createTime;
}
@@ -21,8 +21,6 @@ public class EditorDashboardVO {
public static class Count {
private Long postCount;
private Long viewCount;
private Long likeCount;
private Long favoriteCount;
}
@Data
@@ -13,6 +13,7 @@ import java.util.List;
public class PostCommentVO {
private Long id;
private Long fatherId;
private Long rootId;
private String content;
private Date createTime;
private User publishUser;
@@ -4,6 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
import java.util.List;
@Data
@@ -14,6 +15,7 @@ public class PostCompanyVO {
private String name;
private String logo;
private String profile;
private Date establishmentDate;
private List<String> areas;
private List<String> industries;
}
@@ -7,7 +7,9 @@ import cn.link56.post.model.po.PostComment;
import cn.link56.post.model.po.PostView;
import cn.link56.post.model.vo.CreatePostCommentVO;
import cn.link56.post.model.vo.PostCommentVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
@@ -25,6 +27,9 @@ public class PostCommentService extends ServiceImpl<PostCommentMapper, PostComme
@Resource
private PostCommentMapper postCommentMapper;
@Resource
private PostStatService postStatService;
/**
* 根据文章 id 获取浏览总数
*/
@@ -34,64 +39,77 @@ public class PostCommentService extends ServiceImpl<PostCommentMapper, PostComme
.count();
}
/**
* 根据文章 ID 删除所有浏览记录
*/
public void deleteByPostId(Long postId) {
this.lambdaUpdate()
.eq(PostComment::getPostId, postId)
.remove();
}
/**
* 根据文章 id 获取最新一条评论
*/
public PostComment getLatestCommentByPostId(Long postId) {
return null;
if (postId == null) {
return null;
}
Page<PostComment> page = new Page<>(1, 1);
List<PostComment> list = postCommentMapper.selectPage(
page,
new LambdaQueryWrapper<PostComment>()
.eq(PostComment::getPostId, postId)
.orderByDesc(PostComment::getCreateTime)
).getRecords();
return list.isEmpty() ? null : list.get(0);
}
public List<PostCommentVO> listPostComments(Long id) {
public List<PostCommentVO> listPostComments(Long postId) {
// 1. 查询所有评论
List<PostCommentVO> list = postCommentMapper.selectComments(id);
List<PostCommentVO> list = postCommentMapper.selectComments(postId);
// 2. 构建扁平化树形结构(所有子评论提升到根评论层级)
// 2. 构建 rootId -> 根评论 映射
Map<Long, PostCommentVO> rootMap = new HashMap<>();
for (PostCommentVO comment : list) {
comment.setChildren(new ArrayList<>()); // 统一初始化
// 如果是根评论,直接加入 rootMap
if (comment.getRootId() == null || comment.getRootId().equals(comment.getId())) {
rootMap.put(comment.getId(), comment);
}
}
// 3. 挂载子评论到对应根评论的 children
List<PostCommentVO> rootComments = new ArrayList<>();
Map<Long, PostCommentVO> commentMap = new HashMap<>();
// 2.1 初始化映射并收集根评论
for (PostCommentVO comment : list) {
commentMap.put(comment.getId(), comment);
if (ObjectUtils.isEmpty(comment.getFatherId())) {
// 根评论已经在 rootMap 中
if (comment.getRootId() == null || comment.getRootId().equals(comment.getId())) {
rootComments.add(comment);
continue;
}
// 查找对应的根评论
PostCommentVO root = rootMap.get(comment.getRootId());
if (root != null) {
root.getChildren().add(comment);
} else {
// rootId 不存在或异常,提升为根评论
rootComments.add(comment);
comment.setChildren(new ArrayList<>()); // 提前初始化children
}
}
// 2.2 遍历所有评论,将子评论挂到根祖先的children中
for (PostCommentVO comment : list) {
if (ObjectUtils.isEmpty(comment.getFatherId())) {
continue; // 跳过根评论
}
// 查找当前评论的根祖先(追溯到没有fatherId的祖先)
PostCommentVO rootAncestor = null;
Long currentFatherId = comment.getFatherId();
while (true) {
PostCommentVO parent = commentMap.get(currentFatherId);
if (parent == null) break; // 父节点不存在,终止循环
if (ObjectUtils.isEmpty(comment.getFatherId())) {
rootAncestor = parent; // 找到根祖先
break;
} else {
currentFatherId = parent.getFatherId(); // 继续向上查找
}
}
// 挂载到根祖先的children中
if (rootAncestor != null && rootAncestor.getChildren() != null) {
rootAncestor.getChildren().add(comment);
}
}
// 3. 按原始顺序排序(可选)
rootComments.forEach(root -> {
if (root.getChildren() != null) {
// 根据需求排序,例如按创建时间升序
root.getChildren().sort(Comparator.comparing(PostCommentVO::getCreateTime));
}
});
// 4. 对子评论排序(可选)
rootComments.forEach(root ->
root.getChildren().sort(
Comparator.comparing(PostCommentVO::getCreateTime)
)
);
return rootComments;
}
@@ -103,7 +121,15 @@ public class PostCommentService extends ServiceImpl<PostCommentMapper, PostComme
BeanUtils.copyProperties(dto,postComment);
postComment.setId(IdGenerator.nextId());
if (ObjectUtils.isEmpty(postComment.getFatherId())){
postComment.setRootId(postComment.getId());
}
// 插入评论
postCommentMapper.insert(postComment);
// 评论数量 + 1
postStatService.increaseCommentCount(dto.getPostId());
CreatePostCommentVO commentVO = new CreatePostCommentVO();
BeanUtils.copyProperties(postComment,commentVO);
@@ -112,22 +138,35 @@ public class PostCommentService extends ServiceImpl<PostCommentMapper, PostComme
}
@Transactional
public void deletePostComment(@NotBlank(message = "评论ID不能为空") Long postCommentId) {
public void deletePostComment(Long postCommentId) {
PostComment postComment = postCommentMapper.selectById(postCommentId);
if (ObjectUtils.isEmpty(postComment)){
if (postComment == null) {
return;
}
Long postId = postComment.getPostId();
Long fatherId = postComment.getFatherId();
if(ObjectUtils.isEmpty(fatherId)){
postCommentMapper.deleteById(postCommentId);
}else{
LambdaUpdateWrapper<PostComment> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(PostComment::getId,postCommentId)
.or()
.eq(PostComment::getFatherId,postCommentId);
postCommentMapper.delete(updateWrapper);
int deletedCount = 0;
// 1. 删除根评论(连同子评论)
if (fatherId == null) {
LambdaQueryWrapper<PostComment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PostComment::getId, postCommentId)
.or()
.eq(PostComment::getRootId, postCommentId);
deletedCount = postCommentMapper.delete(wrapper);
}
// 2. 删除子评论
else {
deletedCount = postCommentMapper.deleteById(postCommentId);
}
// 3. 按实际删除条数,减少评论数
if (deletedCount > 0) {
postStatService.decreaseCommentCount(postId, deletedCount);
}
}
}
@@ -1,14 +0,0 @@
package cn.link56.post.service;
import cn.link56.post.mapper.PostFavoriteMapper;
import cn.link56.post.model.po.PostFavorite;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class PostFavoriteService extends ServiceImpl<PostFavoriteMapper, PostFavorite> {
@Resource
private PostFavoriteMapper postFavoriteMapper;
}
@@ -1,14 +0,0 @@
package cn.link56.post.service;
import cn.link56.post.mapper.PostLikeMapper;
import cn.link56.post.model.po.PostLike;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class PostLikeService extends ServiceImpl<PostLikeMapper, PostLike> {
@Resource
private PostLikeMapper postLikeMapper;
}
@@ -4,7 +4,7 @@ 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.model.po.CompanyPO;
import cn.link56.company.model.po.Company;
import cn.link56.company.service.CompanyService;
import cn.link56.post.mapper.PostMapper;
import cn.link56.post.model.dto.*;
@@ -16,13 +16,11 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
@@ -38,6 +36,8 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
private PostCommentService postCommentService;
@Resource
private CompanyService companyService;
@Resource
private PostStatService postStatService;
@Transactional
public EditorPostVO createPost(CreatePostDTO dto) {
@@ -50,7 +50,12 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
handleUpdatePublishTime(post, dto.getStatus());
// 新建帖子
postMapper.insert(post);
// 新建帖子数据综合
postStatService.createPostStat(post.getId());
return buildEditPostVO(post);
}
@@ -135,7 +140,16 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
@Transactional
public void deletePost(DeletePostDTO deletePostDTO) {
postMapper.deleteById(deletePostDTO.getId());
Long postId = deletePostDTO.getId();
// 删除帖子
postMapper.deleteById(postId);
// 删除帖子统计
postStatService.removeById(postId);
// 删除帖子浏览
postViewService.deleteByPostId(postId);
// 删除帖子评论
postCommentService.deleteByPostId(postId);
}
@Transactional
@@ -274,16 +288,16 @@ public class PostService extends ServiceImpl<PostMapper, Post> {
}
public PostCompanyVO getPostCompanyById(Long id) {
CompanyPO companyPO = companyService.getById(id);
if (companyPO == null){
Company company = companyService.getById(id);
if (company == null){
throw new BusinessException("该公司不存在或已注销");
}
PostCompanyVO postCompanyVO = new PostCompanyVO();
BeanUtils.copyProperties(companyPO,postCompanyVO);
BeanUtils.copyProperties(company,postCompanyVO);
postCompanyVO.setAreas(Arrays.stream(StringUtils.split(companyPO.getArea(),",")).collect(Collectors.toList()));
postCompanyVO.setIndustries(Arrays.stream(StringUtils.split(companyPO.getIndustry(),",")).collect(Collectors.toList()));
postCompanyVO.setAreas(Arrays.stream(StringUtils.split(company.getArea(),",")).collect(Collectors.toList()));
postCompanyVO.setIndustries(Arrays.stream(StringUtils.split(company.getIndustry(),",")).collect(Collectors.toList()));
return postCompanyVO;
}
@@ -0,0 +1,42 @@
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.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class PostStatService extends ServiceImpl<PostStatMapper, PostStat> {
@Resource
private PostStatMapper postStatMapper;
public void increaseViewCount(Long postId) {
postStatMapper.increaseViewCount(postId,1);
}
public void increaseCommentCount(Long postId) {
postStatMapper.increaseCommentCount(postId,1);
}
public void decreaseCommentCount(Long postId) {
postStatMapper.decreaseCommentCount(postId,1);
}
public void decreaseCommentCount(Long postId,Integer count) {
postStatMapper.decreaseCommentCount(postId,count);
}
public void createPostStat(Long postId) {
PostStat stat = new PostStat();
stat.setPostId(postId);
stat.setViewCount(0);
stat.setCommentCount(0);
this.save(stat);
}
}
@@ -21,4 +21,13 @@ public class PostViewService extends ServiceImpl<PostViewMapper, PostView> {
.eq(PostView::getPostId, postId)
.count();
}
/**
* 根据文章 ID 删除所有浏览记录
*/
public void deleteByPostId(Long postId) {
this.lambdaUpdate()
.eq(PostView::getPostId, postId)
.remove();
}
}
@@ -1,5 +1,8 @@
server:
port: 8084
netty:
port: 8090
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
@@ -36,4 +36,9 @@ mybatis-plus:
use-generated-keys: true
default-executor-type: simple
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
map-underscore-to-camel-case: true
global-config:
db-config:
logic-delete-field: deleted
logic-not-delete-value: 0
logic-delete-value: 1
@@ -15,7 +15,7 @@
b_order AS [order]
</sql>
<select id="listAllArea" resultType="cn.link56.base.model.po.AreaPO">
<select id="listAllArea" resultType="cn.link56.base.model.po.Area">
SELECT <include refid="area_columns"/> FROM b_area_sheng WHERE b_canuse = '1'
UNION ALL
SELECT <include refid="area_columns"/> FROM b_area_shi WHERE b_canuse = '1'
@@ -5,6 +5,7 @@
<mapper namespace="cn.link56.post.mapper.PostCommentMapper">
<resultMap id="CommentListMap" type="cn.link56.post.model.vo.PostCommentVO">
<id property="id" column="b_id"/>
<result property="rootId" column="b_root_id"/>
<result property="fatherId" column="b_father_id"/>
<result property="content" column="b_content"/>
<result property="createTime" column="b_create_time"/>
@@ -28,14 +29,14 @@
<select id="selectComments" resultMap="CommentListMap">
SELECT c.*,
pc.b_id AS pub_user_id,pc.b_company_id AS pub_company_id,pc.b_name AS pub_user_name,
rc.b_id AS rep_user_id,rc.b_company_id AS rep_company_id,rc.b_name AS rep_company_id
pc.b_id AS pub_user_id,pc.b_company_id AS pub_company_id,pc.b_name AS pub_user_name,pc.b_avatar AS pub_user_avatar,
rc.b_id AS rep_user_id,rc.b_company_id AS rep_company_id,rc.b_name AS rep_user_name,rc.b_avatar AS rep_user_avatar
FROM b_post_comment c
LEFT JOIN b_company_user pc ON c.b_create_by = pc.b_id
LEFT JOIN b_company_user rc ON (
SELECT b_create_by FROM b_post_comment WHERE b_id = c.b_father_id
) = rc.b_id
WHERE c.b_post_id = #{postId}
WHERE c.b_post_id = #{postId} AND c.b_deleted = 0
ORDER BY c.b_create_time
</select>
@@ -56,13 +56,12 @@
p.b_publish_time AS publishTime,
p.b_status AS status,
(SELECT COUNT(1) FROM b_post_view v WHERE v.b_post_id = p.b_id) AS viewCount,
(SELECT COUNT(1) FROM b_post_like l WHERE l.b_post_id = p.b_id) AS likeCount,
(SELECT COUNT(1) FROM b_post_favorite f WHERE f.b_post_id = p.b_id) AS favoriteCount,
(SELECT COUNT(1) FROM b_post_comment c WHERE c.b_post_id = p.b_id) AS commentCount,
ISNULL(ps.b_view_count, 0) AS viewCount,
ISNULL(ps.b_comment_count, 0) AS commentCount,
ROW_NUMBER() OVER (ORDER BY p.b_update_time DESC) AS rn
FROM b_post p
LEFT JOIN b_post_stat ps ON ps.b_post_id = p.b_id
<where>
<if test="userId != null">
AND p.b_create_by = #{userId}
@@ -73,7 +72,7 @@
</if>
<if test="title != null and title != ''">
AND p.b_title LIKE '%' + #{title} + '%'
AND p.b_title LIKE CONCAT('%', #{title}, '%')
</if>
<if test="year != null and year != ''">
@@ -88,6 +87,7 @@
WHERE t.rn BETWEEN #{offset} AND #{limit}
</select>
<select id="selectEditorPostTotal" resultType="java.lang.Long">
SELECT COUNT(*)
FROM b_post p
@@ -114,14 +114,18 @@
</where>
</select>
<select id="selectDashboardOverview" resultType="cn.link56.post.model.vo.EditorDashboardVO$Count">
<select id="selectDashboardOverview"
resultType="cn.link56.post.model.vo.EditorDashboardVO$Count">
SELECT
(SELECT COUNT(*) FROM b_post WHERE b_create_by = #{userId}) AS postCount,
(SELECT COUNT(*) FROM b_post_view WHERE b_post_id IN (SELECT b_id FROM b_post WHERE b_create_by = #{userId})) AS viewCount,
(SELECT COUNT(*) FROM b_post_like WHERE b_post_id IN (SELECT b_id FROM b_post WHERE b_create_by = #{userId})) AS likeCount,
(SELECT COUNT(*) FROM b_post_favorite WHERE b_post_id IN (SELECT b_id FROM b_post WHERE b_create_by = #{userId})) AS favoriteCount
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,
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.link56.post.mapper.PostStatMapper">
<update id="increaseViewCount">
UPDATE b_post_stat
SET
b_view_count = b_view_count + #{count},
b_update_time = GETDATE()
WHERE b_post_id = #{postId}
</update>
<update id="increaseCommentCount">
UPDATE b_post_stat
SET
b_comment_count = b_comment_count + #{count},
b_update_time = GETDATE()
WHERE b_post_id = #{postId}
</update>
<update id="decreaseCommentCount">
UPDATE b_post_stat
SET
b_comment_count = b_comment_count - #{count},
b_update_time = GETDATE()
WHERE b_post_id = #{postId}
AND b_comment_count >= #{count}
</update>
</mapper>