Feick 4 жил өмнө
commit
207bf6092f
36 өөрчлөгдсөн 1174 нэмэгдсэн , 0 устгасан
  1. 33 0
      .gitignore
  2. 5 0
      doc/init.sql
  3. 153 0
      pom.xml
  4. 11 0
      src/main/kotlin/com/yaoxiang/planning/PlaningApplication.kt
  5. 17 0
      src/main/kotlin/com/yaoxiang/planning/action/LoginAction.kt
  6. 16 0
      src/main/kotlin/com/yaoxiang/planning/action/TestAction.kt
  7. 30 0
      src/main/kotlin/com/yaoxiang/planning/config/LoginSuccessHandler.kt
  8. 17 0
      src/main/kotlin/com/yaoxiang/planning/config/PlanningConfig.kt
  9. 52 0
      src/main/kotlin/com/yaoxiang/planning/config/RedisConfig.kt
  10. 40 0
      src/main/kotlin/com/yaoxiang/planning/config/SwaggerConfig.kt
  11. 23 0
      src/main/kotlin/com/yaoxiang/planning/config/WebMvcConfig.kt
  12. 136 0
      src/main/kotlin/com/yaoxiang/planning/config/WebSecurityConfig.kt
  13. 21 0
      src/main/kotlin/com/yaoxiang/planning/config/WebSocketConfig.kt
  14. 9 0
      src/main/kotlin/com/yaoxiang/planning/domain/AnnualPlan.kt
  15. 28 0
      src/main/kotlin/com/yaoxiang/planning/domain/Authority.kt
  16. 24 0
      src/main/kotlin/com/yaoxiang/planning/domain/BaseLongEntity.kt
  17. 35 0
      src/main/kotlin/com/yaoxiang/planning/domain/Department.kt
  18. 40 0
      src/main/kotlin/com/yaoxiang/planning/domain/Planning.kt
  19. 44 0
      src/main/kotlin/com/yaoxiang/planning/domain/PlanningItem.kt
  20. 25 0
      src/main/kotlin/com/yaoxiang/planning/domain/QuarterlyPlan.kt
  21. 32 0
      src/main/kotlin/com/yaoxiang/planning/domain/Role.kt
  22. 55 0
      src/main/kotlin/com/yaoxiang/planning/domain/UserInfo.kt
  23. 22 0
      src/main/kotlin/com/yaoxiang/planning/domain/UserRole.kt
  24. 25 0
      src/main/kotlin/com/yaoxiang/planning/domain/WeeklyPlan.kt
  25. 38 0
      src/main/kotlin/com/yaoxiang/planning/model/AuthUser.kt
  26. 41 0
      src/main/kotlin/com/yaoxiang/planning/model/Reply.kt
  27. 12 0
      src/main/kotlin/com/yaoxiang/planning/repository/UserInfoRepo.kt
  28. 18 0
      src/main/kotlin/com/yaoxiang/planning/service/UserService.kt
  29. 7 0
      src/main/resources/application-dev.properties
  30. 18 0
      src/main/resources/application.properties
  31. 52 0
      src/main/resources/static/app.js
  32. BIN
      src/main/resources/static/favicon.ico
  33. 14 0
      src/main/resources/static/main.css
  34. 52 0
      src/main/resources/static/websocket.html
  35. 16 0
      src/main/resources/templates/login.html
  36. 13 0
      src/test/kotlin/com/yaoxiang/planning/PlaningApplicationTests.kt

+ 33 - 0
.gitignore

@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/

+ 5 - 0
doc/init.sql

@@ -0,0 +1,5 @@
+CREATE DATABASE `planning` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
+
+CREATE USER 'planning'@'%' IDENTIFIED By '134679258Aa_';
+
+GRANT  ALL PRIVILEGES ON planning.* to 'planning'@'%';

+ 153 - 0
pom.xml

@@ -0,0 +1,153 @@
+<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>2.4.1</version>
+		<relativePath/> <!-- lookup parent from repository -->
+	</parent>
+	<groupId>com.yaoxiang</groupId>
+	<artifactId>planning</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<name>planning</name>
+	<description>Demo project for Spring Boot</description>
+
+	<properties>
+		<java.version>1.8</java.version>
+		<kotlin.version>1.4.21</kotlin.version>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-actuator</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-data-jpa</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-redis</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-web</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.module</groupId>
+			<artifactId>jackson-module-kotlin</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.jetbrains.kotlin</groupId>
+			<artifactId>kotlin-reflect</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.jetbrains.kotlin</groupId>
+			<artifactId>kotlin-stdlib-jdk8</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.session</groupId>
+			<artifactId>spring-session-data-redis</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>io.github.microutils</groupId>
+			<artifactId>kotlin-logging</artifactId>
+			<version>1.7.6</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-websocket</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+			<scope>runtime</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-pool2</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>druid</artifactId>
+			<version>1.1.21</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-codec</groupId>
+			<artifactId>commons-codec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.github.xiaoymin</groupId>
+			<artifactId>swagger-bootstrap-ui</artifactId>
+			<version>1.9.4</version>
+		</dependency>
+		<dependency>
+			<groupId>io.springfox</groupId>
+			<artifactId>springfox-swagger2</artifactId>
+			<version>2.9.2</version>
+		</dependency>
+		<dependency>
+			<groupId>io.swagger</groupId>
+			<artifactId>swagger-models</artifactId>
+			<version>1.5.22</version>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.security</groupId>
+			<artifactId>spring-security-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
+		<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+			</plugin>
+			<plugin>
+				<groupId>org.jetbrains.kotlin</groupId>
+				<artifactId>kotlin-maven-plugin</artifactId>
+				<configuration>
+					<args>
+						<arg>-Xjsr305=strict</arg>
+					</args>
+					<compilerPlugins>
+						<plugin>spring</plugin>
+						<plugin>jpa</plugin>
+					</compilerPlugins>
+				</configuration>
+				<dependencies>
+					<dependency>
+						<groupId>org.jetbrains.kotlin</groupId>
+						<artifactId>kotlin-maven-allopen</artifactId>
+						<version>${kotlin.version}</version>
+					</dependency>
+					<dependency>
+						<groupId>org.jetbrains.kotlin</groupId>
+						<artifactId>kotlin-maven-noarg</artifactId>
+						<version>${kotlin.version}</version>
+					</dependency>
+				</dependencies>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>

+ 11 - 0
src/main/kotlin/com/yaoxiang/planning/PlaningApplication.kt

@@ -0,0 +1,11 @@
+package com.yaoxiang.planning
+
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.runApplication
+
+@SpringBootApplication
+class PlaningApplication
+
+fun main(args: Array<String>) {
+	runApplication<PlaningApplication>(*args)
+}

+ 17 - 0
src/main/kotlin/com/yaoxiang/planning/action/LoginAction.kt

@@ -0,0 +1,17 @@
+package com.yaoxiang.planning.action
+
+import org.springframework.stereotype.Controller
+import org.springframework.web.bind.annotation.RequestMapping
+
+
+
+
+//@Controller
+//class LoginAction {
+//
+//    @RequestMapping("/login")
+//    fun hello(): String? {
+//        //这边我们,默认是返到templates下的login.html
+//        return "login"
+//    }
+//}

+ 16 - 0
src/main/kotlin/com/yaoxiang/planning/action/TestAction.kt

@@ -0,0 +1,16 @@
+package com.yaoxiang.planning.action
+
+import com.yaoxiang.planning.model.Reply
+import io.swagger.annotations.Api
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.RequestMapping
+
+@Api(tags = ["Test"])
+@RequestMapping("test")
+class TestAction {
+
+    @GetMapping("hello")
+    fun hello(): Reply<String> {
+        return Reply.ok("hello world")
+    }
+}

+ 30 - 0
src/main/kotlin/com/yaoxiang/planning/config/LoginSuccessHandler.kt

@@ -0,0 +1,30 @@
+package com.yaoxiang.planning.config
+
+import com.yaoxiang.planning.model.AuthUser
+import mu.KotlinLogging
+import org.springframework.security.core.Authentication
+import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler
+import java.io.IOException
+import javax.servlet.ServletException
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import kotlin.jvm.Throws
+
+private val logger = KotlinLogging.logger {}
+
+class LoginSuccessHandler : SavedRequestAwareAuthenticationSuccessHandler() {
+
+    @Throws(IOException::class, ServletException::class)
+    override fun onAuthenticationSuccess(request: HttpServletRequest, response: HttpServletResponse, authentication: Authentication) {
+        val loginFrom = request.getParameter("loginFrom")
+        val user: AuthUser = authentication.principal as AuthUser
+        val username: String = user.username
+//        val authorities = authentication.authorities
+        val msg = String.format("user %s ,login from %s", username, loginFrom)
+        logger.info(msg)
+        response.characterEncoding = "UTF-8"
+        response.contentType = "application/json"
+        logger.debug("登录通过")
+        response.writer.println("{\"ok\":\"1\",\"msg\":\"登录成功\"}")
+    }
+}

+ 17 - 0
src/main/kotlin/com/yaoxiang/planning/config/PlanningConfig.kt

@@ -0,0 +1,17 @@
+package com.yaoxiang.planning.config
+
+import com.alibaba.druid.pool.DruidDataSource
+import org.springframework.boot.context.properties.ConfigurationProperties
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import javax.sql.DataSource
+
+@Configuration
+class PlanningConfig{
+
+    @Bean //    @Profile("dev")
+    @ConfigurationProperties("spring.datasource")
+    fun dataSource(): DataSource {
+        return DruidDataSource()
+    }
+}

+ 52 - 0
src/main/kotlin/com/yaoxiang/planning/config/RedisConfig.kt

@@ -0,0 +1,52 @@
+package com.yaoxiang.planning.config
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect
+import com.fasterxml.jackson.annotation.PropertyAccessor
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.data.redis.connection.RedisConnectionFactory
+import org.springframework.data.redis.core.RedisTemplate
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer
+import org.springframework.data.redis.serializer.StringRedisSerializer
+import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession
+
+@Configuration
+@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400 * 7)
+class RedisConfig {
+
+//    @Bean
+//    fun cacheManager(factory: RedisConnectionFactory): RedisCacheManager {
+//        var redisCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(null)
+//        var redisCacheManager = RedisCacheManager
+//                .builder(RedisCacheWriter.nonLockingRedisCacheWriter(factory))
+//                .cacheDefaults(redisCacheConfig)
+//                .build()
+//        return redisCacheManager
+//    }
+
+    @Bean
+    fun redisTemplate(factory: RedisConnectionFactory): RedisTemplate<String, Any> {
+        val redisTemplate = RedisTemplate<String, Any>()
+        redisTemplate.setConnectionFactory(factory)
+
+        val jackson2JsonRedisSerializer: Jackson2JsonRedisSerializer<*> = Jackson2JsonRedisSerializer(Any::class.java)
+        val om = ObjectMapper()
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY)
+        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL)
+        jackson2JsonRedisSerializer.setObjectMapper(om)
+
+        val stringRedisSerializer = StringRedisSerializer()
+
+        redisTemplate.keySerializer = stringRedisSerializer
+        redisTemplate.hashKeySerializer = stringRedisSerializer
+        redisTemplate.valueSerializer = jackson2JsonRedisSerializer
+        redisTemplate.hashValueSerializer = jackson2JsonRedisSerializer
+        redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer)
+
+        redisTemplate.afterPropertiesSet()
+        return redisTemplate
+    }
+
+}

+ 40 - 0
src/main/kotlin/com/yaoxiang/planning/config/SwaggerConfig.kt

@@ -0,0 +1,40 @@
+package com.yaoxiang.planning.config
+
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.web.context.request.async.DeferredResult
+import springfox.documentation.builders.ApiInfoBuilder
+import springfox.documentation.builders.PathSelectors
+import springfox.documentation.builders.RequestHandlerSelectors
+import springfox.documentation.service.ApiInfo
+import springfox.documentation.spi.DocumentationType
+import springfox.documentation.spring.web.plugins.Docket
+import springfox.documentation.swagger2.annotations.EnableSwagger2
+
+
+@Configuration
+@EnableSwagger2
+class SwaggerConfig {
+
+    @Bean
+    fun createRestApi(): Docket {
+        return Docket(DocumentationType.SWAGGER_2)
+                .groupName("planning")
+                .genericModelSubstitutes(DeferredResult::class.java)
+                .useDefaultResponseMessages(false)
+                .forCodeGeneration(true)
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.yaoxiang.planning"))
+                .paths(PathSelectors.any())
+                .build()
+    }
+
+    private fun apiInfo(): ApiInfo {
+        return ApiInfoBuilder()
+                .title("planning api")
+                .description("使用Restful风格的Http访问协议")
+                .version("1.0")
+                .build()
+    }
+
+}

+ 23 - 0
src/main/kotlin/com/yaoxiang/planning/config/WebMvcConfig.kt

@@ -0,0 +1,23 @@
+package com.yaoxiang.planning.config
+
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.web.servlet.config.annotation.EnableWebMvc
+import org.springframework.web.servlet.config.annotation.ViewControllerRegistry
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
+
+@Configuration
+//@EnableWebMvc
+class WebMvcConfig {
+
+    @Bean
+    fun webMvcConfigurer(): WebMvcConfigurer {
+        return object : WebMvcConfigurer {
+            override fun addViewControllers(registry: ViewControllerRegistry) {
+                registry.addViewController("/login").setViewName("login")
+
+            }
+        }
+    }
+
+}

+ 136 - 0
src/main/kotlin/com/yaoxiang/planning/config/WebSecurityConfig.kt

@@ -0,0 +1,136 @@
+package com.yaoxiang.planning.config
+
+import com.yaoxiang.planning.model.AuthUser
+import com.yaoxiang.planning.service.UserService
+import mu.KotlinLogging
+import org.springframework.beans.factory.annotation.Value
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.builders.WebSecurity
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
+import org.springframework.security.core.Authentication
+import org.springframework.security.core.AuthenticationException
+import org.springframework.security.core.userdetails.UserDetailsService
+import org.springframework.security.core.userdetails.UsernameNotFoundException
+import org.springframework.security.crypto.password.PasswordEncoder
+import org.springframework.security.web.AuthenticationEntryPoint
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher
+import org.springframework.security.web.util.matcher.RequestMatcher
+import org.springframework.util.DigestUtils
+import javax.annotation.Resource
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+
+private val logger = KotlinLogging.logger {}
+
+@Configuration
+@EnableWebSecurity
+class WebSecurityConfig : WebSecurityConfigurerAdapter() {
+
+    @Resource
+    private lateinit var userService: UserService
+
+    @Value("\${web.loginPage:/login}")
+    private val loginPage: String? = null
+
+    @Value("\${web.logoutUrl:/logout}")
+    private var logoutUrl: String? = null
+
+    @Value("\${web.permitUrls:/**/*.png}")
+    private lateinit var permitUrls: Array<String>
+
+    @Bean
+    override fun userDetailsService(): UserDetailsService { //覆盖写userDetailsService方法 (1)
+        return UserDetailsService {
+            val optional = userService.findByUsername(it)
+            if (optional.isPresent) {
+                return@UserDetailsService AuthUser(optional.get())
+            }
+            throw UsernameNotFoundException("$it not found")
+        }
+    }
+
+    @Bean
+    fun loginSuccessHandler(): LoginSuccessHandler {
+        return LoginSuccessHandler()
+    }
+
+
+    @Bean
+    fun logoutSuccessHandler(): LogoutSuccessHandler {
+        return LogoutSuccessHandler { _: HttpServletRequest?, rep: HttpServletResponse, authentication: Authentication ->
+            logger.info { "logout success,user=${authentication.principal}" }
+            rep.characterEncoding = "UTF-8"
+            rep.contentType = "application/json"
+            rep.writer.println("{\"ok\":\"1\",\"msg\":\"注销成功\"}")
+        }
+    }
+
+
+    @Bean
+    fun webAuthenticationDetailsSource(): WebAuthenticationDetailsSource? {
+        return WebAuthenticationDetailsSource()
+    }
+
+    @Bean
+    fun authenticationEntryPoint(): AuthenticationEntryPoint? {
+        return AuthenticationEntryPoint { _: HttpServletRequest?, response: HttpServletResponse, _: AuthenticationException? -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED) }
+    }
+
+
+    @Bean
+    fun requestMatcher(): RequestMatcher {
+        return RequestMatcher {
+            "XMLHttpRequest" == it.getHeader("X-Requested-With") ||
+                    it.getHeader("Accept") != null && it.getHeader("Accept").contains("application/json")
+        }
+    }
+
+    override fun configure(auth: AuthenticationManagerBuilder) {
+        auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder())
+    }
+
+    @Bean
+    fun passwordEncoder(): PasswordEncoder {
+        return object : PasswordEncoder {
+            override fun encode(rawPassword: CharSequence): String {
+                return DigestUtils.md5DigestAsHex((rawPassword as String).toByteArray()).toUpperCase()
+            }
+
+            override fun matches(rawPassword: CharSequence, encodedPassword: String): Boolean {
+                return encodedPassword.equals(DigestUtils.md5DigestAsHex((rawPassword as String).toByteArray()), true)
+            }
+        }
+    }
+
+    override fun configure(http: HttpSecurity) {
+        http.csrf().disable()
+        http.headers().cacheControl().disable()
+        http.headers().frameOptions().sameOrigin()
+        http.authorizeRequests().antMatchers("/login.html", loginPage).permitAll()
+        http.authorizeRequests().anyRequest().authenticated()
+        http.formLogin().loginPage(loginPage).successHandler(loginSuccessHandler())
+                .authenticationDetailsSource(webAuthenticationDetailsSource())
+        http.logout().logoutUrl(logoutUrl).logoutRequestMatcher(AntPathRequestMatcher(logoutUrl))
+                .deleteCookies("JSESSIONID")
+                .logoutSuccessHandler(logoutSuccessHandler())
+        http.sessionManagement().invalidSessionUrl(loginPage)
+
+        http.exceptionHandling().defaultAuthenticationEntryPointFor(authenticationEntryPoint(), requestMatcher())
+    }
+
+
+    override fun configure(web: WebSecurity) {
+        web.ignoring().antMatchers("/resources/**", "/static/**")
+        web.ignoring().antMatchers("/**/*.js", "/lang/*.json", "/**/*.css", "/**/*.map", "/**/*.png",
+                "/**/*.jpg", "/**/*.woff", "/**/*.ttf", "/*.ico")
+        web.ignoring().antMatchers(*permitUrls)
+        web.ignoring().antMatchers("/doc.html")
+        web.ignoring().antMatchers("/open/**")
+    }
+}

+ 21 - 0
src/main/kotlin/com/yaoxiang/planning/config/WebSocketConfig.kt

@@ -0,0 +1,21 @@
+package com.yaoxiang.planning.config
+
+import org.springframework.context.annotation.Configuration
+import org.springframework.messaging.simp.config.MessageBrokerRegistry
+import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker
+import org.springframework.web.socket.config.annotation.StompEndpointRegistry
+import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer
+
+@Configuration
+@EnableWebSocketMessageBroker
+class WebSocketConfig : WebSocketMessageBrokerConfigurer {
+    override fun configureMessageBroker(config: MessageBrokerRegistry) {
+        config.enableSimpleBroker("/topic")
+        config.setApplicationDestinationPrefixes("/app")
+    }
+
+    override fun registerStompEndpoints(registry: StompEndpointRegistry) {
+        registry.addEndpoint("/stomp").withSockJS()
+    }
+}
+

+ 9 - 0
src/main/kotlin/com/yaoxiang/planning/domain/AnnualPlan.kt

@@ -0,0 +1,9 @@
+package com.yaoxiang.planning.domain
+
+import io.swagger.annotations.ApiModel
+import javax.persistence.Entity
+
+@Entity
+@ApiModel("年度计划")
+class AnnualPlan() : Planning() {
+}

+ 28 - 0
src/main/kotlin/com/yaoxiang/planning/domain/Authority.kt

@@ -0,0 +1,28 @@
+package com.yaoxiang.planning.domain
+
+import io.swagger.annotations.ApiModel
+import io.swagger.annotations.ApiModelProperty
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Index
+import javax.persistence.Table
+
+@Entity
+@ApiModel("权限")
+@Table(indexes = [Index(name = "nameTypeEndPointIndex", columnList = "name,type,endpoint", unique = true)])
+class Authority() : BaseLongEntity() {
+
+    @ApiModelProperty("名称")
+    @Column(columnDefinition = "varchar(64) COMMENT '名称'")
+    var name: String? = null
+
+    @ApiModelProperty("终端类型")
+    @Column(columnDefinition = "varchar(64) COMMENT '终端类型'")
+    var endpoint: String? = null
+
+    @ApiModelProperty("类型 fun business")
+    @Column(columnDefinition = "varchar(64) COMMENT '类型'")
+    var type: String? = null
+
+
+}

+ 24 - 0
src/main/kotlin/com/yaoxiang/planning/domain/BaseLongEntity.kt

@@ -0,0 +1,24 @@
+package com.yaoxiang.planning.domain
+
+import org.hibernate.annotations.CreationTimestamp
+import org.hibernate.annotations.UpdateTimestamp
+import java.io.Serializable
+import java.util.*
+import javax.persistence.GeneratedValue
+import javax.persistence.GenerationType
+import javax.persistence.Id
+import javax.persistence.MappedSuperclass
+
+@MappedSuperclass
+open class BaseLongEntity :Serializable{
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    open var id: Long? = null
+
+    @CreationTimestamp
+    open var createTime: Date? = null
+
+    @UpdateTimestamp
+    open var updateTime: Date? = null
+}

+ 35 - 0
src/main/kotlin/com/yaoxiang/planning/domain/Department.kt

@@ -0,0 +1,35 @@
+package com.yaoxiang.planning.domain
+
+import io.swagger.annotations.ApiModel
+import io.swagger.annotations.ApiModelProperty
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Index
+import javax.persistence.Table
+
+@Entity
+@ApiModel("部门信息")
+@Table(indexes = [Index(name = "nameIndex", columnList = "name", unique = true)])
+class Department() : BaseLongEntity() {
+
+    @ApiModelProperty("部门名称")
+    @Column(columnDefinition = "varchar(64) COMMENT '用户名/手机号码'")
+    var name: String? = null
+
+    @ApiModelProperty("职责")
+    @Column(columnDefinition = "mediumtext COMMENT '部门'")
+    var duty: String? = null
+
+    @ApiModelProperty("职责")
+    @Column(columnDefinition = "varchar(255) COMMENT '备注'")
+    var remark: String? = null
+
+    @ApiModelProperty("部门层级")
+    @Column(columnDefinition = "int(11) COMMENT '部门层级'")
+    var level: Int? = null
+
+    @ApiModelProperty("部门负责人Id")
+    @Column(columnDefinition = "int(20) COMMENT '部门负责人Id'")
+    var leader: Long? = null
+
+}

+ 40 - 0
src/main/kotlin/com/yaoxiang/planning/domain/Planning.kt

@@ -0,0 +1,40 @@
+package com.yaoxiang.planning.domain
+
+import io.swagger.annotations.ApiModel
+import io.swagger.annotations.ApiModelProperty
+import javax.persistence.*
+
+
+//@Entity
+@ApiModel("计划")
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+open class Planning() : BaseLongEntity() {
+
+    @ApiModelProperty("计划名称")
+    @Column(columnDefinition = "varchar(255) COMMENT '计划名称'")
+    var name: String? = null
+
+    @ApiModelProperty("计划内容")
+    @Column(columnDefinition = "mediumtext COMMENT '计划内容'")
+    var content: String? = null
+
+    @ApiModelProperty("计划状态 0未开始 1 进行中 2暂搁 3已完成 4中止")
+    @Column(columnDefinition = "int(11) COMMENT '计划状态 0未开始 1 进行中 2暂搁 3已完成 4中止'")
+    var status: Int? = null
+
+    @ApiModelProperty("评价")
+    @Column(columnDefinition = "mediumtext COMMENT '评价'")
+    var evaluation: String? = null
+
+    @ApiModelProperty("计划年份")
+    @Column(columnDefinition = "int(11) COMMENT '计划年份'")
+    var year: Int? = null
+
+    @ApiModelProperty("计划类型 1 年度计划 2 季度计划 3 周计划")
+    @Column(columnDefinition = "int(11) COMMENT '计划类型 1 年度计划 2 季度计划 3 周计划'")
+    var type: Int? = null
+
+    @Transient
+    @ApiModelProperty("计划项列表")
+    var items: List<PlanningItem>? = null
+}

+ 44 - 0
src/main/kotlin/com/yaoxiang/planning/domain/PlanningItem.kt

@@ -0,0 +1,44 @@
+package com.yaoxiang.planning.domain
+
+import io.swagger.annotations.ApiModel
+import io.swagger.annotations.ApiModelProperty
+import javax.persistence.Column
+import javax.persistence.Entity
+
+@Entity
+@ApiModel("分计划")
+class PlanningItem() : BaseLongEntity() {
+
+    @ApiModelProperty("分计划名称")
+    @Column(columnDefinition = "varchar(255) COMMENT '分计划名称'")
+    var name: String? = null
+
+    @ApiModelProperty("分计划内容")
+    @Column(columnDefinition = "mediumtext COMMENT '分计划内容'")
+    var content: String? = null
+
+    @ApiModelProperty("分计划状态")
+    @Column(columnDefinition = "int(11) COMMENT '分计划状态'")
+    var status: Int? = null
+
+    @ApiModelProperty("备注")
+    @Column(columnDefinition = "varchar(255) COMMENT '备注'")
+    var remark: String? = null
+
+    @ApiModelProperty("评价")
+    @Column(columnDefinition = "mediumtext COMMENT '评价'")
+    var evaluation: String? = null
+
+    @ApiModelProperty("分计划所属部门Id")
+    @Column(columnDefinition = "int(20) COMMENT '分计划所属部门Id'")
+    var departmentId: Long? = null
+
+    @ApiModelProperty("分计划序号")
+    @Column(columnDefinition = "int(11) COMMENT '分计序号'")
+    var indexes: Int? = null
+
+    @ApiModelProperty("计划Id")
+    @Column(columnDefinition = "int(20) COMMENT '计划Id'")
+    var planningId: Long? = null
+
+}

+ 25 - 0
src/main/kotlin/com/yaoxiang/planning/domain/QuarterlyPlan.kt

@@ -0,0 +1,25 @@
+package com.yaoxiang.planning.domain
+
+import io.swagger.annotations.ApiModel
+import io.swagger.annotations.ApiModelProperty
+import java.util.*
+import javax.persistence.Column
+import javax.persistence.Entity
+
+@Entity
+@ApiModel("季度计划")
+class QuarterlyPlan() : Planning() {
+
+    @ApiModelProperty("季度 1 2 3 4")
+    @Column(columnDefinition = "int(11) COMMENT '季度 1 2 3 4'")
+    var quarter: Int? = null
+
+    @ApiModelProperty("开始时间")
+    @Column(columnDefinition = "datetime COMMENT '开始时间'")
+    var startTime: Date? = null
+
+    @ApiModelProperty("结束时间")
+    @Column(columnDefinition = "datetime COMMENT '结束时间'")
+    var endTime: Date? = null
+
+}

+ 32 - 0
src/main/kotlin/com/yaoxiang/planning/domain/Role.kt

@@ -0,0 +1,32 @@
+package com.yaoxiang.planning.domain
+
+import io.swagger.annotations.ApiModel
+import io.swagger.annotations.ApiModelProperty
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Index
+import javax.persistence.Table
+
+@Entity
+@ApiModel("角色信息")
+@Table(indexes = [Index(name = "nameIndex", columnList = "name", unique = true)])
+class Role() : BaseLongEntity() {
+
+    @ApiModelProperty("角色名称")
+    @Column(columnDefinition = "varchar(64) COMMENT '角色名称'")
+    var name: String? = null
+
+    @ApiModelProperty("类型")
+    @Column(columnDefinition = "varchar(255) COMMENT '类型'")
+    var type: String? = null
+
+    @ApiModelProperty("父级ID")
+    @Column(columnDefinition = "int(20) COMMENT '父级ID'")
+    var parentId: Long? = null
+
+    @ApiModelProperty("角色优先级,0表示最高级,数字越大角色越低级")
+    @Column(columnDefinition = "int(11) COMMENT '父级ID'")
+    var level: Int? = null
+
+
+}

+ 55 - 0
src/main/kotlin/com/yaoxiang/planning/domain/UserInfo.kt

@@ -0,0 +1,55 @@
+package com.yaoxiang.planning.domain
+
+import io.swagger.annotations.ApiModel
+import io.swagger.annotations.ApiModelProperty
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Index
+import javax.persistence.Table
+
+
+@Entity
+@ApiModel("用户信息")
+@Table(indexes = [Index(name = "usernameIndex", columnList = "username", unique = true)])
+class UserInfo() : BaseLongEntity() {
+
+    @ApiModelProperty("用户名/手机号码")
+    @Column(columnDefinition = "varchar(64) COMMENT '用户名/手机号码'")
+    var username: String? = null
+
+    @ApiModelProperty("密码")
+    @Column(columnDefinition = "varchar(255) COMMENT '密码'")
+    var password: String? = null
+
+    @ApiModelProperty("昵称")
+    @Column(columnDefinition = "varchar(255) COMMENT '昵称'")
+    var name: String? = null
+
+    @ApiModelProperty("混淆")
+    @Column(columnDefinition = "varchar(255) COMMENT '混淆'")
+    var salt: String? = null
+
+    @ApiModelProperty("性别")
+    @Column(columnDefinition = "int(11) COMMENT '性别'")
+    var gender: Int? = null
+
+    @ApiModelProperty("年龄")
+    @Column(columnDefinition = "int(11) COMMENT '年龄'")
+    var age: Int? = null
+
+    @ApiModelProperty("手机号码")
+    @Column(columnDefinition = "varchar(255) COMMENT '手机号码'")
+    var phone: String? = null
+
+    @ApiModelProperty("是否启用")
+    @Column(columnDefinition = "bit(1) COMMENT '是否启用'")
+    var enabled:Boolean=true
+
+    @ApiModelProperty("部门Id")
+    @Column(columnDefinition = "int(20) COMMENT '部门Id'")
+    var departmentId: Long? = null
+
+    @ApiModelProperty("部门名称")
+    @Column(columnDefinition = "varchar(255) COMMENT '部门名称'")
+    var departmentName: String? = null
+}

+ 22 - 0
src/main/kotlin/com/yaoxiang/planning/domain/UserRole.kt

@@ -0,0 +1,22 @@
+package com.yaoxiang.planning.domain
+
+import io.swagger.annotations.ApiModel
+import io.swagger.annotations.ApiModelProperty
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Index
+import javax.persistence.Table
+
+@Entity
+@ApiModel("用户角色映射")
+@Table(indexes = [Index(name = "userRoleIndex", columnList = "userId,roleId", unique = true)])
+class UserRole() : BaseLongEntity() {
+
+    @ApiModelProperty("用户Id")
+    @Column(columnDefinition = "varchar(64) COMMENT '用户Id'")
+    var userId: String? = null
+
+    @ApiModelProperty("角色Id")
+    @Column(columnDefinition = "varchar(64) COMMENT '角色Id'")
+    var roleId: String? = null
+}

+ 25 - 0
src/main/kotlin/com/yaoxiang/planning/domain/WeeklyPlan.kt

@@ -0,0 +1,25 @@
+package com.yaoxiang.planning.domain
+
+import io.swagger.annotations.ApiModel
+import io.swagger.annotations.ApiModelProperty
+import java.util.*
+import javax.persistence.Column
+import javax.persistence.Entity
+
+@Entity
+@ApiModel("周计划")
+class WeeklyPlan() : Planning() {
+
+    @ApiModelProperty("计划所属用户Id")
+    @Column(columnDefinition = "int(20) COMMENT '计划所属用户Id'")
+    var userId: Long? = null
+
+    @Column(columnDefinition = "datetime COMMENT '开始时间'")
+    @ApiModelProperty("开始时间")
+    var startTime: Date? = null
+
+    @ApiModelProperty("开始时间")
+    @Column(columnDefinition = "datetime COMMENT '结束时间'")
+    var endTime: Date? = null
+
+}

+ 38 - 0
src/main/kotlin/com/yaoxiang/planning/model/AuthUser.kt

@@ -0,0 +1,38 @@
+package com.yaoxiang.planning.model
+
+import com.yaoxiang.planning.domain.UserInfo
+import org.springframework.security.core.GrantedAuthority
+import org.springframework.security.core.userdetails.UserDetails
+
+class AuthUser(var userInfo: UserInfo) : UserDetails {
+
+
+    override fun getAuthorities(): MutableCollection<out GrantedAuthority> {
+        return mutableListOf()
+    }
+
+    override fun isEnabled(): Boolean {
+        return userInfo.enabled
+    }
+
+    override fun getUsername(): String {
+        return userInfo.username!!
+    }
+
+    override fun isCredentialsNonExpired(): Boolean {
+        return true
+    }
+
+    override fun getPassword(): String {
+        return userInfo.password!!
+    }
+
+    override fun isAccountNonExpired(): Boolean {
+        return true
+    }
+
+    override fun isAccountNonLocked(): Boolean {
+        return true
+    }
+
+}

+ 41 - 0
src/main/kotlin/com/yaoxiang/planning/model/Reply.kt

@@ -0,0 +1,41 @@
+package com.yaoxiang.planning.model
+
+class Reply<T> {
+
+    var result: Boolean? = null
+    var message: String? = null
+    var code: Int? = null
+    var data: T? = null
+
+    constructor()
+
+    constructor(result: Boolean) {
+        this.result = result
+    }
+
+    constructor(result: Boolean, message: String, data: T? = null) {
+        this.result = result
+        this.message = message
+        code = if (result) 200 else 500
+        this.data = data
+    }
+
+    companion object {
+
+        fun <T> ok(): Reply<T> {
+            return Reply(true, "success", null)
+        }
+
+        fun <T> ok(data: T?): Reply<T> {
+            return Reply(true, "success", data)
+        }
+
+        fun <T> ok(data: T?, message: String): Reply<T> {
+            return Reply(true, message, data)
+        }
+
+        fun <T> fail(message: String = "fail"): Reply<T> {
+            return Reply(false, message)
+        }
+    }
+}

+ 12 - 0
src/main/kotlin/com/yaoxiang/planning/repository/UserInfoRepo.kt

@@ -0,0 +1,12 @@
+package com.yaoxiang.planning.repository
+
+import com.yaoxiang.planning.domain.UserInfo
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.stereotype.Repository
+import java.util.*
+
+@Repository
+interface UserInfoRepo : JpaRepository<UserInfo, Long> {
+
+    fun findByUsername(username: String): Optional<UserInfo>
+}

+ 18 - 0
src/main/kotlin/com/yaoxiang/planning/service/UserService.kt

@@ -0,0 +1,18 @@
+package com.yaoxiang.planning.service
+
+import com.yaoxiang.planning.domain.UserInfo
+import com.yaoxiang.planning.repository.UserInfoRepo
+import org.springframework.stereotype.Service
+import java.util.*
+import javax.annotation.Resource
+
+@Service
+class UserService {
+
+    @Resource
+    private lateinit var userInfoRepo: UserInfoRepo
+
+    fun findByUsername(username: String): Optional<UserInfo> {
+        return userInfoRepo.findByUsername(username)
+    }
+}

+ 7 - 0
src/main/resources/application-dev.properties

@@ -0,0 +1,7 @@
+spring.redis.host=yaoxiangedu.com
+spring.redis.port=32578
+spring.redis.password=134679258Aa_
+
+spring.datasource.url=jdbc:mysql://yaoxiangedu.com:30875/planning?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
+spring.datasource.username=planning
+spring.datasource.password=134679258Aa_

+ 18 - 0
src/main/resources/application.properties

@@ -0,0 +1,18 @@
+spring.profiles.active=dev
+
+spring.redis.lettuce.pool.max-active=8
+spring.redis.lettuce.pool.max-wait=-1
+spring.redis.lettuce.pool.max-idle=8
+spring.redis.lettuce.pool.min-idle=0
+spring.redis.timeout=3000
+
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+spring.jpa.show-sql=true
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
+spring.jpa.hibernate.ddl-auto=update
+
+spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
+spring.datasource.validaionQuery=select 1
+spring.datasource.testWhileIdle=true
+spring.datasource.testOnBorrow=false
+spring.datasource.testOnReturn=false

+ 52 - 0
src/main/resources/static/app.js

@@ -0,0 +1,52 @@
+var stompClient = null;
+
+function setConnected(connected) {
+    $("#connect").prop("disabled", connected);
+    $("#disconnect").prop("disabled", !connected);
+    if (connected) {
+        $("#conversation").show();
+    }
+    else {
+        $("#conversation").hide();
+    }
+    $("#greetings").html("");
+}
+
+function connect() {
+    var socket = new SockJS('/stomp');
+    stompClient = Stomp.over(socket);
+    stompClient.connect({}, function (frame) {
+        setConnected(true);
+        console.log('Connected: ' + frame);
+        stompClient.subscribe('/topic/greetings', function (greeting) {
+//            showGreeting(JSON.parse(greeting.body).content);
+            showGreeting(greeting.body);
+        });
+    });
+}
+
+function disconnect() {
+    if (stompClient !== null) {
+        stompClient.disconnect();
+    }
+    setConnected(false);
+    console.log("Disconnected");
+}
+
+function sendName() {
+//    stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
+    stompClient.send("/app/hello", {}, $("#name").val());
+}
+
+function showGreeting(message) {
+    $("#greetings").append("<tr><td>" + message + "</td></tr>");
+}
+
+$(function () {
+    $("form").on('submit', function (e) {
+        e.preventDefault();
+    });
+    $( "#connect" ).click(function() { connect(); });
+    $( "#disconnect" ).click(function() { disconnect(); });
+    $( "#send" ).click(function() { sendName(); });
+});

BIN
src/main/resources/static/favicon.ico


+ 14 - 0
src/main/resources/static/main.css

@@ -0,0 +1,14 @@
+body {
+    background-color: #f5f5f5;
+}
+
+#main-content {
+    max-width: 940px;
+    padding: 2em 3em;
+    margin: 0 auto 20px;
+    background-color: #fff;
+    border: 1px solid #e5e5e5;
+    -webkit-border-radius: 5px;
+    -moz-border-radius: 5px;
+    border-radius: 5px;
+}

+ 52 - 0
src/main/resources/static/websocket.html

@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Hello WebSocket</title>
+    <link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet">
+    <link href="/main.css" rel="stylesheet">
+    <script src="/webjars/jquery/jquery.min.js"></script>
+    <script src="/webjars/sockjs-client/sockjs.min.js"></script>
+    <script src="/webjars/stomp-websocket/stomp.min.js"></script>
+    <script src="/app.js"></script>
+</head>
+<body>
+<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
+    enabled. Please enable Javascript and reload this page!</h2></noscript>
+<div id="main-content" class="container">
+    <div class="row">
+        <div class="col-md-6">
+            <form class="form-inline">
+                <div class="form-group">
+                    <label for="connect">WebSocket connection:</label>
+                    <button id="connect" class="btn btn-default" type="submit">Connect</button>
+                    <button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
+                    </button>
+                </div>
+            </form>
+        </div>
+        <div class="col-md-6">
+            <form class="form-inline">
+                <div class="form-group">
+                    <label for="name">What is your name?</label>
+                    <input type="text" id="name" class="form-control" placeholder="Your name here...">
+                </div>
+                <button id="send" class="btn btn-default" type="submit">Send</button>
+            </form>
+        </div>
+    </div>
+    <div class="row">
+        <div class="col-md-12">
+            <table id="conversation" class="table table-striped">
+                <thead>
+                <tr>
+                    <th>Greetings</th>
+                </tr>
+                </thead>
+                <tbody id="greetings">
+                </tbody>
+            </table>
+        </div>
+    </div>
+</div>
+</body>
+</html>

+ 16 - 0
src/main/resources/templates/login.html

@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="UTF-8">
+    <title>login</title>
+</head>
+<body>
+<h2>Login</h2>
+<form action="/login" method="post">
+    <input type="text" name="username" value=""/><br/>
+    <input type="password" name="password" value=""/><br/>
+    <input type="text" name="loginFrom" value="student"><br/>
+    <input type="submit" name="submit" value="登录"><br/>
+</form>
+</body>
+</html>

+ 13 - 0
src/test/kotlin/com/yaoxiang/planning/PlaningApplicationTests.kt

@@ -0,0 +1,13 @@
+package com.yaoxiang.planning
+
+import org.junit.jupiter.api.Test
+import org.springframework.boot.test.context.SpringBootTest
+
+@SpringBootTest
+class PlaningApplicationTests {
+
+	@Test
+	fun contextLoads() {
+	}
+
+}