您的位置 首页 java

自写项目总结(传统CRUD,前端+后端)

项目需求:一个客户管理系统、制作单生成、报价表(xls)生成及下载

后端:springboot、springSecurity、Mybatis-Plus、devtools、thymeleaf、poi、lombox、swagger2

前端:vue、elementUI

采用前后端分离开发,实装将前端build的文件复制到后端 static 下,使用maven打包成一个单独的jar文件,放到服务器上双击即可运行。

一、后端知识点:

1.pom.xml文件

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="#34; xmlns:xsi="#34;
         xsi:schemaLocation=" #34;>
    <modelVersion>4.0.0</modelVersion>
    <groupId>icu.woodlum.yderp</groupId>
    <artifactId>demo</artifactId>
    <version>2.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>demo</name>
    <description>Demo  project  for Spring Boot</description>
 
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!--安全框架-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--模板-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--mybatis-plus框架-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        <!--热启动-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <!--  -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--xlsx生成依赖包-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.0.1</version>
        </dependency>
        <!--  -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <!--实体类自动get set-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.6.1</version>
        </dependency>
 
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                < Configuration >
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                < execution s>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
 
</project>  

pom.xml中有个要点:使用maven>package打包jar一直不成功,加上executions中的内容就好了

 <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <executions>
            <execution>
                 <goals>
                       <goal>repackage</goal>
                  </goals>
             </execution>
       </executions>
 </plugin>  

2.session缓存时间

server.servlet.session.timeout=PT9H

Duration转换字符串方式,默认为正,负以-开头,紧接着P,以下字母不区分大小写
D :天; T:天和小时之间的分隔符 H :小时 M:分钟 S:秒 每个单位都必须是数字,且时分秒顺序不能乱
比如P2dt3m5s P3d pt3h

3.开启跨域请求

 @Configuration
public class CorsConfig {
    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*"); // 允许任何域名使用
        corsConfiguration.addAllowedHeader("*"); // 允许任何头
        corsConfiguration.addAllowedMethod("*"); // 允许任何方法(post、get等)
        return corsConfiguration;
    }
 
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig()); // 对接口配置跨域设置
        return new CorsFilter(source);
    }
}  

4.mybatis-plus相关

a.开启分页

 @Configuration
public class MybatisConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }
}  

b.数据库字段自动填充配置

 @Configuration
public class MyMetoObjectHandle implements MetaObjectHandler {
    @ Override 
    public void insertFill(MetaObject metaObject) {
        setFieldValByName("createTime", LocalDateTime.now(),metaObject);
        setFieldValByName("updateTime",LocalDateTime.now(),metaObject);
    }
 
    @Override
    public void updateFill(MetaObject metaObject) {
        setFieldValByName("createTime", LocalDateTime.now(),metaObject);
        setFieldValByName("updateTime",LocalDateTime.now(),metaObject);
    }
}  

实体类字段中

 //自动填充时间
@TableField(value = "createTime",fill = FieldFill.INSERT_UPDATE)
private LocalDateTime createTime;
//此属性在表中不存在
@TableField(exist =  false )
private Customer customer;  

c.mapper接口

 @Mapper
@Component
public interface CustomerMapper extends BaseMapper<Customer> {
    @Results({@Result(column = "tId",property = "tId"),
            @Result(column = "tId",property = "tax",
            one = @One( Select  = "icu.woodlum.yderp. dao .TaxMapper.selectById"))})
    @Select("select * from customer ${ew.customSqlSegment} ")
    Page<Customer> selectCustomerPage(IPage<Customer> page,@Param(Constants.WRAPPER)
                                              Wrapper<Customer> wrapper);
 
    @Results({@Result(column = "tId",property = "tId"),
            @Result(column = "tId",property = "tax",
            one = @One(select = "icu.woodlum.yderp.dao.TaxMapper.selectById"))})
    @Select("select * from customer ${ew.customSqlSegment} ")
    Customer selecCustomertOne(@Param(Constants.WRAPPER) Wrapper<Customer> wrapper);
 
    @Results({@Result(column = "tId",property = "tId"),
            @Result(column = "tId",property = "tax",
                    one = @One(select = "icu.woodlum.yderp.dao.TaxMapper.selectById"))})
    @Select("select * from customer where id = #{id}")
    Customer selecCustomertById(Serializable id);
 
}  

5.中间表的更新,先删除中间表中相应的数据再作insert

6.结果集封装

 @Data
public class Result<T> {
    private T data;
    private boolean  success ;
    private int code;
    private String  message ;
 
    private Result() {
        }
 
    private Result(T data, boolean success, int code, String message) {
        this.data = data;
        this.success = success;
        this.code = code;
        this.message = message;
    }
 
    private Result(boolean success, int code, String message) {
        this.success = success;
        this.code = code;
        this.message = message;
    }
 
    public static <T> Result<T> newInstance(){
        return new Result<T>();
    }
    // 成功
    public static <T> Result<T> defaultSuccess(T data){
        return new Result<T>(data, true, 200, "返回成功");
    }
    // 失败
    public static <T> Result<T> defaultFailure(){
        return new Result<T>(false, 500, "系统内部错误");
    }
     // 自定义失败一
    public static <T> Result<T> failure(T data, int code, String message){
        return new Result<T>(data, false, code, message);
    }
    // 自定义失败二
    public static <T> Result<T> failure(int code, String message){
        return new Result<T>(false, code, message);
    }
}  

7.安全相关

a.User类,实现UserDetails接口

 @Data
public class User  implements UserDetails {
    private int id;
    // 用户名为工号
    private String username;
    private String realname;
    private String password;
    @TableField("createDate")
    private LocalDate createDate;
    @TableField("lastLoginTime")
    private LocalDateTime lastLoginTime;
    //账户是否不可用,数据库为1代表true,0代表false,默认为1
    private boolean enabled;
    //账户是否未过期
    @TableField("accountNonExpired")
    private boolean accountNonExpired;
    //账户是否锁定
    @TableField("accountNonLocked")
    private boolean accountNonLocked;
    //密码是否未过期
    @TableField("credentialsNonExpired")
    private boolean credentialsNonExpired;
    //权限集合
    @TableField(exist = false)
    private List<GrantedAuthority> authorities;
}  

b.用户dao接口

 @Mapper
@Component
public interface UserDao extends BaseMapper<User> {
    //用户名查询用户信息
    @Select("select * from user where username = #{userName}")
    public User selectByUserName(String userName);
    //用户名查询当前用户的权限信息
    @Select("select permission.* FROM" +
            "    user  u" +
            "    INNER JOIN user_role ON u.id = user_role.uid" +
            "    INNER JOIN role_permission on user_role.rid = role_permission.rid" +
            "    INNER JOIN permission on role_permission.pid = permission.id" +
            "    WHERE u.username = #{userName};")
    public List<Permission> findPermissionByUserName(String userName);
    //注册用户
    @Insert("insert into user values(default,#{user.username}," +
            "#{user.realname},#{user.password},now(),now(),default,default,default,default)")
    int insert(@Param("user") User user);
}  

c.用户服务接口

 @Service
public class MyUserDetalisService implements UserDetailsService {
    private UserDao userDao;
    public MyUserDetalisService(UserDao userDao) {
        this.userDao = userDao;
    }
 
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询当前用户信息
        User user = userDao.selectByUserName(username);
        if (user == null){
            throw new RuntimeException("没有用户");
        }
        //查询当前用户权限
        List<Permission> list = userDao.findPermissionByUserName(username);
        List<GrantedAuthority> authorities = new ArrayList<>();
        /*
         * 将getPermTag()构建一个GrantedAuthority接口实例对象,放于List<GrantedAuthority>中
         * */        list.forEach(l ->{
            authorities.add(new SimpleGrantedAuthority(l.getPermTag()));
        });
        //用户信息设置权限集合
        user.setAuthorities(authorities);
        return user;
    }
}  

d.security配置

 @EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    private MyUserDetalisService myUserDetalisService;
    public MySecurityConfig(MyUserDetalisService myUserDetalisService) {
        this.myUserDetalisService = myUserDetalisService;
    }
 
    //http安全配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()  //所有请求拦截
                .antMatchers("/static/**").permitAll() //放在所有拦截的前面放行不需要拦截的资源
                .antMatchers("/login").permitAll()  //放行登录
                .antMatchers("/logout").permitAll() //放行注销
                .antMatchers("/reg").hasAnyAuthority("REGUSER")//此页需要权限
                .anyRequest().authenticated() //除上所有拦截需要用户认证
                .and()
                .formLogin().loginPage("/login") //forlogin认证
                .failureUrl("/login?error=true")//登陆错误页
                .and()
                .csrf().disable(); //关闭csrf校验
 
    }
    //认证管理器配置
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetalisService)
                //查询时需要密码加密后和数据库做比较
                .passwordEncoder(new BCryptPasswordEncoder());
    }
 
}  

e.403权限配置

 @Configuration
public class ErrorPageConfig implements ErrorPageRegistrar {
    @Override
    public void registerErrorPages(ErrorPage registry  registry) {
        //权限不足的页面导向
        ErrorPage error403 = new ErrorPage(HttpStatus.FORBIDDEN,"/403");
        registry.addErrorPages(error403);
    }
}  

8.springMVC配置,其中缓存静态资源,加快访问速度

 @Configuration
public class MyConfig implements WebMvcConfigurer {
    //静态资源URI和位置映射
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/")
                //设置静态资源缓存1年
                .setCachePeriod(3153600);
    }
    //视图映射
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/403").setViewName("403");
    }
}  

9.swagger配置

 @Configuration
public class Swagger2 {
 
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("icu.woodlum.yderp.controller"))
                .paths(PathSelectors.any())
                .build();
    }
 
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("yderp-api文档")
                .description("yderp-api文档")
                .termsOfServiceUrl("#34;)
                .version("2.0")
                .build();
    }
}  

10.favicon.ico图标配置

原则上此图片放置于static文件夹下即可,但是某些情况下可能不会显示,因此要在html中指定

 <link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico">  

二、前端知识点

1.前端采用的vue-cli脚手架构建,注意安装router,ESLint会强制要求代码格式

 npm install -g cnpm --registry=
cnpm install webpack -g
cnpm install vue-cli -g
//
vue init webpack 项目名称
 
cnpm install  

启动命令:npm run dev,关闭Ctrl+C

打包编译:npm run build

2.router路由:router.js

 import Vue from 'vue'
import VueRouter from 'vue-router'
 
import customerheadButton from '../components/cutomer/CustomerHeadButton'
import customerMain from '../components/cutomer/CustomerMain'
 
Vue.use(VueRouter)
export default new VueRouter({
  // mode去除浏览器中的#号
  mode: 'history',
  routes: [
    {
      path: '/',
      component: main,
      children: [
        {
          path: 'file-customer',
          components: {
            'head-button': customerheadButton,
            'tax-main': customerMain
          },
          meta: {
            title: '客户档案'
          }
        }
      ]
    }
  ]
})  

3.Global.vue,全局配置后端接口地址

 <script>
const BASE_URL = '#39;
//const BASE_URL = '/'
export default {
  BASE_URL
}
</script>  
 import global from '../components/Global.vue'
 
 
axios.post(global.BASE_URL + 'customer/selectList', {'name': params.searchName, 'taxName': params.searchTaxName, 'currentPage': params.currentPage, 'perPages': params.pageSize})
        .then(function (res) {
           state .totalpages = res.data.data.total
          state.customerlist = res.data.data.records
        })  

4.title设置

 // title设置
router.beforeEach((to, from, next) => {
  // 在路由进入之前判断是否有标题
  if (to.meta.title) {
    document.title = to.meta.title
  }
  // 继续执行路由
  next()
})  

5.vuex

a.index.js

 import Vue from 'vue'
import Vuex from 'vuex'
import tax from './taxModule'
import customer from './CustomerModule'
import product from './ProductModule'
import designNotice from './DesignNoticeModule'
import price from './PriceModule'
// 挂载Vuex
Vue.use(Vuex)
// 创建VueX对象
export default new Vuex.Store({
  modules: {
    tax, customer, product, designNotice, price
  }
})  

b.CustomerModule.js

 import Vue from 'vue'
import axios from 'axios'
import global from '../components/Global.vue'
Vue.use(axios)
export default {
  // customer模块状态,必须开启命名空间
  namespaced: true,
  state: {
    // customerInfo组件显示状态
    customerFormVisible: false,
    ....略
  },
  mutations: {
    // 注意多参数传递,参数是一个对象
    addcustomerForm (state, params) {
      state.customerFormTitle = params.customerFormTitle
      state.customerFormOkTitle = params.customerFormOkTitle
      state.customerFormInfo = params.customerFormInfo
      state.customerFormVisible = true
    },
    ...略
}  

c.使用

 computed: {
    ...mapState('customer', ['customerFormVisible']),
    ...mapState('tax', ['taxlist'])
  },
 
methods: {
    // 方法注册
    ...mapMutations('customer', ['customerFormCancel']),
    ...mapMutations('tax', ['getTaxListAll']),
   

6.element分页使用

 <!--分页组件-->
        <div class="block" style="display: flex">
            <el-pagination
                    :background="true"
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange"
                    @next-click="currentPage += 1"
                    @pre-click="currentPage -= 1"
                    :current-page="currentPage"
                    :page-sizes="[10, 20]"
                    :page-size="pagesize"
                    layout="total, sizes, prev, pager, next, jumper"
                    :total="totalpages">
            </el-pagination>
        </div>
 
export default {
  data () {
    return {
      // 默认当前为第1页
      currentPage: 1,
      // 默认每页10行
      pagesize: 10
    }
  },
watch: {
    // 监听vuex中的标志
    vuexCurrentPage (newVal) {
      if (newVal === 1) {
        this.currentPage = 1
      }
    },
    // 监听vuex中的每页数目
    vuexPageSizes (newVal) {
      if (newVal === 10) {
        this.pagesize = 10
      }
    }
  },
methods: {
 // 每页数变化时
    handleSizeChange (val) {
      this.pagesize = val
      var params = {searchName: this.searchName, searchTaxName: this.searchTaxName, currentPage: this.currentPage, pageSize: this.pagesize}
      this.getCustomerListAll(params)
      // 清除vuex看每页标志
      this.clearDefaultPageSize()
    },
    // 当前页变化时
    handleCurrentChange (val) {
      this.currentPage = val
      var params = {searchName: this.searchName, searchTaxName: this.searchTaxName, currentPage: this.currentPage, pageSize: this.pagesize}
      this.getCustomerListAll(params)
      // 清除vuex中的当前页标志
      this.clearDefaultCurrentPage()
    }
  }
 
 
 
vuex:
// 以下两个相当于标记符flag
vuexCurrentPage: -1,
vuexPageSizes: -1
 
 
 
    // 清除默认
    clearDefaultPageSize (state) {
      state.vuexPageSizes = -1
    },
    // 清除vuexCurrengPage
    clearDefaultCurrentPage (state) {
      state.vuexCurrentPage = -1
    },  

7.打开新的一页

 <router-link target="_blank" :to="{path:'/showNotice?id='+scope.row.id}">
     <el-button type="success" size="mini">查看</el-button>
</router-link>
 
 
//router.js中
{path: '/showNotice',
      component: showNotice,
      meta: {
        title: '设计制作通知单'
      }
}
 
//showNotice.vue中
created () {
    var _this = this
    this.$axios.post('designNotice/selectById', {id: this.$route.query.id}).then(function (res) {
      if (res.data.success === true) {
        _this.notice = res.data.data
      }
    })
}  

8.项目结构图

9、项目完成图

文章来源:智云一二三科技

文章标题:自写项目总结(传统CRUD,前端+后端)

文章地址:https://www.zhihuclub.com/201240.shtml

关于作者: 智云科技

热门文章

发表回复

您的电子邮箱地址不会被公开。

网站地图