Commit 425afdff by flyxiaozhu

erp module

parents
Showing with 4518 additions and 0 deletions
.idea
*.iml
target
react-admin/node_modules
/temp.sql
ERP Module
==
提供全后端分离的模板
\ No newline at end of file
FROM java:8-alpine
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo 'Asia/Shanghai' >/etc/timezone
ADD target/original-erp-admin-api-1.0-SNAPSHOT.jar app.jar
ENTRYPOINT ["sh","-c","java -jar /app.jar --spring.profiles.active=prod"]
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>erp</artifactId>
<groupId>com.maile</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>erp-admin-api</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<fork>true</fork>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer>
<mainClass>com.maile.erp.admin.AdminApplication</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.hisun.crypt</groupId>
<artifactId>amac</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/library/hiencrypt.jar</systemPath>
</dependency>
</dependencies>
<properties>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>erp</artifactId>
<groupId>com.maile</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>erp-admin-api</artifactId>
<dependencies>
<dependency>
<groupId>com.maile</groupId>
<artifactId>erp-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--aop依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<!--https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>runtime</scope>
<version>2.0.3.RELEASE</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 如果不设置fork,那么不会restart,devtools热部署不会起作用-->
<fork>true</fork>
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.maile.erp.admin.AdminApplication</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package com.maile.erp.admin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableCaching
@EntityScan(basePackages = {"com.maile.erp"})
@ComponentScan(basePackages = {"com.maile.erp"})
@EnableJpaRepositories(basePackages = {"com.maile.erp"})
@EnableAsync
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}
package com.maile.erp.admin.authorize;
import com.maile.erp.core.entities.AdminUser;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class AdminUserDetails implements UserDetails {
private AdminUser adminUser;
private Collection<? extends GrantedAuthority> authorities;
public AdminUserDetails(AdminUser adminUser, Collection<? extends GrantedAuthority> authorities) {
this.adminUser = adminUser;
this.authorities = authorities;
}
public AdminUser getAdminUser() {
return adminUser;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return adminUser.getPassword();
}
@Override
public String getUsername() {
return adminUser.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return adminUser.getStatus() == 0;
}
}
package com.maile.erp.admin.authorize;
import com.maile.erp.core.entities.AdminUser;
import com.maile.erp.core.entities.Permission;
import com.maile.erp.core.repositories.AdminUserRepository;
import com.maile.erp.core.repositories.PermissionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service
public class AdminUserDetailsService implements UserDetailsService {
@Autowired
AdminUserRepository adminUserRepository;
@Autowired
PermissionRepository permissionRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AdminUser user = adminUserRepository.findAdminUserByUsername(username);
if (user != null) {
List<Permission> permissions = permissionRepository.findByAdminUserId(user.getId());
Set<GrantedAuthority> authorities = new HashSet<>();
for (Permission permission : permissions) {
if (permission != null && permission.getName() != null) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_" + permission.getIdentity());
authorities.add(grantedAuthority);
}
}
return new AdminUserDetails(user, authorities);
} else {
throw new UsernameNotFoundException("admin: " + username + " do not exist!");
}
}
}
package com.maile.erp.admin.authorize;
import com.maile.erp.core.entities.AdminUser;
import com.maile.erp.core.utils.CollectionUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class AuthorizeHelper {
public static AdminUserDetails getUserDetails() {
return (AdminUserDetails) SecurityContextHolder.getContext()
.getAuthentication()
.getPrincipal();
}
public static AdminUser getUser() {
return getUserDetails().getAdminUser();
}
public static Collection<String> getPermissions() {
return getPermissions(null);
}
public static Collection<String> getPermissions(UserDetails userDetails) {
if (userDetails == null) {
userDetails = getUserDetails();
}
List<String> permission = CollectionUtils.map(userDetails.getAuthorities(), o -> o.getAuthority().replaceAll("^ROLE_", ""));
Collections.sort(permission);
return permission;
}
}
package com.maile.erp.admin.authorize;
import com.maile.erp.core.entities.Permission;
import com.maile.erp.core.repositories.PermissionRepository;
import com.maile.erp.core.utils.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Component
public class PermissionManager {
private PermissionRepository permissionRepository;
@Autowired
public void setPermissionRepository(PermissionRepository permissionRepository) {
this.permissionRepository = permissionRepository;
}
private Permission buildDefaultPermission(String identify) {
Permission permission = new Permission();
permission.setIdentity(identify);
permission.setName("未定义权限");
return permission;
}
public void init() {
PermissionScanner scanner = new PermissionScanner("com.maile.erp.admin.controllers");
List<Permission> permissions = permissionRepository.findAll();
Set<String> dbPerms = new HashSet<>(CollectionUtils.map(permissions, Permission::getIdentity));
Set<String> realPerms = scanner.getPermissions();
Set<String> newPerms = new HashSet<>(realPerms);
newPerms.removeAll(dbPerms);
Set<String> discardPerms = new HashSet<>(dbPerms);
discardPerms.removeAll(realPerms);
if (discardPerms.size() > 0) {
System.out.println("============无用的权限============");
System.out.println(discardPerms);
}
if (newPerms.size() > 0) {
System.out.println("============新注册权限============");
System.out.println(newPerms);
}
List<Permission> permissiones = new ArrayList<>();
for (String perm : newPerms) {
permissiones.add(buildDefaultPermission(perm));
}
permissionRepository.saveAll(permissiones);
}
}
package com.maile.erp.admin.authorize;
import com.maile.erp.core.libs.ClassScanner;
import com.maile.erp.core.utils.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PermissionScanner extends ClassScanner {
private String basePackage;
private Set<Class<?>> controllers;
public PermissionScanner(String basePackage) {
this.basePackage = basePackage;
}
public Set<Class<?>> getControllers() {
if (controllers == null) {
controllers = new HashSet<>();
Set<Class<?>> clsList = getClasses(basePackage, true);
if (clsList != null && clsList.size() > 0) {
for (Class<?> cls : clsList) {
if (cls.getAnnotation(Controller.class) != null || cls.getAnnotation(RestController.class) != null) {
controllers.add(cls);
}
}
}
}
return controllers;
}
public Set<String> getPermissions() {
Set<String> permissions = new HashSet<>();
for (Class<?> cls : getControllers()) {
Method[] methods = cls.getMethods();
for (Method method : methods) {
Secured securedAnnotation = method.getAnnotation(Secured.class);
if (securedAnnotation != null) {
String[] value = securedAnnotation.value();
permissions.addAll(CollectionUtils.map(value, o -> o.replaceAll("^ROLE_", "")));
} else {
String expression = "";
PreAuthorize preAuthorizeAnnotation = method.getAnnotation(PreAuthorize.class);
if (preAuthorizeAnnotation != null) {
expression = preAuthorizeAnnotation.value();
}
PostAuthorize postAuthorizeAnnotation = method.getAnnotation(PostAuthorize.class);
if (postAuthorizeAnnotation != null) {
expression = postAuthorizeAnnotation.value();
}
if (!StringUtils.isBlank(expression)) {
Pattern pattern = Pattern.compile("ROLE_\\w+");
Matcher matcher = pattern.matcher(expression);
while (matcher.find()) {
permissions.add(matcher.group().replaceAll("^ROLE_", ""));
}
}
}
}
}
return permissions;
}
}
package com.maile.erp.admin.configurations;
import com.maile.erp.core.libs.AppException;
import com.maile.erp.core.libs.ErrorCode;
import com.maile.erp.core.libs.JSONResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.core.serializer.support.SerializationFailedException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Set;
/**
* Description:
*
* @author johnny.lu
* @version 1.0
* @date 2017/12/30 1:06
* Created with IDEA
*/
@ControllerAdvice
public class WebControllerAdvice {
private static final Logger logger = LoggerFactory.getLogger(WebControllerAdvice.class);
@InitBinder
protected void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
@ExceptionHandler(value = ConstraintViolationException.class)
@ResponseBody
public JSONResult handleResourceNotFoundException(ConstraintViolationException e) {
logger.debug("constraint violation exception", e);
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations) {
String errorMsg = violation.getMessage();
strBuilder.append(errorMsg + "\n");
}
return new JSONResult(ErrorCode.INVALID_PARAM, strBuilder.toString());
}
@ExceptionHandler(value = BindException.class)
@ResponseBody
protected JSONResult handleBindException(BindException ex) throws IOException {
JSONResult result = new JSONResult(ex.getBindingResult());
return result;
}
@ExceptionHandler(value = MissingServletRequestParameterException.class)
@ResponseBody
protected JSONResult handleMissingServletRequestParameter(MissingServletRequestParameterException ex) throws IOException {
JSONResult result = new JSONResult(ErrorCode.INVALID_PARAM, "Miss parameter " + ex.getParameterName() + ".");
return result;
}
@ExceptionHandler(value = MissingPathVariableException.class)
@ResponseBody
protected JSONResult handleMissingPathVariable(MissingPathVariableException ex) throws IOException {
JSONResult result = new JSONResult(ErrorCode.INVALID_PARAM, "Miss path variable " + ex.getVariableName() + ".");
return result;
}
@ExceptionHandler(value = DataIntegrityViolationException.class)
@ResponseBody
protected JSONResult handleDataIntegrityViolationException(DataIntegrityViolationException ex) {
JSONResult result = new JSONResult(ErrorCode.DUPLICATE_KEY, "数据发生重复");
return result;
}
@ExceptionHandler(value = AppException.class)
@ResponseBody
protected JSONResult handleAppException(AppException ex) {
JSONResult result = new JSONResult(ex.getCode(), ex.getMessage());
return result;
}
@ExceptionHandler(value = AccessDeniedException.class)
@ResponseBody
protected JSONResult handleAppAccessDenied(AccessDeniedException ex) {
JSONResult result = new JSONResult(ErrorCode.FORBIDDEN, "你没有权限进行此操作");
return result;
}
}
package com.maile.erp.admin.configurations;
import com.maile.erp.admin.authorize.AdminUserDetails;
import com.maile.erp.admin.authorize.AdminUserDetailsService;
import com.maile.erp.admin.authorize.AuthorizeHelper;
import com.maile.erp.core.libs.ErrorCode;
import com.maile.erp.core.libs.JSONResult;
import com.maile.erp.core.libs.RenderUtils;
import com.maile.erp.core.repositories.AdminUserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.DigestUtils;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
AdminUserRepository adminUserRepository;
@Bean
UserDetailsService customUserDetailService() {
return new AdminUserDetailsService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailService())
.passwordEncoder(new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(DigestUtils.md5DigestAsHex(charSequence.toString().getBytes()));
}
});
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/home", "/dist/**", "**.ico").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login")
.permitAll()
.successHandler((request, response, auth) -> {
AdminUserDetails userDetails = (AdminUserDetails) auth.getPrincipal();
JSONResult result = new JSONResult()
.put("user", userDetails.getAdminUser())
.put("permission", AuthorizeHelper.getPermissions(userDetails));
RenderUtils.renderJson(response, result);
})
.failureHandler((request, response, exception) -> {
if (exception instanceof BadCredentialsException) {
RenderUtils.renderJson(response, new JSONResult(ErrorCode.BAD_CREDENTIALS, "账号或密码错误"));
} else if (exception instanceof DisabledException) {
RenderUtils.renderJson(response, new JSONResult(ErrorCode.USER_DISABLED, "此账户已被禁用"));
} else {
RenderUtils.renderJson(response, new JSONResult(ErrorCode.UNKNOWN, "未知错误"));
}
})
.and()
.logout()
.logoutSuccessHandler((request, response, auth) -> response.sendRedirect("/"))
.permitAll()
.and()
.exceptionHandling()
.authenticationEntryPoint((request, response, exception) -> {
RenderUtils.renderJson(response, new JSONResult(ErrorCode.NOT_LOGIN, "尚未登录"));
});
}
}
package com.maile.erp.admin.controllers;
import com.maile.erp.core.entities.Permission;
import com.maile.erp.core.libs.JSONResult;
import com.maile.erp.core.libs.SearchSpecification;
import com.maile.erp.core.repositories.PermissionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@RestController
public class PermissionController {
@Autowired
PermissionRepository permissionRepository;
@RequestMapping("/permission/select")
public JSONResult select(HttpServletRequest request, @PageableDefault Pageable pageable) {
Page<Permission> page = permissionRepository.findAll(new SearchSpecification<>(request), pageable);
return new JSONResult(page, pageable.getPageNumber());
}
@RequestMapping("/permission/all")
public JSONResult all(HttpServletRequest request) {
List<Permission> list = permissionRepository.findAll(new SearchSpecification<>(request));
return new JSONResult().put("list", list);
}
@RequestMapping("/permission/update")
public JSONResult update() {
return new JSONResult().put("gg", "f");
}
}
package com.maile.erp.admin.controllers;
import com.maile.erp.core.entities.Role;
import com.maile.erp.core.entities.RolePermission;
import com.maile.erp.core.libs.ErrorCode;
import com.maile.erp.core.libs.JSONResult;
import com.maile.erp.core.libs.SearchSpecification;
import com.maile.erp.core.repositories.RolePermissionRepository;
import com.maile.erp.core.repositories.RoleRepository;
import com.maile.erp.core.utils.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import java.util.*;
@RestController
public class RoleController {
@Autowired
RoleRepository roleRepository;
@Autowired
RolePermissionRepository rolePermissionRepository;
@Secured("ROLE_ROLE_SELECT")
@RequestMapping("/role/select")
public JSONResult select(HttpServletRequest request, @PageableDefault Pageable pageable) {
Page<Role> page = roleRepository.findAll(new SearchSpecification<>(request), pageable);
List<Long> rolesId = CollectionUtils.map(page.getContent(), Role::getId);
List<RolePermission> permissions = rolePermissionRepository.findByRoleIdIn(rolesId);
Map<Long, List<Long>> perms = CollectionUtils.group(permissions, new CollectionUtils.GroupFilter<RolePermission, Long, Long>() {
@Override
public Long getKey(RolePermission source) {
return source.getRoleId();
}
@Override
public Long getValue(RolePermission source) {
return source.getPermissionId();
}
});
for (Role role : page.getContent()) {
if (perms.containsKey(role.getId())) {
role.setPerms(perms.get(role.getId()));
}
}
return new JSONResult(page, pageable.getPageNumber());
}
@Secured("ROLE_ROLE_INSERT")
@RequestMapping("/role/insert")
public JSONResult insert(@RequestParam("name") String name,
@RequestParam(value = "perms", required = false) List<Long> perms) {
if (roleRepository.findByName(name) != null) {
return new JSONResult(ErrorCode.NO_DATA, "角色名已存在");
}
Role role = new Role();
role.setName(name);
roleRepository.save(role);
if (perms != null && perms.size() > 0) {
putPerms(role.getId(), perms);
}
role.setPerms(perms);
return new JSONResult().put("model", role);
}
@Secured("ROLE_ROLE_UPDATE")
@RequestMapping("/role/update")
@Transactional
public JSONResult update(@RequestParam("id") Long id,
@RequestParam("name") String name,
@RequestParam(value = "perms", required = false) List<Long> perms) {
Role role = roleRepository.findById(id).orElse(null);
if (role == null) {
return new JSONResult(ErrorCode.NO_DATA, "角色ID错误");
}
Role old = roleRepository.findByName(name);
if (old != null && old.getId() != id) {
return new JSONResult(ErrorCode.NO_DATA, "角色名已存在");
}
role.setName(name);
roleRepository.save(role);
rolePermissionRepository.deleteAllByRoleId(role.getId());
if (perms != null && perms.size() > 0) {
putPerms(role.getId(), perms);
}
role.setPerms(perms);
return new JSONResult().put("model", role).put("count", 1);
}
private void putPerms(Long roleId, Collection<Long> perms) {
for (Long permId : perms) {
RolePermission rp = new RolePermission();
rp.setPermissionId(permId);
rp.setRoleId(roleId);
rolePermissionRepository.save(rp);
}
}
}
package com.maile.erp.admin.controllers;
import com.maile.erp.core.libs.ErrorCode;
import com.maile.erp.core.libs.JSONResult;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SiteController {
// @RequestMapping(value = "/")
// public String index() {
// return "this is Index";
// }
}
package com.maile.erp.admin.controllers;
import com.maile.erp.admin.authorize.AdminUserDetails;
import com.maile.erp.admin.authorize.AuthorizeHelper;
import com.maile.erp.core.entities.AdminRole;
import com.maile.erp.core.entities.AdminUser;
import com.maile.erp.core.libs.ErrorCode;
import com.maile.erp.core.libs.JSONResult;
import com.maile.erp.core.libs.SearchSpecification;
import com.maile.erp.core.repositories.AdminRoleRepository;
import com.maile.erp.core.repositories.AdminUserRepository;
import com.maile.erp.core.utils.BeanUtils;
import com.maile.erp.core.utils.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@Slf4j
@RestController
public class UserController {
@Autowired
AdminUserRepository adminUserRepository;
@Autowired
AdminRoleRepository adminRoleRepository;
@RequestMapping("/user/getCurrentUser")
public JSONResult getCurrentUser() {
AdminUserDetails userDetails = AuthorizeHelper.getUserDetails();
return new JSONResult()
.put("user", userDetails.getAdminUser())
.put("permission", AuthorizeHelper.getPermissions());
}
@Secured("ROLE_USER_SELECT")
@RequestMapping("/user/select")
public JSONResult getUserList(HttpServletRequest request, @PageableDefault Pageable pageable) {
Page<AdminUser> page = adminUserRepository.findAll(new SearchSpecification<>(request), pageable);
List<Long> userIds = CollectionUtils.map(page.getContent(), AdminUser::getId);
List<AdminRole> roles = adminRoleRepository.findByAdminUserIdIn(userIds);
Map<Long, List<Long>> userIdGroup = CollectionUtils.group(roles, new CollectionUtils.GroupFilter<AdminRole, Long, Long>() {
@Override
public Long getKey(AdminRole source) {
return source.getAdminUserId();
}
@Override
public Long getValue(AdminRole source) {
return source.getRoleId();
}
});
for (AdminUser adminUser : page.getContent()) {
adminUser.setRoleId(userIdGroup.get(adminUser.getId()));
}
return new JSONResult(page, pageable.getPageNumber());
}
@Secured("ROLE_USER_INSERT")
@PostMapping("/user/insert")
public JSONResult addUser(AdminUser adminUser,
@RequestParam(value = "roleId", required = false) List<Long> roleId) {
AdminUser adminUser1 = adminUserRepository.findAdminUserByUsername(StringUtils.trim(adminUser.getUsername()));
if (adminUser1 != null) {
return new JSONResult(ErrorCode.DUPLICATE_KEY, "该用户名已存在");
}
adminUser.setPassword(DigestUtils.md5Hex(adminUser.getTmpPwd()));
adminUserRepository.save(adminUser);
if (roleId != null) {
assigningRoles(adminUser.getId(), roleId);
}
adminUser.setTmpPwd("******");
return new JSONResult().put("model", adminUser);
}
@Transactional
@Secured("ROLE_USER_UPDATE")
@PostMapping("/user/update")
public JSONResult updateUser(AdminUser adminUser,
@RequestParam(value = "roleId", required = false) List<Long> roleId) {
AdminUser user = adminUserRepository.findById(adminUser.getId()).orElse(null);
if (user == null) {
return new JSONResult(ErrorCode.NO_DATA, "该用户不存在");
}
AdminUser user1 = adminUserRepository.findAdminUserByUsername(StringUtils.trim(adminUser.getUsername()));
if (user1 != null && user1.getId() != adminUser.getId()) {
return new JSONResult(ErrorCode.DUPLICATE_KEY, "该用户名已存在");
}
BeanUtils.copyProperties(adminUser, user);
if (!adminUser.getTmpPwd().equals("******")) {
user.setPassword(DigestUtils.md5Hex(adminUser.getTmpPwd()));
}
adminUserRepository.save(user);
adminRoleRepository.deleteByAdminUserId(adminUser.getId());
if (roleId != null) {
assigningRoles(adminUser.getId(), roleId);
}
user.setTmpPwd("******");
return new JSONResult().put("count", 1).put("model", user);
}
private void assigningRoles(Long userId, Collection<Long> roleIds) {
for (Long roleId : roleIds) {
AdminRole ar = new AdminRole();
ar.setAdminUserId(userId);
ar.setRoleId(roleId);
adminRoleRepository.save(ar);
}
}
}
spring.profiles.active=dev
spring.jackson.serialization.indent_output=true
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
spring.datasource.secondary.max-idle=10
spring.datasource.secondary.max-wait=10000
spring.datasource.secondary.min-idle=5
spring.datasource.secondary.initial-size=5
spring.datasource.secondary.validation-query=SELECT 1
spring.datasource.secondary.test-on-borrow=false
spring.datasource.secondary.test-while-idle=true
spring.datasource.secondary.time-between-eviction-runs-millis=18800
server.port=8081
spring.datasource.url=jdbc:mysql://localhost:3306/erp-module?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false&serverTimezone=UTC&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#设置为create时每次都会重新创建表
#需要初始化数据时用create,会自动加载import.sql
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
logging.level.com.maile.erp=DEBUG
#以下是actuator配置
#如访问:http://localhost:8080/manager/health
management.security.enabled=false
#设置为端点的url设置前缀
management.context-path=/manager
\ No newline at end of file
spring.datasource.url=jdbc:mysql://localhost:3306/erp-module?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#设置为create时每次都会重新创建表
#需要初始化数据时用create,会自动加载import.sql
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=false
logging.level.com.maile.erp=INFO
logging.file=/usr/local/logs/java/admin-api.log
#以下是actuator配置
#如访问:http://localhost:8080/manager/health
management.security.enabled=false
#设置为端点的url设置前缀
management.context-path=/manager
#注意这个初始化脚本只用于开发/测试时使用,上生产时手动执行
set foreign_key_checks=0;
set foreign_key_checks=1;
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>Group 21</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Ant-Design-Pro-3.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="账户密码登录-校验" transform="translate(-79.000000, -82.000000)">
<g id="Group-21" transform="translate(77.000000, 73.000000)">
<g id="Group-18" opacity="0.8" transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
<ellipse id="Oval-11" fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479" ry="21.766008"></ellipse>
<ellipse id="Oval-3" fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913" ry="5.21330997"></ellipse>
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z" id="Oval-3-Copy" fill="#CFDAE6" opacity="0.45"></path>
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" id="Path-12" stroke="#CFDAE6" stroke-width="1.73913043" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" id="Path-16" stroke="#E0B4B7" stroke-width="0.702678964" opacity="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" id="Path-15" stroke="#BACAD9" stroke-width="0.702678964" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<g id="Group-17" transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)" fill="#CFDAE6">
<ellipse id="Oval-4" opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653" ry="9.12768076"></ellipse>
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z" id="Oval-4" transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
</g>
</g>
<g id="Group-14" transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471" ry="29.1402439"></ellipse>
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275" ry="21.5853659"></ellipse>
<ellipse id="Oval-2-Copy" stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902" ry="23.7439024"></ellipse>
<ellipse id="Oval-2" fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137" ry="10.7926829"></ellipse>
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z" id="Oval-2" fill="#BACAD9"></path>
<g id="Group-9" opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
<ellipse id="Oval-2-Copy-2" cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z" id="Oval-2-Copy-2" transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
</g>
<ellipse id="Oval-10" fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy-2" fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy" fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275" ry="2.15853659"></ellipse>
<path d="M28.9985381,29.9671598 L171.151018,132.876024" id="Path-11" stroke="#CFDAE6" opacity="0.8"></path>
</g>
<g id="Group-10" opacity="0.799999952" transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
<ellipse id="Oval-7" stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407" ry="11.2941176"></ellipse>
<g id="Group-12" transform="translate(34.596774, 23.111111)" fill="#BACAD9">
<ellipse id="Oval-7" opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627" ry="8.55614973"></ellipse>
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z" id="Oval-7"></path>
</g>
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" id="Path-2" stroke="#CFDAE6" stroke-width="0.941176471"></path>
<ellipse id="Oval" stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186" ry="3.29411765"></ellipse>
<ellipse id="Oval-Copy" fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" id="Path-10" stroke="#CFDAE6" stroke-width="0.941176471"></path>
</g>
<g id="Group-19" opacity="0.33" transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
<g id="Group-17" transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)" fill="#BACAD9">
<circle id="Oval-4" opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z" id="Oval-4" transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
</g>
<circle id="Oval-5-Copy-6" fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
<path d="M143.5,88.8126685 L155.070501,17.6038544" id="Path-17" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M17.5,37.3333333 L127.466252,97.6449735" id="Path-18" stroke="#BACAD9" stroke-width="1.16666667"></path>
<polyline id="Path-19" stroke="#CFDAE6" stroke-width="1.16666667" points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
<path d="M159.833333,99.7453842 L195.416667,89.25" id="Path-20" stroke="#E0B4B7" stroke-width="1.16666667" opacity="0.6"></path>
<path d="M205.333333,82.1372105 L238.719406,36.1666667" id="Path-24" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M266.723424,132.231988 L207.083333,90.4166667" id="Path-25" stroke="#CFDAE6" stroke-width="1.16666667"></path>
<circle id="Oval-5" fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
<circle id="Oval-5-Copy-3" fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
<circle id="Oval-5-Copy-2" fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
<circle id="Oval-5-Copy-4" fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
<circle id="Oval-5-Copy-5" fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2013-9-30: Created.
-->
<svg>
<metadata>
Created by iconfont
</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1024" >
<font-face
font-family="iconfont"
font-weight="500"
font-stretch="normal"
units-per-em="1024"
ascent="896"
descent="-128"
/>
<missing-glyph />
<glyph glyph-name="jingxuanshichang" unicode="&#58880;" d="M627.977626 476.225434c119.09417-1.609626 238.185267-3.234714 357.295821-4.859699-4.320563 15.968666-8.659558 31.970099-12.985242 47.956173-94.425395-72.591258-188.872192-145.212314-283.298611-217.802547-9.083187-6.970778-15.551795-17.022669-11.830886-29.056819 35.275878-113.777357 70.511718-227.553587 105.774285-341.313536 12.613837 9.670246 25.217331 19.389645 37.817856 29.076173-98.232218 67.357082-196.461363 134.754099-294.693581 202.115277-6.971085 4.792115-19.035341 4.890419-25.973555 0L208.141824-43.727667c12.618957-9.687654 25.200947-19.373261 37.821952-29.057843 33.73056 114.214298 67.447808 228.459213 101.16311 342.70423 3.554099 11.964518-2.581914 22.152499-11.829862 29.056819-95.382221 71.316275-190.799155 142.631526-286.198784 213.963059-4.321587-16.00041-8.661606-31.986483-12.985242-47.956173 119.059354 3.234714 238.11881 6.450893 357.196595 9.669222 12.030464 0.334643 21.047194 7.725978 24.833638 18.90048 38.34071 112.771379 76.697907 225.542861 115.056026 338.296832l-49.636478 0c39.850189-112.235213 79.714714-224.469402 119.579238-336.723046 11.026534-31.032832 60.796109-17.659187 49.634406 13.689754-39.846093 112.235213-79.728026 224.47145-119.57719 336.723046-8.614502 24.230912-41.238835 24.733286-49.636454 0-38.35607-112.771379-76.713267-225.541837-115.052954-338.295808 8.260506 6.283059 16.522957 12.582605 24.80087 18.884096-119.077786-3.21833-238.137242-6.434509-357.196595-9.668198-27.38176-0.753152-32.77783-33.195008-13.003674-47.974605 95.414886-71.315251 190.831923-142.630502 286.212096-213.945651-3.9552 9.684582-7.874662 19.371213-11.830886 29.055795-33.732608-114.247066-67.446784-228.458189-101.179494-342.70423-6.049997-20.51113 18.950349-42.393498 37.80352-29.072179 97.058406 68.499046 194.083123 136.983859 291.10569 205.485978-15.284736-10.792858-34.48576 7.003546-12.482765-8.079053 11.142246-7.640986 22.268006-15.282074 33.380557-22.919987 33.763328-23.160525 67.547136-46.316851 101.29408-69.476352 49.132954-33.679053 98.231194-67.35913 147.331379-101.074022 18.783539-12.868096 44.272128 8.344064 37.83936 29.087437-35.254374 113.779405-70.532198 227.520922-105.772237 341.297152-3.956224-9.685606-7.909478-19.371213-11.84727-29.054771 94.426419 72.58921 188.871168 145.194906 283.312947 217.800499 19.222528 14.779597 14.782259 47.586816-13.00265 47.956173-119.110554 1.62601-238.202675 3.250995-357.295821 4.860723C594.746368 528.15319 594.798592 476.677734 627.977626 476.225434" horiz-adv-x="1024" />
<glyph glyph-name="chexingluntan" unicode="&#59100;" d="M1320.606897 546.427586C1313.544828 564.082759 1292.358621 578.206897 1271.172414 578.206897l-176.551724 0L1094.62069 684.137931c0 21.186207-14.124138 35.310345-35.310345 35.310345L529.655172 719.448276c-21.186207 0-35.310345-14.124138-35.310345-35.310345l0 0c0-21.186207 14.124138-35.310345 35.310345-35.310345l494.344828 0 0-600.275862L632.055172 48.551724c-14.124138 60.027586-70.62069 105.931034-137.710345 105.931034s-120.055172-45.903448-137.710345-105.931034L282.482759 48.551724l0 317.793103c0 21.186207-14.124138 35.310345-35.310345 35.310345l0 0c-21.186207 0-35.310345-14.124138-35.310345-35.310345l0-353.103448c0-21.186207 14.124138-35.310345 35.310345-35.310345l109.462069 0c14.124138-60.027586 70.62069-105.931034 137.710345-105.931034s120.055172 45.903448 137.710345 105.931034l399.006897 0c14.124138-60.027586 70.62069-105.931034 137.710345-105.931034s120.055172 45.903448 137.710345 105.931034L1377.103448-22.068966c21.186207 0 35.310345 14.124138 35.310345 35.310345l0 229.517241C1412.413793 331.034483 1320.606897 546.427586 1320.606897 546.427586zM494.344828-57.37931c-38.841379 0-70.62069 31.77931-70.62069 70.62069s31.77931 70.62069 70.62069 70.62069 70.62069-31.77931 70.62069-70.62069S533.186207-57.37931 494.344828-57.37931zM1165.241379-57.37931c-38.841379 0-70.62069 31.77931-70.62069 70.62069s31.77931 70.62069 70.62069 70.62069 70.62069-31.77931 70.62069-70.62069S1204.082759-57.37931 1165.241379-57.37931zM1341.793103 48.551724l-38.841379 0c-14.124138 60.027586-70.62069 105.931034-137.710345 105.931034-24.717241 0-49.434483-7.062069-70.62069-17.655172L1094.62069 507.586207l158.896552 0c0 0 24.717241-17.655172 28.248276-35.310345l-63.558621 0c-21.186207 0-35.310345-14.124138-35.310345-35.310345l0-141.241379c0-21.186207 14.124138-35.310345 35.310345-35.310345L1341.793103 260.413793C1341.793103 260.413793 1341.793103 48.551724 1341.793103 48.551724zM0 860.689655c0 21.186207 17.655172 35.310345 35.310345 35.310345l353.103448 0c21.186207 0 35.310345-14.124138 35.310345-35.310345 0-21.186207-17.655172-35.310345-35.310345-35.310345L35.310345 825.37931C14.124138 825.37931 0 839.503448 0 860.689655zM105.931034 684.137931c0 21.186207 14.124138 35.310345 35.310345 35.310345l247.172414 0c21.186207 0 35.310345-14.124138 35.310345-35.310345 0-21.186207-14.124138-35.310345-35.310345-35.310345L141.241379 648.827586C120.055172 648.827586 105.931034 662.951724 105.931034 684.137931zM211.862069 507.586207c0 21.186207 17.655172 35.310345 35.310345 35.310345l141.241379 0c17.655172 0 35.310345-14.124138 35.310345-35.310345 0-21.186207-17.655172-35.310345-35.310345-35.310345L247.172414 472.275862C225.986207 472.275862 211.862069 486.4 211.862069 507.586207z" horiz-adv-x="1413" />
</font>
</defs></svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>Group 21</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Ant-Design-Pro-3.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="账户密码登录-校验" transform="translate(-79.000000, -82.000000)">
<g id="Group-21" transform="translate(77.000000, 73.000000)">
<g id="Group-18" opacity="0.8" transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
<ellipse id="Oval-11" fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479" ry="21.766008"></ellipse>
<ellipse id="Oval-3" fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913" ry="5.21330997"></ellipse>
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z" id="Oval-3-Copy" fill="#CFDAE6" opacity="0.45"></path>
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" id="Path-12" stroke="#CFDAE6" stroke-width="1.73913043" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" id="Path-16" stroke="#E0B4B7" stroke-width="0.702678964" opacity="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" id="Path-15" stroke="#BACAD9" stroke-width="0.702678964" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<g id="Group-17" transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)" fill="#CFDAE6">
<ellipse id="Oval-4" opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653" ry="9.12768076"></ellipse>
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z" id="Oval-4" transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
</g>
</g>
<g id="Group-14" transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471" ry="29.1402439"></ellipse>
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275" ry="21.5853659"></ellipse>
<ellipse id="Oval-2-Copy" stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902" ry="23.7439024"></ellipse>
<ellipse id="Oval-2" fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137" ry="10.7926829"></ellipse>
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z" id="Oval-2" fill="#BACAD9"></path>
<g id="Group-9" opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
<ellipse id="Oval-2-Copy-2" cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z" id="Oval-2-Copy-2" transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
</g>
<ellipse id="Oval-10" fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy-2" fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy" fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275" ry="2.15853659"></ellipse>
<path d="M28.9985381,29.9671598 L171.151018,132.876024" id="Path-11" stroke="#CFDAE6" opacity="0.8"></path>
</g>
<g id="Group-10" opacity="0.799999952" transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
<ellipse id="Oval-7" stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407" ry="11.2941176"></ellipse>
<g id="Group-12" transform="translate(34.596774, 23.111111)" fill="#BACAD9">
<ellipse id="Oval-7" opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627" ry="8.55614973"></ellipse>
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z" id="Oval-7"></path>
</g>
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" id="Path-2" stroke="#CFDAE6" stroke-width="0.941176471"></path>
<ellipse id="Oval" stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186" ry="3.29411765"></ellipse>
<ellipse id="Oval-Copy" fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" id="Path-10" stroke="#CFDAE6" stroke-width="0.941176471"></path>
</g>
<g id="Group-19" opacity="0.33" transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
<g id="Group-17" transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)" fill="#BACAD9">
<circle id="Oval-4" opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z" id="Oval-4" transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
</g>
<circle id="Oval-5-Copy-6" fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
<path d="M143.5,88.8126685 L155.070501,17.6038544" id="Path-17" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M17.5,37.3333333 L127.466252,97.6449735" id="Path-18" stroke="#BACAD9" stroke-width="1.16666667"></path>
<polyline id="Path-19" stroke="#CFDAE6" stroke-width="1.16666667" points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
<path d="M159.833333,99.7453842 L195.416667,89.25" id="Path-20" stroke="#E0B4B7" stroke-width="1.16666667" opacity="0.6"></path>
<path d="M205.333333,82.1372105 L238.719406,36.1666667" id="Path-24" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M266.723424,132.231988 L207.083333,90.4166667" id="Path-25" stroke="#CFDAE6" stroke-width="1.16666667"></path>
<circle id="Oval-5" fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
<circle id="Oval-5-Copy-3" fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
<circle id="Oval-5-Copy-2" fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
<circle id="Oval-5-Copy-4" fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
<circle id="Oval-5-Copy-5" fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2013-9-30: Created.
-->
<svg>
<metadata>
Created by iconfont
</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1024" >
<font-face
font-family="iconfont"
font-weight="500"
font-stretch="normal"
units-per-em="1024"
ascent="896"
descent="-128"
/>
<missing-glyph />
<glyph glyph-name="jingxuanshichang" unicode="&#58880;" d="M627.977626 476.225434c119.09417-1.609626 238.185267-3.234714 357.295821-4.859699-4.320563 15.968666-8.659558 31.970099-12.985242 47.956173-94.425395-72.591258-188.872192-145.212314-283.298611-217.802547-9.083187-6.970778-15.551795-17.022669-11.830886-29.056819 35.275878-113.777357 70.511718-227.553587 105.774285-341.313536 12.613837 9.670246 25.217331 19.389645 37.817856 29.076173-98.232218 67.357082-196.461363 134.754099-294.693581 202.115277-6.971085 4.792115-19.035341 4.890419-25.973555 0L208.141824-43.727667c12.618957-9.687654 25.200947-19.373261 37.821952-29.057843 33.73056 114.214298 67.447808 228.459213 101.16311 342.70423 3.554099 11.964518-2.581914 22.152499-11.829862 29.056819-95.382221 71.316275-190.799155 142.631526-286.198784 213.963059-4.321587-16.00041-8.661606-31.986483-12.985242-47.956173 119.059354 3.234714 238.11881 6.450893 357.196595 9.669222 12.030464 0.334643 21.047194 7.725978 24.833638 18.90048 38.34071 112.771379 76.697907 225.542861 115.056026 338.296832l-49.636478 0c39.850189-112.235213 79.714714-224.469402 119.579238-336.723046 11.026534-31.032832 60.796109-17.659187 49.634406 13.689754-39.846093 112.235213-79.728026 224.47145-119.57719 336.723046-8.614502 24.230912-41.238835 24.733286-49.636454 0-38.35607-112.771379-76.713267-225.541837-115.052954-338.295808 8.260506 6.283059 16.522957 12.582605 24.80087 18.884096-119.077786-3.21833-238.137242-6.434509-357.196595-9.668198-27.38176-0.753152-32.77783-33.195008-13.003674-47.974605 95.414886-71.315251 190.831923-142.630502 286.212096-213.945651-3.9552 9.684582-7.874662 19.371213-11.830886 29.055795-33.732608-114.247066-67.446784-228.458189-101.179494-342.70423-6.049997-20.51113 18.950349-42.393498 37.80352-29.072179 97.058406 68.499046 194.083123 136.983859 291.10569 205.485978-15.284736-10.792858-34.48576 7.003546-12.482765-8.079053 11.142246-7.640986 22.268006-15.282074 33.380557-22.919987 33.763328-23.160525 67.547136-46.316851 101.29408-69.476352 49.132954-33.679053 98.231194-67.35913 147.331379-101.074022 18.783539-12.868096 44.272128 8.344064 37.83936 29.087437-35.254374 113.779405-70.532198 227.520922-105.772237 341.297152-3.956224-9.685606-7.909478-19.371213-11.84727-29.054771 94.426419 72.58921 188.871168 145.194906 283.312947 217.800499 19.222528 14.779597 14.782259 47.586816-13.00265 47.956173-119.110554 1.62601-238.202675 3.250995-357.295821 4.860723C594.746368 528.15319 594.798592 476.677734 627.977626 476.225434" horiz-adv-x="1024" />
<glyph glyph-name="chexingluntan" unicode="&#59100;" d="M1320.606897 546.427586C1313.544828 564.082759 1292.358621 578.206897 1271.172414 578.206897l-176.551724 0L1094.62069 684.137931c0 21.186207-14.124138 35.310345-35.310345 35.310345L529.655172 719.448276c-21.186207 0-35.310345-14.124138-35.310345-35.310345l0 0c0-21.186207 14.124138-35.310345 35.310345-35.310345l494.344828 0 0-600.275862L632.055172 48.551724c-14.124138 60.027586-70.62069 105.931034-137.710345 105.931034s-120.055172-45.903448-137.710345-105.931034L282.482759 48.551724l0 317.793103c0 21.186207-14.124138 35.310345-35.310345 35.310345l0 0c-21.186207 0-35.310345-14.124138-35.310345-35.310345l0-353.103448c0-21.186207 14.124138-35.310345 35.310345-35.310345l109.462069 0c14.124138-60.027586 70.62069-105.931034 137.710345-105.931034s120.055172 45.903448 137.710345 105.931034l399.006897 0c14.124138-60.027586 70.62069-105.931034 137.710345-105.931034s120.055172 45.903448 137.710345 105.931034L1377.103448-22.068966c21.186207 0 35.310345 14.124138 35.310345 35.310345l0 229.517241C1412.413793 331.034483 1320.606897 546.427586 1320.606897 546.427586zM494.344828-57.37931c-38.841379 0-70.62069 31.77931-70.62069 70.62069s31.77931 70.62069 70.62069 70.62069 70.62069-31.77931 70.62069-70.62069S533.186207-57.37931 494.344828-57.37931zM1165.241379-57.37931c-38.841379 0-70.62069 31.77931-70.62069 70.62069s31.77931 70.62069 70.62069 70.62069 70.62069-31.77931 70.62069-70.62069S1204.082759-57.37931 1165.241379-57.37931zM1341.793103 48.551724l-38.841379 0c-14.124138 60.027586-70.62069 105.931034-137.710345 105.931034-24.717241 0-49.434483-7.062069-70.62069-17.655172L1094.62069 507.586207l158.896552 0c0 0 24.717241-17.655172 28.248276-35.310345l-63.558621 0c-21.186207 0-35.310345-14.124138-35.310345-35.310345l0-141.241379c0-21.186207 14.124138-35.310345 35.310345-35.310345L1341.793103 260.413793C1341.793103 260.413793 1341.793103 48.551724 1341.793103 48.551724zM0 860.689655c0 21.186207 17.655172 35.310345 35.310345 35.310345l353.103448 0c21.186207 0 35.310345-14.124138 35.310345-35.310345 0-21.186207-17.655172-35.310345-35.310345-35.310345L35.310345 825.37931C14.124138 825.37931 0 839.503448 0 860.689655zM105.931034 684.137931c0 21.186207 14.124138 35.310345 35.310345 35.310345l247.172414 0c21.186207 0 35.310345-14.124138 35.310345-35.310345 0-21.186207-14.124138-35.310345-35.310345-35.310345L141.241379 648.827586C120.055172 648.827586 105.931034 662.951724 105.931034 684.137931zM211.862069 507.586207c0 21.186207 17.655172 35.310345 35.310345 35.310345l141.241379 0c17.655172 0 35.310345-14.124138 35.310345-35.310345 0-21.186207-17.655172-35.310345-35.310345-35.310345L247.172414 472.275862C225.986207 472.275862 211.862069 486.4 211.862069 507.586207z" horiz-adv-x="1413" />
</font>
</defs></svg>
This source diff could not be displayed because it is too large. You can view the blob instead.
{"version":3,"file":"bundle.min.js","sources":["webpack:///bundle.min.js"],"mappings":"AAAA","sourceRoot":""}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
{"version":3,"file":"vendor.min.js","sources":["webpack:///vendor.min.js"],"mappings":"AAAA","sourceRoot":""}
\ No newline at end of file
<!DOCTYPE html><html><head><base href="/"><title>麦乐 Erp 系统</title><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"><link rel="shorticon icon" type="image/x-icon" href="/favicon.ico"></head><body><div id="root"></div><script type="text/javascript" src="/dist/vendor.min.js?0cf9658bc86f37e61b6c"></script><script type="text/javascript" src="/dist/bundle.min.js?0cf9658bc86f37e61b6c"></script></body></html>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>erp</artifactId>
<groupId>com.maile</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>erp-core</artifactId>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-actuator</artifactId>-->
<!--<version>2.0.3.RELEASE</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.apache.directory.studio</groupId>
<artifactId>org.apache.commons.lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<!--oss依赖-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package com.maile.erp.core.entities;
import lombok.Data;
import javax.persistence.*;
@Data
@Entity
public class AdminRole {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "int(11)")
private long id;
@Column(columnDefinition = "int(11)")
private long adminUserId;
@Column(columnDefinition = "int(11)")
private long roleId;
}
package com.maile.erp.core.entities;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
import java.util.List;
/**
* Created by IntelliJ IDEA.
* User: @Faith
* Date: 2018/7/24
* Time: 下午2:16
*/
@Entity
@Data
@DynamicInsert
@DynamicUpdate
@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"})
@Table(indexes = {
@Index(columnList = "username")
})
public class AdminUser extends BaseEntity {
@Column(name = "`username`", columnDefinition = "varchar(64) not null default '' comment '用户名'")
private String username;
@JsonIgnore
@Column(name = "`password`", columnDefinition = "varchar(32) not null default '' comment '密码'")
private String password;
@Column(name = "`realname`", columnDefinition = "varchar(32) not null default '' comment '真实姓名'")
private String realname;
@Column(columnDefinition = "tinyint(11) not null default 0 comment '状态'")
private int status;
@Transient
private String tmpPwd = "******";
@Transient
private List<Long> roleId;
}
package com.maile.erp.core.entities;
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
@Data
@MappedSuperclass
class BaseEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "int(11)")
private long id;
@Column(columnDefinition = "timestamp default CURRENT_TIMESTAMP comment '创建时间'", insertable = false, updatable = false)
private String createTime;
}
package com.maile.erp.core.entities;
import lombok.Data;
import javax.persistence.*;
@Data
@Entity
public class IdSequence {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "int(11)")
private long id;
}
package com.maile.erp.core.entities;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
@Entity
@Data
@DynamicInsert
@DynamicUpdate
@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"})
@NamedNativeQueries({
@NamedNativeQuery(
name = "Permission.findByAdminUserId",
query = "select p.* " +
"from admin_user u " +
"LEFT JOIN admin_role ar on u.id = ar.admin_user_id " +
"LEFT JOIN role r on ar.role_id = r.id " +
"LEFT JOIN role_permission rp on rp.role_id=r.id " +
"LEFT JOIN permission p on p.id =rp.permission_id " +
"where u.id= ?1",
resultSetMapping = "userPermission"
)
})
@SqlResultSetMappings({
@SqlResultSetMapping(
name = "userPermission",
entities = @EntityResult(
entityClass = Permission.class
)
)
})
public class Permission {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "int(11)")
private long id;
@Column(columnDefinition = "int(11) not null default 0 comment '父节点ID'")
private long pid;
@Column(columnDefinition = "varchar(50) not null default '' comment '权限标识'")
private String identity;
@Column(columnDefinition = "varchar(50) not null default '' comment '权限名称'")
private String name;
@Column(columnDefinition = "varchar(50) not null default '' comment '权限别名,配置时显示'")
private String alias;
@Column(columnDefinition = "varchar(255) not null default '' comment '描述'")
private String description;
@Column(columnDefinition = "varchar(255) not null default '' comment '路径'")
private String path;
}
package com.maile.erp.core.entities;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Data
@DynamicInsert
@DynamicUpdate
@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"})
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "int(11)")
private long id;
@Column(columnDefinition = "varchar(50) not null default '' comment '角色名称'")
private String name;
@Transient
List<Long> perms = new ArrayList<>();
}
package com.maile.erp.core.entities;
import lombok.Data;
import javax.persistence.*;
@Entity
@Data
public class RolePermission {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "int(11)")
private long id;
@Column(columnDefinition = "int(11)")
private long roleId;
@Column(columnDefinition = "int(11)")
private long permissionId;
}
package com.maile.erp.core.libs;
import lombok.Data;
@Data
public class AppException extends Exception {
int code = 0;
public AppException(int code) {
this(code, "");
}
public AppException(int code, String message) {
super(message);
this.code = code;
}
public AppException(JSONResult result) {
this(result.getCode(), result.getMessage());
}
}
package com.maile.erp.core.libs;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* 类扫描器
*/
public class ClassScanner {
/**
* 从包package中获取所有的Class
*
* @param packageName
* @param recursive
* @return
* @throws Exception
*/
public Set<Class<?>> getClasses(String packageName, boolean recursive) {
// 第一个class类的集合
//List<Class<?>> classes = new ArrayList<Class<?>>();
Set<Class<?>> classes = new HashSet<>();
// 获取包的名字 并进行替换
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中,以下俩种方法都可以
//网上的第一种方法,
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
//网上的第二种方法
//addClass(classes,filePath,packageName);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();
// 同样的进行循环迭代
while (entries.hasMoreElements()) {
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/开头的
if (name.charAt(0) == '/') {
// 获取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"结尾 是一个包
if (idx != -1) {
// 获取包名 把"/"替换成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
// 如果是一个.class文件 而且不是目录
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉后面的".class" 获取真正的类名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
/**
* 以文件的形式来获取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
public static void findAndAddClassesInPackageByFile(String packageName,
String packagePath, final boolean recursive, Set<Class<?>> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
// log.warn("用户定义包名 " + packageName + " 下没有任何文件");
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
public boolean accept(File file) {
return (recursive && file.isDirectory())
|| (file.getName().endsWith(".class"));
}
});
// 循环所有文件
if (dirfiles != null) {
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "."
+ file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0,
file.getName().length() - 6);
try {
// 添加到集合中去
//classes.add(Class.forName(packageName + '.' + className));
//经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
} catch (ClassNotFoundException e) {
// log.error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
}
public void addClass(Set<Class<?>> classes, String filePath, String packageName) throws Exception {
File[] files = new File(filePath).listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
}
});
if (files != null) {
for (File file : files) {
String fileName = file.getName();
if (file.isFile()) {
String classsName = fileName.substring(0, fileName.lastIndexOf("."));
if (!StringUtils.isBlank(packageName)) {
classsName = packageName + "." + classsName;
}
doAddClass(classes, classsName);
}
}
}
}
public void doAddClass(Set<Class<?>> classes, final String classsName) throws Exception {
ClassLoader classLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return super.loadClass(name);
}
};
classes.add(classLoader.loadClass(classsName));
}
}
package com.maile.erp.core.libs;
public class Constants {
// 常量定义
public static final int COMMON_STATUS_NORMAL = 0;
}
package com.maile.erp.core.libs;
public class ErrorCode {
public final static int NO_DATA = 10000; //找不到数据
public final static int INVALID_PARAM = 10001; //参数错误
public final static int DUPLICATE_KEY = 10002; //数据重复
public final static int MISTAKE_PASSWORD = 10003; //缺少密码
public final static int STATUS_ERROR = 10004; //状态错误
public final static int SMS_CODE_ERROR = 10005; //短信验证码错误
public final static int PASSWORD_ERROR = 10006; //密码错误
public final static int NO_ACCESS = 10007; //没有权限
public final static int SMS_LIMIT = 10008; //短信发送频繁
public final static int SIGN_ERROR = 10009; //签名错误
public final static int LOW_STOCKS = 10010; //库存不足
public final static int API_FAILED = 10011; //库存不足
public final static int NOT_LOGIN = 10012; //尚未登录
public final static int BAD_CREDENTIALS = 10013; //账号/密码错误
public final static int USER_DISABLED = 10014; //账号/密码错误
public final static int NO_STOCK = 10015; //库存不足
public final static int FORBIDDEN = 10403; //账号/密码错误
public final static int SYSTEM_SETTING = 99997; //系统配置错误
public final static int SYSTEM_BUSY = 99998; //系统繁忙
public final static int UNKNOWN = 99999; //找不到数据
}
package com.maile.erp.core.libs;
import java.io.IOException;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import org.apache.commons.lang.StringUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class HuyiEncrypt {
public String des3EncodeCBC(String getKey, String getData) throws Exception {
byte[] data = getData.getBytes("UTF-8");
byte[] key = this.append(getKey, getKey.length(), 24).getBytes("UTF-8");
byte[] keyiv = this.substring(getKey, 0, 8).getBytes("UTF-8");
Key deskey = null;
DESedeKeySpec spec = new DESedeKeySpec(key);
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede" + "/CBC/PKCS5Padding");
IvParameterSpec ips = new IvParameterSpec(keyiv);
cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
byte[] bOut = cipher.doFinal(data);
return new BASE64Encoder().encode(bOut);
}
public String des3DecodeCBC(String getKey, String getData) throws Exception {
byte[] data = new BASE64Decoder().decodeBuffer(getData);
byte[] key = this.append(getKey, getKey.length(), 24).getBytes("UTF-8");
byte[] keyiv = this.substring(getKey, 0, 8).getBytes("UTF-8");
Key deskey = null;
DESedeKeySpec spec = new DESedeKeySpec(key);
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede" + "/CBC/PKCS5Padding");
IvParameterSpec ips = new IvParameterSpec(keyiv);
cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
byte[] bOut = cipher.doFinal(data);
return new String(bOut, "UTF-8");
}
private String substring(String str, int start, int end) throws IOException {
int len = 0;
if (StringUtils.isNotBlank(str)) {
len = str.length();
}
if (len < end) {// ���Ȳ��������� 0
str = append(str, len, end).substring(start, end);
} else {
str = str.substring(start, end);
}
return str;
}
private String append(String str, int start, int end) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append(str);
for (int i = start; i < end; i++) {
sb.append("0");
}
return sb.toString();
}
}
package com.maile.erp.core.libs;
import lombok.Data;
import org.springframework.data.domain.Page;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import java.util.HashMap;
import java.util.Map;
@Data
public class JSONResult {
static private String DEFAULT_MESSAGE_SUCCESS = "success";
static private String DEFAULT_MESSAGE_FAILED = "failed";
private int code = 0;
private Map<String, Object> data = new HashMap<>();
private String message = DEFAULT_MESSAGE_SUCCESS;
public JSONResult() {
}
public JSONResult(int code) {
if (code != 0) {
this.message = DEFAULT_MESSAGE_FAILED;
}
this.code = code;
}
// public JSONResult(Map data) {
// this(0, DEFAULT_MESSAGE_SUCCESS, data);
// }
public JSONResult(int code, String message) {
this.code = code;
this.message = message;
}
public JSONResult(int code, String message, Map<String, Object> data) {
this.code = code;
this.message = message;
this.data = data;
}
public JSONResult(AppException e) {
this.code = e.getCode();
this.message = e.getMessage();
}
public <T> JSONResult(Page<T> page, int currPage) {
this.put("list", page.getContent())
.put("total", page.getTotalElements())
.put("totalPages", page.getTotalPages())
.put("page", currPage);
}
public JSONResult(BindingResult result) {
Map<String, Object> errorMap = new HashMap<>();
for (FieldError err : result.getFieldErrors()) {
errorMap.put(err.getField(), err.getDefaultMessage());
}
this.code = ErrorCode.INVALID_PARAM;
this.message = "invalid params.";
this.data = errorMap;
}
public Boolean getSuccess() {
return code == 0;
}
public JSONResult put(String key, Object value) {
data.put(key, value);
return this;
}
public Object get(String key) {
return data.get(key);
}
}
package com.maile.erp.core.libs;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import org.apache.commons.codec.binary.Base64;
public class JuheEncrypt {
public static void main(String[] args) {
//加密后的内容
String s = "QPBNbRNDohmlvyzLSgCy1iwo+juaDACo";
//密码,用户名前8位,不足补0
String password = "testtest";
//打印解密后的结果
System.out.println(decrypt(s, password));
}
/**
* 解密
*
* @param srcMsg 密文
* @param password 密码
* @return 解密后的明文
*/
public static String decrypt(String srcMsg, String password) {
byte[] bb = Base64.decodeBase64(srcMsg.getBytes());
try {
// DES算法要求有一个可信任的随机数源
SecureRandom random = new SecureRandom();
// 创建一个DESKeySpec对象
DESKeySpec desKey = new DESKeySpec(password.getBytes());
// 创建一个密匙工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
// 将DESKeySpec对象转换成SecretKey对象
SecretKey securekey = keyFactory.generateSecret(desKey);
// Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance("DES");
// 用密匙初始化Cipher对象
cipher.init(Cipher.DECRYPT_MODE, securekey, random);
// 真正开始解密操作
byte[] decryResult = cipher.doFinal(bb);
return new String(decryResult);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 加密
*
* @param srcMsg 待加密字符串
* @param password 密码
* @return 加密后的密文
*/
public static String encrypt(String srcMsg, String password) {
try {
SecureRandom random = new SecureRandom();
DESKeySpec desKey = new DESKeySpec(password.getBytes());
// 创建一个密匙工厂,然后用它把DESKeySpec转换成
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey securekey = keyFactory.generateSecret(desKey);
// Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance("DES");
// 用密匙初始化Cipher对象
cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
// 现在,获取数据并加密
// 正式执行加密操作
byte[] result = cipher.doFinal(srcMsg.getBytes());
//Base64编码
byte[] resultBase = Base64.encodeBase64(result, true);
return new String(resultBase);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
}
package com.maile.erp.core.libs;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RenderUtils {
public static void renderJson(HttpServletResponse response, Object object) throws IOException {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(new ObjectMapper().writeValueAsString(object));
response.getWriter().close();
}
}
package com.maile.erp.core.libs;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.List;
public interface SearchBeforeSpecFilter<T> {
void process(List<Predicate> predicates, Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}
package com.maile.erp.core.libs;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.List;
public interface SearchSpecFilter<T> {
Predicate process(List<Predicate> predicates, String field, String operator, String value, Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}
package com.maile.erp.core.libs;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;
import javax.persistence.Query;
import javax.persistence.criteria.*;
import javax.servlet.http.HttpServletRequest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SearchSpecification<T> implements Specification<T> {
private final String SEARCH_PREFIX = "search_";
private HttpServletRequest request;
private Map<String, SearchSpecFilter> filters = new HashMap<>();
private SearchBeforeSpecFilter beforeSpecFilter = null;
public SearchSpecification(HttpServletRequest request) {
this.request = request;
}
public void setBeforeFilter(SearchBeforeSpecFilter filter) {
this.beforeSpecFilter = filter;
}
public void setFilter(String filed, SearchSpecFilter filter) {
filters.put(filed, filter);
}
@Nullable
@Override
@SuppressWarnings("unchecked")
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<>();
Map<String, String[]> params = request.getParameterMap();
if (beforeSpecFilter != null) {
beforeSpecFilter.process(predicates, root, criteriaQuery, cb);
}
for (String name : params.keySet()) {
if (StringUtils.isBlank(params.get(name)[0]) || name.indexOf(SEARCH_PREFIX) != 0) {
continue;
}
String[] parts = name.split("_");
if (parts.length < 2) {
continue;
}
String value = StringUtils.trim(params.get(name)[0]);
String op;
String filed;
if (parts.length == 2) {
op = "EQ";
filed = StringUtils.trim(parts[1]);
} else {
op = StringUtils.trim(parts[1]);
filed = StringUtils.trim(parts[2]);
}
Object v = value;
if (parts.length >= 4) {
v = transformValue(value, parts[3]);
if (v == null) {
continue;
}
// switch (parts[3].toLowerCase()){
// case "begin":
// v1 = v;
// break;
// case "end":
// v2 = v;
// break;
// }
}
Predicate predicate = null;
if (filters.containsKey(name)) {
predicate = filters.get(name).process(predicates, filed, op, value, root, criteriaQuery, cb);
} else {
try {
Path path = root.get(filed);
switch (op.toUpperCase()) {
case "EQ":
predicate = cb.equal(path, v);
break;
case "LIKE":
predicate = cb.like(path, "%" + v + "%");
break;
case "GT":
predicate = cb.greaterThan(path, (Comparable) v);
break;
case "LT":
predicate = cb.lessThan(path, (Comparable) v);
break;
case "GTE":
predicate = cb.greaterThanOrEqualTo(path, (Comparable) v);
break;
case "LTE":
predicate = cb.lessThanOrEqualTo(path, (Comparable) v);
break;
// case "BW":
// if(v1 != null && v2 != null){
// predicate = cb.between(path, (Comparable) v1,(Comparable) v2);
// }
// break;
}
} catch (Exception e) {
continue;
}
}
if (predicate != null) {
predicates.add(predicate);
}
}
if (predicates.size() > 0) {
return cb.and((Predicate[]) predicates.toArray(new Predicate[predicates.size()]));
}
return null;
}
private Object transformValue(String value, String type) {
try {
switch (type.toLowerCase()) {
case "date":
case "time":
String pattern;
if (value.length() >= 19) {
pattern = "yyyy-MM-dd HH:mm:ss";
} else {
pattern = "yyyy-MM-dd";
}
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
return sdf.parse(value);
}
} catch (ParseException e) {
}
return null;
}
}
package com.maile.erp.core.pojos;
import com.maile.erp.core.utils.BeanUtils;
public class BasePojo {
public BasePojo() {
}
public BasePojo(Object source) {
BeanUtils.copyProperties(source, this);
}
}
package com.maile.erp.core.pojos;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GoodsTagsPojo {
private long goodsId;
private long tagsId;
}
package com.maile.erp.core.repositories;
import com.maile.erp.core.entities.AdminRole;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import java.util.Collection;
import java.util.List;
/**
* Created by IntelliJ IDEA.
* User: @Faith
* Date: 2018/8/9
* Time: 下午6:18
*/
public interface AdminRoleRepository extends JpaRepository<AdminRole, Long> {
List<AdminRole> findByAdminUserIdIn(Collection<Long> userIds);
@Modifying
@Query("delete from AdminRole a where a.adminUserId = ?1")
Integer deleteByAdminUserId(Long userId);
}
package com.maile.erp.core.repositories;
import com.maile.erp.core.entities.AdminUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/**
* Created by IntelliJ IDEA.
* User: @Faith
* Date: 2018/7/24
* Time: 下午2:29
*/
public interface AdminUserRepository extends JpaRepository<AdminUser, Long>, JpaSpecificationExecutor<AdminUser> {
AdminUser findAdminUserByUsername(String username);
}
package com.maile.erp.core.repositories;
import com.maile.erp.core.entities.IdSequence;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import javax.transaction.Transactional;
public interface IdSequenceRepository extends JpaRepository<IdSequence, Long> {
@Query(value = "TRUNCATE TABLE id_sequence", nativeQuery = true)
@Modifying
@Transactional
void resetId();
}
package com.maile.erp.core.repositories;
import com.maile.erp.core.entities.Permission;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface PermissionRepository extends JpaRepository<Permission, Long>, JpaSpecificationExecutor<Permission> {
@Query(nativeQuery = true)
List<Permission> findByAdminUserId(long adminUserId);
}
package com.maile.erp.core.repositories;
import com.maile.erp.core.entities.RolePermission;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import java.util.Collection;
import java.util.List;
public interface RolePermissionRepository extends JpaRepository<RolePermission, Long> {
List<RolePermission> findByRoleIdIn(Collection<Long> rolesId);
List<RolePermission> findByRoleId(Long roleId);
@Modifying
@Query("delete from RolePermission rp where rp.roleId = ?1")
Integer deleteAllByRoleId(Long roleId);
}
package com.maile.erp.core.repositories;
import com.maile.erp.core.entities.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface RoleRepository extends JpaRepository<Role, Long>, JpaSpecificationExecutor<Role> {
Role findByName(String name);
}
package com.maile.erp.core.utils;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import org.springframework.beans.BeansException;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.util.Assert;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
/**
* Created by bls on 2016/6/16.
*/
public class BeanUtils extends org.springframework.beans.BeanUtils {
public static void copyProperties(Object source, Object target) throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class<?> actualEditable = target.getClass();
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
for (PropertyDescriptor targetPd : targetPds) {
if (targetPd.getWriteMethod() != null) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null && sourcePd.getReadMethod() != null) {
try {
Method readMethod = sourcePd.getReadMethod();
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
// 这里判断以下value是否为空 当然这里也能进行一些特殊要求的处理 例如绑定时格式转换等等
if (value != null) {
Method writeMethod = targetPd.getWriteMethod();
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
}
} catch (Throwable ex) {
// throw new FatalBeanException("Could not copy properties from source to target", ex);
}
}
}
//
}
}
/**
* 将Bean转成json
*/
public static String BeanToJson(Object bdPushID){
Gson gson = new Gson();
String jsonBDID = gson.toJson(bdPushID);
return jsonBDID;
}
}
\ No newline at end of file
package com.maile.erp.core.utils;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by Nekoing on 2016/6/27.
*/
@Slf4j
public class HttpUtils {
final static public String CHARSET = "UTF-8";
final static private int httpTimeOut = 30000;
static public Object get(String url) throws IOException {
return get(url, null, null, true);
}
static public <T> Object get(String url, Map<String, T> params) throws IOException {
return get(url, params, null, true);
}
static public <T> Object get(String url, Map<String, T> params, Map<String, String> headers, boolean json) throws IOException {
if (params != null && params.size() > 0) {
url = urlCatParams(url, HttpUtils.buildQuery(params, CHARSET));
}
log.debug("http request: " + url);
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
return doQuery(httpClient, httpGet, headers, json);
}
static public <T> Object post(String url, String body, boolean json) throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
HttpEntity entity = new StringEntity(body);
httpPost.setEntity(entity);
return doQuery(httpClient, httpPost, json);
}
static public <T> Object post(String url, Map<String, T> params) throws IOException {
return post(url, params, true);
}
static public <T> Object post(String url, Map<String, T> params, boolean json) throws IOException {
log.debug("http request: " + url + "; " + HttpUtils.buildQuery(params, CHARSET));
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
if (params != null && params.size() > 0) {
List<NameValuePair> paramsList = new ArrayList<NameValuePair>();
for (String key : params.keySet()) {
paramsList.add(new BasicNameValuePair(key, String.valueOf(params.get(key))));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramsList, CHARSET);
httpPost.setEntity(entity);
}
return doQuery(httpClient, httpPost, json);
}
static public Object postXml(String url, String xml, boolean json) throws Exception {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setSocketTimeout(httpTimeOut)
.setConnectTimeout(httpTimeOut)
.setConnectionRequestTimeout(httpTimeOut)
.build();
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
return true;
}
}).build();
CloseableHttpResponse response = null;
String result = null;
CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).
setSSLHostnameVerifier(new NoopHostnameVerifier()).setDefaultRequestConfig(defaultRequestConfig).build();
HttpPost httpPost = new HttpPost(url);
StringEntity entity = new StringEntity(xml);
httpPost.setEntity(entity);
httpPost.setHeader("Content-Type", "text/xml;charset=ISO-8859-1");
response = httpClient.execute(httpPost);
if (response.getStatusLine().getStatusCode() != 200) {
throw new IOException("HttpUtils status error, code: " + response.getStatusLine().getStatusCode());
}
HttpEntity ResponseEntity = response.getEntity();
result = EntityUtils.toString(ResponseEntity, CHARSET);
httpClient.close();
return result;
}
static public String urlCatParams(String url, String paramsStr) {
if (url.indexOf('?') > 0) {
return url + "&" + paramsStr;
} else {
return url + "?" + paramsStr;
}
}
static protected Object doQuery(CloseableHttpClient httpClient, HttpUriRequest request, boolean json) throws IOException {
return doQuery(httpClient, request, null, json);
}
static protected Object doQuery(CloseableHttpClient httpClient, HttpUriRequest request, Map<String, String> headers, boolean json) throws IOException {
CloseableHttpResponse response = null;
String result = null;
try {
if (headers != null) {
for (Map.Entry<String, String> header : headers.entrySet()) {
request.setHeader(header.getKey(), header.getValue());
}
}
// request.setHeader("Content-Type", "text/html;charset=" + CHARSET);
response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() != 200) {
throw new IOException("HttpUtils status error, code: " + response.getStatusLine().getStatusCode());
}
HttpEntity entity = response.getEntity();
result = EntityUtils.toString(entity, CHARSET);
log.debug("HTTP Response:" + result);
if (json) {
return new Gson().fromJson(result, new TypeToken<HashMap<String, Object>>() {
}.getType());
} else {
return result;
}
} catch (IOException e) {
throw e;
} finally {
try {
if (response != null) {
response.close();
}
} catch (IOException ignore) {
}
}
}
static public <T> String buildQuery(Map<String, T> params) {
return buildQuery(params, "UTF-8", true);
}
static public <T> String buildQuery(Map<String, T> params, boolean encode) {
return buildQuery(params, "UTF-8", encode);
}
static public <T> String buildQuery(Map<String, T> params, String charset) {
return buildQuery(params, charset, true);
}
static public <T> String buildQuery(Map<String, T> params, String charset, boolean encode) {
String result = "";
if (params == null || params.size() == 0) {
return result;
}
for (String key : params.keySet()) {
try {
if (encode) {
result += URLEncoder.encode(key, charset) + "=" + URLEncoder.encode(String.valueOf(params.get(key)), charset) + "&";
} else {
result += key + "=" + String.valueOf(params.get(key)) + "&";
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
result = result.substring(0, result.length() - 1);
return result;
}
}
package com.maile.erp.core.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class LoggerUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
private LoggerUtils() {}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(LoggerUtils.applicationContext == null) {
LoggerUtils.applicationContext = applicationContext;
}
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
/**
* 获取客户端ip地址
* @param request
* @return
*/
public static String getCliectIp(HttpServletRequest request)
{
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.trim() == "" || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.trim() == "" || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.trim() == "" || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 多个路由时,取第一个非unknown的ip
final String[] arr = ip.split(",");
for (final String str : arr) {
if (!"unknown".equalsIgnoreCase(str)) {
ip = str;
break;
}
}
return ip;
}
}
package com.maile.erp.core.utils;
public class MethodType {
public final static String INSERT="新增";
public final static String UPDATE="修改";
public final static String DELETE="删除";
}
package com.maile.erp.core.utils;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URL;
import java.util.Date;
import java.util.Random;
/**
* OSS工具类
*/
public class OSSClientUtils {
// 本地异常日志
public static final Logger logger = LoggerFactory.getLogger(OSSClientUtils.class);
// 存储空间
private String bucketName;
// 文件存储目录
private String fileDir;
private OSSClient ossClient;
public OSSClientUtils(String endpoint,String accessKeyId,String accessKeySecret,String bucketName,String fileDir) {
ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
this.bucketName = bucketName;
this.fileDir = fileDir;
}
/**
* 销毁
*/
public void destory() {
ossClient.shutdown();
}
/**
* 上传图片
*
* @param url 图片上传路径
*/
public void uploadImg2OSS(String url) {
File file = new File(url);
FileInputStream fileInputStream;
try {
fileInputStream = new FileInputStream(file);
String[] split = url.split("/");
this.uploadFile2OSS(fileInputStream, split[split.length - 1]);
} catch (FileNotFoundException e) {
logger.error("图片上传失败:" + e.getMessage());
}
}
public String uploadImg2Oss(MultipartFile file) {
String originalFilename = file.getOriginalFilename();
String substring = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase();
Random random = new Random();
String name = random.nextInt(10000) + System.currentTimeMillis() + substring;
try {
InputStream inputStream = file.getInputStream();
this.uploadFile2OSS(inputStream, name);
} catch (Exception e) {
logger.error("图片上传失败:" + e.getMessage());
}
return name;
}
/**
* 上传到OSS服务器,如果同名文件或覆盖
*
* @param inputStream 文件流
* @param filename 文件名称,包括后缀
* @return 出错返回 "" ,正确返回MD5数字签名
*/
private String uploadFile2OSS(InputStream inputStream, String filename) {
String ret = "";
try {
// 创建上传的Object的Metadata
ObjectMetadata objectMetadata = new ObjectMetadata();
// 上传的文件的长度
objectMetadata.setContentLength(inputStream.available());
// 指定该Object被下载时的网页的缓存行为
objectMetadata.setCacheControl("not-cache");
// 指定该Object下设置Header
objectMetadata.setHeader("Pragma", "no-cache");
//指定该Object被下载时的内容编码格式
objectMetadata.setContentEncoding("utf-8");
//文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成,
//如果没有扩展名则填默认值application/octet-stream
objectMetadata.setContentType(getContentType(filename.substring(filename.lastIndexOf("."))));
//指定该Object被下载时的名称(指示MINME用户代理如何显示附加的文件,打开或下载,及文件名称)
objectMetadata.setContentDisposition("inline;filename=" + filename);
// 上传文件
PutObjectResult putObjectRequest = ossClient.putObject(bucketName, fileDir + filename, inputStream, objectMetadata);
// 解析结果
ret = putObjectRequest.getETag();
} catch (IOException e) {
logger.error("IO异常:" + e.getMessage());
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return ret;
}
/**
* 判断OSS服务文件上传时文件的contentType
*
* @param filenameExtension 文件后缀
* @return String
*/
private static String getContentType(String filenameExtension) {
if (filenameExtension.equalsIgnoreCase("bmp")) {
return "image/bmp";
}
if (filenameExtension.equalsIgnoreCase("gif")) {
return "image/gif";
}
if (filenameExtension.equalsIgnoreCase("jpeg") || filenameExtension.equalsIgnoreCase("jpg")
|| filenameExtension.equalsIgnoreCase("png")) {
return "image/jpeg";
}
if (filenameExtension.equalsIgnoreCase("html")) {
return "text/html";
}
if (filenameExtension.equalsIgnoreCase("txt")) {
return "text/plain";
}
if (filenameExtension.equalsIgnoreCase("vsd")) {
return "application/vnd.visio";
}
if (filenameExtension.equalsIgnoreCase("pptx") || filenameExtension.equalsIgnoreCase("ppt")) {
return "application/vnd.ms-powerpoint";
}
if (filenameExtension.equalsIgnoreCase("docx") || filenameExtension.equalsIgnoreCase("doc")) {
return "application/msword";
}
if (filenameExtension.equalsIgnoreCase("xml")) {
return "text/xml";
}
return "image/jpeg";
}
/**
* 获得图片路径
*
* @param fileUrl
* @return
*/
public String getImgUrl(String fileUrl) {
if (!StringUtils.isEmpty(fileUrl)) {
String[] split = fileUrl.split("/");
return this.getUrl(this.fileDir + split[split.length - 1]);
}
return null;
}
/**
* 获得url链接
*
* @param key
* @return
*/
private String getUrl(String key) {
// 设置URL过期时间为10年 3600l* 1000*24*365*10
Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 10);
// 生成URL
URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);
if (url != null) {
return url.toString().split("\\?")[0];
}
return null;
}
}
package com.maile.erp.core.utils;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import org.springframework.cglib.beans.BeanMap;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class ObjectMapUtils {
/**
* 将对象装换为map
*
* @param bean
* @return
*/
public static <T> Map<String, Object> beanToMap(T bean) {
Map<String, Object> map = Maps.newHashMap();
if (bean != null) {
BeanMap beanMap = BeanMap.create(bean);
for (Object key : beanMap.keySet()) {
map.put(key + "", beanMap.get(key));
}
}
return map;
}
/**
* 将map装换为javabean对象
*
* @param map
* @param bean
* @return
*/
public static <T> T mapToBean(Map<String, Object> map, T bean) {
BeanMap beanMap = BeanMap.create(bean);
beanMap.putAll(map);
return bean;
}
/**
* 将List<T>转换为List<Map<String, Object>>
*
* @param objList
* @return
* @throws JsonGenerationException
* @throws JsonMappingException
* @throws IOException
*/
public static <T> List<Map<String, Object>> objectsToMaps(List<T> objList) {
List<Map<String, Object>> list = Lists.newArrayList();
if (objList != null && objList.size() > 0) {
Map<String, Object> map = null;
T bean = null;
for (int i = 0, size = objList.size(); i < size; i++) {
bean = objList.get(i);
map = beanToMap(bean);
list.add(map);
}
}
return list;
}
/**
* 将List<Map<String,Object>>转换为List<T>
*
* @param maps
* @param clazz
* @return
* @throws InstantiationException
* @throws IllegalAccessException
*/
public static <T> List<T> mapsToObjects(List<Map<String, Object>> maps, Class<T> clazz) throws InstantiationException, IllegalAccessException {
List<T> list = Lists.newArrayList();
if (maps != null && maps.size() > 0) {
Map<String, Object> map = null;
T bean = null;
for (int i = 0, size = maps.size(); i < size; i++) {
map = maps.get(i);
bean = clazz.newInstance();
mapToBean(map, bean);
list.add(bean);
}
}
return list;
}
/**
*将map转成json字符串
*/
public static <T> String mapToJson(Map<String, T> map) {
Gson gson = new Gson();
String jsonStr = gson.toJson(map);
return jsonStr;
}
}
package com.maile.erp.core.utils;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
public class RequestUtils {
@SuppressWarnings("unchecked")
public static <T> Map<String, T> getParams(HttpServletRequest request) {
Map<String, T> result = new HashMap<>();
Map<String, String[]> ps = request.getParameterMap();
for (String k : ps.keySet()) {
String[] v = ps.get(k);
if (v == null) {
result.put(k, null);
} else {
result.put(k, (T) StringUtils.trim(v[0]));
}
}
return result;
}
}
package com.maile.erp.core.utils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.logging.log4j.util.Strings;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class SignatureUtils {
public static String getSign(Map<String, ?> params, String secret, String secretName) {
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
String signStr = "";
for (String key : keys) {
String value = String.valueOf(params.get(key));
if (Strings.isNotBlank(value)) {
signStr += key + "=" + String.valueOf(params.get(key)) + "&";
}
}
signStr += secretName + "=" + secret;
return DigestUtils.md5Hex(signStr).toLowerCase();
}
}
package com.maile.erp.core.utils;
import com.maile.erp.core.entities.IdSequence;
import com.maile.erp.core.repositories.IdSequenceRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Calendar;
@Service
public class SnUtils {
@Autowired
IdSequenceRepository idSequenceRepository;
private final long MAX_ID = 99900;
/**
* 生成订单号
*
* @param code 业务代码
* @return 订单号
*/
public String buildSn(BusinessCode code) {
return buildSn(code, 12);
}
public String buildSn(BusinessCode code, int length) {
Calendar calendar = Calendar.getInstance();
String orderSn = String.format("%d%02d%02d%02d",
code.value, //长度1
calendar.get(Calendar.YEAR), //长度2
calendar.get(Calendar.MONTH), //长度2
calendar.get(Calendar.DAY_OF_MONTH) //长度2
);
int sLen = length - orderSn.length();
orderSn += String.format("%0" + sLen + "d", getLatestId());
return orderSn;
}
private long getLatestId() {
IdSequence idSequence = new IdSequence();
idSequenceRepository.save(idSequence);
if (idSequence.getId() >= MAX_ID) {
idSequenceRepository.resetId();
}
//每涨到1000个就清空
if (idSequence.getId() % 1000 == 0) {
idSequenceRepository.deleteAll();
}
return idSequence.getId();
}
public enum BusinessCode {
SHOPPING_ORDER(1),
AFTER_SALE_ORDER(2);
int value;
BusinessCode(int index) {
this.value = index;
}
}
}
自定义原生查询结果绑定到指定类上
--
1. 创建接收结果的类(Pojo),设置一个无惨构造函数和一个全参构造函数
2. 在实体类上添加@NamedNativeQuery和@SqlResultSetMapping注解,NamedNativeQuery的名字为“实体类名.Repository方法名”,设置resultSetMapping为体SqlResultSetMapping的name,具体查看案例
3. 在实体类对应的Repository类上添加对应的方法(NamedNativeQuery的名字,不含实体类名),添加@Query注解,设置nativeQuery值为true
4. 返回类型为Pojo类或者Pojo类的列表
案例:
```java
//Pojo类
@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"})
@Data
public class AccessAppPojo {
private String name;
@JsonIgnore
private String desc;
public AccessAppPojo() {
}
public AccessAppPojo(String name, String desc) {
this.name = name;
this.desc = desc;
}
}
```
```java
//实体类
@Entity
@Data
@DynamicInsert
@DynamicUpdate
@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"})
@NamedNativeQuery(
name = Application,
query = "select name, `desc` from access_app as a where a.id = 1 limit 1",
resultSetMapping = "gg"
)
@SqlResultSetMapping(
name = "gg",
classes = @ConstructorResult(
targetClass = AccessAppPojo.class,
columns = {
@ColumnResult(name = "name"),
@ColumnResult(name = "desc")
}
)
)
public class AccessApp extends BaseEntity {
@Column(columnDefinition = "varchar(32) not null default '' comment '应用名称'")
private String name;
@Column(name = "`desc`", columnDefinition = "varchar(255) not null default '' comment '应用描述'")
private String desc;
@Column(name = "`key`", columnDefinition = "varchar(32) not null default '' comment '应用key'")
private String key;
@Column(columnDefinition = "varchar(32) not null default '' comment '应用secret'")
private String secret;
@Column(columnDefinition = "tinyint(11) not null default 0 comment '状态'")
private int status;
}
```
```java
//Repository类
public interface AccessAppRepository extends JpaRepository<AccessApp, Long> {
@Query(nativeQuery = true)
AccessAppPojo test();
}
```
```java
//实际运用
@RestController
public class WelcomeController {
@Autowired
AccessAppRepository accessAppRepository;
@RequestMapping("/")
public JSONResult index() throws AppException {
AccessAppPojo app = accessAppRepository.test();
return new JSONResult().put("app", app);
}
}
```
\ No newline at end of file
#售后系统流程图
![img](afterSale.png)
\ No newline at end of file
#订单系统流程图
![img](order.png)
\ No newline at end of file
框架相关技术文档
--
本目录包含了一些框架上的技术文档
\ No newline at end of file
{
"lockfileVersion": 1
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.maile</groupId>
<artifactId>erp</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>admin-api</module>
<module>core</module>
</modules>
<name>maile-erp</name>
<description>Maile ERP System</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
{
"presets": [
"latest",
"stage-0",
"react"
]
}
\ No newline at end of file
{
// "parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 7, // ECMAScript版本,7为ES7
"sourceType": "module", //默认script,如果代码是ECMAScript模块,设置为module
"ecmaFeatures": { // 使用额外的语言特性
"jsx": true // 启用JSX
}
},
"extends": "airbnb",
"plugins": [
"react"
],
"rules": {
"indent": ["error", 4],
"comma-dangle": ["error", "never"]
}
}
\ No newline at end of file
### 开发环境部署
- nmp i # 安装依赖包
- npm run dev 启动项目
- 访问 http://localhost:8080
<!DOCTYPE html>
<html>
<head>
<base href='/' />
<!-- ejs语法 -->
<title><%= htmlWebpackPlugin.options.title %></title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<!-- 设置favicon -->
<% if (htmlWebpackPlugin.options.favIcon && htmlWebpackPlugin.options.favIcon.length > 0) { %>
<link rel="shorticon icon" type="image/x-icon" href="<%= htmlWebpackPlugin.options.favIcon %>">
<% } %>
</head>
<body>
<div id="root"></div>
</body>
<!-- 是否是开发环境? -->
<% if (htmlWebpackPlugin.options.devMode) { %>
<script src="http://localhost:8080/webpack-dev-server.js"></script>
<% } %>
</html>
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "react-admin",
"private": true,
"version": "1.0.0",
"description": "React Admin Boilerplate, with React + Ant Design",
"author": "",
"engines": {
"node": ">=5.0 <7",
"npm": ">=3.3 <4"
},
"keywords": [
"ant",
"react",
"admin",
"frontend"
],
"scripts": {
"build": "webpack --progress --colors",
"prod": "cross-env NODE_ENV=production webpack --progress --colors --config webpack.config.prod.js -p",
"clean": "rimraf dist/*bundle* dist/*vendor* dist/*index*",
"dev": "webpack-dev-server --devtool eval --progress --colors --content-base dist --hot --inline",
"eslint": "eslint --ext .js,.jsx src",
"stylelint": "stylelint \"src/**/*.css\"",
"lesshint": "lesshint src/"
},
"devDependencies": {
"antd": "^3.4.3",
"babel-core": "6.26.0",
"babel-eslint": "7.2.3",
"babel-loader": "7.1.2",
"babel-plugin-import": "1.1.0",
"babel-polyfill": "^6.20.0",
"babel-preset-env": "^1.7.0",
"babel-preset-latest": "^6.16.0",
"babel-preset-react": "^6.16.0",
"babel-preset-stage-0": "^6.16.0",
"compression-webpack-plugin": "0.3.2",
"create-react-class": "^15.6.3",
"cross-env": "3.1.4",
"css-loader": "0.28.7",
"eslint": "2.7.0",
"eslint-config-airbnb": "6.x",
"eslint-plugin-react": "4.x",
"extract-text-webpack-plugin": "3.0.2",
"file-loader": "0.9.0",
"html-webpack-plugin": "2.29.0",
"install": "^0.12.1",
"less": "^2.7.3",
"less-loader": "^4.1.0",
"lesshint": "2.0.2",
"prop-types": "^15.6.2",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-hot-loader": "^4.0.0",
"redux": "^3.7.2",
"redux-logger": "2.7.4",
"redux-thunk": "2.1.0",
"rimraf": "2.5.4",
"strip-loader": "0.1.2",
"style-loader": "0.19.0",
"stylelint": "6.6.0",
"stylelint-config-standard": "9.0.0",
"url-loader": "^0.5.9",
"webpack": "3.8.1",
"webpack-dev-server": "2.9.4"
},
"dependencies": {
"braft-editor": "^1.9.8",
"china-division": "^1.1.0",
"classnames": "^2.2.6",
"collect.js": "^4.0.22",
"react-redux": "^4.4.0",
"react-router": "^3.0.0",
"superagent": "2.3.0",
"webpack-dev-server": "^2.9.4"
}
}
/*定义整体layout的样式, 直接从antd官网copy的, 稍微修改了一些*/
#root {
height: 100%;
}
.ant-layout-base {
position: relative;
height: 100%;
width: 100%;
}
.ant-layout-main-base {
transition: all 0.3s ease;
clear: left;
}
.ant-layout-base .ant-layout-main {
// margin-left: 224px; // 正常侧边栏的宽度
.ant-layout-main-base;
}
.ant-layout-base .ant-layout-main-collapse {
// margin-left: 64px; // 侧边栏折叠时的宽度
.ant-layout-main-base;
}
.ant-layout-base .ant-layout-container {
// margin: 24px 16px;
// padding: 24px;
margin: 0;
padding: .2em;
background: #fff;
}
.ant-layout-base .ant-layout-tab { // tab模式时的样式
margin: 8px 8px;
background: #fff;
}
.ant-layout-base .ant-layout-tab-text {
font-size: 12px;
}
// 为了在屏幕中间显示一个loading
.center-div {
position: absolute; /*绝对定位*/
top: 50%; /* 距顶部50%*/
left: 50%; /* 距左边50%*/
height: 2px;
margin-top: -1px; /*margin-top为height一半的负值*/
width: 2px;
margin-left: -1px; /*margin-left为width一半的负值*/
}
.table-responsive div.ant-table{
overflow-x: auto;
}
@media screen and (max-width: 767px) {
.table-responsive {
width: 100%;
div.ant-table .ant-table-body > table {
> thead, > tbody, > tfoot {
> tr {
> th, > td {
white-space: nowrap;
}
}
}
}
}
}
@media screen and (min-width: 992px) {
.ant-layout-base {
.ant-layout-main, .ant-layout-main-collapse {
clear: none;
height: 100%;
overflow: auto;
}
}
.ant-layout-base .ant-layout-container {
margin: 24px 16px;
padding: 24px;
}
}
.row-thumb {
max-height: 27px;
max-width: 27px;
}
\ No newline at end of file
import React, {PureComponent} from 'react';
import BraftEditor from 'braft-editor';
import 'braft-editor/dist/braft.css';
import globalConfig from '../../config';
class BraftEditorComponent extends PureComponent {
state = {
initialContent: '',
contentId: ''
};
// 商品详情预览
handleEditorPreview = () => {
if (window.previewWindow) {
window.previewWindow.close();
}
window.previewWindow = window.open();
window.previewWindow.document.write(this.buildPreviewHtml());
};
componentWillReceiveProps(nextProps) {
if (this.props.contentId && !this.props.value) {
this.state.contentId = nextProps.contentId;
this.state.initialContent = nextProps.value;
}
}
// 当编辑器内容改变时触发此事件
handleEditorChange = (content) => {
const {onChange} = this.props;
this.state.initialContent = content;
if (onChange) {
onChange(content);
}
};
// 编辑器图上传
handleEditorUpload = (param) => {
const serverURL = globalConfig.getAPIPath() + this.props.uploadUrl;
const xhr = new XMLHttpRequest;
const fd = new FormData();
// 上传成功后调用并传入上传后的文件地址
const successFn = (response) => {
const result = JSON.parse(xhr.responseText);
if (result && result.code === 0) {
param.success({
url: result.data.url // 服务端返回文件上传后的地址
});
} else {
param.error({
msg: '图片上传失败,请明天再试.'
});
}
};
// 上传进度发生变化时调用
const progressFn = (event) => {
param.progress(event.loaded / event.total * 100);
};
// 上传发生错误时调用
const errorFn = (response) => {
param.error({
msg: '图片上传失败,请明天再试.'
});
};
xhr.upload.addEventListener("progress", progressFn, false);
xhr.addEventListener("load", successFn, false);
xhr.addEventListener("error", errorFn, false);
xhr.addEventListener("abort", errorFn, false);
fd.append('file', param.file);
xhr.open('POST', serverURL, true);
xhr.send(fd);
};
buildPreviewHtml = () => {
const htmlContent = this.state.initialContent;
return `
<!Doctype html>
<html>
<head>
<title>商品详情预览</title>
<style>
html,body{
height: 100%;
margin: 0;
padding: 0;
background-color: #f1f2f3;
}
.container{
box-sizing: border-box;
width: 1000px;
max-width: 100%;
min-height: 100%;
margin: 20px auto;
padding: 30px 20px;
background-color: #fff;
border-right: solid 1px #eee;
border-left: solid 1px #eee;
border-radius: 10px;
}
.container img,
.container audio,
.container video{
max-width: 100%;
height: auto;
}
</style>
</head>
<body>
<div class="container">${htmlContent}</div>
</body>
</html>
`;
};
editorProps = () => {
return {
height: 500,
contentFormat: 'html',
initialContent: this.state.initialContent,
contentId: this.state.contentId,
fontSizes: [
12, 14, 16, 18, 20, 24,
28, 30, 32, 36, 40, 48
],
extendControls: [
{
type: 'split'
},
// 自定义预览按钮
{
type: 'button',
text: <span className="braft-control-text">预览</span>,
className: 'preview-button',
onClick: () => this.handleEditorPreview()
}
],
// 配置编辑器可插入的外部网络媒体类型
media: {
externalMedias: {
image: false,
audio: false,
video: false,
embed: false
},
uploadFn: this.handleEditorUpload
// 限制文件上传大小(待定)
// validateFn: (file) => {
// return file.size < 1024 * 100;
// }
},
onChange: this.handleEditorChange
};
};
render() {
// 富文本编辑器相关配置
const editorProps = this.editorProps();
return (
<div>
<BraftEditor {...editorProps} ref={instance => this.editorInstance = instance}/>
</div>
);
}
}
export default BraftEditorComponent;
\ No newline at end of file
import React from 'react';
import {Breadcrumb, Icon} from 'antd';
import sidebarMenu, {headerMenu} from '../../menu.js'; // 注意这种引用方式
import Logger from '../../utils/Logger';
import './index.less';
const Item = Breadcrumb.Item;
const logger = Logger.getLogger('Breadcrumb');
/**
* 定义面包屑导航, 由于和已有的组件重名, 所以改个类名
*/
class Bread extends React.PureComponent {
//static inited = false; // 表示下面两个map是否初始化
//static iconMap = new Map(); // 暂存menu.js中key->icon的对应关系
//static nameMap = new Map(); // 暂存menu.js中key->name的对应关系
// 上面两个map本来是做成static变量的, 后来感觉还是当成普通的成员变量好些
// 如果是static变量, 那就跟react组件的生命周期完全没关系了
// 话说, 虽然constructor和componentWillMount方法作用差不多, 但我还是觉得componentWillMount更好用
// 因为constructor还要super(props), 有点啰嗦
// 虽然react官方推荐constructor, 因为constructor中可以设置初始状态
// 不过实际上初始状态可以直接通过定义成员变量的方式设置, 不一定要在constructor中
componentWillMount() {
// 准备初始化iconMap和nameMap
const iconMap = new Map();
const nameMap = new Map();
// 这是个很有意思的函数, 本质是dfs, 但用js写出来就觉得很神奇
const browseMenu = (item) => {
nameMap.set(item.key, item.name);
logger.debug('nameMap add entry: key=%s, value=%s', item.key, item.name);
iconMap.set(item.key, item.icon);
logger.debug('iconMap add entry: key=%s, value=%s', item.key, item.icon);
if (item.child) {
item.child.forEach(browseMenu);
}
};
sidebarMenu.forEach(browseMenu);
headerMenu.forEach(browseMenu);
this.iconMap = iconMap;
this.nameMap = nameMap;
}
render() {
const itemArray = [];
// 面包屑导航的最开始都是一个home图标, 并且这个图标是可以点击的
itemArray.push(<Item key="systemHome" href="#"><Icon type="home"/> 首页</Item>);
// this.props.routes是react-router传进来的
for (const route of this.props.routes) {
logger.debug('path=%s, route=%o', route.path, route);
const name = this.nameMap.get(route.path);
if (name) {
const icon = this.iconMap.get(route.path);
if (icon) {
itemArray.push(<Item key={name}><Icon type={icon}/> {name}</Item>); // 有图标的话带上图标
} else {
// 这个key属性不是antd需要的, 只是react要求同一个array中各个元素要是不同的, 否则有warning
itemArray.push(<Item key={name}>{name}</Item>);
}
}
}
// 这个面包屑是不可点击的(除了第一级的home图标), 只是给用户一个提示
return (
<div className="ant-layout-breadcrumb">
<Breadcrumb>{itemArray}</Breadcrumb>
</div>
);
}
}
export default Bread;
.ant-layout-breadcrumb {
> .ant-breadcrumb {
.ant-breadcrumb-link {
display: inline-block;
padding: .8em .4em;
}
}
}
import React from 'react';
import {
Icon,
Row,
Col,
Button,
message,
Upload,
notification
} from 'antd';
import globalConfig from '../../config.js';
import moment from 'moment';
import FormSchemaUtils from './InnerFormSchemaUtils';
import Utils from '../../utils';
import Logger from '../../utils/Logger';
// 这个组件的实现还是有点技巧的, 因为对于不同的schema要显示不同的表单项, 不同表的schema肯定是不同的
// 我最开始是将InnerForm做成一个大的组件, 但这意味着必须要在render方法里解析schema, 虽然能实现功能, 但不完美, 效率也会比较差
// 而且antd的form是controlled components, 每输入一个字符都要重新render一次, 意味着每输入一个字符都要重新解析一次schema, 很蛋疼
// 这种实现见我以前的代码
// 在表名不变的情况下, schema也是固定的, 能不能只解析一次, 之后每次复用呢?
// 绞尽脑汁想到一个办法, 将每个表的表单都做成一个单独的组件, 这个组件是根据schema动态生成的, 在InnerForm的render方法中, 根据当前表名选择对应的组件去渲染
// 这样对InnerForm而言, 每个表单都是黑盒了, 不用关心里面的状态了
// 但要生成antd的表单必须配合一个getFieldDecorator函数, 很难搞, 不能简单的做到模版/数据的分离
// 我甚至考虑过是不是在编译期去解决, 根据schema动态生成js文件之类的, 但这样太麻烦, 最好是能在运行时搞定, 也考虑过eval方法之类的
// 后来参考了函数式语言的惰性求值, 终于想到一个解决办法, 解析schema后不返回具体的元素, 而是返回一个回调函数, 这个函数的参数是getFieldDecorator
// 在真正render的时候, 将getFieldDecorator作为参数传进去
// 此外, 还有一些问题, 比如如何动态生成组件, 如何获取表单的值之类的, 最后也都一一找到办法, 真是不容易...
// 应用的一些技巧: 高阶函数/高阶组件/ref/闭包
// 但表单项一多还是会有点卡...
const logger = Logger.getLogger('InnerForm');
/**
* 内部表单组件(条件以及导入导出)
*/
class InnerForm extends React.PureComponent {
// 什么情况会导致InnerForm re-render?
// 1. 这个组件没有状态
// 2. 只有props会导致re-render, 但由于这个组件是pure的, 所以只有表名变化时才会re-render
componentDidMount() {
this.processQueryParams();
}
componentDidUpdate() {
this.processQueryParams();
}
/**
* 处理url参数, 填入表单
*/
processQueryParams() {
const {form} = this.formComponent.props;
this.formComponent = form;
const params = Utils.getAllQueryParams();
if (Object.keys(params).length > 0) {
this.formComponent.setFieldsValue(params);
}
}
/**
* 表单的查询条件不能直接传给后端, 要处理一下
*
* @param oldObj
* @returns {{}}
*/
filterQueryObj(oldObj) {
// 将提交的值中undefined/null去掉
const newObj = {};
for (const key in oldObj) {
if (oldObj[key] !== undefined && oldObj[key] !== null) {
// 对于js的日期类型, 要转换成字符串再传给后端
if (oldObj[key] instanceof Date) {
newObj[key] = oldObj[key].format('yyyy-MM-dd HH:mm:ss');
} else if (moment.isMoment(oldObj[key])) { // 处理moment对象
newObj[key] = oldObj[key].format('YYYY-MM-DD HH:mm:ss');
} else {
newObj[key] = oldObj[key];
}
}
}
logger.debug('old queryObj: %o, new queryObj %o', oldObj, newObj);
return newObj;
}
/**
* 处理表单提交
*
* @param e
*/
handleSubmit = (e) => {
e.preventDefault();
// 这种用法是非官方的, 直接从代码里扒出来的...
// this.formComponent是通过ref方式获取到的一个react组件
const oldObj = this.formComponent.getFieldsValue();
const newObj = this.filterQueryObj(oldObj);
// 还是要交给上层组件处理, 因为要触发table组件的状态变化...
this.props.parentHandleSubmit(newObj);
};
/**
* 清空表单的值
*
* @param e
*/
handleReset = (e) => {
e.preventDefault();
this.formComponent.resetFields();
};
/**
* 处理数据导入
*/
handleImport = (info) => {
logger.debug('upload status: %s', info.file.status);
// 正在导入时显示一个提示信息
if (info.file.status === 'uploading') {
if (!this.hideLoading) {
let hide = message.loading('正在导入...');
this.hideLoading = hide;
}
}
// 导入完成, 无论成功或失败, 必须给出提示, 并且要用户手动关闭
else if (info.file.status === 'error') {
this.hideLoading();
this.hideLoading = undefined;
notification.error({
message: '导入失败',
description: '文件上传失败, 请联系管理员',
duration: 0,
});
}
// done的情况下还要判断返回值
else if (info.file.status === 'done') {
this.hideLoading();
this.hideLoading = undefined;
logger.debug('upload result %o', info.file.response);
if (!info.file.response.success) {
notification.error({
message: '导入失败',
description: ` ${info.file.response.message}`,
duration: 0,
});
} else {
notification.success({
message: '导入成功',
description: info.file.response.data,
duration: 0,
});
}
}
};
/**
* 处理数据导出
* 本质上也是提交表单, 跟handleSubmit有点类似
* 但不用再提交给上层组件处理了, 因为不需要改变表格组件的状态
*/
handleExport = (e) => {
e.preventDefault();
const oldObj = this.formComponent.getFieldsValue();
const newObj = this.filterQueryObj(oldObj);
// 导出前必须选定了一些查询条件, 不允许导出全表
// 防止误操作
if (Object.keys(newObj).length === 0) {
message.warning('导出时查询条件不能为空', 4.5);
return;
}
// ajax是不能处理下载请求的, 必须交给浏览器自己去处理
// 坏处是我就不知道用户的下载是否成功了
const url = `${globalConfig.getAPIPath()}/${this.props.tableName}/export`;
window.open(`${url}?q=${encodeURIComponent(JSON.stringify(newObj))}`); // 注意url编码
};
render() {
const {tableName, schema, tableConfig} = this.props;
// 根据当前的tableName, 获取对应的表单组件
const FormComponent = FormSchemaUtils.getForm(tableName, schema);
// 导入
const uploadProps = {
name: 'file',
action: `${globalConfig.getAPIPath()}/${this.props.tableName}/import`,
showUploadList: false,
onChange: this.handleImport,
};
// 表单的前面是一堆输入框, 最后一行是按钮
return (
<div className="ant-advanced-search-form">
{/*这个渲染组件的方法很有意思, 另外注意这里的ref*/}
<FormComponent wrappedComponentRef={(input) => {
this.formComponent = input;
}}/>
<Row>
<Col span={24} style={{textAlign: 'right'}}>
<Button type="primary" onClick={this.handleSubmit}><Icon type="search"/>查询</Button>
<Button onClick={this.handleReset}><Icon type="cross"/>清除条件</Button>
{tableConfig.showExport ?
<Button onClick={this.handleExport}><Icon
type="export"/>导出</Button> : ''}
{tableConfig.showImport ?
<Upload {...uploadProps}><Button><Icon type="upload"/>导入</Button></Upload> : ''}
</Col>
</Row>
</div>
);
}
}
export default InnerForm;
import React from 'react';
import {Pagination, Select} from 'antd';
/**
* 内部分页器组件
*/
class InnerPagination extends React.PureComponent {
render() {
// 有些状态要传到父组件中去处理
return (
<div className="db-pagination">
<Pagination
simple={this.props.isSimple}
showQuickJumper
selectComponentClass={Select}
total={this.props.total}
showTotal={(total) => `每页${this.props.pageSize}条, 共 ${total} 条`}
pageSize={this.props.pageSize} defaultCurrent={1}
current={this.props.currentPage}
onChange={this.props.parentHandlePageChange}
//是否显示“每页显示条目数”,对应 antd Pagination组件的showSizeChanger属性
showSizeChanger={this.props.showSizeChanger}
//修改“每页显示条目数”时触发,对应 antd Pagination组件的onShowSizeChange属性
onShowSizeChange={this.props.parentHandleShowPageChange}
pageSizeOptions={this.props.pageSizeOptions}
/>
</div>
);
}
}
export default InnerPagination;
import React from 'react';
import TableUtils from './TableUtils.js';
import Logger from '../../utils/Logger';
import Utils from '../../utils';
const logger = Logger.getLogger('InnerTableRenderUtils');
// 自定义操作字段, 在dataSchema中是用一个特殊的key来标识的
const ACTION_KEY = 'singleRecordActions';
/**
* 表格的render函数有个比较蛋疼的问题, 就是this绑定, 专门写个工具类去处理
*/
const RenderUtils = {
// 这个utils是有状态的
// 用一个set保存目前已经处理过哪些表的render, 已经处理过的就不用再处理了
tableNameSet: new Set(),
/**
* 重置状态, InnerTable组件unmount时调用
* 因为只有组件unmount后才可能需要重新绑定this
*/
reset() {
this.tableNameSet.clear();
},
/**
* 处理表格的schema, 根据情况赋值render函数
*
* @param tableSchema 表格的schema
* @param tableName 表名
* @param innerTableComponent 对应的InnerTable组件, 换句话说, 要绑定的this对象
* @returns {*}
*/
bindRender(tableSchema, tableName, innerTableComponent) {
const {onClickImage, onSingleRecordUpdate, onSingleRecordDelete, onSingleRecordComponent, fieldMap, primaryKey} = innerTableComponent;
// 命中缓存
if (this.tableNameSet.has(tableName)) {
return tableSchema;
}
tableSchema.forEach(col => {
const field = fieldMap.get(col.key);
if (!field) { // 这种情况理论上不会出现
logger.warn('unknown tableSchema col: %o', col);
return;
}
// 用户自己配置的render最优先
if (field.render) {
logger.debug('bind user-defined render for field %o', field);
col.render = field.render.bind(innerTableComponent); // 绑定this
}
// 对于某些showType我会给个默认的render
else if (field.showType === 'image') {
logger.debug('bind image render for field %o', field);
col.render = this.getImageRender()(onClickImage);
} else if (field.showType === 'file') {
logger.debug('bind file render for field %o', field);
col.render = this.getFileRender;
} else if (field.key === ACTION_KEY && field.actions && field.actions.length > 0) {
logger.debug('bind actions render for field %o', field);
col.render = this.getActionRender(field, primaryKey)(onSingleRecordUpdate, onSingleRecordDelete, onSingleRecordComponent);
}
});
const ignoreCache = TableUtils.shouldIgnoreSchemaCache(tableName);
if (!ignoreCache) {
this.tableNameSet.add(tableName);
}
return tableSchema;
},
/**
* 针对image字段的render方法
*
* @returns {function(): function()}
*/
getImageRender() {
return onClickImagePreview => text => {
if (Utils.isString(text)) {
return <img src={text} alt="图片加载失败" style={{width: '100%'}} onClick={e => onClickImagePreview(text)}/>;
} else if (text instanceof Array) {
// 如果是多张图片, 只取第一张图片在表格中显示
return <img src={text[0]} alt="图片加载失败" style={{width: '100%'}}
onClick={e => onClickImagePreview(text)}/>;
}
return null;
};
},
/**
* 针对file字段的render方法
*
* @param text
* @returns {*}
*/
getFileRender(text) {
if (Utils.isString(text) && text.length > 0) {
// 单个文件, 显示为超链接
return <a href={text} target="_blank">{text.substr(text.lastIndexOf('/') + 1)}</a>;
} else if (text instanceof Array) {
if (text.length === 0) {
return null;
}
// 多个文件, 显示为一组超链接
const urlArray = [];
urlArray.push(<a key={0} href={text[0]} target="_blank">{text[0].substr(text[0].lastIndexOf('/') + 1)}</a>);
for (let i = 1; i < text.length; i++) {
urlArray.push(<br key={-1 - i}/>);
urlArray.push(<a key={i} href={text[i]}
target="_blank">{text[i].substr(text[i].lastIndexOf('/') + 1)}</a>);
}
return <div>{urlArray}</div>;
}
return null;
},
/**
* 渲染自定义操作列
*
* @param field
* @param primaryKey
* @returns {function(): function()}
*/
getActionRender(field, primaryKey) {
// 返回一个高阶函数, 输入是3个函数
// 1. singleRecordUpdate用于更新单条记录的函数, 参数是(record:记录本身, updateKeys:要更新哪些字段)
// 2. singleRecordDelete用于删除单条记录, 参数是record
// 3. singleRecordComponent用于自定义组件实现单条记录的更新, 参数是(record:记录本身, component:要渲染的组件, name:在modal中显示时的标题)
return (singleRecordUpdate, singleRecordDelete, singleRecordComponent) => (text, record) => {
let actions;
if (typeof field.actions === 'function') {
actions = field.actions(text, record) || [];
} else {
actions = field.actions || [];
}
const actionArray = [];
// 最后一个push到array中的元素是否是分割符? 为了排版好看要处理下
let lastDivider = false;
for (let i = 0; i < actions.length; i++) {
const action = actions[i];
// visible函数用于控制当前行是否显示某个操作
if (action.visible && !action.visible(record)) {
continue;
}
// 如果没有定义主键, 不允许更新/删除
if (!primaryKey && (action.type === 'update' || action.type === 'delete')) {
continue;
}
// 换行符, 单纯为了美观
if (action.type === 'newLine') {
// 是否要去掉上一个分隔符
if (lastDivider) {
actionArray.pop();
}
actionArray.push(<br key={i}/>);
lastDivider = false;
continue;
}
// 要push到actionArray的元素
let tmp;
switch (action.type) {
// 更新单条记录, 可以控制更新哪些字段
case 'update':
tmp = <a href="#" key={i}
onClick={e => {
e.preventDefault();
singleRecordUpdate(record, action.keys);
}}>
{action.name}
</a>;
break;
// 删除单条记录
case 'delete':
tmp = <a href="#" key={i}
onClick={e => {
e.preventDefault();
singleRecordDelete(record);
}}>
{action.name}
</a>;
break;
// 自定义组件
case 'component':
tmp = <a href="#" key={i}
onClick={e => {
e.preventDefault();
singleRecordComponent(record, action.component, action.name);
}}>
{action.name}
</a>;
break;
default:
// 如果type不是预定义的几种, 就看用户是否自定义了render函数
if (action.render) {
tmp = <span key={i}>{action.render(record)}</span>;
}
}
// 如果还是不行, 那就说明用户定义的action格式有问题, 忽略
if (!tmp) {
continue;
}
actionArray.push(tmp);
actionArray.push(<span key={-1 - i} className="ant-divider"/>); // 分隔符
lastDivider = true;
}
// 去除最后一个分隔符, 为了美观
if (lastDivider) {
actionArray.pop();
}
return <span>{actionArray}</span>;
};
},
};
export default RenderUtils;
export {ACTION_KEY};
import React from 'react';
import {notification} from 'antd';
import globalConfig from '../../config.js';
import ajax from '../../utils/ajax';
import Logger from '../../utils/Logger';
const logger = Logger.getLogger('TableUtils');
// 缓存, key是tableName, value是{querySchema, dataSchema}
const tableMap = new Map();
// 缓存, key是tableName, value是tableConfig
const configMap = new Map();
/**
* 用于解析表schema的工具类
*/
export default {
// 将getSchema的函数分为3个, 分别用于不同情况
// 其实就是从远程加载schema时比较特殊, 要显示一个loading提示给用户, 必须是async函数, 其他的就是普通的同步函数
/**
* 从缓存中获取schema
*
* @param tableName
* @returns {V}
*/
getCacheSchema(tableName) {
return tableMap.get(tableName);
},
/**
* 从本地的js文件中读取schema, 会更新缓存
*
* @param tableName
* @returns {{querySchema: *, dataSchema: *}}
*/
getLocalSchema(tableName) {
const ignoreCache = this.shouldIgnoreSchemaCache(tableName);
let querySchema, dataSchema;
try {
querySchema = require(`../../schema/${tableName}.querySchema.js`);
// 如果是忽略cache, 每次读取的schema都必须是全新的
if (ignoreCache) {
querySchema = querySchema.map(item => Object.assign({}, item)); // Object.assign是浅拷贝, 不过没啥影响
}
} catch (e) {
logger.error('load query schema error: %o', e);
}
try {
dataSchema = require(`../../schema/${tableName}.dataSchema.js`);
if (ignoreCache) {
dataSchema = dataSchema.map(item => Object.assign({}, item));
}
} catch (e) {
logger.error('load data schema error: %o', e);
}
// 注意这里会更新缓存
const toCache = {querySchema, dataSchema};
if (!ignoreCache) {
tableMap.set(tableName, toCache);
}
return toCache;
},
/**
* 从远程获取某个表的schema, 如果有本地schema的话会合并
* 这个方法会更新缓存
*
* @param tableName
* @returns {{querySchema: *, dataSchema: *}}
*/
async getRemoteSchema(tableName) {
const ignoreCache = this.shouldIgnoreSchemaCache(tableName);
const localSchema = this.getLocalSchema(tableName);
let querySchema, dataSchema;
try {
const res = await ajax.CRUD(tableName).getRemoteSchema();
logger.debug('get remote schema for table %s, res = %o', tableName, res);
if (res.success) {
querySchema = this.merge(localSchema.querySchema, res.data.querySchema);
dataSchema = this.merge(localSchema.dataSchema, res.data.dataSchema);
} else {
logger.error('getRemoteSchema response error: %o', res);
this.error(`请求asyncSchema失败: ${res.message}`);
}
} catch (e) {
logger.error('getRemoteSchema network request error: %o', e);
this.error(`请求asyncSchema时网络失败: ${e.message}`);
}
// 更新缓存
const toCache = {querySchema, dataSchema};
if (!ignoreCache) {
tableMap.set(tableName, toCache);
}
return toCache;
},
/**
* 合并本地的schema和远程的schema, 其实就是合并两个array
*
* @param local 本地schema
* @param remote 远程schema
* @returns {*}
*/
merge(local, remote) {
// 注意local和remote都可能是undefined
// 只有二者都不是undefined时, 才需要merge
if (local && remote) {
const result = local; // 合并后的结果
const map = new Map();
result.forEach(item => map.set(item.key, item));
// 注意合并的逻辑: 如果远程的key本地也有, 就更新; 否则新增, 新增的列都放在最后
remote.forEach(item => {
if (map.has(item.key)) {
// 注意传值vs传引用的区别
Object.assign(map.get(item.key), item);
} else {
result.push(item);
}
});
return result;
} else {
// 注意这个表达式
return local || remote;
}
},
/**
* 弹出一个错误信息提示用户
*
* @param errorMsg
*/
error(errorMsg) {
notification.error({
message: '出错啦!',
description: ` ${errorMsg}`,
duration: 0,
});
},
/**
* 获取某个表的个性化配置, 会合并默认配置
*
* @param tableName
* @returns {*}
*/
getTableConfig(tableName) {
if (configMap.has(tableName)) {
return configMap.get(tableName);
}
let tableConfig;
try {
const tmp = require(`../../schema/${tableName}.config.js`);
tableConfig = Object.assign({}, globalConfig.DBTable.default, tmp);
} catch (e) {
logger.warn('can not find config for table %s, use default instead', tableName);
tableConfig = Object.assign({}, globalConfig.DBTable.default);
}
configMap.set(tableName, tableConfig);
return tableConfig;
},
/**
* 某个表是否应该忽略缓存
*
* @param tableName
* @returns {boolean}
*/
shouldIgnoreSchemaCache(tableName) {
const tableConfig = this.getTableConfig(tableName);
return tableConfig.asyncSchema === true && tableConfig.ignoreSchemaCache === true;
},
};
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment