فهرست منبع

Merge branch 'master' of http://git.izouma.com/xiongzhu/9th

panhui 4 سال پیش
والد
کامیت
5605fb026e
26فایلهای تغییر یافته به همراه777 افزوده شده و 25 حذف شده
  1. 49 0
      src/main/java/com/izouma/nineth/domain/Banner.java
  2. 37 0
      src/main/java/com/izouma/nineth/domain/Collection.java
  3. 2 0
      src/main/java/com/izouma/nineth/domain/User.java
  4. 13 0
      src/main/java/com/izouma/nineth/dto/UserDTO.java
  5. 18 0
      src/main/java/com/izouma/nineth/enums/BannerType.java
  6. 16 0
      src/main/java/com/izouma/nineth/repo/BannerRepo.java
  7. 2 0
      src/main/java/com/izouma/nineth/repo/FollowRepo.java
  8. 2 0
      src/main/java/com/izouma/nineth/repo/LikeRepo.java
  9. 16 0
      src/main/java/com/izouma/nineth/repo/UserRepo.java
  10. 2 4
      src/main/java/com/izouma/nineth/security/Authority.java
  11. 2 0
      src/main/java/com/izouma/nineth/security/WebSecurityConfig.java
  12. 20 0
      src/main/java/com/izouma/nineth/service/BannerService.java
  13. 46 0
      src/main/java/com/izouma/nineth/service/CollectionService.java
  14. 8 2
      src/main/java/com/izouma/nineth/service/FollowService.java
  15. 39 3
      src/main/java/com/izouma/nineth/service/UserService.java
  16. 8 0
      src/main/java/com/izouma/nineth/utils/SecurityUtils.java
  17. 62 0
      src/main/java/com/izouma/nineth/web/BannerController.java
  18. 4 4
      src/main/java/com/izouma/nineth/web/CollectionController.java
  19. 16 7
      src/main/java/com/izouma/nineth/web/UserController.java
  20. 1 0
      src/main/resources/genjson/Banner.json
  21. 16 0
      src/main/vue/src/router.js
  22. 176 0
      src/main/vue/src/views/BannerEdit.vue
  23. 209 0
      src/main/vue/src/views/BannerList.vue
  24. 10 1
      src/main/vue/src/views/MinterEdit.vue
  25. 0 1
      src/main/vue/src/views/UserEdit.vue
  26. 3 3
      src/test/java/com/izouma/nineth/CommonTest.java

+ 49 - 0
src/main/java/com/izouma/nineth/domain/Banner.java

@@ -0,0 +1,49 @@
+package com.izouma.nineth.domain;
+
+import com.izouma.nineth.annotations.Searchable;
+import com.izouma.nineth.enums.BannerType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+
+@Data
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@ApiModel("Banner")
+public class Banner extends BaseEntity {
+
+    @ApiModelProperty("名称")
+    @Searchable
+    private String name;
+
+    @ApiModelProperty("图片")
+    private String pic;
+
+    @ApiModelProperty("备注")
+    private String remark;
+
+    @ApiModelProperty("排序")
+    private int sort;
+
+    @ApiModelProperty("展示位置")
+    @Enumerated(EnumType.STRING)
+    private BannerType type;
+
+    @ApiModelProperty("跳转")
+    private boolean link;
+
+    @ApiModelProperty("跳转类型")
+    private String linkType;
+
+    @ApiModelProperty("跳转内容")
+    private String linkContent;
+}

+ 37 - 0
src/main/java/com/izouma/nineth/domain/Collection.java

@@ -1,5 +1,7 @@
 package com.izouma.nineth.domain;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.izouma.nineth.annotations.Searchable;
 import com.izouma.nineth.converter.StringArrayConverter;
 import com.izouma.nineth.enums.CollectionSource;
@@ -10,6 +12,7 @@ import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
 
 import javax.persistence.*;
 import java.math.BigDecimal;
@@ -70,4 +73,38 @@ public class Collection extends BaseEntity {
     @ApiModelProperty("价格")
     @Column(precision = 10, scale = 2)
     private BigDecimal price;
+
+    @Column(columnDefinition = "TEXT")
+    @Convert(converter = PropertyListConverter.class)
+    @ApiModelProperty("特性")
+    private List<CollectionProperty> properties;
+
+    @Data
+    public static class CollectionProperty {
+        private String name;
+
+        private String value;
+    }
+
+    public static class PropertyListConverter implements AttributeConverter<List<CollectionProperty>, String> {
+        @Override
+        public String convertToDatabaseColumn(List<CollectionProperty> collectionProperties) {
+            if (collectionProperties == null) {
+                return null;
+            }
+            return JSON.toJSONString(collectionProperties);
+        }
+
+        @Override
+        public List<CollectionProperty> convertToEntityAttribute(String s) {
+            if (StringUtils.isEmpty(s)) {
+                return null;
+            }
+            try {
+                return JSONArray.parseArray(s, CollectionProperty.class);
+            } catch (Exception ignored) {
+            }
+            return null;
+        }
+    }
 }

+ 2 - 0
src/main/java/com/izouma/nineth/domain/User.java

@@ -71,8 +71,10 @@ public class User extends BaseEntity implements Serializable {
 
     private String email;
 
+    @ApiModelProperty("关注数量")
     private int follows;
 
+    @ApiModelProperty("粉丝数量")
     private int followers;
 
     private int sales;

+ 13 - 0
src/main/java/com/izouma/nineth/dto/UserDTO.java

@@ -0,0 +1,13 @@
+package com.izouma.nineth.dto;
+
+import com.izouma.nineth.domain.User;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class UserDTO extends User {
+
+    @ApiModelProperty("是否关注")
+    private boolean follow;
+
+}

+ 18 - 0
src/main/java/com/izouma/nineth/enums/BannerType.java

@@ -0,0 +1,18 @@
+package com.izouma.nineth.enums;
+
+public enum BannerType {
+    HOME("首页"),
+    DISCOVER("探索"),
+    MINTER("铸造者"),
+    MARKET("市场");
+
+    private final String description;
+
+    BannerType(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 16 - 0
src/main/java/com/izouma/nineth/repo/BannerRepo.java

@@ -0,0 +1,16 @@
+package com.izouma.nineth.repo;
+
+import com.izouma.nineth.domain.Banner;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import javax.transaction.Transactional;
+
+public interface BannerRepo extends JpaRepository<Banner, Long>, JpaSpecificationExecutor<Banner> {
+    @Query("update Banner t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+}

+ 2 - 0
src/main/java/com/izouma/nineth/repo/FollowRepo.java

@@ -16,4 +16,6 @@ public interface FollowRepo extends JpaRepository<Follow, Long>, JpaSpecificatio
     void softDelete(Long id);
 
     List<Follow> findByUserIdAndFollowUserId(Long userId, Long to);
+
+    List<Follow> findByUserId(Long userId);
 }

+ 2 - 0
src/main/java/com/izouma/nineth/repo/LikeRepo.java

@@ -16,4 +16,6 @@ public interface LikeRepo extends JpaRepository<Like, Long>, JpaSpecificationExe
     void softDelete(Long id);
 
     List<Like> findByUserIdAndCollectionId(Long userId, Long collectionId);
+
+    List<Like> findByUserId(Long userId);
 }

+ 16 - 0
src/main/java/com/izouma/nineth/repo/UserRepo.java

@@ -31,4 +31,20 @@ public interface UserRepo extends JpaRepository<User, Long>, JpaSpecificationExe
     @Modifying
     @Query("update User u set u.followers = u.followers + ?1 where u.id = ?1")
     void addFollow(Long userId, int num);
+
+    @Query("select distinct u from User u join Follow f on u.id = f.followUserId " +
+            "where f.userId = ?1 and u.del = false ")
+    List<User> userFollows(Long userId);
+
+    @Transactional
+    @Modifying
+    @Query(value = "update user set follows = (select count(*) from follow " +
+            "where follow.user_id = ?1) where user.id = ?1", nativeQuery = true)
+    void updateFollows(Long userId);
+
+    @Transactional
+    @Modifying
+    @Query(value = "update user set followers = (select count(*) from follow " +
+            "where follow.follow_user_id = ?1) where user.id = ?1", nativeQuery = true)
+    void updateFollowers(Long userId);
 }

+ 2 - 4
src/main/java/com/izouma/nineth/security/Authority.java

@@ -2,10 +2,7 @@ package com.izouma.nineth.security;
 
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.izouma.nineth.enums.AuthorityName;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
+import lombok.*;
 
 import javax.persistence.*;
 import javax.validation.constraints.NotNull;
@@ -19,6 +16,7 @@ import java.util.Objects;
 @AllArgsConstructor
 @Builder
 @JsonIgnoreProperties(ignoreUnknown = true)
+@EqualsAndHashCode
 public class Authority implements Serializable {
 
     public static Authority get(AuthorityName name) {

+ 2 - 0
src/main/java/com/izouma/nineth/security/WebSecurityConfig.java

@@ -80,6 +80,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .antMatchers("/MP_verify*").permitAll()
                 .antMatchers("/payOrder/**").permitAll()
                 .antMatchers("/notify/**").permitAll()
+                .antMatchers("/collection/all").permitAll()
+                .antMatchers("/user/all").permitAll()
                 // all other requests need to be authenticated
                 .anyRequest().authenticated().and()
                 // make sure we use stateless session; session won't be used to

+ 20 - 0
src/main/java/com/izouma/nineth/service/BannerService.java

@@ -0,0 +1,20 @@
+package com.izouma.nineth.service;
+
+import com.izouma.nineth.domain.Banner;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.repo.BannerRepo;
+import com.izouma.nineth.utils.JpaUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+
+@Service
+@AllArgsConstructor
+public class BannerService {
+
+    private BannerRepo bannerRepo;
+
+    public Page<Banner> all(PageQuery pageQuery) {
+        return bannerRepo.findAll(JpaUtils.toSpecification(pageQuery, Banner.class), JpaUtils.toPageRequest(pageQuery));
+    }
+}

+ 46 - 0
src/main/java/com/izouma/nineth/service/CollectionService.java

@@ -1,20 +1,66 @@
 package com.izouma.nineth.service;
 
 import com.izouma.nineth.domain.Collection;
+import com.izouma.nineth.domain.Follow;
+import com.izouma.nineth.domain.Like;
+import com.izouma.nineth.domain.User;
+import com.izouma.nineth.dto.CollectionDTO;
 import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.dto.UserDTO;
 import com.izouma.nineth.repo.CollectionRepo;
+import com.izouma.nineth.repo.LikeRepo;
 import com.izouma.nineth.utils.JpaUtils;
+import com.izouma.nineth.utils.SecurityUtils;
 import lombok.AllArgsConstructor;
+import org.springframework.beans.BeanUtils;
 import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+import java.util.stream.Collectors;
+
 @Service
 @AllArgsConstructor
 public class CollectionService {
 
     private CollectionRepo collectionRepo;
+    private LikeRepo       likeRepo;
 
     public Page<Collection> all(PageQuery pageQuery) {
         return collectionRepo.findAll(JpaUtils.toSpecification(pageQuery, Collection.class), JpaUtils.toPageRequest(pageQuery));
     }
+
+    public CollectionDTO toDTO(Collection collection) {
+        return toDTO(collection, true);
+    }
+
+    public CollectionDTO toDTO(Collection collection, boolean join) {
+        CollectionDTO collectionDTO = new CollectionDTO();
+        BeanUtils.copyProperties(collection, collectionDTO);
+        if (join) {
+            if (SecurityUtils.getAuthenticatedUser() != null) {
+                List<Like> list = likeRepo.findByUserIdAndCollectionId(SecurityUtils.getAuthenticatedUser().getId(),
+                        collection.getId());
+                collectionDTO.setLiked(!list.isEmpty());
+            }
+        }
+        return collectionDTO;
+    }
+
+    public List<CollectionDTO> toDTO(List<Collection> collections) {
+        return collections.stream().parallel().map(collection -> {
+            CollectionDTO dto = toDTO(collection, false);
+            if (SecurityUtils.getAuthenticatedUser() != null) {
+                List<Like> likes = likeRepo.findByUserId(SecurityUtils.getAuthenticatedUser().getId());
+                dto.setLiked(likes.stream().anyMatch(l -> l.getCollectionId().equals(collection.getId())));
+            }
+            return dto;
+        }).collect(Collectors.toList());
+    }
+
+    public Page<CollectionDTO> toDTO(Page<Collection> collections) {
+        List<CollectionDTO> userDTOS = toDTO(collections.getContent());
+        return new PageImpl<>(userDTOS, collections.getPageable(), collections.getTotalElements());
+    }
 }

+ 8 - 2
src/main/java/com/izouma/nineth/service/FollowService.java

@@ -30,14 +30,20 @@ public class FollowService {
                 .userId(userId)
                 .followUserId(to)
                 .build());
-        userRepo.addFollow(to, 1);
+        userRepo.updateFollows(userId);
+        userRepo.updateFollowers(to);
     }
 
     public void unfollow(Long userId, Long to) {
         List<Follow> list = followRepo.findByUserIdAndFollowUserId(userId, to);
         if (!list.isEmpty()) {
             followRepo.deleteAll(list);
-            userRepo.addFollow(to, -list.size());
+            userRepo.updateFollows(userId);
+            userRepo.updateFollowers(to);
         }
     }
+
+    boolean isFollow(Long userId, Long to) {
+        return followRepo.findByUserIdAndFollowUserId(userId, to).size() > 0;
+    }
 }

+ 39 - 3
src/main/java/com/izouma/nineth/service/UserService.java

@@ -4,12 +4,15 @@ import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
 import com.izouma.nineth.config.Constants;
+import com.izouma.nineth.domain.Follow;
 import com.izouma.nineth.domain.User;
 import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.dto.UserDTO;
 import com.izouma.nineth.dto.UserRegister;
 import com.izouma.nineth.enums.AuthStatus;
 import com.izouma.nineth.enums.AuthorityName;
 import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.FollowRepo;
 import com.izouma.nineth.repo.UserRepo;
 import com.izouma.nineth.security.Authority;
 import com.izouma.nineth.security.JwtTokenUtil;
@@ -17,6 +20,7 @@ import com.izouma.nineth.security.JwtUserFactory;
 import com.izouma.nineth.service.sms.SmsService;
 import com.izouma.nineth.service.storage.StorageService;
 import com.izouma.nineth.utils.JpaUtils;
+import com.izouma.nineth.utils.SecurityUtils;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
@@ -27,14 +31,14 @@ import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
 import org.springframework.data.jpa.domain.Specification;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.stereotype.Service;
 
 import java.text.SimpleDateFormat;
-import java.util.Collections;
-import java.util.Date;
-import java.util.UUID;
+import java.util.*;
+import java.util.stream.Collectors;
 
 @Service
 @Slf4j
@@ -47,6 +51,8 @@ public class UserService {
     private StorageService storageService;
     private JwtTokenUtil   jwtTokenUtil;
     private CaptchaService captchaService;
+    private FollowService  followService;
+    private FollowRepo     followRepo;
 
     public Page<User> all(PageQuery pageQuery) {
         Specification<User> specification = JpaUtils.toSpecification(pageQuery, User.class);
@@ -253,4 +259,34 @@ public class UserService {
         userRepo.save(user);
     }
 
+    public UserDTO toDTO(User user) {
+        return toDTO(user, true);
+    }
+
+    public UserDTO toDTO(User user, boolean join) {
+        UserDTO userDTO = new UserDTO();
+        BeanUtils.copyProperties(user, userDTO);
+        if (join) {
+            if (SecurityUtils.getAuthenticatedUser() != null) {
+                userDTO.setFollow(followService.isFollow(SecurityUtils.getAuthenticatedUser().getId(), user.getId()));
+            }
+        }
+        return userDTO;
+    }
+
+    public List<UserDTO> toDTO(List<User> users) {
+        return users.stream().parallel().map(user -> {
+            UserDTO dto = toDTO(user, false);
+            if (SecurityUtils.getAuthenticatedUser() != null) {
+                List<Follow> follows = followRepo.findByUserId(SecurityUtils.getAuthenticatedUser().getId());
+                dto.setFollow(follows.stream().anyMatch(f -> f.getFollowUserId().equals(user.getId())));
+            }
+            return dto;
+        }).collect(Collectors.toList());
+    }
+
+    public Page<UserDTO> toDTO(Page<User> users) {
+        List<UserDTO> userDTOS = toDTO(users.getContent());
+        return new PageImpl<>(userDTOS, users.getPageable(), users.getTotalElements());
+    }
 }

+ 8 - 0
src/main/java/com/izouma/nineth/utils/SecurityUtils.java

@@ -1,6 +1,8 @@
 package com.izouma.nineth.utils;
 
 import com.izouma.nineth.domain.User;
+import com.izouma.nineth.enums.AuthorityName;
+import com.izouma.nineth.security.Authority;
 import com.izouma.nineth.security.JwtUser;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -14,4 +16,10 @@ public class SecurityUtils {
         }
         return user;
     }
+
+    public static boolean hasRole(AuthorityName name){
+        User user = getAuthenticatedUser();
+        if (null==user) return false;
+        return user.getAuthorities().contains(Authority.get(name));
+    }
 }

+ 62 - 0
src/main/java/com/izouma/nineth/web/BannerController.java

@@ -0,0 +1,62 @@
+package com.izouma.nineth.web;
+
+import com.izouma.nineth.domain.Banner;
+import com.izouma.nineth.service.BannerService;
+import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.repo.BannerRepo;
+import com.izouma.nineth.utils.ObjUtils;
+import com.izouma.nineth.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/banner")
+@AllArgsConstructor
+public class BannerController extends BaseController {
+    private BannerService bannerService;
+    private BannerRepo    bannerRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public Banner save(@RequestBody Banner record) {
+        if (record.getId() != null) {
+            Banner orig = bannerRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return bannerRepo.save(orig);
+        }
+        return bannerRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<Banner> all(@RequestBody PageQuery pageQuery) {
+        pageQuery.getQuery().put("del", false);
+        return bannerService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public Banner get(@PathVariable Long id) {
+        return bannerRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        bannerRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<Banner> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 4 - 4
src/main/java/com/izouma/nineth/web/CollectionController.java

@@ -1,6 +1,7 @@
 package com.izouma.nineth.web;
 
 import com.izouma.nineth.domain.Collection;
+import com.izouma.nineth.dto.CollectionDTO;
 import com.izouma.nineth.service.CollectionService;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.exception.BusinessException;
@@ -38,11 +39,10 @@ public class CollectionController extends BaseController {
         return collectionRepo.save(record);
     }
 
-
     //@PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/all")
-    public Page<Collection> all(@RequestBody PageQuery pageQuery) {
-        return collectionService.all(pageQuery);
+    public Page<CollectionDTO> all(@RequestBody PageQuery pageQuery) {
+        return collectionService.toDTO(collectionService.all(pageQuery));
     }
 
     @GetMapping("/get/{id}")
@@ -58,7 +58,7 @@ public class CollectionController extends BaseController {
     @GetMapping("/excel")
     @ResponseBody
     public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
-        List<Collection> data = all(pageQuery).getContent();
+        List<Collection> data = collectionService.all(pageQuery).getContent();
         ExcelUtils.export(response, data);
     }
 

+ 16 - 7
src/main/java/com/izouma/nineth/web/UserController.java

@@ -2,6 +2,7 @@ package com.izouma.nineth.web;
 
 import com.izouma.nineth.domain.User;
 import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.dto.UserDTO;
 import com.izouma.nineth.dto.UserRegister;
 import com.izouma.nineth.enums.AuthorityName;
 import com.izouma.nineth.exception.BusinessException;
@@ -30,9 +31,9 @@ import java.util.List;
 @RestController
 @RequestMapping("/user")
 public class UserController extends BaseController {
-    private UserRepo     userRepo;
-    private UserService  userService;
-    private JwtTokenUtil jwtTokenUtil;
+    private UserRepo      userRepo;
+    private UserService   userService;
+    private JwtTokenUtil  jwtTokenUtil;
     private FollowService followService;
 
     @PostMapping("/register")
@@ -77,10 +78,13 @@ public class UserController extends BaseController {
                 .orElseThrow(new BusinessException("用户不存在"));
     }
 
-    @PreAuthorize("hasRole('ADMIN')")
+    //    @PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/all")
-    public Page<User> all(@RequestBody PageQuery pageQuery) {
-        return userService.all(pageQuery);
+    public Page<UserDTO> all(@RequestBody PageQuery pageQuery) {
+        if (!SecurityUtils.hasRole(AuthorityName.ROLE_ADMIN)) {
+            pageQuery.getQuery().put("hasRole", "ROLE_MINTER");
+        }
+        return userService.toDTO(userService.all(pageQuery));
     }
 
     @PreAuthorize("hasRole('ADMIN')")
@@ -98,7 +102,7 @@ public class UserController extends BaseController {
     @GetMapping("/excel")
     @ResponseBody
     public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
-        List<User> data = all(pageQuery).getContent();
+        List<User> data = userService.all(pageQuery).getContent();
         ExcelUtils.export(response, data);
     }
 
@@ -145,6 +149,11 @@ public class UserController extends BaseController {
         followService.unfollow(SecurityUtils.getAuthenticatedUser().getId(), id);
     }
 
+    @GetMapping("/myFollows")
+    @ApiOperation("我的关注")
+    public List<UserDTO> myFollows() {
+        return userService.toDTO(userRepo.userFollows(SecurityUtils.getAuthenticatedUser().getId()));
+    }
 }
 
 

+ 1 - 0
src/main/resources/genjson/Banner.json

@@ -0,0 +1 @@
+{"tableName":"Banner","className":"Banner","remark":"Banner","genTable":true,"genClass":true,"genList":true,"genForm":true,"genRouter":true,"javaPath":"/Users/drew/Projects/Java/9th/src/main/java/com/izouma/nineth","viewPath":"/Users/drew/Projects/Java/9th/src/main/vue/src/views","routerPath":"/Users/drew/Projects/Java/9th/src/main/vue/src","resourcesPath":"/Users/drew/Projects/Java/9th/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"name","modelName":"name","remark":"名称","showInList":true,"showInForm":true,"formType":"singleLineText","required":true},{"name":"pic","modelName":"pic","remark":"图片","showInList":true,"showInForm":true,"formType":"singleImage","required":true},{"name":"remark","modelName":"remark","remark":"备注","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"sort","modelName":"sort","remark":"排序","showInList":true,"showInForm":true,"formType":"number","required":true},{"name":"type","modelName":"type","remark":"展示位置","showInList":true,"showInForm":true,"formType":"select","required":true,"apiFlag":"1","optionsValue":"[{\"label\":\"首页\",\"value\":\"HOME\"},{\"label\":\"探索\",\"value\":\"DISCOVER\"},{\"label\":\"铸造者\",\"value\":\"MINTER\"},{\"label\":\"市场\",\"value\":\"MARKET\"}]"},{"name":"link","modelName":"link","remark":"跳转","showInList":true,"showInForm":true,"formType":"switch","required":true},{"name":"linkType","modelName":"linkType","remark":"跳转类型","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"linkContent","modelName":"linkContent","remark":"跳转内容","showInList":true,"showInForm":true,"formType":"singleLineText"}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.nineth","tablePackage":"com.izouma.nineth.domain.Banner"}

+ 16 - 0
src/main/vue/src/router.js

@@ -246,6 +246,22 @@ const router = new Router({
                     meta: {
                        title: '身份认证',
                     },
+               },
+                {
+                    path: '/bannerEdit',
+                    name: 'BannerEdit',
+                    component: () => import(/* webpackChunkName: "bannerEdit" */ '@/views/BannerEdit.vue'),
+                    meta: {
+                       title: 'Banner编辑',
+                    },
+                },
+                {
+                    path: '/bannerList',
+                    name: 'BannerList',
+                    component: () => import(/* webpackChunkName: "bannerList" */ '@/views/BannerList.vue'),
+                    meta: {
+                       title: 'Banner',
+                    },
                }
                 /**INSERT_LOCATION**/
             ]

+ 176 - 0
src/main/vue/src/views/BannerEdit.vue

@@ -0,0 +1,176 @@
+<template>
+    <div class="edit-view">
+        <page-title>
+            <el-button @click="$router.go(-1)" :disabled="saving">取消</el-button>
+            <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id"> 删除 </el-button>
+            <el-button @click="onSave" :loading="saving" type="primary">保存</el-button>
+        </page-title>
+        <div class="edit-view__content-wrapper">
+            <div class="edit-view__content-section">
+                <el-form
+                    :model="formData"
+                    :rules="rules"
+                    ref="form"
+                    label-width="80px"
+                    label-position="right"
+                    size="small"
+                    style="max-width: 500px"
+                >
+                    <el-form-item prop="name" label="名称">
+                        <el-input v-model="formData.name"></el-input>
+                    </el-form-item>
+                    <el-form-item prop="pic" label="图片">
+                        <single-upload v-model="formData.pic"></single-upload>
+                    </el-form-item>
+                    <el-form-item prop="remark" label="备注">
+                        <el-input v-model="formData.remark"></el-input>
+                    </el-form-item>
+                    <el-form-item prop="sort" label="排序">
+                        <el-input-number type="number" v-model="formData.sort"></el-input-number>
+                    </el-form-item>
+                    <el-form-item prop="type" label="展示位置">
+                        <el-select v-model="formData.type" clearable filterable placeholder="请选择">
+                            <el-option
+                                v-for="item in typeOptions"
+                                :key="item.value"
+                                :label="item.label"
+                                :value="item.value"
+                            >
+                            </el-option>
+                        </el-select>
+                    </el-form-item>
+                    <el-form-item prop="link" label="跳转">
+                        <el-switch v-model="formData.link"></el-switch>
+                    </el-form-item>
+                    <el-form-item prop="linkType" label="跳转类型">
+                        <el-input v-model="formData.linkType"></el-input>
+                    </el-form-item>
+                    <el-form-item prop="linkContent" label="跳转内容">
+                        <el-input v-model="formData.linkContent"></el-input>
+                    </el-form-item>
+                    <el-form-item class="form-submit">
+                        <el-button @click="onSave" :loading="saving" type="primary"> 保存 </el-button>
+                        <el-button @click="onDelete" :disabled="saving" type="danger" v-if="formData.id">
+                            删除
+                        </el-button>
+                        <el-button @click="$router.go(-1)" :disabled="saving">取消</el-button>
+                    </el-form-item>
+                </el-form>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+export default {
+    name: 'BannerEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('banner/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    },
+    data() {
+        return {
+            saving: false,
+            formData: {
+                link: false
+            },
+            rules: {
+                name: [
+                    {
+                        required: true,
+                        message: '请输入名称',
+                        trigger: 'blur'
+                    }
+                ],
+                pic: [
+                    {
+                        required: true,
+                        message: '请输入图片',
+                        trigger: 'blur'
+                    }
+                ],
+                sort: [
+                    {
+                        required: true,
+                        message: '请输入排序',
+                        trigger: 'blur'
+                    }
+                ],
+                type: [
+                    {
+                        required: true,
+                        message: '请输入展示位置',
+                        trigger: 'blur'
+                    }
+                ],
+                link: [
+                    {
+                        required: true,
+                        message: '请输入跳转',
+                        trigger: 'blur'
+                    }
+                ]
+            },
+            typeOptions: [
+                { label: '首页', value: 'HOME' },
+                { label: '探索', value: 'DISCOVER' },
+                { label: '铸造者', value: 'MINTER' },
+                { label: '市场', value: 'MARKET' }
+            ]
+        };
+    },
+    methods: {
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+
+            this.saving = true;
+            this.$http
+                .post('/banner/save', data, { body: 'json' })
+                .then(res => {
+                    this.saving = false;
+                    this.$message.success('成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.saving = false;
+                    this.$message.error(e.error);
+                });
+        },
+        onDelete() {
+            this.$confirm('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/banner/del/${this.formData.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        console.log(e);
+                        this.$message.error((e || {}).error || '删除失败');
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 209 - 0
src/main/vue/src/views/BannerList.vue

@@ -0,0 +1,209 @@
+<template>
+    <div class="list-view">
+        <page-title>
+            <el-button
+                @click="addRow"
+                type="primary"
+                icon="el-icon-plus"
+                :disabled="fetchingData || downloading"
+                class="filter-item"
+            >
+                新增
+            </el-button>
+        </page-title>
+        <div class="filters-container">
+            <el-select v-model="type" clearable placeholder="筛选类型" @change="getData">
+                <el-option
+                    v-for="item in typeOptions"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                ></el-option>
+            </el-select>
+            <el-input
+                placeholder="搜索..."
+                v-model="search"
+                clearable
+                class="filter-item search"
+                @keyup.enter.native="getData"
+            >
+                <el-button @click="getData" slot="append" icon="el-icon-search"> </el-button>
+            </el-input>
+        </div>
+        <el-table
+            :data="tableData"
+            row-key="id"
+            ref="table"
+            header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell"
+            row-class-name="table-row"
+            cell-class-name="table-cell"
+            :height="tableHeight"
+            v-loading="fetchingData"
+        >
+            <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+            <el-table-column prop="id" label="ID" width="100"> </el-table-column>
+            <el-table-column prop="name" label="名称"> </el-table-column>
+            <el-table-column prop="pic" label="图片">
+                <template slot-scope="{ row }">
+                    <el-image
+                        style="width: 30px; height: 30px"
+                        :src="row.pic"
+                        fit="cover"
+                        :preview-src-list="[row.pic]"
+                    ></el-image>
+                </template>
+            </el-table-column>
+            <el-table-column prop="remark" label="备注"> </el-table-column>
+            <el-table-column prop="sort" label="排序"> </el-table-column>
+            <el-table-column prop="type" label="展示位置" :formatter="typeFormatter"> </el-table-column>
+            <el-table-column prop="link" label="跳转">
+                <template slot-scope="{ row }">
+                    <el-tag :type="row.link ? '' : 'info'">{{ row.link }}</el-tag>
+                </template>
+            </el-table-column>
+            <el-table-column prop="linkType" label="跳转类型"> </el-table-column>
+            <el-table-column prop="linkContent" label="跳转内容"> </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" width="150">
+                <template slot-scope="{ row }">
+                    <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
+                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)">批量编辑</el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1">批量操作1</el-button>
+                    <el-button @click="operation2">批量操作2</el-button>
+                    <el-button @click="toggleMultipleMode(false)">取消</el-button>
+                </el-button-group>
+            </div> -->
+            <el-pagination
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                :current-page="page"
+                :page-sizes="[10, 20, 30, 40, 50]"
+                :page-size="pageSize"
+                layout="total, sizes, prev, pager, next, jumper"
+                :total="totalElements"
+            >
+            </el-pagination>
+        </div>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+    name: 'BannerList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/banner/all',
+            downloading: false,
+            typeOptions: [
+                { label: '首页', value: 'HOME' },
+                { label: '探索', value: 'DISCOVER' },
+                { label: '铸造者', value: 'MINTER' },
+                { label: '市场', value: 'MARKET' }
+            ],
+            type: null
+        };
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        }
+    },
+    methods: {
+        typeFormatter(row, column, cellValue, index) {
+            let selectedOption = this.typeOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        beforeGetData() {
+            return { search: this.search, query: { del: false, type: this.type }, sort: 'sort' };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/bannerEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/bannerEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/banner/excel', {
+                    responseType: 'blob',
+                    params: { size: 10000 }
+                })
+                .then(res => {
+                    console.log(res);
+                    this.downloading = false;
+                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+                    const link = document.createElement('a');
+                    link.href = downloadUrl;
+                    link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+                    document.body.appendChild(link);
+                    link.click();
+                    link.remove();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.downloading = false;
+                    this.$message.error(e.error);
+                });
+        },
+        operation1() {
+            this.$notify({
+                title: '提示',
+                message: this.selection
+            });
+        },
+        operation2() {
+            this.$message('操作2');
+        },
+        deleteRow(row) {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/banner/del/${row.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.getData();
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped>
+</style>

+ 10 - 1
src/main/vue/src/views/MinterEdit.vue

@@ -40,6 +40,15 @@
                     <el-form-item prop="phone" label="手机">
                         <el-input v-model="formData.phone" :disabled="formData.id"></el-input>
                     </el-form-item>
+                    <el-form-item prop="intro" label="简介">
+                        <el-input
+                            v-model="formData.intro"
+                            placeholder="200字以内"
+                            maxlength="200"
+                            type="textarea"
+                            autosize
+                        ></el-input>
+                    </el-form-item>
                     <!-- <el-form-item prop="authorities" label="角色">
                         <el-select
                             v-model="formData.authorities"
@@ -132,7 +141,7 @@ export default {
                     }
                 ],
                 authorities: [{ required: true, message: '请选择角色', trigger: 'blur' }],
-                saving: false
+                intro: [{ required: true, message: '请输入简介', trigger: 'blur' }]
             },
             authorities: [
                 { name: 'ROLE_MINTER', description: '铸造者' },

+ 0 - 1
src/main/vue/src/views/UserEdit.vue

@@ -116,7 +116,6 @@ export default {
                     }
                 ],
                 authorities: [{ required: true, message: '请选择角色', trigger: 'blur' }],
-                saving: false
             },
             authorities: []
         };

+ 3 - 3
src/test/java/com/izouma/nineth/CommonTest.java

@@ -2,6 +2,8 @@ package com.izouma.nineth;
 
 import com.izouma.nineth.domain.BaseEntity;
 import com.izouma.nineth.domain.User;
+import com.izouma.nineth.enums.AuthorityName;
+import com.izouma.nineth.security.Authority;
 import com.izouma.nineth.utils.UrlUtils;
 import com.izouma.nineth.web.BaseController;
 import lombok.SneakyThrows;
@@ -35,10 +37,8 @@ import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
-import java.util.ArrayList;
+import java.util.*;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 import java.util.regex.Pattern;
 
 import static java.nio.file.StandardOpenOption.CREATE;