Explorar o código

init:初始化0.0.1版本

刘传伟 hai 7 meses
achega
e0ebee1a80
Modificáronse 68 ficheiros con 4163 adicións e 0 borrados
  1. 29 0
      .gitignore
  2. 129 0
      pom.xml
  3. 107 0
      src/main/java/Ognl.java
  4. 31 0
      src/main/java/com/anyway/favor/controller/DemoController.java
  5. 167 0
      src/main/java/com/anyway/favor/controller/FavorController.java
  6. 116 0
      src/main/java/com/anyway/favor/controller/FavorItemController.java
  7. 21 0
      src/main/java/com/anyway/favor/controller/IndexController.java
  8. 198 0
      src/main/java/com/anyway/favor/controller/PersonController.java
  9. 70 0
      src/main/java/com/anyway/favor/mapper/FavorItemMapper.java
  10. 56 0
      src/main/java/com/anyway/favor/mapper/FavorMapper.java
  11. 83 0
      src/main/java/com/anyway/favor/mapper/PersonMapper.java
  12. 62 0
      src/main/java/com/anyway/favor/mapper/UserMapper.java
  13. 28 0
      src/main/java/com/anyway/favor/model/BaseModel.java
  14. 56 0
      src/main/java/com/anyway/favor/model/Favor.java
  15. 55 0
      src/main/java/com/anyway/favor/model/FavorItem.java
  16. 72 0
      src/main/java/com/anyway/favor/model/Person.java
  17. 27 0
      src/main/java/com/anyway/favor/model/User.java
  18. 57 0
      src/main/java/com/anyway/favor/service/FavorItemService.java
  19. 57 0
      src/main/java/com/anyway/favor/service/FavorService.java
  20. 71 0
      src/main/java/com/anyway/favor/service/PersonService.java
  21. 56 0
      src/main/java/com/anyway/favor/service/UserService.java
  22. 53 0
      src/main/java/com/anyway/favor/service/impl/FavorItemServiceImpl.java
  23. 80 0
      src/main/java/com/anyway/favor/service/impl/FavorServiceImpl.java
  24. 98 0
      src/main/java/com/anyway/favor/service/impl/PersonServiceImpl.java
  25. 47 0
      src/main/java/com/anyway/favor/service/impl/UserServiceImpl.java
  26. 37 0
      src/main/java/com/anyway/util/Couple.java
  27. 31 0
      src/main/java/com/anyway/util/Page.java
  28. 33 0
      src/main/java/com/anyway/util/PageQuery.java
  29. 53 0
      src/main/java/com/anyway/util/PageUtils.java
  30. 102 0
      src/main/java/com/anyway/util/R.java
  31. 32 0
      src/main/java/com/anyway/util/SessionUtils.java
  32. 37 0
      src/main/java/com/anyway/util/SpringUtils.java
  33. 45 0
      src/main/resources/applicationContext.xml
  34. 72 0
      src/main/resources/init.sql
  35. 163 0
      src/main/resources/mapper/FavorItemMapper.xml
  36. 91 0
      src/main/resources/mapper/FavorMapper.xml
  37. 198 0
      src/main/resources/mapper/PersonMapper.xml
  38. 83 0
      src/main/resources/mapper/UserMapper.xml
  39. 10 0
      src/main/resources/mybatis-config.xml
  40. 9 0
      src/main/webapp/WEB-INF/pages/employee/list.jsp
  41. 305 0
      src/main/webapp/WEB-INF/pages/favor/add.jsp
  42. 67 0
      src/main/webapp/WEB-INF/pages/favor/detail.jsp
  43. 46 0
      src/main/webapp/WEB-INF/pages/favor/item/list.jsp
  44. 206 0
      src/main/webapp/WEB-INF/pages/favor/list.jsp
  45. 9 0
      src/main/webapp/WEB-INF/pages/hello.jsp
  46. 10 0
      src/main/webapp/WEB-INF/pages/index.jsp
  47. 169 0
      src/main/webapp/WEB-INF/pages/person/edit.jsp
  48. 269 0
      src/main/webapp/WEB-INF/pages/person/list.jsp
  49. 32 0
      src/main/webapp/WEB-INF/spring-servlet.xml
  50. 40 0
      src/main/webapp/WEB-INF/web.xml
  51. 23 0
      src/main/webapp/asserts/js/custom.js
  52. 138 0
      src/main/webapp/asserts/js/favor.js
  53. 1 0
      src/main/webapp/asserts/js/jquery-3.7.1.min.js
  54. 0 0
      src/main/webapp/asserts/layui/css/layui.css
  55. 1 0
      src/main/webapp/asserts/layui/css/modules/code.css
  56. 0 0
      src/main/webapp/asserts/layui/css/modules/laydate/default/laydate.css
  57. BIN=BIN
      src/main/webapp/asserts/layui/css/modules/layer/default/icon-ext.png
  58. BIN=BIN
      src/main/webapp/asserts/layui/css/modules/layer/default/icon.png
  59. 0 0
      src/main/webapp/asserts/layui/css/modules/layer/default/layer.css
  60. BIN=BIN
      src/main/webapp/asserts/layui/css/modules/layer/default/loading-0.gif
  61. BIN=BIN
      src/main/webapp/asserts/layui/css/modules/layer/default/loading-1.gif
  62. BIN=BIN
      src/main/webapp/asserts/layui/css/modules/layer/default/loading-2.gif
  63. BIN=BIN
      src/main/webapp/asserts/layui/font/iconfont.eot
  64. 25 0
      src/main/webapp/asserts/layui/font/iconfont.svg
  65. BIN=BIN
      src/main/webapp/asserts/layui/font/iconfont.ttf
  66. BIN=BIN
      src/main/webapp/asserts/layui/font/iconfont.woff
  67. BIN=BIN
      src/main/webapp/asserts/layui/font/iconfont.woff2
  68. 0 0
      src/main/webapp/asserts/layui/layui.js

+ 29 - 0
.gitignore

@@ -0,0 +1,29 @@
+# Idea file
+.idea/
+
+# Compiled class directory
+target/
+
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*

+ 129 - 0
pom.xml

@@ -0,0 +1,129 @@
+<?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.anyway.favor</groupId>
+    <artifactId>favor-bill</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>war</packaging>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <springframework.version>5.2.15.RELEASE</springframework.version>
+    </properties>
+    <dependencies>
+        <!--Spring核心依赖-->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-core</artifactId>
+            <version>${springframework.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+            <version>${springframework.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <version>${springframework.version}</version>
+        </dependency>
+
+        <!--SpringMVC依赖-->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+            <version>${springframework.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <version>4.0.1</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/taglibs/standard -->
+        <dependency>
+            <groupId>taglibs</groupId>
+            <artifactId>standard</artifactId>
+            <version>1.1.2</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>jstl</artifactId>
+            <version>1.2</version>
+        </dependency>
+
+
+        <!-- 消息转换器处理Json格式数据 -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.15.2</version>
+        </dependency>
+
+        <!--mysql驱动-->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.28</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-jdbc</artifactId>
+            <version>${springframework.version}</version>
+        </dependency>
+        <!--mybatis-->
+        <dependency>
+            <groupId>org.mybatis</groupId>
+            <artifactId>mybatis</artifactId>
+            <version>3.5.9</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis</groupId>
+            <artifactId>mybatis-spring</artifactId>
+            <version>2.1.1</version>
+        </dependency>
+        <!--mybatis分页-->
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper</artifactId>
+            <version>6.1.0</version>
+        </dependency>
+        <!--mybatis plus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus</artifactId>
+            <version>3.5.3.2</version>
+        </dependency>
+
+        <!--lombok-->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.24</version>
+        </dependency>
+
+        <!--加载日志-->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.25</version>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <version>1.2.17</version>
+        </dependency>
+
+        <!-- StringUtils -->
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.6</version>
+        </dependency>
+
+    </dependencies>
+</project>

+ 107 - 0
src/main/java/Ognl.java

@@ -0,0 +1,107 @@
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+
+
+
+
+/**
+ * Ognl工具类,主要是为了在ognl表达式访问静态方法时可以减少长长的类名称编写
+ * Ognl访问静态方法的表达式为: @class@method(args)
+ *
+ * 示例使用:
+ * <pre>
+ * 	&lt;if test="@Ognl@isNotEmpty(userId)">
+ *		and user_id = #{userId}
+ *	&lt;/if>
+ * </pre>
+ * @author badqiu
+ *
+ */
+public class Ognl {
+
+    /**
+     * 可以用于判断String,Map,Collection,Array是否为空
+     * @param o
+     * @return
+     */
+    @SuppressWarnings("rawtypes")
+    public static boolean isEmpty(Object o) throws IllegalArgumentException {
+        if(o == null) {return true;}
+
+        if(o instanceof String) {
+            if(((String)o).length() == 0){
+                return true;
+            }
+        } else if(o instanceof Collection) {
+            if(((Collection)o).isEmpty()){
+                return true;
+            }
+        } else if(o.getClass().isArray()) {
+            if(Array.getLength(o) == 0){
+                return true;
+            }
+        } else if(o instanceof Map) {
+            if(((Map)o).isEmpty()){
+                return true;
+            }
+        }else {
+            return false;
+//			throw new IllegalArgumentException("Illegal argument type,must be : Map,Collection,Array,String. but was:"+o.getClass());
+        }
+
+        return false;
+    }
+
+    /**
+     * 可以用于判断 Map,Collection,String,Array是否不为空
+     * @param c
+     * @return
+     */
+    public static boolean isNotEmpty(Object o) {
+        return !isEmpty(o);
+    }
+
+    public static boolean isNotBlank(Object o) {
+        return !isBlank(o);
+    }
+
+    public static boolean isNumber(Object o) {
+        if(o == null) {return false;}
+        if(o instanceof Number) {
+            return true;
+        }
+        if(o instanceof String) {
+            String str = (String)o;
+            if(str.length() == 0) {return false;}
+            if(str.trim().length() == 0) {return false;}
+            return org.apache.commons.lang.StringUtils.isNumeric(str);
+        }
+        return false;
+    }
+
+    public static boolean isBlank(Object o) {
+        if(o == null){
+            return true;
+        }
+        if(o instanceof String) {
+            String str = (String)o;
+            return isBlank(str);
+        }
+        return false;
+    }
+
+    public static boolean isBlank(String str) {
+        if(str == null || str.length() == 0) {
+            return true;
+        }
+
+        for (int i = 0; i < str.length(); i++) {
+            if (!Character.isWhitespace(str.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}

+ 31 - 0
src/main/java/com/anyway/favor/controller/DemoController.java

@@ -0,0 +1,31 @@
+package com.anyway.favor.controller;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 演示案例
+ * @author liuchuanwei.ex
+ * @date 2024/1/23
+ */
+@Controller
+@RequestMapping("/demo")
+public class DemoController {
+
+    @RequestMapping("/hello")
+    public String hello() {
+        return "hello";
+    }
+
+    @ResponseBody
+    @RequestMapping("/bye")
+    public Map<String, String> bye() {
+        Map<String, String> map = new HashMap<>();
+        map.put("bye", "再见");
+        return map;
+    }
+}

+ 167 - 0
src/main/java/com/anyway/favor/controller/FavorController.java

@@ -0,0 +1,167 @@
+package com.anyway.favor.controller;
+
+import com.anyway.favor.model.Favor;
+import com.anyway.favor.model.FavorItem;
+import com.anyway.favor.model.Person;
+import com.anyway.favor.model.User;
+import com.anyway.favor.service.FavorItemService;
+import com.anyway.favor.service.FavorService;
+import com.anyway.favor.service.PersonService;
+import com.anyway.util.PageQuery;
+import com.anyway.util.R;
+import com.anyway.util.SessionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.ModelAndView;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 人情控制器
+ *
+ * @author liuchuanwei
+ * @date 2024-02-24
+ */
+@Controller
+@RequestMapping("/favor")
+public class FavorController {
+    @Autowired
+    private FavorService favorService;
+    @Autowired
+    private FavorItemService favorItemService;
+    @Autowired
+    private PersonService personService;
+
+    /**
+     * 跳转列表页面
+     *
+     * @param mv
+     * @return
+     */
+    @RequestMapping("/toList")
+    public ModelAndView toList(@RequestParam Map<String, Object> map, ModelAndView mv) {
+        User currentUser = SessionUtils.currentUser();
+        map.put("createBy", currentUser.getId());
+        if(!map.containsKey("sortName")) {
+            map.put("sortName", "occur_date");
+            map.put("sortOrder", "asc");
+        }
+        List<Favor> favorList = favorService.findByCondition(map);
+        mv.addObject("favorList", favorList);
+        mv.setViewName("/favor/list");
+        return mv;
+    }
+
+
+    /**
+     * 人情事件列表
+     *
+     * @param pageQuery
+     * @return
+     */
+    @ResponseBody
+    @RequestMapping("/listPage")
+    public R listPage(@RequestBody PageQuery<Map<String, Object>> pageQuery) {
+        User currentUser = SessionUtils.currentUser();
+        Map<String, Object> mapCondition = pageQuery.getTerms();
+        //查询和当前登录用户相关的人员
+        mapCondition.put("relatedId", currentUser.getId());
+        List<Favor> favorList = favorService.findPage(pageQuery);
+        return R.page(favorList, pageQuery.getPage());
+    }
+
+    /**
+     * 跳转新增页面
+     *
+     * @param mv
+     * @return
+     */
+    @RequestMapping("/toAdd")
+    public ModelAndView toAdd(ModelAndView mv, @RequestParam(required = false) Long personId, @RequestParam(required = false) String sourcePersonIds) {
+        mv.setViewName("favor/add");
+        //人员列表
+        Map<String, Object> map = new HashMap<>();
+        map.put("relatedId", SessionUtils.currentUserId());
+        List<Person> personList = personService.findByCondition(map);
+        mv.addObject("personList", personList);
+        if (!StringUtils.isEmpty(sourcePersonIds)) {
+            map.put("idList", Arrays.asList(sourcePersonIds.split(",")));
+            List<Person> sourcePersonList = personService.findByCondition(map);
+            mv.addObject("sourcePersonList", sourcePersonList);
+        }
+        Person person = personService.findById(personId != null ? personId : 1L);
+        mv.addObject("person", person);
+        return mv;
+    }
+
+    /**
+     * 添加人情
+     *
+     * @param favor
+     * @return
+     */
+    @ResponseBody
+    @RequestMapping("/add")
+    public R add(@RequestBody Favor favor) {
+        User currentUser = SessionUtils.currentUser();
+        favor.setCreateBy(currentUser.getId());
+        favor.setModifyBy(currentUser.getId());
+        return favorService.add(favor) ? R.ok() : R.fail();
+    }
+
+    /**
+     * 跳转编辑人情页面
+     *
+     * @return
+     */
+    @RequestMapping("/edit/{id}")
+    public ModelAndView edit(@PathVariable Long id, ModelAndView mv) {
+        mv.setViewName("favor/add");
+        Favor favor = favorService.findById(id);
+        List<FavorItem> favorItemList = favorItemService.findByFavorId(favor.getId());
+        favor.setFavorItemList(favorItemList);
+        mv.addObject("favor", favor);
+        //人员列表
+        List<Person> personList = personService.findAll();
+        mv.addObject("personList", personList);
+        return mv;
+    }
+
+    /**
+     * 更新人情
+     *
+     * @param favor
+     * @return
+     */
+    @ResponseBody
+    @RequestMapping("/update")
+    public R update(@RequestBody Favor favor) {
+        User currentUser = SessionUtils.currentUser();
+        favor.setModifyBy(currentUser.getId());
+        return favorService.update(favor) ? R.ok() : R.fail();
+    }
+
+    /**
+     * 跳转人情详情页面
+     *
+     * @return
+     */
+    @RequestMapping("/detail/{id}")
+    public ModelAndView detail(@PathVariable Long id, ModelAndView mv) {
+        mv.setViewName("/favor/detail");
+        Favor favor = favorService.findById(id);
+        List<FavorItem> favorItemList = favorItemService.findByFavorId(favor.getId());
+        favor.setFavorItemList(favorItemList);
+        mv.addObject("favor", favor);
+        //人员列表
+        List<Person> personList = personService.findAll();
+        mv.addObject("personList", personList);
+        return mv;
+    }
+
+}

+ 116 - 0
src/main/java/com/anyway/favor/controller/FavorItemController.java

@@ -0,0 +1,116 @@
+package com.anyway.favor.controller;
+
+import com.anyway.favor.model.Favor;
+import com.anyway.favor.model.FavorItem;
+import com.anyway.favor.model.Person;
+import com.anyway.favor.model.User;
+import com.anyway.favor.service.FavorItemService;
+import com.anyway.favor.service.FavorService;
+import com.anyway.favor.service.PersonService;
+import com.anyway.util.R;
+import com.anyway.util.SessionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 人情明细控制器
+ *
+ * @author liuchuanwei
+ * @date 2024-02-24
+ */
+@Controller
+@RequestMapping("/favorItem")
+public class FavorItemController {
+    @Autowired
+    private FavorService favorService;
+    @Autowired
+    private FavorItemService favorItemService;
+    @Autowired
+    private PersonService personService;
+
+    /**
+     * 人情往来明细
+     *
+     * @param favorId
+     * @return
+     */
+    @RequestMapping("/listAll/{favorId}")
+    @ResponseBody
+    public R listAll(@PathVariable Long favorId) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("favorId", favorId);
+        List<FavorItem> favorItemList = favorItemService.findByCondition(map);
+        return R.data(favorItemList);
+    }
+
+    /**
+     * 跳转新增页面
+     *
+     * @param mv
+     * @return
+     */
+    @RequestMapping("/toAdd")
+    public ModelAndView toAdd(ModelAndView mv) {
+        mv.setViewName("edit");
+        //人员列表
+        Map<String, Object> map = new HashMap<>();
+        map.put("createBy", SessionUtils.currentUserId());
+        List<Person> personList = personService.findByCondition(map);
+        mv.addObject("personList", personList);
+        return mv;
+    }
+
+    /**
+     * 添加人情
+     *
+     * @param favor
+     * @return
+     */
+    @ResponseBody
+    @RequestMapping("/add")
+    public R add(@RequestBody Favor favor) {
+        User currentUser = SessionUtils.currentUser();
+        favor.setCreateBy(currentUser.getId());
+        favor.setModifyBy(currentUser.getId());
+        return favorService.add(favor) ? R.ok() : R.fail();
+    }
+
+    /**
+     * 跳转编辑人情页面
+     *
+     * @return
+     */
+    @RequestMapping("/edit/{id}")
+    public ModelAndView edit(@PathVariable Long id, ModelAndView mv) {
+        mv.setViewName("edit2");
+        Favor favor = favorService.findById(id);
+        mv.addObject("favor", favor);
+        //人员列表
+        List<Person> personList = personService.findAll();
+        mv.addObject("personList", personList);
+        return mv;
+    }
+
+    /**
+     * 更新人情
+     *
+     * @param favor
+     * @return
+     */
+    @ResponseBody
+    @RequestMapping("/update")
+    public R update(Favor favor) {
+        User currentUser = SessionUtils.currentUser();
+        favor.setModifyBy(currentUser.getId());
+        return favorService.update(favor) ? R.ok() : R.fail();
+    }
+}

+ 21 - 0
src/main/java/com/anyway/favor/controller/IndexController.java

@@ -0,0 +1,21 @@
+package com.anyway.favor.controller;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * 首页控制器
+ *
+ * @author liuchuanwei
+ * @date 2024-02-21
+ */
+@Controller
+@RequestMapping("/index")
+public class IndexController {
+
+    @RequestMapping("/")
+    public String index() {
+        return "index";
+    }
+
+}

+ 198 - 0
src/main/java/com/anyway/favor/controller/PersonController.java

@@ -0,0 +1,198 @@
+package com.anyway.favor.controller;
+
+import com.anyway.favor.model.Person;
+import com.anyway.favor.model.User;
+import com.anyway.favor.service.PersonService;
+import com.anyway.util.Couple;
+import com.anyway.util.PageQuery;
+import com.anyway.util.R;
+import com.anyway.util.SessionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.ModelAndView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 人员控制器
+ *
+ * @author liuchuanwei
+ * @date 2024-02-20
+ */
+@Controller
+@RequestMapping("/person")
+public class PersonController {
+    @Autowired
+    private PersonService personService;
+
+    /**
+     * 跳转添加人员页面
+     *
+     * @return
+     */
+    @RequestMapping("/toAdd")
+    public ModelAndView toAdd(ModelAndView mv) {
+        User currentUser = SessionUtils.currentUser();
+        Map<String, Object> mapCondition = new HashMap<>();
+        mapCondition.put("relatedId", currentUser.getId());
+        //母亲列表
+        mapCondition.put("gender", "F");
+        mapCondition.put("maritalStatus", "1");
+        List<Person> motherPersonList = personService.findByCondition(mapCondition);
+        mv.addObject("motherPersonOptionList", Couple.toList(motherPersonList, e -> e.getCallName() + "(" + e.getName() + ")", e -> String.valueOf(e.getId())));
+        //父亲列表
+        mapCondition.put("gender", "M");
+        List<Person> fatherPersonList = personService.findByCondition(mapCondition);
+        mv.addObject("fatherPersonOptionList", Couple.toList(fatherPersonList, e -> e.getCallName() + "(" + e.getName() + ")", e -> String.valueOf(e.getId())));
+        //配偶列表
+        mapCondition.put("gender", null);
+        mapCondition.put("maritalStatus", null);
+        mapCondition.put("emptyMate", true);
+        mapCondition.put("sortName", "marital_status");
+        mapCondition.put("sortOrder", "asc");
+        List<Person> matePersonList = personService.findByCondition(mapCondition);
+        mv.addObject("matePersonOptionList", Couple.toList(matePersonList, e -> e.getCallName() + "(" + e.getName() + ")", e -> String.valueOf(e.getId())));
+        mv.setViewName("/person/edit");
+        return mv;
+    }
+
+    /**
+     * 添加人员
+     *
+     * @param person
+     * @return
+     */
+    @ResponseBody
+    @RequestMapping("/add")
+    public R add(@RequestBody Person person) {
+        User currentUser = SessionUtils.currentUser();
+        person.setRelatedId(currentUser.getId());
+        person.setCreateBy(currentUser.getId());
+        person.setModifyBy(currentUser.getId());
+        return personService.add(person) ? R.ok() : R.fail();
+    }
+
+    /**
+     * 跳转编辑人员页面
+     *
+     * @return
+     */
+    @RequestMapping("/edit/{id}")
+    public ModelAndView edit(@PathVariable Long id) {
+        ModelAndView mv = new ModelAndView();
+        Person person = personService.findById(id);
+        mv.addObject("person", person);
+        mv.setViewName("/person/edit");
+
+        User currentUser = SessionUtils.currentUser();
+        Map<String, Object> mapCondition = new HashMap<>();
+        mapCondition.put("relatedId", currentUser.getId());
+        //母亲列表(女且已婚)
+        mapCondition.put("gender", "F");
+        mapCondition.put("maritalStatus", "1");
+        List<Person> motherPersonList = personService.findByCondition(mapCondition);
+        mv.addObject("motherPersonOptionList", Couple.toList(motherPersonList, e -> e.getCallName() + "(" + e.getName() + ")", e -> String.valueOf(e.getId())));
+        //父亲列表(男且已婚)
+        mapCondition.put("gender", "M");
+        List<Person> fatherPersonList = personService.findByCondition(mapCondition);
+        mv.addObject("fatherPersonOptionList", Couple.toList(fatherPersonList, e -> e.getCallName() + "(" + e.getName() + ")", e -> String.valueOf(e.getId())));
+        //配偶列表(男/女且配偶为空且未婚)
+        mapCondition.put("gender", "F".equals(person.getGender()) ? "M" : "F");
+        mapCondition.put("emptyMate", true);
+        mapCondition.put("maritalStatus", "0");
+        List<Person> matePersonList = personService.findByCondition(mapCondition);
+        mv.addObject("matePersonOptionList", Couple.toList(matePersonList, e -> e.getCallName() + "(" + e.getName() + ")", e -> String.valueOf(e.getId())));
+        return mv;
+    }
+    /**
+     * 更新人员
+     *
+     * @param person
+     * @return
+     */
+    @ResponseBody
+    @RequestMapping("/update")
+    public R update(@RequestBody Person person) {
+        User currentUser = SessionUtils.currentUser();
+        person.setModifyBy(currentUser.getId());
+        return personService.update(person) ? R.ok() : R.fail();
+    }
+
+    /**
+     * 跳转人员列表页面
+     *
+     * @return
+     */
+    @RequestMapping("/toList")
+    public ModelAndView toList(@RequestParam Map<String, Object> mapCondition) {
+        ModelAndView mv = new ModelAndView();
+        //查询和当前登录用户相关的人员
+        User currentUser = SessionUtils.currentUser();
+        mapCondition.put("relatedId", currentUser.getId());
+        List<Person> personList = personService.findByCondition(mapCondition);
+        mv.addObject("familyPersonList", personList);
+        mv.setViewName("/person/list");
+        return mv;
+    }
+
+    /**
+     * 人员列表
+     *
+     * @param pageQuery
+     * @return
+     */
+    @ResponseBody
+    @RequestMapping("/listPage")
+    public R listPage(@RequestBody PageQuery<Map<String, Object>> pageQuery) {
+        User currentUser = SessionUtils.currentUser();
+        Map<String, Object> mapCondition = pageQuery.getTerms();
+        //查询和当前登录用户相关的人员
+        mapCondition.put("relatedId", currentUser.getId());
+        List<Person> personList = personService.findPage(pageQuery);
+        return R.page(personList, pageQuery.getPage());
+    }
+
+    /**
+     * 人员列表选项
+     *
+     * @return
+     */
+    @ResponseBody
+    @RequestMapping("/listOption")
+    public List<Couple> listOption(Person person) {
+        User currentUser = SessionUtils.currentUser();
+        Map<String, Object> mapCondition = new HashMap<>();
+        if (!StringUtils.isEmpty(person.getGender())) {
+            mapCondition.put("gender", person.getGender());
+        }
+        if (person.getMaritalStatus() != null) {
+            mapCondition.put("maritalStatus", person.getMaritalStatus());
+        }
+        mapCondition.put("relatedId", currentUser.getId());
+        List<Person> personList = personService.findByCondition(mapCondition);
+        List<Couple> coupleList = new ArrayList<>();
+        for (Person p : personList) {
+            coupleList.add(new Couple(p.getId().toString(), p.getCallName() + "("+p.getName()+")"));
+        }
+        return coupleList;
+    }
+
+
+    /**
+     * 删除人员
+     *
+     * @param id
+     * @return
+     */
+    @ResponseBody
+    @RequestMapping("/del/{id}")
+    public R del(@PathVariable Long id) {
+        int c = personService.delete(id);
+        return  c > 0? R.ok() : R.fail();
+    }
+}

+ 70 - 0
src/main/java/com/anyway/favor/mapper/FavorItemMapper.java

@@ -0,0 +1,70 @@
+package com.anyway.favor.mapper;
+
+import com.anyway.favor.model.FavorItem;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 人情明细数据层
+ *
+ * @author liuchuanwei
+ * @date 2024-02-24
+ */
+public interface FavorItemMapper {
+    /**
+     * 查询全部
+     *
+     * @return
+     */
+    List<FavorItem> findAll();
+
+    /**
+     * 根据条件查询列表
+     *
+     * @param map
+     * @return
+     */
+    List<FavorItem> findByCondition(Map<String, Object> map);
+
+    /**
+     * 根据主键ID查询
+     *
+     * @param id
+     * @return
+     */
+    FavorItem findById(Long id);
+
+    /**
+     * 插入
+     *
+     * @param favorItemList
+     * @return
+     */
+    int batchAdd(List<FavorItem> favorItemList);
+
+    /**
+     * 更新
+     *
+     * @param favorItem
+     * @return
+     */
+    int update(FavorItem favorItem);
+
+    /**
+     * 根据ID删除
+     *
+     * @param id
+     * @return
+     */
+    int deleteById(Long id);
+
+    /**
+     * 根据人情ID删除
+     *
+     * @param favorId
+     * @return
+     */
+    int deleteByFavorId(Long favorId);
+
+}

+ 56 - 0
src/main/java/com/anyway/favor/mapper/FavorMapper.java

@@ -0,0 +1,56 @@
+package com.anyway.favor.mapper;
+
+import com.anyway.favor.model.Favor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 人情数据层
+ *
+ * @author liuchuanwei
+ * @date 2024-02-24
+ */
+public interface FavorMapper {
+
+    /**
+     * 根据条件查询列表
+     *
+     * @param map
+     * @return
+     */
+    List<Favor> findByCondition(Map<String, Object> map);
+
+    /**
+     * 根据主键ID查询
+     *
+     * @param id
+     * @return
+     */
+    Favor findById(Long id);
+
+    /**
+     * 插入
+     *
+     * @param Favor
+     * @return
+     */
+    int add(Favor Favor);
+
+    /**
+     * 更新
+     *
+     * @param Favor
+     * @return
+     */
+    int update(Favor Favor);
+
+    /**
+     * 根据ID删除
+     *
+     * @param id
+     * @return
+     */
+    int deleteById(Long id);
+
+}

+ 83 - 0
src/main/java/com/anyway/favor/mapper/PersonMapper.java

@@ -0,0 +1,83 @@
+package com.anyway.favor.mapper;
+
+import com.anyway.favor.model.Person;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 人员数据层
+ *
+ * @author anyway
+ * @date 2024-02-20
+ */
+public interface PersonMapper {
+    /**
+     * 查询全部
+     *
+     * @return
+     */
+    List<Person> findAll();
+
+    /**
+     * 根据条件查询列表
+     *
+     * @param map
+     * @return
+     */
+    List<Person> findByCondition(Map<String, Object> map);
+
+    /**
+     * 根据主键ID查询
+     *
+     * @param id
+     * @return
+     */
+    Person findById(Long id);
+
+    /**
+     * 插入
+     *
+     * @param person
+     * @return
+     */
+    int add(Person person);
+
+    /**
+     * 更新
+     *
+     * @param person
+     * @return
+     */
+    int update(Person person);
+
+    /**
+     * 根据ID列表删除
+     *
+     * @param idList
+     * @return
+     */
+    int deleteByIds(List<Long> idList);
+
+    /**
+     * 更新配偶为空
+     *
+     * @param id
+     * @return
+     */
+    int updateNullMate(Long id);
+    /**
+     * 更新配偶为空
+     *
+     * @param id
+     * @return
+     */
+    int updateNullMother(Long id);
+    /**
+     * 更新父亲为空
+     *
+     * @param id
+     * @return
+     */
+    int updateNullFather(Long id);
+}

+ 62 - 0
src/main/java/com/anyway/favor/mapper/UserMapper.java

@@ -0,0 +1,62 @@
+package com.anyway.favor.mapper;
+
+import com.anyway.favor.model.User;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 用户数据层
+ *
+ * @author liuchuanwei
+ * @date 2024-02-24
+ */
+public interface UserMapper {
+    /**
+     * 查询全部
+     *
+     * @return
+     */
+    List<User> findAll();
+
+    /**
+     * 根据条件查询列表
+     *
+     * @param map
+     * @return
+     */
+    List<User> findByCondition(Map<String, Object> map);
+
+    /**
+     * 根据主键ID查询
+     *
+     * @param id
+     * @return
+     */
+    User findById(Long id);
+
+    /**
+     * 插入
+     *
+     * @param user
+     * @return
+     */
+    int add(User user);
+
+    /**
+     * 更新
+     *
+     * @param user
+     * @return
+     */
+    int update(User user);
+
+    /**
+     * 根据ID删除
+     *
+     * @param id
+     * @return
+     */
+    int deleteById(Long id);
+
+}

+ 28 - 0
src/main/java/com/anyway/favor/model/BaseModel.java

@@ -0,0 +1,28 @@
+package com.anyway.favor.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * 基础实体类
+ *
+ * @author anyway
+ * @date 2023-02-20
+ */
+@Setter
+@Getter
+public class BaseModel {
+    /** 创建时间 */
+    private Date createTime;
+
+    /** 创建者 */
+    private Long createBy;
+
+    /** 修改时间 */
+    private Date modifyTime;
+
+    /** 修改者 */
+    private Long modifyBy;
+}

+ 56 - 0
src/main/java/com/anyway/favor/model/Favor.java

@@ -0,0 +1,56 @@
+package com.anyway.favor.model;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 人情实体类
+ * 
+ * @author anyway
+ * @date 2024-02-20
+ */
+@Setter
+@Getter
+public class Favor extends BaseModel implements java.io.Serializable {
+    /** 版本号 */
+    private static final long serialVersionUID = 666680999611094083L;
+
+    /** id */
+    private Long id;
+
+    /** 发生日期 */
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date occurDate;
+
+    /** 标题 */
+    private String title;
+
+    /** 主题人 */
+    private Long personId;
+    @TableField(exist = false)
+    private String personName;
+
+    /** 收入金额 */
+    private BigDecimal incomeAmount;
+
+    /** 支出金额 */
+    private BigDecimal payAmount;
+
+    /** 备注 */
+    private String remark;
+
+    /** 人情明细 */
+    private List<FavorItem> favorItemList;
+    /** 地址 */
+    @TableField(exist = false)
+    private String address;
+
+}

+ 55 - 0
src/main/java/com/anyway/favor/model/FavorItem.java

@@ -0,0 +1,55 @@
+package com.anyway.favor.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 人情明细表(t_favor_item)
+ * 
+ * @author anyway
+ * @date 2024-02-20
+ */
+@Setter
+@Getter
+public class FavorItem extends BaseModel implements java.io.Serializable {
+    /** 版本号 */
+    private static final long serialVersionUID = 6101318181722924652L;
+
+    /** id */
+    private Long id;
+
+    /** 事件ID */
+    private Long favorId;
+
+    /** 对象ID */
+    private Long personId;
+    /** 对象 */
+    private Person person;
+
+    /** 来源对象ID */
+    private Long sourcePersonId;
+    /** 来源对象 */
+    private Person sourcePerson;
+
+    /** 类型,S收入Z支出 */
+    private Integer type;
+
+    /** 金额 */
+    private BigDecimal amount;
+
+    /** 回赠 */
+    private String returnGift;
+
+    /** 备注 */
+    private String remark;
+
+    /** 发生日期 */
+    private Date occurDate;
+
+    /** 人情 */
+    private Favor favor;
+
+}

+ 72 - 0
src/main/java/com/anyway/favor/model/Person.java

@@ -0,0 +1,72 @@
+package com.anyway.favor.model;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+/**
+ * 人员表(t_person)
+ * 
+ * @author anyway
+ * @date 2024-02-20
+ */
+@Setter
+@Getter
+public class Person extends BaseModel implements java.io.Serializable {
+    /** 版本号 */
+    private static final long serialVersionUID = -476789774769490644L;
+
+    /** id */
+    private Long id;
+
+    /** 姓名 */
+    private String name;
+
+    /** 身份证号 */
+    private String cardId;
+
+    /** 称呼 */
+    private String callName;
+
+    /** 性别,M男F女 */
+    private String gender;
+
+    /** 生日 */
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date birthday;
+
+    /** 去世日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date deathDate;
+
+    /** 婚姻状况,0未婚1已婚 */
+    private Integer maritalStatus;
+
+    /** 配偶ID */
+    private Long mateId;
+    private String mateName;
+
+    /** 母亲ID */
+    private Long motherId;
+    private String motherName;
+
+    /** 父亲ID */
+    private Long fatherId;
+    private String fatherName;
+
+    /** 家中排行 */
+    private Integer sort;
+
+    /** 关联人 */
+    private Long relatedId;
+    /** 详细地址 */
+    private String address;
+    /** 备注 */
+    private String remark;
+
+}

+ 27 - 0
src/main/java/com/anyway/favor/model/User.java

@@ -0,0 +1,27 @@
+package com.anyway.favor.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 用户实体类
+ * 
+ * @author liuchuanwei
+ * @date 2024-02-24
+ */
+@Setter
+@Getter
+public class User extends BaseModel implements java.io.Serializable {
+    /** 版本号 */
+    private static final long serialVersionUID = 666680999611094083L;
+
+    /** id */
+    private Long id;
+
+    /** 账号 */
+    private String account;
+
+    /** 密码 */
+    private String password;
+
+}

+ 57 - 0
src/main/java/com/anyway/favor/service/FavorItemService.java

@@ -0,0 +1,57 @@
+package com.anyway.favor.service;
+
+import com.anyway.favor.model.FavorItem;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 人情明细业务接口
+ *
+ * @author liuchuanwei
+ * @date 2024-02-24
+ */
+public interface FavorItemService {
+
+    /**
+     * 根据条件查询列表
+     *
+     * @param map
+     * @return
+     */
+    List<FavorItem> findByCondition(Map<String, Object> map);
+
+    /**
+     * 根据人情ID查询列表
+     *
+     * @param favorId
+     * @return
+     */
+    List<FavorItem> findByFavorId(Long favorId);
+
+    /**
+     * 添加
+     *
+     * @param favorItem
+     * @return
+     */
+    boolean add(FavorItem favorItem);
+
+    /**
+     * 更新
+     *
+     * @param favorItem
+     * @return
+     */
+    boolean update(FavorItem favorItem);
+
+    /**
+     * 根据ID查询
+     *
+     * @param id
+     * @return
+     */
+    FavorItem findById(Long id);
+
+
+}

+ 57 - 0
src/main/java/com/anyway/favor/service/FavorService.java

@@ -0,0 +1,57 @@
+package com.anyway.favor.service;
+
+import com.anyway.favor.model.Favor;
+import com.anyway.util.PageQuery;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 人情业务接口
+ *
+ * @author liuchuanwei
+ * @date 2024-02-24
+ */
+public interface FavorService {
+
+    /**
+     * 根据条件查询列表
+     *
+     * @param map
+     * @return
+     */
+    List<Favor> findByCondition(Map<String, Object> map);
+
+    /**
+     * 分页查询
+     *
+     * @param pageQuery
+     * @return
+     */
+    List<Favor> findPage(PageQuery<Map<String, Object>> pageQuery);
+
+    /**
+     * 添加
+     *
+     * @param favor
+     * @return
+     */
+    boolean add(Favor favor);
+
+    /**
+     * 更新
+     *
+     * @param favor
+     * @return
+     */
+    boolean update(Favor favor);
+
+    /**
+     * 根据ID查询
+     *
+     * @param id
+     * @return
+     */
+    Favor findById(Long id);
+
+}

+ 71 - 0
src/main/java/com/anyway/favor/service/PersonService.java

@@ -0,0 +1,71 @@
+package com.anyway.favor.service;
+
+import com.anyway.favor.model.Person;
+import com.anyway.util.PageQuery;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 人员业务接口
+ *
+ * @author anyway
+ * @date 2024-02-20
+ */
+public interface PersonService {
+
+    /**
+     * 查询全部
+     *
+     * @return
+     */
+    List<Person> findAll();
+
+    /**
+     * 根据条件查询列表
+     *
+     * @param map
+     * @return
+     */
+    List<Person> findByCondition(Map<String, Object> map);
+
+    /**
+     * 根据条件分页查询
+     *
+     * @param pageQuery
+     * @return
+     */
+    List<Person> findPage(PageQuery<Map<String, Object>> pageQuery);
+
+    /**
+     * 添加
+     *
+     * @param person
+     * @return
+     */
+    boolean add(Person person);
+
+    /**
+     * 更新
+     *
+     * @param person
+     * @return
+     */
+    boolean update(Person person);
+
+    /**
+     * 根据ID查询
+     *
+     * @param id
+     * @return
+     */
+    Person findById(Long id);
+
+    /**
+     * 根据ID删除
+     * @param id
+     * @return
+     */
+    int delete(Long id);
+
+}

+ 56 - 0
src/main/java/com/anyway/favor/service/UserService.java

@@ -0,0 +1,56 @@
+package com.anyway.favor.service;
+
+import com.anyway.favor.model.User;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 用户业务接口
+ *
+ * @author liuchuanwei
+ * @date 2024-02-24
+ */
+public interface UserService {
+
+    /**
+     * 查询全部
+     *
+     * @return
+     */
+    List<User> findAll();
+
+    /**
+     * 根据条件查询列表
+     *
+     * @param map
+     * @return
+     */
+    List<User> findByCondition(Map<String, Object> map);
+
+    /**
+     * 添加
+     *
+     * @param favor
+     * @return
+     */
+    boolean add(User favor);
+
+    /**
+     * 更新
+     *
+     * @param favor
+     * @return
+     */
+    boolean update(User favor);
+
+    /**
+     * 根据ID查询
+     *
+     * @param id
+     * @return
+     */
+    User findById(Long id);
+
+
+}

+ 53 - 0
src/main/java/com/anyway/favor/service/impl/FavorItemServiceImpl.java

@@ -0,0 +1,53 @@
+package com.anyway.favor.service.impl;
+
+import com.anyway.favor.mapper.FavorItemMapper;
+import com.anyway.favor.model.FavorItem;
+import com.anyway.favor.service.FavorItemService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+/**
+ * 人情明细业务实现类
+ *
+ * @author liuchuanwei
+ * @date 2024-02-24
+ */
+@Service
+public class FavorItemServiceImpl implements FavorItemService {
+    @Autowired
+    private FavorItemMapper favorItemMapper;
+
+    @Override
+    public List<FavorItem> findByCondition(Map<String, Object> map) {
+        return favorItemMapper.findByCondition(map);
+    }
+
+    @Override
+    public List<FavorItem> findByFavorId(Long favorId) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("favorId", favorId);
+        if (!map.containsKey("sortName")) {
+            map.put("sortName", "create_time");
+            map.put("sortOrder", "asc");
+        }
+        return favorItemMapper.findByCondition(map);
+    }
+
+    @Override
+    public boolean add(FavorItem favorItem) {
+        return false;
+    }
+
+    @Override
+    public boolean update(FavorItem favorItem) {
+        return false;
+    }
+
+    @Override
+    public FavorItem findById(Long id) {
+        return null;
+    }
+}

+ 80 - 0
src/main/java/com/anyway/favor/service/impl/FavorServiceImpl.java

@@ -0,0 +1,80 @@
+package com.anyway.favor.service.impl;
+
+import com.anyway.favor.mapper.FavorItemMapper;
+import com.anyway.favor.mapper.FavorMapper;
+import com.anyway.favor.model.Favor;
+import com.anyway.favor.model.FavorItem;
+import com.anyway.favor.service.FavorService;
+import com.anyway.util.PageQuery;
+import com.anyway.util.PageUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+/**
+ * 人情业务实现类
+ *
+ * @author liuchuanwei
+ * @date 2024-02-24
+ */
+@Service
+public class FavorServiceImpl implements FavorService {
+    @Autowired
+    private FavorMapper favorMapper;
+    @Autowired
+    private FavorItemMapper favorItemMapper;
+
+    @Override
+    public List<Favor> findByCondition(Map<String, Object> map) {
+        return favorMapper.findByCondition(map);
+    }
+
+    @Override
+    public List<Favor> findPage(PageQuery<Map<String, Object>> pageQuery) {
+        PageUtils.startPage(pageQuery);
+        List<Favor> favorList = favorMapper.findByCondition(pageQuery.getTerms());
+        PageUtils.setPageTotal(favorList, pageQuery);
+        return favorList;
+    }
+
+    @Override
+    public boolean add(Favor favor) {
+        int r = favorMapper.add(favor);
+        if (r > 0) {
+            List<FavorItem> favorItemList = favor.getFavorItemList();
+            for (FavorItem favorItem : favorItemList) {
+                favorItem.setFavorId(favor.getId());
+                favorItem.setOccurDate(favor.getOccurDate());
+                favorItem.setCreateBy(favor.getCreateBy());
+                favorItem.setModifyBy(favor.getModifyBy());
+            }
+            favorItemMapper.batchAdd(favorItemList);
+        }
+        return r > 0;
+    }
+
+    @Override
+    public boolean update(Favor favor) {
+        int r = favorMapper.update(favor);
+        if (r > 0) {
+            int i = favorItemMapper.deleteByFavorId(favor.getId());
+            if (i > 0) {
+                List<FavorItem> favorItemList = favor.getFavorItemList();
+                for (FavorItem favorItem : favorItemList) {
+                    favorItem.setFavorId(favor.getId());
+                    favorItem.setOccurDate(favor.getOccurDate());
+                    favorItem.setCreateBy(favor.getCreateBy());
+                    favorItem.setModifyBy(favor.getModifyBy());
+                }
+                favorItemMapper.batchAdd(favorItemList);
+            }
+        }
+        return r > 0;
+    }
+
+    @Override
+    public Favor findById(Long id) {
+        return favorMapper.findById(id);
+    }
+}

+ 98 - 0
src/main/java/com/anyway/favor/service/impl/PersonServiceImpl.java

@@ -0,0 +1,98 @@
+package com.anyway.favor.service.impl;
+
+import com.anyway.favor.mapper.PersonMapper;
+import com.anyway.favor.model.Person;
+import com.anyway.favor.service.PersonService;
+import com.anyway.util.PageQuery;
+import com.anyway.util.PageUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 人员业务实现类
+ *
+ * @author anyway
+ * @date 2024-02-20
+ */
+@Slf4j
+@Service
+public class PersonServiceImpl implements PersonService {
+
+    @Autowired
+    private PersonMapper personMapper;
+
+    @Override
+    public List<Person> findAll() {
+        return personMapper.findAll();
+    }
+
+    @Override
+    public List<Person> findByCondition(Map<String, Object> map) {
+        return personMapper.findByCondition(map);
+    }
+
+    @Override
+    public List<Person> findPage(PageQuery<Map<String, Object>> pageQuery) {
+        PageUtils.startPage(pageQuery);
+        List<Person> personList = personMapper.findByCondition(pageQuery.getTerms());
+        PageUtils.setPageTotal(personList, pageQuery);
+        return personList;
+    }
+
+    @Override
+    public boolean add(Person person) {
+        int c = personMapper.add(person);
+        this.updateMate(person);
+        return c > 0;
+    }
+
+    @Override
+    public boolean update(Person person) {
+        this.updateMate(person);
+        return personMapper.update(person) > 0;
+    }
+
+    @Override
+    public Person findById(Long id) {
+        return personMapper.findById(id);
+    }
+
+    @Override
+    public int delete(Long id) {
+        int c = personMapper.deleteByIds(Arrays.asList(id));
+        if (c < 1) {
+            return c;
+        }
+        //更新相关人员的配偶、父母
+        personMapper.updateNullMate(id);
+        personMapper.updateNullMother(id);
+        personMapper.updateNullFather(id);
+        return c;
+    }
+
+    /**
+     * 更新配偶人员
+     *
+     * @param person
+     */
+    private void updateMate(Person person) {
+        if(person.getMateId() != null) {
+            Person matePerson = this.findById(person.getMateId());
+            if (matePerson != null) {
+                if(matePerson.getMateId() == null) {
+                    matePerson.setMateId(person.getId());
+                    personMapper.update(matePerson);
+                } else if (!person.getMateId().equals(matePerson.getMateId())) {
+                    log.error("该配偶存在其他配偶");
+                } else {
+                    //
+                }
+            }
+        }
+    }
+}

+ 47 - 0
src/main/java/com/anyway/favor/service/impl/UserServiceImpl.java

@@ -0,0 +1,47 @@
+package com.anyway.favor.service.impl;
+
+import com.anyway.favor.mapper.UserMapper;
+import com.anyway.favor.model.User;
+import com.anyway.favor.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 人员业务实现类
+ *
+ * @author liuchuanwei
+ * @date 2024-02-24
+ */
+@Service
+public class UserServiceImpl implements UserService {
+    @Autowired
+    private UserMapper userMapper;
+
+    @Override
+    public List<User> findAll() {
+        return null;
+    }
+
+    @Override
+    public List<User> findByCondition(Map<String, Object> map) {
+        return null;
+    }
+
+    @Override
+    public boolean add(User favor) {
+        return false;
+    }
+
+    @Override
+    public boolean update(User favor) {
+        return false;
+    }
+
+    @Override
+    public User findById(Long id) {
+        return userMapper.findById(id);
+    }
+}

+ 37 - 0
src/main/java/com/anyway/util/Couple.java

@@ -0,0 +1,37 @@
+package com.anyway.util;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * 成对的对象
+ *
+ * @author liuchuanwei
+ * @date 2024-02-21
+ */
+@Setter
+@Getter
+public class Couple {
+    private String title;
+    private String value;
+
+    public Couple() {
+    }
+
+    public Couple(String title, String value) {
+        this.title = title;
+        this.value = value;
+    }
+
+    public static <T> List<Couple> toList(List<T> list, Function<T, String> titleFun, Function<T, String> valFun) {
+        List<Couple> coupleList = new ArrayList<>();
+        for (T t : list) {
+            coupleList.add(new Couple(titleFun.apply(t), valFun.apply(t)));
+        }
+        return coupleList;
+    }
+}

+ 31 - 0
src/main/java/com/anyway/util/Page.java

@@ -0,0 +1,31 @@
+package com.anyway.util;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * 分页对象
+ *
+ * @author liuchuanwei
+ * @date 2024-02-25
+ */
+@Setter
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public class Page {
+    /** 页码 */
+    private int pageNo;
+    /** 每页大小 */
+    private int pageSize;
+    /** 排序字段 */
+    private String sortName;
+    /** 排序 */
+    private String sortOrder;
+    /** 总数 */
+    private long total;
+    /** 总页数 */
+    private int totalPage;
+}

+ 33 - 0
src/main/java/com/anyway/util/PageQuery.java

@@ -0,0 +1,33 @@
+package com.anyway.util;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 查询条件
+ *
+ * @author liuchuanwei
+ * @date 2024-02-25
+ */
+@Setter
+@Getter
+public class PageQuery<T> extends Page{
+    /**
+     * 查询条件
+     */
+    @JsonInclude(JsonInclude.Include.NON_ABSENT)
+    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+    private T terms;
+
+    /**
+     * 返回分页对象
+     *
+     * @return
+     */
+    public Page getPage() {
+        return new Page(this.getPageNo(), this.getPageSize(), this.getSortName(), this.getSortOrder(), this.getTotal(), this.getTotalPage());
+    }
+
+}

+ 53 - 0
src/main/java/com/anyway/util/PageUtils.java

@@ -0,0 +1,53 @@
+package com.anyway.util;
+
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+
+/**
+ * 分页工具类
+ *
+ * @author liuchuanwei
+ * @date 2024-02-25
+ */
+public class PageUtils {
+
+    /**
+     * 开始分页
+     *
+     * @param page
+     */
+    public static void startPage(Page page) {
+        if (StringUtils.isEmpty(page.getSortName())) {
+            PageHelper.startPage(page.getPageNo(), page.getPageSize());
+        } else {
+            PageHelper.startPage(page.getPageNo(), page.getPageSize(), page.getSortName() + " " + page.getSortOrder());
+        }
+    }
+
+    /**
+     * 获取总数
+     *
+     * @param list
+     * @param <T>
+     * @return
+     */
+    public static <T> long getPageTotal(List<T> list) {
+        return new PageInfo<>(list).getTotal();
+    }
+
+    /**
+     * 设置分页总数
+     *
+     * @param list
+     * @param page
+     * @param <T>
+     * @return
+     */
+    public static <T> void setPageTotal(List<T> list, Page page) {
+        PageInfo<T> pageInfo = new PageInfo<>(list);
+        page.setTotal(pageInfo.getTotal());
+    }
+}

+ 102 - 0
src/main/java/com/anyway/util/R.java

@@ -0,0 +1,102 @@
+package com.anyway.util;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 接口返回类
+ *
+ * @author anyway
+ * @date 2024-02-20
+ */
+@Setter
+@Getter
+public class R {
+    /**
+     * 消息码
+     */
+    private int code;
+    /**
+     * 消息
+     */
+    private String msg;
+    /**
+     * 数据
+     */
+    private Object data;
+
+    /**
+     * 分页
+     */
+    private Page page;
+    /**
+     * 时间戳
+     */
+    private long ts = System.currentTimeMillis();
+
+    /**
+     * 成功返回
+     *
+     * @return
+     */
+    public static R ok() {
+        R r = new R();
+        r.setCode(0);
+        r.setMsg("成功");
+        return r;
+    }
+    /**
+     * 失败返回
+     *
+     * @return
+     */
+    public static R fail() {
+        R r = new R();
+        r.setCode(1);
+        r.setMsg("失败");
+        return r;
+    }
+
+    /**
+     * 失败返回
+     *
+     * @param code
+     * @param msg
+     * @return
+     */
+    public static R fail(int code, String msg) {
+        R r = new R();
+        r.setCode(code);
+        r.setMsg(msg);
+        return r;
+    }
+    /**
+     * 成功返回数据
+     *
+     * @param data
+     * @return
+     */
+    public static R data(Object data) {
+        R r = new R();
+        r.setCode(0);
+        r.setMsg("成功");
+        r.setData(data);
+        return r;
+    }
+
+    /**
+     * 成功返回分页
+     *
+     * @param data
+     * @param page
+     * @return
+     */
+    public static R page(Object data, Page page) {
+        R r = new R();
+        r.setCode(0);
+        r.setMsg("成功");
+        r.setData(data);
+        r.setPage(page);
+        return r;
+    }
+}

+ 32 - 0
src/main/java/com/anyway/util/SessionUtils.java

@@ -0,0 +1,32 @@
+package com.anyway.util;
+
+import com.anyway.favor.model.User;
+import com.anyway.favor.service.UserService;
+
+/**
+ * Session 工具类
+ *
+ * @author liuchuanwei
+ * @date 2024-02-21
+ */
+public class SessionUtils {
+    /**
+     * 获取当前登录用户
+     *
+     * @return
+     */
+    public static User currentUser() {
+        //TODO 暂时固定用户
+        UserService userService = SpringUtils.getBean("userServiceImpl");
+        return userService.findById(1L);
+    }
+    /**
+     * 获取当前登录用户ID
+     *
+     * @return
+     */
+    public static Long currentUserId() {
+        User currentUser = SessionUtils.currentUser();
+        return currentUser.getId();
+    }
+}

+ 37 - 0
src/main/java/com/anyway/util/SpringUtils.java

@@ -0,0 +1,37 @@
+package com.anyway.util;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * Spring 工具类
+ *
+ * @author liuchuanwei
+ * @date 2024-02-21
+ */
+@Component
+public class SpringUtils implements ApplicationContextAware {
+
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        SpringUtils.applicationContext = applicationContext;
+    }
+
+    public static <T> T getBean(String beanName) {
+        if (applicationContext.containsBean(beanName)) {
+            return (T) applicationContext.getBean(beanName);
+        } else {
+            return null;
+        }
+    }
+
+    public static <T> Map<String, T> getBeansOfType(Class<T> baseType) {
+        return applicationContext.getBeansOfType(baseType);
+    }
+}

+ 45 - 0
src/main/resources/applicationContext.xml

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+				http://www.springframework.org/schema/beans/spring-beans.xsd
+				http://www.springframework.org/schema/context
+				http://www.springframework.org/schema/context/spring-context.xsd">
+    <context:component-scan base-package="com.anyway" />
+    <!--数据源-->
+    <bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource">
+        <property name="driver" value="com.mysql.jdbc.Driver" />
+        <property name="url" value="jdbc:mysql://81.70.105.158:3306/favorbill" />
+        <property name="username" value="root" />
+        <property name="password" value="dx3906" />
+    </bean>
+    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
+        <property name="configLocation" value="classpath:mybatis-config.xml" />
+        <property name="dataSource" ref="dataSource" />
+        <property name="mapperLocations" >
+            <array>
+                <value>classpath*:mapper/*.xml</value>
+            </array>
+        </property>
+        <property name="typeAliasesPackage" value="com.anyway.favor.model" />
+        <!-- 配置PageHelper分页插件 -->
+        <property name="plugins">
+            <array>
+                <bean class="com.github.pagehelper.PageInterceptor">
+                    <property name="properties">
+                        <props>
+                            <prop key="helperDialect">mysql</prop>
+                            <prop key="reasonable">true</prop>
+                        </props>
+                    </property>
+                </bean>
+            </array>
+        </property>
+    </bean>
+    <!-- 自动扫描所有的mapper -->
+    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
+        <property name="basePackage" value="com.anyway.favor.**.mapper" />
+    </bean>
+
+</beans>

+ 72 - 0
src/main/resources/init.sql

@@ -0,0 +1,72 @@
+-- 创建数据库favor
+create database favorBill default character set utf8mb4 collate utf8mb4_general_ci;
+use favorBill;
+
+drop table if exists t_user;
+create table t_user(
+    id int(11) primary key auto_increment,
+    account varchar(50) not null comment '账号',
+    password varchar(100) not null comment '密码',
+    create_time timestamp default current_timestamp comment '创建时间',
+    create_by int(11) comment '创建者',
+    modify_time timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP comment '修改时间',
+    modify_by int(11) comment '修改者'
+) comment '用户表';
+
+drop table if exists t_person;
+create table t_person(
+    id int(11) primary key auto_increment,
+    name varchar(20) comment '姓名',
+    card_id varchar(20) comment '身份证号',
+    call_name varchar(50) comment '称呼',
+    gender char(1) comment '性别,M男F女',
+    birthday date comment '生日',
+    death_date date comment '去世日期',
+    marital_status tinyint default 0 comment '婚姻状况,0未婚1已婚',
+    mate_id int(11) comment '配偶ID',
+    mother_id int(11) comment '母亲ID',
+    father_id int(11) comment '父亲ID',
+    sort tinyint comment '家中排行',
+    related_id int(11) comment '关联人',
+    address varchar(100) comment '详细地址',
+    remark varchar(255) comment '备注',
+    create_time timestamp default current_timestamp comment '创建时间',
+    create_by int(11) comment '创建者',
+    modify_time timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP comment '修改时间',
+    modify_by int(11) comment '修改者'
+) comment '人员表';
+drop table if exists t_favor;
+create table t_favor(
+    id int(11) primary key auto_increment,
+    occur_date date comment '发生日期',
+    title varchar(50) comment '标题',
+    person_id int(11) comment '主题人',
+    income_amount decimal(10,2) comment '收入金额',
+    pay_amount decimal(10,2) comment '支出金额',
+    remark varchar(255) comment '备注',
+    create_time timestamp default current_timestamp comment '创建时间',
+    create_by int(11) comment '创建者',
+    modify_time timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP comment '修改时间',
+    modify_by int(11) comment '修改者'
+) comment '事件表';
+drop table if exists t_favor_item;
+create table t_favor_item(
+    id int(11) primary key auto_increment,
+    favor_id int(11) comment '事件ID',
+    person_id int(11) comment '对象ID',
+    source_person_id int(11) comment '来源对象ID',
+    type char(1) comment '类型,S收入Z支出',
+    amount decimal(10,2) comment '金额',
+    return_gift varchar(255) comment '回赠',
+    remark varchar(255) comment '备注',
+    occur_date date comment '发生日期',
+    create_time timestamp default current_timestamp comment '创建时间',
+    create_by int(11) comment '创建者',
+    modify_time timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP comment '修改时间',
+    modify_by int(11) comment '修改者'
+) comment '人情明细表';
+-- 插入一条原始数据
+insert into t_user(account, password, create_by) values ('liuchuanwei', '123456', 'system');
+INSERT INTO t_person (name, card_id, call_name, gender, marital_status, birthday, sort, address)
+values ('刘传伟', '371321199301303114', '自己', 'M', 1, '1992-12-29', 1, '刘家河疃');
+

+ 163 - 0
src/main/resources/mapper/FavorItemMapper.xml

@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<!-- 人情明细表(t_favor_item) -->
+<mapper namespace="com.anyway.favor.mapper.FavorItemMapper">
+    <!-- 字段映射 -->
+    <resultMap id="favorItemMap" type="com.anyway.favor.model.FavorItem"></resultMap>
+
+    <!-- This code was generated by TableGo tools, mark 1 begin. -->
+    <!-- 表查询字段 -->
+    <sql id="allColumns">
+        fi.id, fi.favor_id, fi.person_id, fi.source_person_id, fi.type, fi.amount, fi.return_gift, fi.remark, 
+        fi.occur_date, fi.create_time, fi.create_by, fi.modify_time, fi.modify_by
+    </sql>
+    <!-- This code was generated by TableGo tools, mark 1 end. -->
+
+    <!-- 查询所有人情明细表 -->
+    <select id="findAll" resultMap="favorItemMap">
+        SELECT
+            <include refid="allColumns" />
+        FROM t_favor_item fi
+    </select>
+
+    <!-- 根据条件参数查询人情明细表列表 -->
+    <select id="findByCondition" resultMap="favorItemMap" parameterType="map">
+        SELECT
+            <include refid="allColumns" />,
+            f.title as "favor.title",
+            f.occur_date as "favor.occurDate",
+            p.name as "person.name",
+            p.call_name as "person.callName",
+            ps.name as "sourcePerson.name",
+            ps.call_name as "sourcePerson.callName",
+            ps.address as "sourcePerson.address"
+        FROM t_favor_item fi
+        JOIN t_favor f ON f.id=fi.favor_id
+        LEFT JOIN t_person p ON p.id=fi.person_id
+        LEFT JOIN t_person ps ON ps.id=fi.source_person_id
+        WHERE 1 = 1
+        <if test="favorId != null">
+            AND fi.favor_id = #{favorId}
+        </if>
+        <if test="personId != null">
+            AND fi.person_id = #{personId}
+        </if>
+        <if test="sourcePersonId != null">
+            AND fi.source_person_id = #{sourcePersonId}
+        </if>
+        <if test="type != null">
+            AND fi.type = #{type}
+        </if>
+        <if test="amount != null">
+            AND fi.amount = #{amount}
+        </if>
+        <if test="returnGift != null and returnGift != ''">
+            AND fi.return_gift LIKE CONCAT('%', #{returnGift}, '%')
+        </if>
+        <if test="remark != null and remark != ''">
+            AND fi.remark LIKE CONCAT('%', #{remark}, '%')
+        </if>
+        <if test="occurDate != null">
+            AND fi.occur_date = #{occurDate}
+        </if>
+        <if test="createTime != null">
+            AND fi.create_time = #{createTime}
+        </if>
+        <if test="createBy != null">
+            AND fi.create_by = #{createBy}
+        </if>
+        <if test="modifyTime != null">
+            AND fi.modify_time = #{modifyTime}
+        </if>
+        <if test="modifyBy != null">
+            AND fi.modify_by = #{modifyBy}
+        </if>
+        <if test="sortName != null">
+            ORDER BY fi.${sortName} ${sortOrder}
+        </if>
+    </select>
+
+    <!-- 根据主键查询人情明细表信息 -->
+    <select id="findById" resultMap="favorItemMap">
+        SELECT
+            <include refid="allColumns" />
+        FROM t_favor_item fi WHERE fi.id = #{id}
+    </select>
+
+    <!-- 批量新增人情明细表信息 -->
+    <insert id="batchAdd" parameterType="list">
+        INSERT INTO t_favor_item (
+            favor_id, person_id, source_person_id, type, amount, return_gift, remark, occur_date, create_by, modify_by
+        ) VALUES
+        <foreach collection="list" index="index" item="item" separator=",">
+            (
+                #{item.favorId},
+                #{item.personId},
+                #{item.sourcePersonId},
+                #{item.type},
+                #{item.amount},
+                #{item.returnGift},
+                #{item.remark},
+                #{item.occurDate},
+                #{item.createBy},
+                #{item.modifyBy}
+            )
+        </foreach>
+    </insert>
+
+    <!-- 修改人情明细表信息 -->
+    <update id="update">
+        UPDATE t_favor_item
+        <set>
+            <if test="favorId != null">
+                favor_id = #{favorId},
+            </if>
+            <if test="personId != null">
+                person_id = #{personId},
+            </if>
+            <if test="sourcePersonId != null">
+                source_person_id = #{sourcePersonId},
+            </if>
+            <if test="type != null">
+                type = #{type},
+            </if>
+            <if test="amount != null">
+                amount = #{amount},
+            </if>
+            <if test="returnGift != null">
+                return_gift = #{returnGift},
+            </if>
+            <if test="remark != null">
+                remark = #{remark},
+            </if>
+            <if test="occurDate != null">
+                occur_date = #{occurDate},
+            </if>
+            <if test="createTime != null">
+                create_time = #{createTime},
+            </if>
+            <if test="createBy != null">
+                create_by = #{createBy},
+            </if>
+            <if test="modifyTime != null">
+                modify_time = #{modifyTime},
+            </if>
+            <if test="modifyBy != null">
+                modify_by = #{modifyBy}
+            </if>
+        </set>
+        WHERE id = #{id}
+    </update>
+
+    <!-- 根据主键删除人情明细表 -->
+    <delete id="deleteById">
+        DELETE FROM t_favor_item WHERE id = #{id}
+    </delete>
+
+    <!-- 根据人情ID删除事件表 -->
+    <delete id="deleteByFavorId">
+        DELETE FROM t_favor_item WHERE favor_id = #{favorId}
+    </delete>
+
+</mapper>

+ 91 - 0
src/main/resources/mapper/FavorMapper.xml

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<!-- 事件表(t_favor) -->
+<mapper namespace="com.anyway.favor.mapper.FavorMapper">
+    <!-- 字段映射 -->
+    <resultMap id="favorMap" type="com.anyway.favor.model.Favor"></resultMap>
+
+    <!-- This code was generated by TableGo tools, mark 1 begin. -->
+    <!-- 表查询字段 -->
+    <sql id="allColumns">
+        f.id, f.occur_date, f.title, f.person_id, f.income_amount, f.pay_amount, f.remark, f.create_time, f.create_by, f.modify_time,
+        f.modify_by
+    </sql>
+
+    <!-- 根据条件参数查询事件表列表 -->
+    <select id="findByCondition" resultMap="favorMap" parameterType="map">
+        SELECT
+            <include refid="allColumns" />,
+            concat(p.name, '(', p.call_name, ')') as person_name
+        FROM t_favor f
+        LEFT JOIN t_person p ON p.id=f.person_id
+        WHERE 1 = 1
+        <if test="occurDate != null">
+            AND f.occur_date = #{occurDate}
+        </if>
+        <if test="title != null and title != ''">
+            AND f.title LIKE CONCAT('%', #{title}, '%')
+        </if>
+        <if test="personId != null">
+            AND f.person_id = #{personId}
+        </if>
+        <if test="remark != null and remark != ''">
+            AND f.remark LIKE CONCAT('%', #{remark}, '%')
+        </if>
+        <if test="createBy != null">
+            AND f.create_by = #{createBy}
+        </if>
+        <if test="sortName != null">
+            ORDER BY f.${sortName} ${sortOrder}
+        </if>
+    </select>
+
+    <!-- 根据主键查询事件表信息 -->
+    <select id="findById" resultMap="favorMap">
+        SELECT
+            <include refid="allColumns" />
+        FROM t_favor f WHERE f.id = #{id}
+    </select>
+
+    <!-- 新增事件表信息 -->
+    <insert id="add" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
+        INSERT INTO t_favor (occur_date, title, person_id, income_amount, pay_amount, remark, create_by, modify_by)
+        VALUES (#{occurDate}, #{title}, #{personId}, #{incomeAmount}, #{payAmount}, #{remark}, #{createBy}, #{modifyBy})
+    </insert>
+
+    <!-- 修改事件表信息 -->
+    <update id="update">
+        UPDATE t_favor
+        <set>
+            <if test="occurDate != null">
+                occur_date = #{occurDate},
+            </if>
+            <if test="title != null">
+                title = #{title},
+            </if>
+            <if test="personId != null">
+                person_id = #{personId},
+            </if>
+            <if test="incomeAmount != null">
+                income_amount = #{incomeAmount},
+            </if>
+            <if test="payAmount != null">
+                pay_amount = #{payAmount},
+            </if>
+            <if test="remark != null">
+                remark = #{remark},
+            </if>
+            <if test="modifyBy != null">
+                modify_by = #{modifyBy}
+            </if>
+        </set>
+        WHERE id = #{id}
+    </update>
+
+    <!-- 根据主键删除事件表 -->
+    <delete id="deleteById">
+        DELETE FROM t_favor WHERE id = #{id}
+    </delete>
+
+</mapper>

+ 198 - 0
src/main/resources/mapper/PersonMapper.xml

@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<!-- 人员表(t_person) -->
+<mapper namespace="com.anyway.favor.mapper.PersonMapper">
+    <!-- 字段映射 -->
+    <resultMap id="personMap" type="com.anyway.favor.model.Person"></resultMap>
+
+    <!-- 表查询字段 -->
+    <sql id="allColumns">
+        p.id, p.name, p.card_id, p.call_name, p.gender, p.birthday, p.death_date, p.marital_status, 
+        p.mate_id, p.mother_id, p.father_id, p.sort, p.related_id, p.address, p.remark, p.create_time, p.create_by, p.modify_time,
+        p.modify_by
+    </sql>
+
+    <!-- 查询所有人员表 -->
+    <select id="findAll" resultMap="personMap">
+        SELECT
+            <include refid="allColumns" />
+        FROM t_person p
+    </select>
+
+    <!-- 根据条件参数查询人员表列表 -->
+    <select id="findByCondition" resultMap="personMap" parameterType="map">
+        SELECT
+            <include refid="allColumns" />,
+            concat(mp.name, '(', mp.call_name, ')') as mate_name,
+            concat(m.name, '(', m.call_name, ')') as mother_name,
+            concat(f.name, '(', f.call_name, ')') as father_name
+        FROM t_person p
+        LEFT JOIN t_person mp ON mp.id=p.mate_id
+        LEFT JOIN t_person m ON m.id=p.mother_id
+        LEFT JOIN t_person f ON f.id=p.father_id
+        WHERE 1 = 1
+        <if test="idList != null and idList.size() > 0">
+            AND p.id in
+            <foreach collection="idList" index="index" item="id" open="(" separator="," close=")">
+                #{id}
+            </foreach>
+        </if>
+        <if test="name != null and name != ''">
+            AND (p.name LIKE CONCAT('%', #{name}, '%') OR p.call_name LIKE CONCAT('%', #{name}, '%'))
+        </if>
+        <if test="callName != null and callName != ''">
+            AND (p.call_name LIKE CONCAT('%', #{callName}, '%') OR p.name LIKE CONCAT('%', #{callName}, '%'))
+        </if>
+        <if test="gender != null and gender != ''">
+            AND p.gender = #{gender}
+        </if>
+        <if test="maritalStatus != null">
+            AND p.marital_status = #{maritalStatus}
+        </if>
+        <if test="mateId != null">
+            AND p.mate_id = #{mateId}
+        </if>
+        <if test="motherId != null">
+            AND p.mother_id = #{motherId}
+        </if>
+        <if test="fatherId != null">
+            AND p.father_id = #{fatherId}
+        </if>
+        <if test="address != null and address != ''">
+            AND p.address = #{address}
+        </if>
+        <if test="relatedId != null">
+            AND p.related_id = #{relatedId}
+        </if>
+        <if test="familyPersonId != null and familyPersonId != ''">
+            AND (
+                p.id=#{familyPersonId} OR p.mate_id=#{familyPersonId} OR p.mother_id=#{familyPersonId} OR p.father_id=#{familyPersonId}
+            )
+        </if>
+        <if test="emptyMate == true">
+            AND p.mate_id is null
+        </if>
+        <if test="@Ognl@isNotEmpty(createByList)">
+            AND p.create_by in
+            <foreach collection="createByList" index="index" item="item" open="(" separator="," close=")">
+                #{item}
+            </foreach>
+        </if>
+        <if test="sortName != null and sortName !=''">
+            ORDER BY p.${sortName} ${sortOrder}
+        </if>
+    </select>
+
+    <!-- 根据主键ID查询人员表信息 -->
+    <select id="findById" resultMap="personMap">
+        SELECT
+        <include refid="allColumns" />,
+        concat(mp.name, '(', mp.call_name, ')') as mate_name,
+        concat(m.name, '(', m.call_name, ')') as mother_name,
+        concat(f.name, '(', f.call_name, ')') as father_name
+        FROM t_person p
+        LEFT JOIN t_person mp ON mp.id=p.mate_id
+        LEFT JOIN t_person m ON m.id=p.mother_id
+        LEFT JOIN t_person f ON f.id=p.father_id
+        WHERE p.id = #{id}
+    </select>
+
+    <!-- 新增人员表信息 -->
+    <insert id="add" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
+        INSERT INTO t_person (
+            name, card_id, call_name, gender, birthday, death_date, marital_status,
+            mate_id, mother_id, father_id, sort, related_id, address, remark, create_by
+        ) VALUES (
+            #{name},
+            #{cardId},
+            #{callName},
+            #{gender},
+            #{birthday},
+            #{deathDate},
+            #{maritalStatus},
+            #{mateId},
+            #{motherId},
+            #{fatherId},
+            #{sort},
+            #{relatedId},
+            #{address},
+            #{remark},
+            #{createBy}
+        )
+    </insert>
+
+    <!-- 修改人员表信息 -->
+    <update id="update">
+        UPDATE t_person
+        <set>
+            <if test="name != null">
+                name = #{name},
+            </if>
+            <if test="cardId != null">
+                card_id = #{cardId},
+            </if>
+            <if test="callName != null">
+                call_name = #{callName},
+            </if>
+            <if test="gender != null">
+                gender = #{gender},
+            </if>
+            <if test="birthday != null">
+                birthday = #{birthday},
+            </if>
+            <if test="deathDate != null">
+                death_date = #{deathDate},
+            </if>
+            <if test="maritalStatus != null">
+                marital_status = #{maritalStatus},
+            </if>
+            <if test="mateId != null">
+                mate_id = #{mateId},
+            </if>
+            <if test="motherId != null">
+                mother_id = #{motherId},
+            </if>
+            <if test="fatherId != null">
+                father_id = #{fatherId},
+            </if>
+            <if test="sort != null">
+                sort = #{sort},
+            </if>
+            <if test="relatedId != null">
+                related_id = #{relatedId},
+            </if>
+            <if test="address != null">
+                address = #{address},
+            </if>
+            <if test="remark != null">
+                remark = #{remark},
+            </if>
+            <if test="modifyBy != null">
+                modify_by = #{modifyBy}
+            </if>
+        </set>
+        WHERE id = #{id}
+    </update>
+
+    <!-- 根据主键批量删除人员表 -->
+    <delete id="deleteByIds" parameterType="list">
+        DELETE FROM t_person WHERE id IN
+        <foreach collection="list" index="index" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <!-- 更新配偶为空 -->
+    <update id="updateNullMate">
+        UPDATE t_person p set p.mate_id=Null WHERE p.mate_id=#{id}
+    </update>
+    <!-- 更新母亲为空 -->
+    <update id="updateNullMother">
+        UPDATE t_person p set p.mother_id=Null WHERE p.mother_id=#{id}
+    </update>
+    <!-- 更新父亲为空 -->
+    <update id="updateNullFather">
+        UPDATE t_person p set p.father_id=Null WHERE p.father_id=#{id}
+    </update>
+</mapper>

+ 83 - 0
src/main/resources/mapper/UserMapper.xml

@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<!-- 用户表(t_user) -->
+<mapper namespace="com.anyway.favor.mapper.UserMapper">
+    <!-- 字段映射 -->
+    <resultMap id="userMap" type="com.anyway.favor.model.User"></resultMap>
+
+    <!-- This code was generated by TableGo tools, mark 1 begin. -->
+    <!-- 表查询字段 -->
+    <sql id="allColumns">
+        u.id, u.account, u.password, u.create_time, u.create_by, u.modify_time, u.modify_by
+    </sql>
+    <!-- This code was generated by TableGo tools, mark 1 end. -->
+
+    <!-- 查询所有用户表 -->
+    <select id="findAll" resultMap="userMap">
+        SELECT
+            <include refid="allColumns" />
+        FROM t_user u
+    </select>
+
+    <!-- 根据条件参数查询用户表列表 -->
+    <select id="findByCondition" resultMap="userMap" parameterType="map">
+        SELECT
+            <include refid="allColumns" />
+        FROM t_user u WHERE 1 = 1
+        <if test="account != null and account != ''">
+            AND u.account LIKE CONCAT('%', #{account}, '%')
+        </if>
+        <if test="password != null and password != ''">
+            AND u.password LIKE CONCAT('%', #{password}, '%')
+        </if>
+        <if test="createTime != null">
+            AND u.create_time = #{createTime}
+        </if>
+        <if test="createBy != null">
+            AND u.create_by = #{createBy}
+        </if>
+        <if test="modifyTime != null">
+            AND u.modify_time = #{modifyTime}
+        </if>
+        <if test="modifyBy != null">
+            AND u.modify_by = #{modifyBy}
+        </if>
+    </select>
+
+    <!-- 根据主键查询用户表信息 -->
+    <select id="findById" resultMap="userMap">
+        SELECT
+            <include refid="allColumns" />
+        FROM t_user u WHERE u.id = #{id}
+    </select>
+
+    <!-- 新增用户表信息 -->
+    <insert id="add">
+        INSERT INTO t_user (account, password, create_by)
+        VALUES (#{account}, #{password}, #{createBy})
+    </insert>
+
+    <!-- 修改用户表信息 -->
+    <update id="update">
+        UPDATE t_user
+        <set>
+            <if test="account != null">
+                account = #{account},
+            </if>
+            <if test="password != null">
+                password = #{password},
+            </if>
+            <if test="modifyBy != null">
+                modify_by = #{modifyBy}
+            </if>
+        </set>
+        WHERE id = #{id}
+    </update>
+
+    <!-- 根据主键删除用户表 -->
+    <delete id="deleteById">
+        DELETE FROM t_user WHERE id = #{id}
+    </delete>
+
+</mapper>

+ 10 - 0
src/main/resources/mybatis-config.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
+<configuration>
+    <settings>
+        <setting name="logImpl" value="STDOUT_LOGGING"/>
+        <setting name="cacheEnabled" value="true"/>
+        <setting name="mapUnderscoreToCamelCase" value="true"/>
+        <setting name="aggressiveLazyLoading" value="false"/>
+    </settings>
+</configuration>

+ 9 - 0
src/main/webapp/WEB-INF/pages/employee/list.jsp

@@ -0,0 +1,9 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<html>
+<head>
+    <title>HelloWorld</title>
+</head>
+<body>
+    <h1>HelloWorld</h1>
+</body>
+</html>

+ 305 - 0
src/main/webapp/WEB-INF/pages/favor/add.jsp

@@ -0,0 +1,305 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
+<html>
+<head>
+    <title>添加人情</title>
+    <meta name="renderer" content="webkit">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+    <link rel="stylesheet" href="${pageContext.request.contextPath}/asserts/layui/css/layui.css">
+    <style>
+        td .layui-form-select {
+            margin-top: -10px;
+            margin-left: -15px;
+            margin-right: -15px;
+        }
+        .layui-form-item {
+            margin-bottom: 5px;
+            clear: both;
+        }
+        .layui-input-inline {
+            width: 30% !important;
+        }
+    </style>
+</head>
+<body>
+<div style="margin: 0 auto; width: 60%;">
+    <fieldset class="layui-elem-field layui-field-title">
+    <c:choose>
+        <c:when test="${favor == null}">
+            <legend>新增人情</legend>
+        </c:when>
+        <c:otherwise>
+            <legend>编辑人情</legend>
+        </c:otherwise>
+    </c:choose>
+    </fieldset>
+
+    <form class="layui-form layui-form-pane" action="${pageContext.request.contextPath}/favor/update">
+        <div class="layui-form-item"> <label class="layui-form-label">标题</label>
+            <div class="layui-input-inline">
+                <input type="hidden" name="id" value="${favor.id}" />
+                <input type="text" name="title" value="${favor.title}" lay-verify="required" lay-reqtext="标题不能为空" placeholder="请输入" autocomplete="off" class="layui-input">
+            </div>
+        </div>
+        <div class="layui-form-item"> <label class="layui-form-label">对象</label>
+            <div class="layui-input-inline">
+                <select id="personId" name="personId" lay-filter="personId" lay-search="">
+                    <c:forEach items="${personList}" var="p">
+                        <c:choose>
+                            <c:when test="${p.id != favor.personId}">
+                                <option value="${p.id}" ${p.id eq person.id ? "selected" : ""} >${p.name}(${p.callName})</option>
+                            </c:when>
+                            <c:otherwise>
+                                <option value="${p.id}" selected>${p.name}(${p.callName})</option>
+                            </c:otherwise>
+                        </c:choose>
+                    </c:forEach>
+                </select>
+
+            </div>
+        </div>
+        <div class="layui-form-item"> <label class="layui-form-label">日期</label>
+            <div class="layui-input-inline">
+                <input type="text" name="occurDate" id="occurDate" value="<fmt:formatDate value='${favor.occurDate}' pattern='yyyy-MM-dd'/>" placeholder="yyyy-MM-dd" autocomplete="off" class="layui-input">
+            </div>
+        </div>
+        <div class="layui-form-item"> <label class="layui-form-label">收入金额</label>
+            <div class="layui-input-inline">
+                <input type="number" name="incomeAmount" value="${favor.incomeAmount}" min="0" autocomplete="off" class="layui-input">
+            </div>
+        </div>
+        <div class="layui-form-item"> <label class="layui-form-label">支出金额</label>
+            <div class="layui-input-inline">
+                <input type="number" name="payAmount" value="${favor.payAmount}" min="0" autocomplete="off" class="layui-input">
+            </div>
+        </div>
+        <div class="layui-form-item"> <label class="layui-form-label">备注</label>
+            <div class="layui-input-block">
+                <input type="text" name="remark" value="${favor.remark}" placeholder="请输入内容" class="layui-input" />
+            </div>
+        </div>
+
+        <table id="favorItemTab" class="layui-hide" lay-filter="favorItem">
+
+
+        </table>
+        <div class="layui-form-item">
+            <div class="layui-input-block">
+                <button type="submit" class="layui-btn" lay-submit="" lay-filter="submit">保存</button>
+                <button type="reset" class="layui-btn layui-btn-primary">取消</button>
+            </div>
+        </div>
+    </form>
+
+    <%-- 表格上方按钮 --%>
+    <script type="text/html" id="toolbarDemo">
+        <div class="layui-btn-container">
+            <button class="layui-btn layui-btn-sm" lay-event="addFavorItem">添加明细</button>
+        </div>
+    </script>
+    <%-- 表格行操作栏 --%>
+    <script type="text/html" id="barDemo">
+        <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
+    </script>
+    <%-- 送礼人下拉 --%>
+    <script type="text/html" id="sourcePersonTpl">
+        <select class="sourcePersonId" lay-filter="sourcePerson" lay-search="" data-value="{{ d.sourcePersonId }}">
+            <option value="">请选择</option>
+            <c:forEach items="${personList}" var="p">
+                <option value="${p.id}" data-address="${p.address}">${p.name}(${p.callName})</option>
+            </c:forEach>
+        </select>
+    </script>
+    <%-- 收礼人下拉 --%>
+    <script type="text/html" id="personTpl">
+        <select class="personId" lay-filter="person" lay-search="" data-value="{{ d.personId }}">
+            <option value="">请选择</option>
+            <c:forEach items="${personList}" var="p">
+                <option value="${p.id}">${p.name}(${p.callName})</option>
+            </c:forEach>
+        </select>
+    </script>
+
+</div>
+<script type="text/javascript" src="${pageContext.request.contextPath}/asserts/js/jquery-3.7.1.min.js"></script>
+<script type="text/javascript" src="${pageContext.request.contextPath}/asserts/layui/layui.js" charset="utf-8"></script>
+<script type="text/javascript" src="${pageContext.request.contextPath}/asserts/js/custom.js"></script>
+<script>
+    let headUrl = "${pageContext.request.contextPath}";
+    layui.use(['jquery', 'table', 'form', 'laydate'], function tab(){
+        let $ = layui.$, table = layui.table, laydate = layui.laydate, form = layui.form;
+        // 日期
+        laydate.render({
+            elem: '#occurDate'
+        });
+        // 监听修改update到表格中
+        form.on('select(sourcePerson)', function (data) {
+            let elem = $(data.elem);
+            let trElem = elem.parents('tr');
+            let tableData = table.cache['favorItemTab']; // grid为table id
+            // 更新到表格的缓存数据中,才能在获得选中行等等其他的方法中得到更新之后的值
+            tableData[trElem.data('index')][elem.attr('class')] = data.value;
+            //设置地址
+            let address = elem.find("option:selected").data('address');
+            tableData[trElem.data('index')]['address'] = address;
+            $(trElem).find('td[data-field="address"] .layui-table-cell').html(address);
+            console.log("选中送礼人", tableData);
+        });
+        form.on('select(person)', function (data) {
+            setValue(data);
+        });
+
+        function setValue(data) {
+            let elem = $(data.elem);
+            let trElem = elem.parents('tr');
+            let tableData = table.cache['favorItemTab']; // grid为table id
+            // 更新到表格的缓存数据中,才能在获得选中行等等其他的方法中得到更新之后的值
+            tableData[trElem.data('index')][elem.attr('class')] = data.value;
+            console.log("列表数据:", tableData);
+        }
+
+        //送礼人列表
+        let dataa = [];
+        <c:forEach items="${sourcePersonList}" var="p">
+        dataa.push({
+            sourcePersonId:"${p.id}",
+            address:"${p.address}",
+            amount:"",
+            returnGift:"",
+            personId: $("#personId").val(),
+            remark:""
+        });
+        </c:forEach>
+        <c:forEach items="${favor.favorItemList}" var="fi">
+        dataa.push({
+            sourcePersonId:"${fi.sourcePersonId}",
+            address:"${fi.sourcePerson.address}",
+            amount:"${fi.amount}",
+            returnGift:"${fi.returnGift}",
+            personId: ${fi.personId},
+            remark:"${fi.remark}"
+        });
+        </c:forEach>
+        if(dataa.length < 1) {
+            dataa.push({
+                sourcePersonId:"",
+                address:"",
+                amount:"",
+                returnGift:"",
+                personId: $("#personId").val(),
+                remark:""
+            })
+        }
+        console.log(dataa);
+        table.render({
+            elem: '#favorItemTab',
+            data:dataa,
+            toolbar: '#toolbarDemo', //开启头部工具栏,并为其绑定左侧模板
+            title: '人情明细',
+            cols: [[
+                {field:'sourcePersonId', title:'送礼人', width:200, templet: '#sourcePersonTpl'},
+                {field:'address', title:'地址', width:150},
+                {field:'amount', title:'金额', width:100, edit: 'number', event: 'clickInputNumber'},
+                {field:'returnGift', title:'还礼', width:150, edit: 'text'},
+                {field:'personId', title:'收礼人', width:200, templet: '#personTpl'},
+                {field:'remark', title:'备注', edit: 'text'},
+                {fixed:'right', align:'center', toolbar: '#barDemo'}
+            ]],
+            page: false,
+            limit: 10000,
+            done: function (res, curr, count) {
+                //数据渲染完的回调
+                $(".layui-table-body").css('overflow','visible');
+                $(".layui-table-box").css('overflow','visible');
+                $(".layui-table-view").css('overflow','visible');
+                let tableElem = this.elem.next('.layui-table-view');
+                count || tableElem.find('.layui-table-header').css('overflow', 'auto');
+                layui.each(tableElem.find('select'), function (index, item) {
+                    let elem = $(item);
+                    elem.val(elem.data('value')).parents('div.layui-table-cell').css('overflow', 'visible');
+                });
+                form.render();
+            }
+        });
+        //监听行工具事件
+        table.on('tool(favorItem)', function(obj){
+            if(obj.event === 'del'){
+                obj.del();
+                let index = $(obj.tr).attr("data-index");
+                removeArrEle(table.cache["favorItemTab"], index);
+                console.log("删除一条明细", table.cache["favorItemTab"])
+            } else if (obj.event === 'clickInputNumber') {
+                $(obj).find("input").attr("type","number");
+                $(obj).find("input").attr("step","100");
+            }
+        });
+
+        //头工具栏事件
+        table.on('toolbar(favorItem)', function (obj) {
+            switch (obj.event) {
+                case 'addFavorItem':
+                    let oldData = table.cache["favorItemTab"];
+                    let newRow = {
+                        sourcePersonId:"",
+                        address:"",
+                        amount:"",
+                        returnGift:"",
+                        personId: $("#personId").val(),
+                        remark:""
+                    }
+                    oldData.push(newRow);
+                    console.log("新增一条明细", oldData);
+                    table.reload('favorItemTab', {
+                        data: oldData
+                    });
+                    break;
+            }
+        });
+
+        //监听提交
+        form.on('submit(submit)', function(data){
+            let url = data.form.action;
+            let formJson = data.field;
+            if (formJson.id === undefined || formJson.id === '') {
+                url = "${pageContext.request.contextPath}/favor/add";
+            }
+            //删除不需要的属性
+            deleteObjKey(formJson, "sourcePersonId");
+            formJson.favorItemList = [];
+            let arr = table.cache['favorItemTab'];
+            for(let i=0; i<arr.length; i++) {
+                if(!!arr[i].sourcePersonId && !!arr[i].personId) {
+                    //删除不需要的属性
+                    deleteObjKey(arr[i], "LAY_TABLE_INDEX");
+                    deleteObjKey(arr[i], "address");
+                    formJson.favorItemList.push(arr[i]);
+                }
+            }
+            console.log("提交地址:" + url);
+            console.log(formJson);
+            $.ajax({
+                url:url,
+                type:"POST",
+                data:JSON.stringify(formJson),
+                contentType:"application/json; charset=utf-8",
+                dataType:"json",
+                success: function(res){
+                    console.log(res);
+                    if (res.code === 1) {
+                        layer.msg(res.msg, {icon: 2, time: 2000});
+                    } else {
+                        //先给成功提示,然后关闭弹窗
+                        layer.msg('保存成功', {icon: 1, time: 1000}, function() {
+                            window.close();
+                        });
+                    }
+                }
+            })
+            return false;
+        });
+    });
+</script>
+</body>
+</html>

+ 67 - 0
src/main/webapp/WEB-INF/pages/favor/detail.jsp

@@ -0,0 +1,67 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
+<html>
+<head>
+    <title>人情详情</title>
+</head>
+<body>
+<div style="margin: 0 auto; width: 60%;">
+    <h2>人情详情</h2>
+    <table id="favorTab" border="1" cellspacing="1" cellpadding="5" style="width: 40%;">
+        <tbody>
+        <tr>
+            <td>标题:</td>
+            <td>${favor.title}</td>
+        </tr>
+        <tr>
+            <td>对象:</td>
+            <td>${favor.person.callName}(${favor.person.name})</td>
+        </tr>
+        <tr>
+            <td>日期:</td>
+            <td><fmt:formatDate value='${favor.occurDate}' pattern='yyyy-MM-dd'/>"</td>
+        </tr>
+        <tr>
+            <td>收入金额:</td>
+            <td>${favor.incomeAmount}</td>
+        </tr>
+        <tr>
+            <td>支出金额:</td>
+            <td>${favor.payAmount}</td>
+        </tr>
+        <tr>
+            <td>备注:</td>
+            <td>${favor.remark}</td>
+        </tr>
+        </tbody>
+    </table>
+    <br/>
+    <b>人情明细</b>
+    <table id="favorItemTab" border="1" cellspacing="1" cellpadding="5" style="margin: 0 auto; width: 100%;">
+        <thead>
+        <tr>
+            <th>送礼人</th>
+            <th style="width: 15%">地址</th>
+            <th style="width: 10%">金额</th>
+            <th style="width: 10%">还礼</th>
+            <th>收礼人</th>
+            <th>备注</th>
+        </tr>
+        </thead>
+        <tbody>
+        <c:forEach items="${favor.favorItemList}" var="p">
+        <tr>
+            <td>${p.sourcePerson.callName}(${p.sourcePerson.name})</td>
+            <td class="address">${p.sourcePerson.address}</td>
+            <td>${p.amount}"</td>
+            <td>${p.returnGift}</td>
+            <td>${p.person.callName}(${p.person.name})</td>
+            <td>${p.remark}</td>
+        </tr>
+        </c:forEach>
+        </tbody>
+    </table>
+</div>
+</body>
+</html>

+ 46 - 0
src/main/webapp/WEB-INF/pages/favor/item/list.jsp

@@ -0,0 +1,46 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
+<html>
+<head>
+    <title>人情明细</title>
+</head>
+<body>
+<h2>人情明细</h2>
+<table border="1" cellspacing="1" cellpadding="5" style="margin: 0 auto; width: 80%;">
+    <a href="/favorItem/toAdd">添加人情明细</a>
+    <thead>
+        <tr>
+            <th>ID</th>
+            <th>事件</th>
+            <th>日期</th>
+            <th>收礼人</th>
+            <th>送礼人</th>
+            <th>金额</th>
+            <th>回礼</th>
+            <th>备注</th>
+            <th>创建日期</th>
+            <th>操作</th>
+        </tr>
+    </thead>
+    <tbody>
+    <c:forEach items="${favorItemList}" var="p" varStatus="c">
+        <tr>
+            <td>${p.id}</td>
+            <td><b>${p.favor.title}</b></td>
+            <td><fmt:formatDate value='${p.favor.occurDate}' pattern='yyyy-MM-dd'/></td>
+            <td><a href="/favorItem/toList?personId=${p.personId}">${p.person.name}(${p.person.callName})</a></td>
+            <td><a href="/favorItem/toList?personId=${p.sourcePersonId}">${p.sourcePerson.name}(${p.sourcePerson.callName})</a></td>
+            <td>${p.amount}</td>
+            <td>${p.returnGift}</td>
+            <td>${p.remark}</td>
+            <td><fmt:formatDate value='${p.createTime}' pattern='yyyy-MM-dd'/></td>
+            <td>
+                <a href="/favorItem/edit/${p.id}">编辑</a>
+            </td>
+        </tr>
+    </c:forEach>
+    </tbody>
+</table>
+</body>
+</html>

+ 206 - 0
src/main/webapp/WEB-INF/pages/favor/list.jsp

@@ -0,0 +1,206 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
+<html>
+<head>
+    <title>人情事件</title>
+    <meta name="renderer" content="webkit">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+    <link rel="stylesheet" href="${pageContext.request.contextPath}/asserts/layui/css/layui.css">
+</head>
+<body>
+
+<%-- 人员列表 --%>
+<div style="margin: 0 auto; width: 90%;">
+    <form class="layui-form" action="">
+        <div class="layui-inline">
+            <label class="layui-form-label">事件</label>
+            <div class="layui-input-inline">
+                <input type="text" id="title" name="title" autocomplete="off" class="layui-input">
+            </div>
+        </div>
+        <div class="layui-inline">
+            <label class="layui-form-label">人员</label>
+            <div class="layui-input-inline">
+                <select id="personId" name="personId" lay-verify="" lay-search>
+                    <option value="">请选择</option>
+                    <c:forEach items="${personList}" var="p">
+                        <option value="${p.id}">${p.callName}(${p.name})</option>
+                    </c:forEach>
+                </select>
+            </div>
+        </div>
+        <div class="layui-inline">
+            <label class="layui-form-label">日期范围:</label>
+            <div class="layui-input-inline">
+                <input type="text" name="occurDate" id="occurDate" autocomplete="off" value="" placeholder="请选择日期范围" style="width: 180px;" class="layui-input">
+            </div>
+        </div>
+
+        <div class="layui-inline">
+            <div class="layui-input-inline">
+                <button class="layui-btn" id="searchBtn" data-type="reload" style="margin-left: 15px">
+                    <i class="layui-icon layui-icon-search"></i> 查询
+                </button>
+                <button type="reset" class="layui-btn layui-btn-primary">重置</button>
+            </div>
+        </div>
+    </form>
+
+    <table id="favorTab" class="layui-hide" lay-filter="person"></table>
+</div>
+
+<%-- 表格上方按钮 --%>
+<script type="text/html" id="toolbarDemo">
+    <div class="layui-btn-container">
+        <button class="layui-btn layui-btn-sm" lay-event="addFavor">添加人情</button>
+    </div>
+</script>
+
+<%-- 表格行操作栏 --%>
+<script type="text/html" id="barDemo">
+    <a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
+    <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
+</script>
+
+<script type="text/javascript" src="${pageContext.request.contextPath}/asserts/js/jquery-3.7.1.min.js"></script>
+<script type="text/javascript" src="${pageContext.request.contextPath}/asserts/layui/layui.js" charset="utf-8"></script>
+<script>
+    let headUrl = "${pageContext.request.contextPath}";
+    layui.use(['laydate', 'table',], function () {
+        var $ = layui.$,//jquery
+            table = layui.table,//表格
+            layer = layui.layer,//基础
+            laydate = layui.laydate;//日期
+        var myDate = new Date();//获取当前时间设置选择时间的范围
+        var maxtime = myDate.getFullYear() + "-" + (myDate.getMonth() + 1) + "-" + (myDate.getDate());//拼接当前是时间
+
+        //初始化选择时间控件
+        laydate.render({
+            elem: '#occurDate'
+            , type: 'date'
+            , range: '~'
+            , max: maxtime//设置选择最大时间为当前
+            , calendar: true
+            , btns: ['clear', 'confirm', 'now']
+            , done: function (value, date) {
+                //这里时选中后触发事件
+                //value是选中的值
+                console.log("日期范围的值:" + value)
+            }
+        });
+        //数据表格渲染
+        table.render({
+            id: 'favorTab',
+            elem: '#favorTab',
+            toolbar: '#toolbarDemo', //开启头部工具栏,并为其绑定左侧模板
+            url: '${pageContext.request.contextPath}/favor/listPage',
+            method: 'post',
+            contentType: "application/json;charset=UTF-8",
+            dataType: "json",
+            request: {
+                pageName: 'pageNo', //页码的参数名称,默认:page
+                limitName: 'pageSize' //每页数据量的参数名,默认:limit
+            },
+            where: {
+                terms: {},
+                sortName: "occur_date",
+                sortOrder: "asc"
+            },
+            //开启分页并解析数据
+            parseData: function(res) {
+                res.count = res.page.total;
+                return res;
+            },
+            page: true,
+            limit: 10,
+            cellMinWidth: 50, //全局定义常规单元格的最小宽度,layui 2.2.1 新增
+            cols: [[
+                { field: 'occurDate', title: '日期', sort: true },
+                { field: 'title', title: '事件'},
+                { field: 'personName', title: '主体人员'},
+                { field: 'incomeAmount', title: '收入金额'},
+                { field: 'payAmount', title: '支出金额' },
+                { field: 'remark', title: '备注'},
+                { fixed: 'right', align:'center', width: 240, toolbar: '#barDemo'}
+            ]]
+        });
+        //搜索
+        $('#searchBtn').on('click', function () {
+            table.reload('favorTab', {
+                url: '${pageContext.request.contextPath}/favor/listPage',
+                method: 'post',
+                contentType: "application/json;charset=UTF-8",
+                dataType: "json",
+                request: {
+                    pageName: 'pageNo', //页码的参数名称,默认:page
+                    limitName: 'pageSize' //每页数据量的参数名,默认:limit
+                },
+                page: {
+                    pageNo: 1 //重新从第 1 页开始
+                },
+                where : {
+                    terms: {
+                        title: $("#title").val(),
+                        personId: $("#personId").val(),
+                        familyPersonId: $("#familyPersonId").val()
+                    },
+                    sortName: "p.create_time",
+                    sortOrder: "asc"
+                }
+            });
+            return false;
+        });
+
+        //头工具栏事件
+        table.on('toolbar(person)', function (obj) {
+            switch (obj.event) {
+                case 'addFavor':
+                    window.location.href = "${pageContext.request.contextPath}/favor/toAdd";
+                    break;
+            };
+        });
+
+        //监听行工具事件
+        table.on('tool(person)', function (obj) {
+            var data = obj.data;
+            if (obj.event === 'clickAddress') {
+                $("#address").val(data.address);
+                $("#searchBtn").click();
+            } else if (obj.event === 'addFavor') {
+                $("#address").val(data.address);
+                $("#searchBtn").click();
+            } else if (obj.event === 'del') {
+                //eg1
+                layer.confirm('确定要删除?<br/><i>会删掉关联的明细等</i>', {icon: 3, title:'提示'}, function(index){
+                    let url = "${pageContext.request.contextPath}/favor/del/" + data.id;
+                    $.ajax({
+                        url:url,
+                        type:"POST",
+                        contentType:"application/json; charset=utf-8",
+                        dataType:"json",
+                        success: function(res){
+                            console.log(res);
+                            if (res.code === 1) {
+                                layer.msg(res.msg, {icon: 2});
+                            } else {
+                                //先给成功提示,然后刷新列表
+                                layer.msg('删除成功', {icon: 1, time: 1000}, function() {
+                                    layui.table.reload("favorTab");
+                                });
+                            }
+                        }
+                    })
+                    layer.close(index);
+                });
+
+            } else if (obj.event === 'edit') {
+                window.location.href = "${pageContext.request.contextPath}/favor/edit/" + data.id;
+            }
+        });
+
+    });
+</script>
+</body>
+</html>

+ 9 - 0
src/main/webapp/WEB-INF/pages/hello.jsp

@@ -0,0 +1,9 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<html>
+<head>
+    <title>HelloWorld</title>
+</head>
+<body>
+    <h1>HelloWorld</h1>
+</body>
+</html>

+ 10 - 0
src/main/webapp/WEB-INF/pages/index.jsp

@@ -0,0 +1,10 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<html>
+<head>
+    <title>首页</title>
+</head>
+<body>
+    <div><a href="/person/toList">人员列表</a></div>
+    <div><a href="/favor/toList">人情列表</a></div>
+</body>
+</html>

+ 169 - 0
src/main/webapp/WEB-INF/pages/person/edit.jsp

@@ -0,0 +1,169 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
+<html>
+<head>
+    <title>添加人员</title>
+    <meta name="renderer" content="webkit">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+    <link rel="stylesheet" href="${pageContext.request.contextPath}/asserts/layui/css/layui.css">
+</head>
+<body>
+<div style="margin: 10px 0 0 10px">
+    <form class="layui-form layui-form-pane" action="${pageContext.request.contextPath}/person/update">
+        <input type="hidden" name="id" value="${person.id}" />
+        <div class="layui-form-item"> <label class="layui-form-label">称呼</label>
+            <div class="layui-input-inline">
+                <input type="text" name="callName" value="${person.callName}" lay-verify="required" lay-reqtext="称呼不能为空" placeholder="请输入" autocomplete="off" class="layui-input">
+            </div>
+        </div>
+        <div class="layui-form-item"> <label class="layui-form-label">姓名</label>
+            <div class="layui-input-inline">
+                <input type="text" name="name" value="${person.name}" lay-verify="required" lay-reqtext="姓名不能为空" placeholder="请输入" autocomplete="off" class="layui-input">
+            </div>
+        </div>
+        <div class="layui-form-item"> <label class="layui-form-label">地址</label>
+            <div class="layui-input-inline">
+                <input type="text" name="address" value="${person.address}" autocomplete="off" class="layui-input">
+            </div>
+        </div>
+        <div class="layui-form-item"> <label class="layui-form-label">性别</label>
+            <div class="layui-input-block">
+                <input type="radio" name="gender" value="M" title="男" <c:if test="${person.gender == 'M' or person.gender == null}">checked=""</c:if>>
+                <input type="radio" name="gender" value="F" title="女" <c:if test="${person.gender == 'F'}">checked=""</c:if>>
+            </div>
+        </div>
+        <div class="layui-form-item"> <label class="layui-form-label">配偶</label>
+            <div class="layui-input-inline">
+                <select name="mateId" lay-filter="mateId" lay-search="">
+                    <option value="">请选择</option>
+                    <if test="${person.mateId != null}">
+                        <option value="${person.mateId}" selected>${person.mateName}</option>
+                    </if>
+                    <c:forEach items="${matePersonOptionList}" var="opt" varStatus="c">
+                        <option value="${opt.value}">${opt.title}</option>
+                    </c:forEach>
+                </select>
+            </div>
+        </div>
+        <div class="layui-form-item">
+            <div class="layui-inline"> <label class="layui-form-label">父亲</label>
+                <div class="layui-input-inline">
+                    <select name="fatherId" lay-search="">
+                        <option value="">请选择</option>
+                        <if test="${person.fatherId != null}">
+                            <option value="${person.fatherId}" selected>${person.fatherName}</option>
+                        </if>
+                        <c:forEach items="${fatherPersonOptionList}" var="opt" varStatus="c">
+                            <option value="${opt.value}">${opt.title}</option>
+                        </c:forEach>
+                    </select>
+                </div>
+            </div>
+            <div class="layui-inline"> <label class="layui-form-label">母亲</label>
+                <div class="layui-input-inline">
+                    <select name="motherId" lay-search="">
+                        <option value="">请选择</option>
+                        <if test="${person.motherId != null}">
+                            <option value="${person.motherId}" selected>${person.motherName}</option>
+                        </if>
+                        <c:forEach items="${motherPersonOptionList}" var="opt" varStatus="c">
+                            <option value="${opt.value}">${opt.title}</option>
+                        </c:forEach>
+                    </select>
+                </div>
+            </div>
+        </div>
+        <div class="layui-form-item"> <label class="layui-form-label">家中排行</label>
+            <div class="layui-input-inline">
+                <input type="number" name="sort" value="${person.sort}" autocomplete="off" class="layui-input" value="1">
+            </div>
+        </div>
+        <div class="layui-form-item"> <label class="layui-form-label">婚姻状况</label>
+            <div class="layui-input-inline">
+                <input type="radio" name="maritalStatus" value="0" title="未婚" <c:if test="${person.maritalStatus == 0}">checked=""</c:if>>
+                <input type="radio" name="maritalStatus" value="1" title="已婚" <c:if test="${person.maritalStatus == 1}">checked=""</c:if>>
+            </div>
+        </div>
+        <div class="layui-form-item">
+            <div class="layui-inline"> <label class="layui-form-label">出生日期</label>
+                <div class="layui-input-inline">
+                    <input type="text" name="birthday" id="birthday" value="<fmt:formatDate value='${person.birthday}' pattern='yyyy-MM-dd'/>" placeholder="yyyy-MM-dd" autocomplete="off" class="layui-input">
+                </div>
+            </div>
+            <div class="layui-inline"> <label class="layui-form-label">去世日期</label>
+                <div class="layui-input-inline">
+                    <input type="text" name="deathDate" id="deathDate" value="${person.deathDate}" placeholder="yyyy-MM-dd" autocomplete="off" class="layui-input">
+                </div>
+            </div>
+        </div>
+        <div class="layui-form-item layui-form-text"> <label class="layui-form-label">备注</label>
+            <div class="layui-input-block">
+                <textarea name="remark" placeholder="请输入内容" class="layui-textarea">${person.remark}</textarea>
+            </div>
+        </div>
+        <div class="layui-form-item">
+            <div class="layui-input-block">
+                <button type="submit" class="layui-btn" lay-submit="" lay-filter="submit">保存</button>
+                <button type="reset" class="layui-btn layui-btn-primary">取消</button>
+            </div>
+        </div>
+    </form>
+</div>
+<script type="text/javascript" src="${pageContext.request.contextPath}/asserts/js/jquery-3.7.1.min.js"></script>
+<script type="text/javascript" src="${pageContext.request.contextPath}/asserts/layui/layui.js" charset="utf-8"></script>
+<script>
+    layui.use(['form', 'laydate'], function() {
+        let form = layui.form, laydate = layui.laydate;
+        //日期
+        laydate.render({
+            elem: '#birthday'
+        });
+        laydate.render({
+            elem: '#deathDate'
+        });
+        //配偶下拉框选中触发事件
+        form.on("select(mateId)", function (data) {
+            if (data.value !== "") {
+                $('input[name="maritalStatus"][value="1"]').prop('checked', 'checked');
+            } else {
+                $('input[name="maritalStatus"][value="0"]').prop('checked', 'checked');
+            }
+            layui.form.render();
+        });
+        //监听提交
+        form.on('submit(submit)', function(data){
+            let url = data.form.action;
+            let formJson = data.field;
+            if (formJson.id === undefined || formJson.id === '') {
+                url = "${pageContext.request.contextPath}/person/add";
+            }
+            console.log("提交地址:" + url);
+            console.log(JSON.stringify(data.field));
+            $.ajax({
+                url:url,
+                type:"POST",
+                data:JSON.stringify(formJson),
+                contentType:"application/json; charset=utf-8",
+                dataType:"json",
+                success: function(res){
+                    console.log(res);
+                    if (res.code === 1) {
+                        layer.msg(res.msg, {icon: 2, time: 2000});
+                    } else {
+                        //先给成功提示,然后关闭弹窗
+                        layer.msg('保存成功', {icon: 1, time: 1000}, function() {
+                            let index = parent.layer.getFrameIndex(window.name); //先得到当前iframe层的索引
+                            parent.layer.close(index);
+                            parent.layui.table.reload("personTab");
+                        });
+                    }
+                }
+            })
+            return false;
+        });
+    });
+</script>
+</body>
+</html>

+ 269 - 0
src/main/webapp/WEB-INF/pages/person/list.jsp

@@ -0,0 +1,269 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
+<html>
+<head>
+    <title>人员列表</title>
+    <meta name="renderer" content="webkit">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+    <link rel="stylesheet" href="${pageContext.request.contextPath}/asserts/layui/css/layui.css">
+</head>
+<body>
+
+<%-- 人员列表 --%>
+<div style="margin: 0 auto; width: 90%;">
+    <form class="layui-form" action="">
+        <div class="layui-inline">
+            <label class="layui-form-label">姓名</label>
+            <div class="layui-input-inline">
+                <input type="text" id="name" name="name" autocomplete="off" class="layui-input">
+            </div>
+        </div>
+        <div class="layui-inline">
+            <label class="layui-form-label">家庭成员</label>
+            <div class="layui-input-inline">
+                <select id="familyPersonId" name="familyPersonId" lay-verify="" lay-search>
+                    <option value="">请选择</option>
+                    <c:forEach items="${familyPersonList}" var="p">
+                        <option value="${p.id}">${p.callName}(${p.name})</option>
+                    </c:forEach>
+                </select>
+            </div>
+        </div>
+        <div class="layui-inline">
+            <label class="layui-form-label">地址</label>
+            <div class="layui-input-inline">
+                <input id="address" type="text" name="address" autocomplete="off" class="layui-input">
+            </div>
+        </div>
+        <div class="layui-inline">
+            <div class="layui-input-inline">
+                <button class="layui-btn" id="searchBtn" data-type="reload" style="margin-left: 15px">
+                    <i class="layui-icon layui-icon-search"></i> 查询
+                </button>
+                <button type="reset" class="layui-btn layui-btn-primary">重置</button>
+            </div>
+        </div>
+    </form>
+
+    <table id="personTab" class="layui-hide" lay-filter="person"></table>
+</div>
+
+<%-- 表格上方按钮 --%>
+<script type="text/html" id="toolbarDemo">
+    <div class="layui-btn-container">
+        <button class="layui-btn layui-btn-sm" lay-event="addPerson">添加人员</button>
+        <button class="layui-btn layui-btn-sm" lay-event="addFavor">添加人情</button>
+    </div>
+</script>
+
+<%-- 表格行操作栏 --%>
+<script type="text/html" id="barDemo">
+    <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="addFavor">添加人情</a>
+    <a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
+    <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
+</script>
+
+<script type="text/javascript" src="${pageContext.request.contextPath}/asserts/js/jquery-3.7.1.min.js"></script>
+<script type="text/javascript" src="${pageContext.request.contextPath}/asserts/layui/layui.js" charset="utf-8"></script>
+<script>
+    let headUrl = "${pageContext.request.contextPath}";
+    /** 性别格式化 */
+    function genderFormat(obj) {
+        return obj.gender === 'M' ? '男' : '女';
+    }
+    /** 婚姻状况格式化 */
+    function maritalStatusFormat(obj) {
+        return obj.maritalStatus === 1 ? '已婚' : '未婚';
+    }
+    /** 称呼格式化 */
+    function callNameFormat(obj) {
+        if(obj.deathDate !== undefined && obj.deathDate !== null) {
+            return "<span style='background-color: gray; color: #fff;'>"+obj.callName+"</span>";
+        }
+        return obj.callName;
+    }
+
+    layui.use('table', function () {
+        let table = layui.table;
+        //数据表格渲染
+        table.render({
+            id: 'personTab',
+            elem: '#personTab',
+            toolbar: '#toolbarDemo', //开启头部工具栏,并为其绑定左侧模板
+            url: '${pageContext.request.contextPath}/person/listPage',
+            method: 'post',
+            contentType: "application/json;charset=UTF-8",
+            dataType: "json",
+            request: {
+                pageName: 'pageNo', //页码的参数名称,默认:page
+                limitName: 'pageSize' //每页数据量的参数名,默认:limit
+            },
+            where: {
+                terms: {},
+                sortName: "p.create_time",
+                sortOrder: "asc"
+            },
+            //开启分页并解析数据
+            parseData: function(res) {
+                res.count = res.page.total;
+                return res;
+            },
+            page: true,
+            limit: 20,
+            cellMinWidth: 50, //全局定义常规单元格的最小宽度,layui 2.2.1 新增
+            cols: [[
+                { type: 'checkbox', field: 'id' },
+                { field: 'callName', title: '称呼', width: 80, templet: callNameFormat },
+                { field: 'name', title: '姓名', event:'clickName',style:'cursor: pointer;', width: 80 },
+                { field: 'mateId', title: '配偶ID', hide:true },
+                { field: 'mateName', title: '配偶', event:'clickMate',style:'cursor: pointer;' },
+                { field: 'motherId', title: '母亲ID', hide:true },
+                { field: 'motherName', title: '母亲', event:'clickMother',style:'cursor: pointer;' },
+                { field: 'fatherId', title: '父亲ID', hide:true },
+                { field: 'fatherName', title: '父亲', event:'clickFather',style:'cursor: pointer;' },
+                { field: 'sort', title: '家中排行', width: 90 },
+                { field: 'address', title: '地址', event:'clickAddress',style:'cursor: pointer;' },
+                { field: 'gender', title: '性别', width: 80, sort: true, templet: genderFormat},
+                { field: 'maritalStatus', title: '婚姻状况', width: 110, sort: true, templet: maritalStatusFormat },
+                { field: 'birthday', title: '生日', width: 110 },
+                { fixed: 'right', align:'center', width: 240, toolbar: '#barDemo'}
+            ]]
+        });
+        //搜索
+        $('#searchBtn').on('click', function () {
+            table.reload('personTab', {
+                url: '${pageContext.request.contextPath}/person/listPage',
+                method: 'post',
+                contentType: "application/json;charset=UTF-8",
+                dataType: "json",
+                request: {
+                    pageName: 'pageNo', //页码的参数名称,默认:page
+                    limitName: 'pageSize' //每页数据量的参数名,默认:limit
+                },
+                page: {
+                    pageNo: 1 //重新从第 1 页开始
+                },
+                where : {
+                    terms: {
+                        name: $("#name").val(),
+                        address: $("#address").val(),
+                        familyPersonId: $("#familyPersonId").val()
+                    },
+                    sortName: "p.create_time",
+                    sortOrder: "asc"
+                }
+            });
+            return false;
+        });
+
+        //头工具栏事件
+        table.on('toolbar(person)', function (obj) {
+            var checkStatus = table.checkStatus(obj.config.id);
+            switch (obj.event) {
+                case 'addPerson':
+                    //通过这种方式弹出的层,每当它被选择,就会置顶。
+                    layer.open({
+                        type: 2,
+                        title: '新增人员',
+                        shade: false,
+                        area: ['700px', '600px'],
+                        maxmin: true,
+                        content: headUrl + "/person/toAdd",
+                        zIndex: layer.zIndex, //重点1
+                        success: function(layero){
+                            layer.setTop(layero); //重点2
+                        }
+                    });
+                    break;
+                case 'addFavor':
+                    let data = checkStatus.data;
+                    let personIds = [];
+                    for(let i=0; i<data.length; i++) {
+                        personIds.push(data[i].id);
+                    }
+                    console.log("选中的ID:" + personIds);
+                    window.location.href = "${pageContext.request.contextPath}/favor/toAdd?sourcePersonIds=" + personIds.toString();
+                    break;
+                case 'isAll':
+                    layer.msg(checkStatus.isAll ? '全选' : '未全选');
+                    break;
+
+                //自定义头工具栏右侧图标 - 提示
+                case 'LAYTABLE_TIPS':
+                    layer.alert('这是工具栏右侧自定义的一个图标按钮');
+                    break;
+            };
+        });
+
+        //监听行工具事件
+        table.on('tool(person)', function (obj) {
+            let data = obj.data;
+            console.log("点击行数据:" + data);
+            if (obj.event === 'clickAddress') {
+                $("#address").val(data.address);
+                $("#searchBtn").click();
+            } else if (obj.event === 'addFavor') {
+                $("#address").val(data.address);
+                $("#searchBtn").click();
+            } else if (obj.event === 'del') {
+                //eg1
+                layer.confirm('确定要删除?<br/><i>会删掉相关人员的配偶、父母等</i>', {icon: 3, title:'提示'}, function(index){
+                    let url = "${pageContext.request.contextPath}/person/del/" + data.id;
+                    $.ajax({
+                        url:url,
+                        type:"POST",
+                        contentType:"application/json; charset=utf-8",
+                        dataType:"json",
+                        success: function(res){
+                            console.log(res);
+                            if (res.code === 1) {
+                                layer.msg(res.msg, {icon: 2});
+                            } else {
+                                //先给成功提示,然后刷新列表
+                                layer.msg('删除成功', {icon: 1, time: 1000}, function() {
+                                    layui.table.reload("personTab");
+                                });
+                            }
+                        }
+                    })
+                    layer.close(index);
+                });
+
+            } else if (obj.event === 'edit') {
+                //通过这种方式弹出的层,每当它被选择,就会置顶。
+                layer.open({
+                    type: 2,
+                    title: '编辑人员',
+                    shade: false,
+                    area: ['700px', '600px'],
+                    maxmin: true,
+                    content: headUrl + "/person/edit/" + data.id,
+                    zIndex: layer.zIndex, //重点1
+                    success: function(layero){
+                        layer.setTop(layero); //重点2
+                    }
+                });
+            } else {
+                //点击名字、配偶、母亲、父亲
+                if (obj.event === 'clickName') {
+                    $("#familyPersonId").val(data.id);
+                } else if (obj.event === 'clickMate') {
+                    $("#familyPersonId").val(data.mateId);
+                } else if (obj.event === 'clickMother') {
+                    $("#familyPersonId").val(data.motherId);
+                } else if (obj.event === 'clickFather') {
+                    $("#familyPersonId").val(data.fatherId);
+                }
+                layui.form.render();
+                $("#searchBtn").click();
+            }
+
+
+        });
+
+    });
+</script>
+</body>
+</html>

+ 32 - 0
src/main/webapp/WEB-INF/spring-servlet.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+	   xmlns:p="http://www.springframework.org/schema/p"
+	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	   xmlns:context="http://www.springframework.org/schema/context"
+	   xmlns:mvc="http://www.springframework.org/schema/mvc"
+	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+			http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
+	<!-- 开启MVC注解驱动 -->
+	<mvc:annotation-driven>
+		<mvc:message-converters>
+			<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
+				<property name="objectMapper">
+					<bean class="com.fasterxml.jackson.databind.ObjectMapper">
+						<!-- 为null字段时不显示 -->
+						<property name="serializationInclusion">
+							<value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
+						</property>
+					</bean>
+				</property>
+			</bean>
+		</mvc:message-converters>
+	</mvc:annotation-driven>
+	<!-- 设置使用注解的类所在的jar包 -->
+	<context:component-scan base-package="com.anyway.favor.**.controller"/>
+	<!-- 资源映射,因为spring mvc接受了所有的请求,所以要在这个地方设置一下资源 -->
+	<mvc:resources mapping="/asserts/**" location="/asserts/"/>
+	<!-- 对转向页面的路径解析。prefix:前缀, suffix:后缀 -->
+	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/pages/" p:suffix=".jsp" />
+
+</beans>

+ 40 - 0
src/main/webapp/WEB-INF/web.xml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
+         version="4.0">
+    <context-param>
+        <param-name>contextConfigLocation</param-name>
+        <param-value>classpath*:applicationContext.xml</param-value>
+    </context-param>
+    <listener>
+        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+    </listener>
+    <servlet>
+        <servlet-name>spring</servlet-name>
+        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+        <load-on-startup>0</load-on-startup>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>spring</servlet-name>
+        <url-pattern>/</url-pattern>
+    </servlet-mapping>
+
+    <!--编码过滤器-->
+    <filter>
+        <filter-name>SpringEncodingFilter</filter-name>
+        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
+        <init-param>
+            <param-name>encoding</param-name>
+            <param-value>UTF-8</param-value>
+        </init-param>
+        <init-param>
+            <param-name>forceEncoding</param-name>
+            <param-value>true</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>SpringEncodingFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>

+ 23 - 0
src/main/webapp/asserts/js/custom.js

@@ -0,0 +1,23 @@
+/**
+ * 删除对象属性
+ *
+ * @param object
+ * @param key
+ */
+function deleteObjKey(object, key) {
+    if (!!object && object.hasOwnProperty(key)) {
+        delete object[key];
+    }
+}
+
+/**
+ * 根据索引删除数组元素
+ *
+ * @param array
+ * @param index
+ */
+function removeArrEle(array, index) {
+    if (array.length > index) {
+        array.splice(index, 1);
+    }
+}

+ 138 - 0
src/main/webapp/asserts/js/favor.js

@@ -0,0 +1,138 @@
+/**
+ * 添加人情明细
+ */
+function addTr() {
+    let tbodyEle = document.querySelector("#favorItemTab tbody");
+    let trEle = document.createElement("tr");
+    trEle.innerHTML = trHtmlTemplate;
+    tbodyEle.appendChild(trEle)
+    //设置默认值
+    let personId = $("#favorTab select[name='personId']").val();
+    $(trEle).find("select[name='personId']").val(personId);
+    //计算收入金额
+    calcIncomeAmount();
+}
+/**
+ * 删除人情明细
+ */
+function deleteTr(_this) {
+    $(_this).parent().parent().remove();
+}
+/**
+ * 选中送礼人
+ */
+function chooseSourcePerson() {
+    let _this = window.event.srcElement || window.event.target;
+    let index = _this.options.selectedIndex;
+    let addrAttr = _this.options[index].attributes['data-address'];
+    if (addrAttr === undefined) {
+        _this.parentElement.nextElementSibling.innerText = '';
+    } else {
+        _this.parentElement.nextElementSibling.innerText = addrAttr.value;
+    }
+}
+/**
+ * 计算人情明细金额
+ */
+function calcIncomeAmount() {
+    let amountEles = $("#favorItemTab tr input[name='amount']");
+    let incomeAmount = 0;
+    for(let i=0; i<amountEles.length; i++) {
+        incomeAmount = incomeAmount + parseInt(amountEles[i].value);
+    }
+    $("#favorTab input[name='incomeAmount']").val(incomeAmount);
+}
+/**
+ * 保存人情
+ */
+function saveFavor() {
+    let url = headUrl + '/favor/add';
+    //人情
+    let favorJson = {
+        'title': $("input[name='title']").val(),
+        'personId': $("select[name='personId']").val(),
+        'occurDate': $("input[name='occurDate']").val(),
+        'incomeAmount': $("input[name='incomeAmount']").val(),
+        'payAmount': $("input[name='payAmount']").val(),
+        'remark': $("input[name='remark']").val(),
+    };
+    //人情明细
+    let favorItemList = [];
+    let trs = $('#favorItemTab tbody tr');
+    for(let i=0; i<trs.length; i++) {
+        let tr = trs[i];
+        let sourcePersonId = $(tr).find("select[name='sourcePersonId']").val();
+        if (sourcePersonId !== undefined) {
+            let favorJson = {
+                'sourcePersonId': sourcePersonId,
+                'personId': $(tr).find("select[name='personId']").val(),
+                'amount': $(tr).find("input[name='amount']").val(),
+                'returnGift': $(tr).find("input[name='returnGift']").val(),
+                'remark': $(tr).find("input[name='remark']").val(),
+            };
+            favorItemList.push(favorJson);
+        }
+    }
+    favorJson['favorItemList'] = favorItemList;
+    $.ajax({
+        url: url,
+        type: "POST",
+        data: JSON.stringify(favorJson),
+        contentType: "application/json;charset=utf-8",
+        success: function (res) {
+            if (res.code !== 0) {
+                alert(res.msg)
+            } else {
+                window.location.href = headUrl + "/favor/toList";
+            }
+        }
+    });
+}
+
+/**
+ * 保存人情
+ */
+function updateFavor() {
+    let url = headUrl + "/favor/update";
+    //人情
+    let favorJson = {
+        'id': $("input[name='id']").val(),
+        'title': $("input[name='title']").val(),
+        'personId': $("select[name='personId']").val(),
+        'occurDate': $("input[name='occurDate']").val(),
+        'incomeAmount': $("input[name='incomeAmount']").val(),
+        'payAmount': $("input[name='payAmount']").val(),
+        'remark': $("input[name='remark']").val(),
+    };
+    //人情明细
+    let favorItemList = [];
+    let trs = $('#favorItemTab tbody tr');
+    for(let i=0; i<trs.length; i++) {
+        let tr = trs[i];
+        let sourcePersonId = $(tr).find("select[name='sourcePersonId']").val();
+        if (sourcePersonId !== undefined) {
+            let favorJson = {
+                'sourcePersonId': sourcePersonId,
+                'personId': $(tr).find("select[name='personId']").val(),
+                'amount': $(tr).find("input[name='amount']").val(),
+                'returnGift': $(tr).find("input[name='returnGift']").val(),
+                'remark': $(tr).find("input[name='remark']").val(),
+            };
+            favorItemList.push(favorJson);
+        }
+    }
+    favorJson['favorItemList'] = favorItemList;
+    $.ajax({
+        url: url,
+        type: "POST",
+        data: JSON.stringify(favorJson),
+        contentType: "application/json;charset=utf-8",
+        success: function (res) {
+            if (res.code !== 0) {
+                alert(res.msg)
+            } else {
+                window.location.href = headUrl + "/favor/toList";
+            }
+        }
+    });
+}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
src/main/webapp/asserts/js/jquery-3.7.1.min.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
src/main/webapp/asserts/layui/css/layui.css


+ 1 - 0
src/main/webapp/asserts/layui/css/modules/code.css

@@ -0,0 +1 @@
+html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #eee;border-left-width:6px;background-color:#FAFAFA;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:40px;line-height:40px;border-bottom:1px solid #eee}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 10px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view .layui-code-ol li:first-child{padding-top:10px}.layui-code-view .layui-code-ol li:last-child{padding-bottom:10px}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none}.layui-code-demo .layui-code{visibility:visible!important;margin:-15px;border-top:none;border-right:none;border-bottom:none}.layui-code-demo .layui-tab-content{padding:15px;border-top:none}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
src/main/webapp/asserts/layui/css/modules/laydate/default/laydate.css


BIN=BIN
src/main/webapp/asserts/layui/css/modules/layer/default/icon-ext.png


BIN=BIN
src/main/webapp/asserts/layui/css/modules/layer/default/icon.png


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
src/main/webapp/asserts/layui/css/modules/layer/default/layer.css


BIN=BIN
src/main/webapp/asserts/layui/css/modules/layer/default/loading-0.gif


BIN=BIN
src/main/webapp/asserts/layui/css/modules/layer/default/loading-1.gif


BIN=BIN
src/main/webapp/asserts/layui/css/modules/layer/default/loading-2.gif


BIN=BIN
src/main/webapp/asserts/layui/font/iconfont.eot


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 25 - 0
src/main/webapp/asserts/layui/font/iconfont.svg


BIN=BIN
src/main/webapp/asserts/layui/font/iconfont.ttf


BIN=BIN
src/main/webapp/asserts/layui/font/iconfont.woff


BIN=BIN
src/main/webapp/asserts/layui/font/iconfont.woff2


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
src/main/webapp/asserts/layui/layui.js


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio