Bläddra i källkod

新增宗教场所管理页面v0.1

yawuga 5 månader sedan
förälder
incheckning
c471b43cd0

+ 976 - 0
doc/宗教管理系统DEMO开发技术说明文档.md

@@ -0,0 +1,976 @@
+# 宗教管理系统 DEMO 开发技术说明文档(1–21 章完整版)
+
+> Version: 1.0  
+> Author: ChatGPT  
+> Purpose: 作为 Codex / Cursor / VSCode / 开发团队统一技术规范与唯一事实来源(Single Source of Truth)  
+> Framework: RuoYi-SpringBoot (Thymeleaf)  
+> Backend: Java 8 / Spring Boot 2.2.x / MyBatis / Druid  
+> Frontend: Thymeleaf + Bootstrap 3.3.7  
+> Database: MySQL 5.7+  
+> Date: 2025-01  
+
+---
+
+# 第 1 章:系统概述
+
+本 DEMO 系统为《西安市民宗委宗教管理信息系统建设项目》立项评审所需的演示版本。  
+目标是在现场评审中,通过 **10 分钟内的真实系统操作演示**,向评审专家展示系统在以下方面的能力:
+
+- 宗教活动场所信息管理(场所新增、修改、校验、日志)  
+- 教职人员信息管理(批量导入、批量维护、状态流转)  
+- 数据质量控制(格式校验、冲突识别、冲突处理)  
+- 多级管理与区县校核流程(区县工作台、按教别筛选、批量校核)  
+- 审计与追责能力(操作留痕、字段级差异记录)  
+- 完整的登录与权限体系(管理员/区县用户角色分工)  
+
+本 DEMO 不追求功能“大而全”,而是围绕评审要求的两个核心演示点,聚焦于:
+
+- **“一套完整可用的业务闭环”**  
+- **“相对规范的系统架构与代码结构”**  
+
+系统架构采用 RuoYi-SpringBoot 单体框架,以降低搭建成本,加快 DEMO 开发效率,并便于今后升级为正式系统。
+
+---
+
+# 第 2 章:DEMO 范围(评审要求与覆盖关系)
+
+## 2.1 评审项一:宗教信息录入与数据维护
+
+**评审要求概要:**
+
+1. 登录模拟管理账号,进入宗教活动场所管理模块;  
+2. 新增清真寺场所信息,填写 18 个核心字段(名称、宗教类别、地址、面积等),系统自动校验必填项和数据格式;  
+3. 演示信息修改功能:修改场所负责人信息;  
+4. 系统自动记录修改前后的数据变化;  
+5. 日志中需包含修改人、修改时间、修改内容等关键信息;  
+6. 使用批量维护功能,选择该场所的 3 名人员,批量更新政治面貌为“群众”;  
+7. 系统提供修改预览和二次确认;  
+8. 执行后,所有相关记录同步更新,并记录批量操作日志。
+
+**DEMO 实现范围:**
+
+- 模块:场所管理、教职人员管理、审计日志  
+- 页面:场所列表、场所表单、人员列表、批量维护弹框、审计日志列表/详情  
+- 核心点:  
+  - 场所新增(18 字段)  
+  - 场所修改(字段差异分析 + 写审计日志)  
+  - 批量修改政治面貌(预览 + 二次确认 + 日志)
+
+---
+
+## 2.2 评审项二:智能数据导入与多级审批工作流
+
+**评审要求概要:**
+
+1. 上传包含 500 条虚拟人员信息的 Excel;  
+2. 系统自动进行智能校验,显示格式错误与数据冲突;  
+3. 对冲突数据,系统提供三种处理方式:  
+   - 覆盖现有数据;  
+   - 保留现有数据;  
+   - 手工处理(逐条比对与修改);  
+4. 进入区县校核工作台,对本区县有效记录进行批量和逐条校核;  
+5. 按教别分类处理,校核通过后数据状态变更为“已校核待上报”。
+
+**DEMO 实现范围:**
+
+- 模块:导入管理、人员管理、区县校核工作台、审计日志  
+- 页面:人员导入页面、冲突处理页面、校核工作台页面、日志页面  
+- 核心点:  
+  - Excel 导入并解析 500 条(正常 + 错误 + 冲突)  
+  - 自动格式校验(必填、身份证、日期、枚举值等)  
+  - 冲突识别(姓名 + 身份证号)  
+  - 三种冲突处理模式  
+  - 区县视角的校核与状态变更  
+  - 审计日志记录导入与校核操作
+
+---
+
+# 第 3 章:角色与权限模型
+
+系统采用 RuoYi 原生 RBAC(基于角色的访问控制)模型。  
+
+## 3.1 角色定义
+
+1. **管理员(Admin)**
+   - 负责全局配置与演示操作;
+   - 权限包含:
+     - 场所管理:增删改查;
+     - 人员管理:增删改查、批量维护;
+     - 人员导入:上传、解析、冲突处理、提交;
+     - 审计日志:查看;
+     - 字典管理:可维护教别、政治面貌等;
+     - 用户与角色管理:可选。
+
+2. **区县用户(CountyUser)**
+   - 对应区县民宗部门业务人员;
+   - 权限包含:
+     - 仅可查看本区县的人员数据;
+     - 进入“区县校核工作台”;
+     - 对本区县的待校核人员进行校核(批量/单条);
+     - 无权新增/修改场所;
+     - 无权查看审计日志模块。
+
+## 3.2 权限控制方式
+
+- RuoYi 菜单绑定权限字符串,例如:
+  - `religion:place:list`  
+  - `religion:place:add`  
+  - `religion:place:edit`  
+  - `religion:person:list`  
+  - `religion:person:import`  
+  - `religion:check:list`  
+  - `religion:audit:list` 等。
+- Controller 方法上使用 `@RequiresPermissions("xxx")` 注解进行细粒度控制;
+- 数据权限(区县)可以简单通过当前登录用户的 `deptId` 或扩展字段进行过滤,例如:  
+  `WHERE county = #{user.county}`。
+
+---
+
+# 第 4 章:数据模型(ERD + SQL)
+
+## 4.1 核心业务实体
+
+1. **religion_place**:宗教活动场所信息表  
+2. **religion_person**:宗教教职人员信息表  
+3. **religion_import_batch**:导入批次表  
+4. **religion_import_item**:导入项表(每一行 Excel 记录在解析阶段对应一个 item)  
+5. **religion_audit_log**:业务操作审计日志表
+
+## 4.2 表结构 SQL(含中文注释)
+
+### 4.2.1 场所表 religion_place
+
+```sql
+CREATE TABLE religion_place (
+  place_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '场所ID',
+  place_name VARCHAR(256) NOT NULL COMMENT '场所名称',
+  religion_type VARCHAR(64) NOT NULL COMMENT '宗教类别',
+  place_category VARCHAR(64) NOT NULL COMMENT '场所类别(寺/观/清真寺/教堂等)',
+  county VARCHAR(128) NOT NULL COMMENT '所属区县',
+  address VARCHAR(512) NOT NULL COMMENT '详细地址',
+  charge_name VARCHAR(128) NOT NULL COMMENT '负责人姓名',
+  charge_phone VARCHAR(32) NOT NULL COMMENT '负责人联系电话',
+  place_area DECIMAL(10,2) COMMENT '场所占地面积(平方米)',
+  building_area DECIMAL(10,2) COMMENT '建筑面积(平方米)',
+  approve_organ VARCHAR(128) COMMENT '批准设立机关',
+  approve_time DATE COMMENT '批准设立时间',
+  reg_organ VARCHAR(128) COMMENT '登记机关',
+  reg_no VARCHAR(64) COMMENT '登记证号',
+  reg_time DATE COMMENT '登记时间',
+  legal_qualified TINYINT DEFAULT 0 COMMENT '是否具有法人资格:0否1是',
+  legal_time DATE COMMENT '法人资格取得时间',
+  credit_code VARCHAR(32) COMMENT '统一社会信用代码',
+  longitude DECIMAL(10,6) COMMENT '经度',
+  latitude DECIMAL(10,6) COMMENT '纬度',
+  create_by VARCHAR(64) COMMENT '创建人',
+  create_time DATETIME COMMENT '创建时间',
+  update_by VARCHAR(64) COMMENT '更新人',
+  update_time DATETIME COMMENT '更新时间'
+);
+```
+
+---
+
+### 4.2.2 教职人员表 religion_person
+
+```sql
+CREATE TABLE religion_person (
+  person_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '人员ID',
+  place_id BIGINT NOT NULL COMMENT '所属场所ID(外键,关联 religion_place.place_id)',
+  name VARCHAR(128) NOT NULL COMMENT '姓名',
+  religion_type VARCHAR(64) NOT NULL COMMENT '宗教类别',
+  gender VARCHAR(8) COMMENT '性别',
+  id_number VARCHAR(32) NOT NULL COMMENT '身份证号',
+  birthday DATE COMMENT '出生日期',
+  county VARCHAR(128) COMMENT '所属区县',
+  address VARCHAR(512) COMMENT '家庭地址/联系地址',
+  political_status VARCHAR(128) COMMENT '政治面貌',
+  edu_national VARCHAR(128) COMMENT '国民教育学历',
+  edu_religious VARCHAR(128) COMMENT '宗教学历',
+  join_time DATE COMMENT '入教/出家时间',
+  phone VARCHAR(32) COMMENT '联系电话',
+  domicile VARCHAR(128) COMMENT '户籍所在地',
+  native_place VARCHAR(128) COMMENT '籍贯',
+  org_name VARCHAR(256) COMMENT '所属宫观名称(Excel 原始值,仅留档)',
+  org_address VARCHAR(512) COMMENT '所属宫观地址(Excel 原始值,仅留档)',
+  status VARCHAR(32) DEFAULT 'TO_CHECK' COMMENT '状态:TO_CHECK待校核/CHECKED已校核/REPORTED已上报',
+  create_by VARCHAR(64) COMMENT '创建人',
+  create_time DATETIME COMMENT '创建时间',
+  update_by VARCHAR(64) COMMENT '更新人',
+  update_time DATETIME COMMENT '更新时间',
+  UNIQUE KEY uk_id_name (id_number, name),
+  INDEX idx_place_id (place_id)
+);
+```
+
+---
+
+### 4.2.3 导入批次表 religion_import_batch
+
+```sql
+CREATE TABLE religion_import_batch (
+  batch_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '批次ID',
+  batch_no VARCHAR(64) COMMENT '批次号(可用时间戳+随机数生成)',
+  file_name VARCHAR(256) COMMENT '导入文件名',
+  uploader VARCHAR(64) COMMENT '上传人登录名',
+  upload_time DATETIME COMMENT '上传时间',
+  total_rows INT COMMENT '总记录数',
+  valid_rows INT COMMENT '有效记录数',
+  error_rows INT COMMENT '格式错误记录数',
+  conflict_rows INT COMMENT '冲突记录数',
+  status VARCHAR(32) COMMENT '状态:PARSED已解析/PARTIAL部分处理/FINISHED已完成',
+  extra JSON COMMENT '扩展字段(预留)'
+);
+```
+
+---
+
+### 4.2.4 导入项表 religion_import_item
+
+```sql
+CREATE TABLE religion_import_item (
+  item_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '导入项ID',
+  batch_id BIGINT COMMENT '批次ID,对应 religion_import_batch.batch_id',
+  row_no INT COMMENT 'Excel 行号(从1开始)',
+  json_data JSON COMMENT '该行原始数据的 JSON 表示',
+  error_msg VARCHAR(512) COMMENT '格式错误说明,为空表示无格式错误',
+  conflict_flag TINYINT COMMENT '是否为冲突记录:0否1是',
+  processed_flag TINYINT COMMENT '是否已完成处理:0否1是',
+  create_time DATETIME COMMENT '记录创建时间',
+  INDEX idx_batch (batch_id)
+);
+```
+
+---
+
+### 4.2.5 审计日志表 religion_audit_log
+
+```sql
+CREATE TABLE religion_audit_log (
+  log_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '日志ID',
+  user_id VARCHAR(64) COMMENT '操作用户ID',
+  user_name VARCHAR(128) COMMENT '操作用户姓名',
+  op_type VARCHAR(64) COMMENT '操作类型(ADD_PLACE/UPDATE_PLACE/BATCH_UPDATE_PERSON/IMPORT_PERSON/CHECK_PERSON等)',
+  resource_type VARCHAR(64) COMMENT '资源类型 place/person/import',
+  resource_id VARCHAR(64) COMMENT '资源ID(如 place_id/person_id/batch_id)',
+  op_detail JSON COMMENT '操作详情(字段差异JSON或附加说明)',
+  op_time DATETIME COMMENT '操作时间',
+  ip VARCHAR(64) COMMENT '操作IP地址',
+  extra JSON COMMENT '扩展字段(可记录请求参数快照等)'
+);
+```
+
+---
+
+# 第 5 章:字段字典(Field Dictionary)
+
+用于:
+
+- 表单中的下拉选项;  
+- Excel 导入时的枚举校验;  
+- 系统数据一致性控制。
+
+## 5.1 宗教类别(religion_type)
+
+- 佛教  
+- 道教  
+- 伊斯兰教  
+- 基督教  
+- 天主教  
+
+## 5.2 政治面貌(political_status)
+
+- 群众  
+- 中共党员  
+- 民革党员  
+- 民盟盟员  
+- 其它民主党派  
+
+## 5.3 国民教育学历(edu_national)
+
+- 小学  
+- 初中  
+- 高中  
+- 中专  
+- 大专  
+- 本科  
+- 研究生  
+
+## 5.4 宗教学历(edu_religious)
+
+- 无  
+- 经学院  
+- 宗教院校本科  
+- 宗教院校研究生  
+
+## 5.5 人员状态(person.status)
+
+- TO_CHECK:待区县校核  
+- CHECKED:已区县校核  
+- REPORTED:已上报(本 DEMO 中仅作为预留状态,不实现上报流程)
+
+## 5.6 导入批次状态(batch.status)
+
+- PARSED:文件已解析,等待冲突处理与导入;  
+- PARTIAL:部分冲突已通过手工处理;  
+- FINISHED:导入完全完成。  
+
+---
+
+# 第 6 章:目录结构规范(RuoYi 标准)
+
+以 RuoYi-SpringBoot 标准单体工程结构为基础,在 `ruoyi-admin` 模块中新增业务包。
+
+```text
+ruoyi-admin
+ └── src/main/java/com/ruoyi/project/religion
+      ├── place
+      │    ├── controller    # ReligionPlaceController
+      │    ├── domain        # ReligionPlace 实体类
+      │    ├── service       # ReligionPlaceService / impl
+      │    ├── mapper        # ReligionPlaceMapper + XML
+      │    └── mapper/xml    # MyBatis XML
+      ├── person
+      │    ├── controller    # ReligionPersonController
+      │    ├── domain        # ReligionPerson
+      │    ├── service
+      │    ├── mapper
+      │    └── mapper/xml
+      ├── import
+      │    ├── controller    # ReligionImportController
+      │    ├── service
+      │    ├── mapper        # 如需要,可存 batch/item 等
+      │    └── mapper/xml
+      └── audit
+           ├── controller    # ReligionAuditController
+           ├── domain        # ReligionAuditLog
+           ├── service
+           ├── mapper
+           └── mapper/xml
+```
+
+前端 Thymeleaf 模板位于:
+
+```text
+ruoyi-admin/src/main/resources/templates/religion
+ ├── place
+ │    ├── place_list.html
+ │    └── place_form.html
+ ├── person
+ │    ├── person_list.html
+ │    ├── person_import.html
+ │    └── person_conflict.html
+ ├── check
+ │    └── check_workbench.html
+ └── audit
+      └── audit_list.html
+```
+
+---
+
+# 第 7 章:业务流程说明
+
+## 7.1 场所新增流程
+
+1. 管理员登录系统;  
+2. 进入“场所管理”菜单,点击“新增场所”;  
+3. 打开 `place_form.html`,填写 18 个字段;  
+4. 前端进行必填与基本格式校验(如手机号、面积数字等);  
+5. 点击“保存”;  
+6. 后端进行二次校验(避免绕过前端);  
+7. 成功保存 `religion_place`;  
+8. 使用 `ReligionAuditLogService.logCreate(...)` 写一条新增日志;  
+9. 前端提示“保存成功”,返回列表页。
+
+## 7.2 场所修改流程
+
+1. 在“场所列表”中选中某条记录,点击“编辑”;  
+2. 进入 `place_form.html`,展示原有字段;  
+3. 用户修改负责人姓名或电话等信息;  
+4. 提交后,Service 层获取旧记录 + 新记录;  
+5. 计算字段差异(例如通过反射比对或手动比对重要字段);  
+6. 构造 `op_detail` JSON,如:  
+   ```json
+   {
+     "charge_name": ["张三", "李四"],
+     "charge_phone": ["13800000000", "13900000000"]
+   }
+   ```  
+7. 写入 `religion_audit_log`;  
+8. 返回成功。
+
+## 7.3 批量修改政治面貌流程
+
+1. 在“人员列表”选中 3 个教职人员;  
+2. 点击“批量维护” → 弹出批量修改对话框;  
+3. 选择“政治面貌”字段,新值选择为“群众”;  
+4. 点击“预览”,系统展示:  
+   - 每个人员原政治面貌 → 新政治面貌;  
+5. 用户确认后点击“执行”;  
+6. Service 执行批量更新;  
+7. 写审计日志:记录操作人、时间、受影响人数、字段名及新值;  
+8. 返回成功并刷新列表。
+
+## 7.4 人员导入流程
+
+1. 管理员进入“人员导入”页面 `person_import.html`;  
+2. 顶部选择“所属场所”;  
+3. 选择导入文件(500 条 Excel);  
+4. 点击“上传并解析”;  
+5. 后端:
+   - 创建 `religion_import_batch` 记录;
+   - 解析 Excel,每行构造 JSON,判定格式错误;
+   - 根据 `name + id_number` 与 `religion_person` 比较,标记冲突;
+   - 将结果写入 `religion_import_item`;
+   - 返回统计信息(总数 / 正常 / 错误 / 冲突)。  
+6. 前端展示:
+   - 错误数据表格(红色高亮);  
+   - 冲突数据表格;  
+   - 三种处理模式选项(覆盖/保留/手工);  
+7. 用户根据需要:
+   - 选择“直接覆盖”或“保留”;或  
+   - 进入“手工处理”弹框逐行对比修改;  
+8. 处理完成后点击“提交导入”;  
+9. 后端:
+   - 将有效数据写入 `religion_person`;
+   - 冲突行根据模式分别处理;
+   - 写审计日志;  
+10. 更新批次状态为 FINISHED。
+
+## 7.5 区县校核流程
+
+1. 区县用户登录;  
+2. 进入“区县校核工作台” `check_workbench.html`;  
+3. 输入筛选条件(教别、姓名、身份证等);  
+4. 列表展示“待校核(TO_CHECK)”人员;  
+5. 用户可以:
+   - 勾选多条,点击“批量通过”;  
+   - 或单击某条,打开详情后“通过校核”;  
+6. 通过校核后:
+   - `religion_person.status` 更新为 `CHECKED` 或 `REPORTED`(按需求);  
+   - 写审计日志记录校核人、时间、人数;  
+7. 列表刷新后,不再展示已校核的数据。
+
+---
+
+# 第 8 章:模块功能描述
+
+## 8.1 场所管理模块
+
+- 场所列表:查询、分页、导出(可选);  
+- 场所新增:18字段录入;  
+- 场所编辑:字段修改;  
+- 场所详情:只读查看;  
+
+## 8.2 教职人员管理模块
+
+- 人员列表:按姓名、身份证、教别、场所、状态筛选;  
+- 人员编辑:单条修改基本信息;  
+- 批量维护:批量修改政治面貌(DEMO 重点)。
+
+## 8.3 人员导入模块
+
+- 上传 Excel;  
+- 展示解析统计(总/错误/冲突);  
+- 展示格式错误列表;  
+- 展示冲突列表;  
+- 提供冲突处理模式选择与手工处理界面;  
+- 最终导入。
+
+## 8.4 区县校核工作台模块
+
+- 按教别查看待校核人员;  
+- 批量通过;  
+- 单条通过;  
+
+## 8.5 业务审计日志模块
+
+- 日志列表:查询条件包括操作人、操作类型、资源类型、时间范围;  
+- 日志详情:展示 `op_detail` JSON 格式化内容;  
+- 支持导出(可选)。
+
+---
+
+# 第 9 章:后端接口设计(Controller 层)
+
+以下为主要接口清单(以 RESTful 风格描述),实际实现时按 RuoYi 要求返回 AjaxResult。
+
+## 9.1 场所管理 PlaceController
+
+基路径:`/religion/place`
+
+- `GET /list`  
+  场所列表查询;支持分页与条件过滤。
+
+- `POST /create`  
+  新增场所;Body 为 JSON,包含 18 字段。
+
+- `PUT /update/{placeId}`  
+  修改场所;需比对旧值并写日志。
+
+- `GET /detail/{placeId}`  
+  获取场所详情。
+
+## 9.2 教职人员管理 PersonController
+
+基路径:`/religion/person`
+
+- `GET /list`  
+  人员列表查询。
+
+- `PUT /update/{personId}`  
+  修改人员信息。
+
+- `POST /batch/updatePoliticalStatus`  
+  批量修改政治面貌:
+  ```json
+  {
+    "personIds": [1,2,3],
+    "politicalStatus": "群众"
+  }
+  ```
+
+## 9.3 导入 ImportController
+
+基路径:`/religion/import`
+
+- `POST /upload?placeId={placeId}`  
+  上传 Excel 并解析;参数中 placeId 作为人员绑定场所。
+
+- `GET /result/{batchId}`  
+  获取解析结果,包括错误列表和冲突列表。
+
+- `POST /resolveConflict`  
+  对冲突数据进行处理:
+  ```json
+  {
+    "batchId": 11,
+    "mode": "override | keep | manual",
+    "manualList": [
+      { "itemId": 123, "finalData": { ... } }
+    ]
+  }
+  ```
+
+- `POST /commit/{batchId}`  
+  提交导入:将解析完成的数据写入 `religion_person`。
+
+## 9.4 区县校核 CheckController
+
+基路径:`/religion/check`
+
+- `GET /listByCounty`  
+  查询本区县待校核数据,支持教别筛选。
+
+- `POST /singleCheck/{personId}`  
+  单条通过校核。
+
+- `POST /batchCheck`  
+  批量通过校核:
+  ```json
+  {
+    "ids": [1,2,3]
+  }
+  ```
+
+## 9.5 审计日志 AuditController
+
+基路径:`/religion/audit`
+
+- `GET /list`  
+  审计日志列表。
+
+- `GET /detail/{logId}`  
+  日志详情。
+
+---
+
+# 第 10 章:Service 与 Mapper 方法定义(示意)
+
+## 10.1 ReligionPlaceService
+
+```java
+public interface ReligionPlaceService {
+    List<ReligionPlace> list(PlaceQuery query);
+    ReligionPlace getById(Long id);
+    int create(ReligionPlace place);
+    int update(ReligionPlace place);
+}
+```
+
+## 10.2 ReligionPersonService
+
+```java
+public interface ReligionPersonService {
+    List<ReligionPerson> list(PersonQuery query);
+    ReligionPerson getById(Long id);
+    int update(ReligionPerson person);
+    int batchUpdatePoliticalStatus(List<Long> ids, String status, String operator);
+}
+```
+
+## 10.3 ReligionImportService
+
+```java
+public interface ReligionImportService {
+    ImportBatch upload(MultipartFile file, Long placeId, String uploader);
+    ParseResult getParseResult(Long batchId);
+    HandleResult resolveConflict(ConflictRequest request);
+    int commit(Long batchId, String operator);
+}
+```
+
+## 10.4 ReligionCheckService
+
+```java
+public interface ReligionCheckService {
+    List<ReligionPerson> listToCheck(CountyQuery query);
+    int singleCheck(Long personId, String operator);
+    int batchCheck(List<Long> ids, String operator);
+}
+```
+
+## 10.5 ReligionAuditLogService
+
+```java
+public interface ReligionAuditLogService {
+    List<ReligionAuditLog> list(AuditQuery query);
+    ReligionAuditLog detail(Long id);
+
+    void logCreate(String resourceType, Long resourceId, Object newValue);
+    void logUpdate(String resourceType, Long resourceId, Object oldValue, Object newValue);
+    void logBatch(String resourceType, List<Long> ids, Object diff);
+}
+```
+
+---
+
+# 第 11 章:Excel 导入与校验规则
+
+## 11.1 Excel 模板字段(示例)
+
+- 姓名  
+- 教别  
+- 性别  
+- 身份证号  
+- 出生日期  
+- 所在县区  
+- 详细地址  
+- 政治面貌  
+- 国民教育学历  
+- 宗教学历  
+- 入教/出家时间  
+- 电话  
+- 户籍所在地  
+- 籍贯  
+- 所属宫观  
+- 宫观地址  
+
+## 11.2 校验规则
+
+1. 必填项:姓名、教别、身份证号、政治面貌等不可为空;  
+2. 身份证:长度为 18 位,可做简单正则校验;  
+3. 日期字段:须为 `yyyy-MM-dd` 格式;  
+4. 枚举字段:教别、政治面貌、学历等必须在字典中;  
+5. 若字段不满足规则,则该行标记为“格式错误”,写入 `error_msg`。
+
+---
+
+# 第 12 章:冲突处理规则
+
+冲突定义:**姓名 + 身份证号** 与数据库中既有数据完全相同,视为同一人,存在“数据冲突”。
+
+支持三种处理模式:
+
+1. **覆盖(override)**  
+   - 使用 Excel 中的数据覆盖数据库中的对应记录;  
+2. **保留(keep)**  
+   - 不导入该条 Excel 记录,保留库中记录;  
+3. **手工处理(manual)**  
+   - 打开手工处理界面,显示“旧值 vs 新值”,由操作员逐字段决定最终值。
+
+所有冲突处理操作需要写审计日志,记录操作人、时间和处理方式。
+
+---
+
+# 第 13 章:审计日志规范
+
+## 13.1 日志记录场景
+
+- 新增场所、修改场所;  
+- 批量修改人员政治面貌;  
+- 导入提交;  
+- 校核操作(批量/单条)。
+
+## 13.2 字段差异 JSON 示例
+
+```json
+{
+  "charge_name": ["张三", "李四"],
+  "charge_phone": ["13800000000", "13900000000"]
+}
+```
+
+## 13.3 日志查看
+
+- 列表展示操作时间、操作人、操作类型、资源类型、资源ID;  
+- 详情展示 `op_detail` JSON 的格式化内容。
+
+---
+
+# 第 14 章:接口返回与异常处理规范
+
+## 14.1 返回结构
+
+统一使用 RuoYi 风格的 AjaxResult:
+
+成功:
+
+```json
+{
+  "code": 200,
+  "msg": "success",
+  "data": { ... }
+}
+```
+
+失败:
+
+```json
+{
+  "code": 500,
+  "msg": "错误信息"
+}
+```
+
+## 14.2 异常处理
+
+- 业务异常(如导入批次不存在)抛 `ServiceException`;  
+- 参数校验异常由全局异常处理器统一转换为 500 错误。
+
+---
+
+# 第 15 章:数据字段字典(Field Dictionary)
+
+> 本章整理系统中用到的枚举/字典值,便于前后端统一。
+
+## 15.1 宗教类别(religion_type)
+
+- 佛教  
+- 道教  
+- 伊斯兰教  
+- 基督教  
+- 天主教  
+
+## 15.2 政治面貌(political_status)
+
+- 群众  
+- 中共党员  
+- 民革党员  
+- 民盟盟员  
+- 其它民主党派  
+
+## 15.3 国民教育学历(edu_national)
+
+- 小学  
+- 初中  
+- 高中  
+- 中专  
+- 大专  
+- 本科  
+- 研究生  
+
+## 15.4 宗教学历(edu_religious)
+
+- 无  
+- 经学院  
+- 宗教院校本科  
+- 宗教院校研究生  
+
+## 15.5 人员状态(person.status)
+
+- TO_CHECK:待区县校核  
+- CHECKED:已区县校核  
+- REPORTED:已上报(本 DEMO 不实现上报流程)
+
+## 15.6 导入批次状态(batch.status)
+
+- PARSED:已解析,未最终导入;  
+- PARTIAL:部分冲突已手工处理;  
+- FINISHED:导入完成。
+
+---
+
+# 第 16 章:页面交互逻辑(Thymeleaf)
+
+## 16.1 场所表单 place_form.html
+
+- 上方为基本信息(名称、宗教类别、场所类型、区县、地址);  
+- 中间为负责人信息(姓名、电话);  
+- 下方为资质信息与坐标信息;  
+- “保存”按钮提交至 `/religion/place/create` 或 `/update`。
+
+## 16.2 人员导入页面 person_import.html
+
+元素说明:
+
+1. 场所选择下拉框(必选)  
+2. 文件上传控件  
+3. 解析结果统计区域:总数/错误数/冲突数  
+4. 错误数据表格(可分页)  
+5. 冲突数据表格(带“查看详情/手工处理”按钮)  
+6. 冲突模式选择(覆盖/保留/手工)  
+7. “提交导入”按钮
+
+## 16.3 冲突处理弹框 person_conflict.html
+
+布局:
+
+- 左侧为“系统中已有数据”(旧值);  
+- 右侧为“Excel 导入数据”(新值);  
+- 中间可加小图标/箭头表示字段比对;  
+- 下方“采用旧值/新值” 或由用户编辑最终值。
+
+## 16.4 校核工作台 check_workbench.html
+
+- 顶部筛选区域:
+  - 教别下拉;
+  - 姓名关键字;
+  - 身份证号;  
+- 表格区域展示待校核人员:姓名、身份证、教别、场所、状态等;  
+- 按钮:
+  - 批量通过;
+  - 单条通过(详情页中)。
+
+## 16.5 审计日志列表 audit_list.html
+
+- 筛选项:
+  - 操作人;
+  - 操作类型;
+  - 资源类型;
+  - 时间范围;  
+- 表格列:
+  - 操作时间;
+  - 操作人;
+  - 类型;
+  - 资源类型;
+  - 资源ID;
+  - “查看详情”链接;  
+- 详情页展示 `op_detail` JSON 的格式化结构。
+
+---
+
+# 第 17 章:开发规范(命名 / 结构 / 代码)
+
+## 17.1 包结构规范
+
+```text
+com.ruoyi.project.religion.place
+com.ruoyi.project.religion.person
+com.ruoyi.project.religion.import
+com.ruoyi.project.religion.audit
+```
+
+## 17.2 Controller 命名规范
+
+- ReligionPlaceController  
+- ReligionPersonController  
+- ReligionImportController  
+- ReligionCheckController  
+- ReligionAuditController  
+
+## 17.3 Service/Mapper 命名规范
+
+- Interface:`XxxService`  
+- Impl:`XxxServiceImpl`  
+- Mapper:`XxxMapper` + 对应 XML 文件。
+
+## 17.4 HTML 页面命名规范
+
+- place_list.html  
+- place_form.html  
+- person_list.html  
+- person_import.html  
+- person_conflict.html  
+- check_workbench.html  
+- audit_list.html  
+
+## 17.5 代码风格
+
+- 遵循 Java 命名规范(驼峰);  
+- Controller 中仅做参数处理和结果封装,业务逻辑放在 Service 中;  
+- Mapper 层只做 SQL 映射,不写业务逻辑。
+
+---
+
+# 第 18 章:接口返回结构规范(再次强调)
+
+所有 REST 接口返回结构应统一为:
+
+```json
+{
+  "code": 200,
+  "msg": "success",
+  "data": { ... }
+}
+```
+
+异常时:
+
+```json
+{
+  "code": 500,
+  "msg": "错误描述"
+}
+```
+
+其中 `code` 可扩展为其它业务码,但 DEMO 中保持 200 / 500 即可。
+
+---
+
+# 第 19 章:异常与日志策略
+
+- 对于参数错误及时抛出 ServiceException;  
+- 对于系统异常由统一异常处理器捕获并打印日志;  
+- 关键业务操作必须调用 `ReligionAuditLogService` 写业务日志。
+
+---
+
+# 第 20 章:与 Codex 协同开发的建议(可选使用)
+
+- 在 VSCode 中打开项目后,可将本 md 文档作为上下文输入给 Codex;  
+- 在编写某模块时,将相关章节(数据结构 + 接口 + 页面说明)复制给 Codex,要求其生成对应 Controller/Service/Mapper/HTML;  
+- 开发顺序建议:
+  1. 建表(MySQL)  
+  2. Entity + Mapper  
+  3. Service 实现  
+  4. Controller  
+  5. 页面 HTML + JS  
+
+---
+
+# 第 21 章:ER 关系说明
+
+```text
+religion_place (1) —— (N) religion_person
+    一个场所可以对应多个教职人员;
+    教职人员通过 place_id 关联所属场所。
+
+religion_import_batch (1) —— (N) religion_import_item
+    一个导入批次对应多条导入记录(每条对应 Excel 中一行)。
+
+religion_person (N) —— (1) religion_place
+    人员通过 place_id 绑定到唯一的场所。
+```
+
+ER 关系可以绘制成图(可交由 Codex 或 UML 工具):
+
+- 实体:Place、Person、ImportBatch、ImportItem、AuditLog;
+- 关系:
+  - Place 1 — N Person  
+  - ImportBatch 1 — N ImportItem  
+  - AuditLog 与 Place/Person/Batch 为 N — 1(通过 resource_id)。
+
+---
+
+**文档完毕:1–21 章完整开发规范,可作为 DEMO 项目开发与后期维护的基础文档。**

+ 150 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/place/controller/ReligionPlaceController.java

@@ -0,0 +1,150 @@
+package com.ruoyi.project.religion.place.controller;
+
+import java.util.List;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.project.religion.place.domain.ReligionPlace;
+import com.ruoyi.project.religion.place.service.IReligionPlaceService;
+
+/**
+ * 宗教活动场所Controller
+ * 
+ * @author ruoyi
+ */
+@Controller
+@RequestMapping("/religion/place")
+public class ReligionPlaceController extends BaseController
+{
+    private String prefix = "religion/place";
+
+    @Autowired
+    private IReligionPlaceService religionPlaceService;
+
+    /**
+     * 跳转到场所列表页面
+     */
+    @RequiresPermissions("religion:place:view")
+    @GetMapping()
+    public String place()
+    {
+        return prefix + "/place";
+    }
+
+    /**
+     * 查询宗教活动场所列表
+     */
+    @RequiresPermissions("religion:place:list")
+    @PostMapping("/list")
+    @ResponseBody
+    public TableDataInfo list(ReligionPlace place)
+    {
+        startPage();
+        List<ReligionPlace> list = religionPlaceService.selectPlaceList(place);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出宗教活动场所列表
+     */
+    @RequiresPermissions("religion:place:export")
+    @Log(title = "宗教活动场所", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @ResponseBody
+    public AjaxResult export(ReligionPlace place)
+    {
+        List<ReligionPlace> list = religionPlaceService.selectPlaceList(place);
+        ExcelUtil<ReligionPlace> util = new ExcelUtil<ReligionPlace>(ReligionPlace.class);
+        return util.exportExcel(list, "场所数据");
+    }
+
+    /**
+     * 新增宗教活动场所页面
+     */
+    @RequiresPermissions("religion:place:add")
+    @GetMapping("/add")
+    public String add()
+    {
+        return prefix + "/add";
+    }
+
+    /**
+     * 新增保存宗教活动场所
+     */
+    @RequiresPermissions("religion:place:add")
+    @Log(title = "宗教活动场所", businessType = BusinessType.INSERT)
+    @PostMapping("/add")
+    @ResponseBody
+    public AjaxResult addSave(ReligionPlace place)
+    {
+        place.setCreateBy(getLoginName());
+        return toAjax(religionPlaceService.insertPlace(place));
+    }
+
+    /**
+     * 修改宗教活动场所页面
+     */
+    @RequiresPermissions("religion:place:edit")
+    @GetMapping("/edit/{placeId}")
+    public String edit(@PathVariable("placeId") Long placeId, ModelMap mmap)
+    {
+        ReligionPlace place = religionPlaceService.selectPlaceById(placeId);
+        mmap.put("place", place);
+        return prefix + "/edit";
+    }
+
+    /**
+     * 修改保存宗教活动场所
+     */
+    @RequiresPermissions("religion:place:edit")
+    @Log(title = "宗教活动场所", businessType = BusinessType.UPDATE)
+    @PostMapping("/edit")
+    @ResponseBody
+    public AjaxResult editSave(ReligionPlace place)
+    {
+        place.setUpdateBy(getLoginName());
+        return toAjax(religionPlaceService.updatePlace(place));
+    }
+
+    /**
+     * 删除宗教活动场所
+     */
+    @RequiresPermissions("religion:place:remove")
+    @Log(title = "宗教活动场所", businessType = BusinessType.DELETE)
+    @PostMapping("/remove")
+    @ResponseBody
+    public AjaxResult remove(String ids)
+    {
+        String[] idArray = ids.split(",");
+        Long[] placeIds = new Long[idArray.length];
+        for (int i = 0; i < idArray.length; i++)
+        {
+            placeIds[i] = Long.parseLong(idArray[i]);
+        }
+        return toAjax(religionPlaceService.deletePlaceByIds(placeIds));
+    }
+
+    /**
+     * 根据场所ID查询场所详情(API接口)
+     */
+    @RequiresPermissions("religion:place:query")
+    @GetMapping("/{placeId}")
+    @ResponseBody
+    public AjaxResult getInfo(@PathVariable("placeId") Long placeId)
+    {
+        return AjaxResult.success(religionPlaceService.selectPlaceById(placeId));
+    }
+}
+

+ 333 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/place/domain/ReligionPlace.java

@@ -0,0 +1,333 @@
+package com.ruoyi.project.religion.place.domain;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.annotation.Excel.ColumnType;
+import com.ruoyi.common.core.domain.BaseEntity;
+
+/**
+ * 宗教活动场所对象 religion_place
+ * 
+ * @author ruoyi
+ */
+public class ReligionPlace extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 场所ID */
+    private Long placeId;
+
+    /** 场所名称 */
+    @Excel(name = "场所名称")
+    private String placeName;
+
+    /** 宗教类别 */
+    @Excel(name = "宗教类别")
+    private String religionType;
+
+    /** 场所类别(寺/观/清真寺/教堂等) */
+    @Excel(name = "场所类别")
+    private String placeCategory;
+
+    /** 所属区县 */
+    @Excel(name = "所属区县")
+    private String county;
+
+    /** 详细地址 */
+    @Excel(name = "详细地址")
+    private String address;
+
+    /** 负责人姓名 */
+    @Excel(name = "负责人姓名")
+    private String chargeName;
+
+    /** 负责人联系电话 */
+    @Excel(name = "负责人电话")
+    private String chargePhone;
+
+    /** 场所占地面积(平方米) */
+    @Excel(name = "占地面积", cellType = ColumnType.NUMERIC)
+    private BigDecimal placeArea;
+
+    /** 建筑面积(平方米) */
+    @Excel(name = "建筑面积", cellType = ColumnType.NUMERIC)
+    private BigDecimal buildingArea;
+
+    /** 批准设立机关 */
+    @Excel(name = "批准设立机关")
+    private String approveOrgan;
+
+    /** 批准设立时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "批准设立时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date approveTime;
+
+    /** 登记机关 */
+    @Excel(name = "登记机关")
+    private String regOrgan;
+
+    /** 登记证号 */
+    @Excel(name = "登记证号")
+    private String regNo;
+
+    /** 登记时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "登记时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date regTime;
+
+    /** 是否具有法人资格:0否1是 */
+    @Excel(name = "是否具有法人资格", readConverterExp = "0=否,1=是")
+    private Integer legalQualified;
+
+    /** 法人资格取得时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "法人资格取得时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date legalTime;
+
+    /** 统一社会信用代码 */
+    @Excel(name = "统一社会信用代码")
+    private String creditCode;
+
+    /** 经度 */
+    @Excel(name = "经度", cellType = ColumnType.NUMERIC)
+    private BigDecimal longitude;
+
+    /** 纬度 */
+    @Excel(name = "纬度", cellType = ColumnType.NUMERIC)
+    private BigDecimal latitude;
+
+    public void setPlaceId(Long placeId) 
+    {
+        this.placeId = placeId;
+    }
+
+    public Long getPlaceId() 
+    {
+        return placeId;
+    }
+
+    public void setPlaceName(String placeName) 
+    {
+        this.placeName = placeName;
+    }
+
+    public String getPlaceName() 
+    {
+        return placeName;
+    }
+
+    public void setReligionType(String religionType) 
+    {
+        this.religionType = religionType;
+    }
+
+    public String getReligionType() 
+    {
+        return religionType;
+    }
+
+    public void setPlaceCategory(String placeCategory) 
+    {
+        this.placeCategory = placeCategory;
+    }
+
+    public String getPlaceCategory() 
+    {
+        return placeCategory;
+    }
+
+    public void setCounty(String county) 
+    {
+        this.county = county;
+    }
+
+    public String getCounty() 
+    {
+        return county;
+    }
+
+    public void setAddress(String address) 
+    {
+        this.address = address;
+    }
+
+    public String getAddress() 
+    {
+        return address;
+    }
+
+    public void setChargeName(String chargeName) 
+    {
+        this.chargeName = chargeName;
+    }
+
+    public String getChargeName() 
+    {
+        return chargeName;
+    }
+
+    public void setChargePhone(String chargePhone) 
+    {
+        this.chargePhone = chargePhone;
+    }
+
+    public String getChargePhone() 
+    {
+        return chargePhone;
+    }
+
+    public void setPlaceArea(BigDecimal placeArea) 
+    {
+        this.placeArea = placeArea;
+    }
+
+    public BigDecimal getPlaceArea() 
+    {
+        return placeArea;
+    }
+
+    public void setBuildingArea(BigDecimal buildingArea) 
+    {
+        this.buildingArea = buildingArea;
+    }
+
+    public BigDecimal getBuildingArea() 
+    {
+        return buildingArea;
+    }
+
+    public void setApproveOrgan(String approveOrgan) 
+    {
+        this.approveOrgan = approveOrgan;
+    }
+
+    public String getApproveOrgan() 
+    {
+        return approveOrgan;
+    }
+
+    public void setApproveTime(Date approveTime) 
+    {
+        this.approveTime = approveTime;
+    }
+
+    public Date getApproveTime() 
+    {
+        return approveTime;
+    }
+
+    public void setRegOrgan(String regOrgan) 
+    {
+        this.regOrgan = regOrgan;
+    }
+
+    public String getRegOrgan() 
+    {
+        return regOrgan;
+    }
+
+    public void setRegNo(String regNo) 
+    {
+        this.regNo = regNo;
+    }
+
+    public String getRegNo() 
+    {
+        return regNo;
+    }
+
+    public void setRegTime(Date regTime) 
+    {
+        this.regTime = regTime;
+    }
+
+    public Date getRegTime() 
+    {
+        return regTime;
+    }
+
+    public void setLegalQualified(Integer legalQualified) 
+    {
+        this.legalQualified = legalQualified;
+    }
+
+    public Integer getLegalQualified() 
+    {
+        return legalQualified;
+    }
+
+    public void setLegalTime(Date legalTime) 
+    {
+        this.legalTime = legalTime;
+    }
+
+    public Date getLegalTime() 
+    {
+        return legalTime;
+    }
+
+    public void setCreditCode(String creditCode) 
+    {
+        this.creditCode = creditCode;
+    }
+
+    public String getCreditCode() 
+    {
+        return creditCode;
+    }
+
+    public void setLongitude(BigDecimal longitude) 
+    {
+        this.longitude = longitude;
+    }
+
+    public BigDecimal getLongitude() 
+    {
+        return longitude;
+    }
+
+    public void setLatitude(BigDecimal latitude) 
+    {
+        this.latitude = latitude;
+    }
+
+    public BigDecimal getLatitude() 
+    {
+        return latitude;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("placeId", getPlaceId())
+            .append("placeName", getPlaceName())
+            .append("religionType", getReligionType())
+            .append("placeCategory", getPlaceCategory())
+            .append("county", getCounty())
+            .append("address", getAddress())
+            .append("chargeName", getChargeName())
+            .append("chargePhone", getChargePhone())
+            .append("placeArea", getPlaceArea())
+            .append("buildingArea", getBuildingArea())
+            .append("approveOrgan", getApproveOrgan())
+            .append("approveTime", getApproveTime())
+            .append("regOrgan", getRegOrgan())
+            .append("regNo", getRegNo())
+            .append("regTime", getRegTime())
+            .append("legalQualified", getLegalQualified())
+            .append("legalTime", getLegalTime())
+            .append("creditCode", getCreditCode())
+            .append("longitude", getLongitude())
+            .append("latitude", getLatitude())
+            .append("createBy", getCreateBy())
+            .append("createTime", getCreateTime())
+            .append("updateBy", getUpdateBy())
+            .append("updateTime", getUpdateTime())
+            .toString();
+    }
+}
+

+ 61 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/place/mapper/ReligionPlaceMapper.java

@@ -0,0 +1,61 @@
+package com.ruoyi.project.religion.place.mapper;
+
+import java.util.List;
+import com.ruoyi.project.religion.place.domain.ReligionPlace;
+
+/**
+ * 宗教活动场所Mapper接口
+ * 
+ * @author ruoyi
+ */
+public interface ReligionPlaceMapper 
+{
+    /**
+     * 查询宗教活动场所列表
+     * 
+     * @param place 宗教活动场所
+     * @return 宗教活动场所集合
+     */
+    public List<ReligionPlace> selectPlaceList(ReligionPlace place);
+
+    /**
+     * 根据场所ID查询宗教活动场所
+     * 
+     * @param placeId 场所ID
+     * @return 宗教活动场所
+     */
+    public ReligionPlace selectPlaceById(Long placeId);
+
+    /**
+     * 新增宗教活动场所
+     * 
+     * @param place 宗教活动场所
+     * @return 结果
+     */
+    public int insertPlace(ReligionPlace place);
+
+    /**
+     * 修改宗教活动场所
+     * 
+     * @param place 宗教活动场所
+     * @return 结果
+     */
+    public int updatePlace(ReligionPlace place);
+
+    /**
+     * 删除宗教活动场所
+     * 
+     * @param placeId 场所ID
+     * @return 结果
+     */
+    public int deletePlaceById(Long placeId);
+
+    /**
+     * 批量删除宗教活动场所
+     * 
+     * @param placeIds 需要删除的场所ID
+     * @return 结果
+     */
+    public int deletePlaceByIds(Long[] placeIds);
+}
+

+ 61 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/place/service/IReligionPlaceService.java

@@ -0,0 +1,61 @@
+package com.ruoyi.project.religion.place.service;
+
+import java.util.List;
+import com.ruoyi.project.religion.place.domain.ReligionPlace;
+
+/**
+ * 宗教活动场所Service接口
+ * 
+ * @author ruoyi
+ */
+public interface IReligionPlaceService 
+{
+    /**
+     * 查询宗教活动场所列表
+     * 
+     * @param place 宗教活动场所
+     * @return 宗教活动场所集合
+     */
+    public List<ReligionPlace> selectPlaceList(ReligionPlace place);
+
+    /**
+     * 根据场所ID查询宗教活动场所
+     * 
+     * @param placeId 场所ID
+     * @return 宗教活动场所
+     */
+    public ReligionPlace selectPlaceById(Long placeId);
+
+    /**
+     * 新增宗教活动场所
+     * 
+     * @param place 宗教活动场所
+     * @return 结果
+     */
+    public int insertPlace(ReligionPlace place);
+
+    /**
+     * 修改宗教活动场所
+     * 
+     * @param place 宗教活动场所
+     * @return 结果
+     */
+    public int updatePlace(ReligionPlace place);
+
+    /**
+     * 批量删除宗教活动场所
+     * 
+     * @param placeIds 需要删除的场所ID
+     * @return 结果
+     */
+    public int deletePlaceByIds(Long[] placeIds);
+
+    /**
+     * 删除宗教活动场所信息
+     * 
+     * @param placeId 场所ID
+     * @return 结果
+     */
+    public int deletePlaceById(Long placeId);
+}
+

+ 109 - 0
ruoyi-admin/src/main/java/com/ruoyi/project/religion/place/service/impl/ReligionPlaceServiceImpl.java

@@ -0,0 +1,109 @@
+package com.ruoyi.project.religion.place.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.project.religion.place.mapper.ReligionPlaceMapper;
+import com.ruoyi.project.religion.place.domain.ReligionPlace;
+import com.ruoyi.project.religion.place.service.IReligionPlaceService;
+
+/**
+ * 宗教活动场所Service业务层处理
+ * 
+ * @author ruoyi
+ */
+@Service
+public class ReligionPlaceServiceImpl implements IReligionPlaceService 
+{
+    @Autowired
+    private ReligionPlaceMapper religionPlaceMapper;
+
+    /**
+     * 查询宗教活动场所列表
+     * 
+     * @param place 宗教活动场所
+     * @return 宗教活动场所
+     */
+    @Override
+    public List<ReligionPlace> selectPlaceList(ReligionPlace place)
+    {
+        return religionPlaceMapper.selectPlaceList(place);
+    }
+
+    /**
+     * 根据场所ID查询宗教活动场所
+     * 
+     * @param placeId 场所ID
+     * @return 宗教活动场所
+     */
+    @Override
+    public ReligionPlace selectPlaceById(Long placeId)
+    {
+        return religionPlaceMapper.selectPlaceById(placeId);
+    }
+
+    /**
+     * 新增宗教活动场所
+     * 
+     * @param place 宗教活动场所
+     * @return 结果
+     */
+    @Override
+    public int insertPlace(ReligionPlace place)
+    {
+        place.setCreateTime(DateUtils.getNowDate());
+        int result = religionPlaceMapper.insertPlace(place);
+        
+        // TODO: 调用审计日志服务,记录新增操作
+        // auditLogService.logCreate("place", place.getPlaceId(), place);
+        
+        return result;
+    }
+
+    /**
+     * 修改宗教活动场所
+     * 
+     * @param place 宗教活动场所
+     * @return 结果
+     */
+    @Override
+    public int updatePlace(ReligionPlace place)
+    {
+        // TODO: 查询旧数据,计算字段差异
+        // ReligionPlace oldPlace = religionPlaceMapper.selectPlaceById(place.getPlaceId());
+        
+        place.setUpdateTime(DateUtils.getNowDate());
+        int result = religionPlaceMapper.updatePlace(place);
+        
+        // TODO: 调用审计日志服务,记录修改操作及字段差异
+        // auditLogService.logUpdate("place", place.getPlaceId(), oldPlace, place);
+        
+        return result;
+    }
+
+    /**
+     * 批量删除宗教活动场所
+     * 
+     * @param placeIds 需要删除的场所ID
+     * @return 结果
+     */
+    @Override
+    public int deletePlaceByIds(Long[] placeIds)
+    {
+        return religionPlaceMapper.deletePlaceByIds(placeIds);
+    }
+
+    /**
+     * 删除宗教活动场所信息
+     * 
+     * @param placeId 场所ID
+     * @return 结果
+     */
+    @Override
+    public int deletePlaceById(Long placeId)
+    {
+        return religionPlaceMapper.deletePlaceById(placeId);
+    }
+}
+

+ 2 - 2
ruoyi-admin/src/main/resources/application-druid.yml

@@ -6,9 +6,9 @@ spring:
         druid:
             # 主库数据源
             master:
-                url: jdbc:mysql://localhost:3306/ry?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                url: jdbc:mysql://121.4.16.100:3630/ry-mzw?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                 username: root
-                password: 123456
+                password: zmj666
             # 从库数据源
             slave:
                 # 从数据源开关/默认关闭

+ 1 - 1
ruoyi-admin/src/main/resources/logback.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <configuration>
     <!-- 日志存放路径 -->
-	<property name="log.path" value="/home/ruoyi/logs" />
+    <!--<property name="log.path" value="/home/ruoyi/logs" />-->
     <!-- 日志输出格式 -->
 	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
 

+ 162 - 0
ruoyi-admin/src/main/resources/mapper/religion/place/ReligionPlaceMapper.xml

@@ -0,0 +1,162 @@
+<?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">
+<mapper namespace="com.ruoyi.project.religion.place.mapper.ReligionPlaceMapper">
+    
+    <resultMap type="com.ruoyi.project.religion.place.domain.ReligionPlace" id="ReligionPlaceResult">
+        <id     property="placeId"          column="place_id"          />
+        <result property="placeName"        column="place_name"        />
+        <result property="religionType"     column="religion_type"     />
+        <result property="placeCategory"    column="place_category"    />
+        <result property="county"           column="county"            />
+        <result property="address"          column="address"           />
+        <result property="chargeName"       column="charge_name"       />
+        <result property="chargePhone"      column="charge_phone"      />
+        <result property="placeArea"        column="place_area"        />
+        <result property="buildingArea"     column="building_area"     />
+        <result property="approveOrgan"     column="approve_organ"     />
+        <result property="approveTime"      column="approve_time"      />
+        <result property="regOrgan"         column="reg_organ"         />
+        <result property="regNo"            column="reg_no"            />
+        <result property="regTime"          column="reg_time"          />
+        <result property="legalQualified"   column="legal_qualified"   />
+        <result property="legalTime"        column="legal_time"        />
+        <result property="creditCode"       column="credit_code"       />
+        <result property="longitude"        column="longitude"         />
+        <result property="latitude"         column="latitude"          />
+        <result property="createBy"         column="create_by"         />
+        <result property="createTime"       column="create_time"       />
+        <result property="updateBy"         column="update_by"         />
+        <result property="updateTime"       column="update_time"       />
+    </resultMap>
+
+    <sql id="selectPlaceVo">
+        select place_id, place_name, religion_type, place_category, county, address, 
+               charge_name, charge_phone, place_area, building_area, approve_organ, 
+               approve_time, reg_organ, reg_no, reg_time, legal_qualified, legal_time, 
+               credit_code, longitude, latitude, create_by, create_time, update_by, update_time
+        from religion_place
+    </sql>
+
+    <select id="selectPlaceList" parameterType="com.ruoyi.project.religion.place.domain.ReligionPlace" resultMap="ReligionPlaceResult">
+        <include refid="selectPlaceVo"/>
+        <where>  
+            <if test="placeName != null and placeName != ''">
+                AND place_name like concat('%', #{placeName}, '%')
+            </if>
+            <if test="religionType != null and religionType != ''">
+                AND religion_type = #{religionType}
+            </if>
+            <if test="placeCategory != null and placeCategory != ''">
+                AND place_category = #{placeCategory}
+            </if>
+            <if test="county != null and county != ''">
+                AND county = #{county}
+            </if>
+            <if test="chargeName != null and chargeName != ''">
+                AND charge_name like concat('%', #{chargeName}, '%')
+            </if>
+            <if test="chargePhone != null and chargePhone != ''">
+                AND charge_phone like concat('%', #{chargePhone}, '%')
+            </if>
+        </where>
+        order by create_time desc
+    </select>
+    
+    <select id="selectPlaceById" parameterType="Long" resultMap="ReligionPlaceResult">
+        <include refid="selectPlaceVo"/>
+        where place_id = #{placeId}
+    </select>
+        
+    <insert id="insertPlace" parameterType="com.ruoyi.project.religion.place.domain.ReligionPlace" useGeneratedKeys="true" keyProperty="placeId">
+        insert into religion_place
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="placeName != null and placeName != ''">place_name,</if>
+            <if test="religionType != null and religionType != ''">religion_type,</if>
+            <if test="placeCategory != null and placeCategory != ''">place_category,</if>
+            <if test="county != null and county != ''">county,</if>
+            <if test="address != null and address != ''">address,</if>
+            <if test="chargeName != null and chargeName != ''">charge_name,</if>
+            <if test="chargePhone != null and chargePhone != ''">charge_phone,</if>
+            <if test="placeArea != null">place_area,</if>
+            <if test="buildingArea != null">building_area,</if>
+            <if test="approveOrgan != null">approve_organ,</if>
+            <if test="approveTime != null">approve_time,</if>
+            <if test="regOrgan != null">reg_organ,</if>
+            <if test="regNo != null">reg_no,</if>
+            <if test="regTime != null">reg_time,</if>
+            <if test="legalQualified != null">legal_qualified,</if>
+            <if test="legalTime != null">legal_time,</if>
+            <if test="creditCode != null">credit_code,</if>
+            <if test="longitude != null">longitude,</if>
+            <if test="latitude != null">latitude,</if>
+            <if test="createBy != null">create_by,</if>
+            create_time
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="placeName != null and placeName != ''">#{placeName},</if>
+            <if test="religionType != null and religionType != ''">#{religionType},</if>
+            <if test="placeCategory != null and placeCategory != ''">#{placeCategory},</if>
+            <if test="county != null and county != ''">#{county},</if>
+            <if test="address != null and address != ''">#{address},</if>
+            <if test="chargeName != null and chargeName != ''">#{chargeName},</if>
+            <if test="chargePhone != null and chargePhone != ''">#{chargePhone},</if>
+            <if test="placeArea != null">#{placeArea},</if>
+            <if test="buildingArea != null">#{buildingArea},</if>
+            <if test="approveOrgan != null">#{approveOrgan},</if>
+            <if test="approveTime != null">#{approveTime},</if>
+            <if test="regOrgan != null">#{regOrgan},</if>
+            <if test="regNo != null">#{regNo},</if>
+            <if test="regTime != null">#{regTime},</if>
+            <if test="legalQualified != null">#{legalQualified},</if>
+            <if test="legalTime != null">#{legalTime},</if>
+            <if test="creditCode != null">#{creditCode},</if>
+            <if test="longitude != null">#{longitude},</if>
+            <if test="latitude != null">#{latitude},</if>
+            <if test="createBy != null">#{createBy},</if>
+            sysdate()
+         </trim>
+    </insert>
+
+    <update id="updatePlace" parameterType="com.ruoyi.project.religion.place.domain.ReligionPlace">
+        update religion_place
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="placeName != null and placeName != ''">place_name = #{placeName},</if>
+            <if test="religionType != null and religionType != ''">religion_type = #{religionType},</if>
+            <if test="placeCategory != null and placeCategory != ''">place_category = #{placeCategory},</if>
+            <if test="county != null and county != ''">county = #{county},</if>
+            <if test="address != null and address != ''">address = #{address},</if>
+            <if test="chargeName != null and chargeName != ''">charge_name = #{chargeName},</if>
+            <if test="chargePhone != null and chargePhone != ''">charge_phone = #{chargePhone},</if>
+            <if test="placeArea != null">place_area = #{placeArea},</if>
+            <if test="buildingArea != null">building_area = #{buildingArea},</if>
+            <if test="approveOrgan != null">approve_organ = #{approveOrgan},</if>
+            <if test="approveTime != null">approve_time = #{approveTime},</if>
+            <if test="regOrgan != null">reg_organ = #{regOrgan},</if>
+            <if test="regNo != null">reg_no = #{regNo},</if>
+            <if test="regTime != null">reg_time = #{regTime},</if>
+            <if test="legalQualified != null">legal_qualified = #{legalQualified},</if>
+            <if test="legalTime != null">legal_time = #{legalTime},</if>
+            <if test="creditCode != null">credit_code = #{creditCode},</if>
+            <if test="longitude != null">longitude = #{longitude},</if>
+            <if test="latitude != null">latitude = #{latitude},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            update_time = sysdate()
+        </trim>
+        where place_id = #{placeId}
+    </update>
+
+    <delete id="deletePlaceById" parameterType="Long">
+        delete from religion_place where place_id = #{placeId}
+    </delete>
+
+    <delete id="deletePlaceByIds" parameterType="Long">
+        delete from religion_place where place_id in 
+        <foreach item="placeId" collection="array" open="(" separator="," close=")">
+            #{placeId}
+        </foreach>
+    </delete>
+
+</mapper>
+

+ 331 - 0
ruoyi-admin/src/main/resources/templates/religion/place/add.html

@@ -0,0 +1,331 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <th:block th:include="include :: header('新增宗教活动场所')" />
+    <th:block th:include="include :: datetimepicker-css" />
+</head>
+<body>
+    <div class="main-content">
+        <form id="form-place-add" class="form-horizontal">
+            <h4 class="form-header h4">基本信息</h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label is-required">场所名称:</label>
+                        <div class="col-sm-8">
+                            <input name="placeName" placeholder="请输入场所名称" class="form-control" type="text" maxlength="256" required>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label is-required">宗教类别:</label>
+                        <div class="col-sm-8">
+                            <select name="religionType" class="form-control" required th:with="type=${@dict.getType('religion_type')}">
+                                <option value="">请选择宗教类别</option>
+                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                            </select>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">场所类别:</label>
+                        <div class="col-sm-8">
+                            <!--<input name="placeCategory" placeholder="如:寺/观/清真寺/教堂" class="form-control" type="text" maxlength="64">-->
+                            <select name="placeCategory" class="form-control" required th:with="type=${@dict.getType('category_type')}">
+                                <option value="">请选择场所类别</option>
+                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                            </select>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">所属区县:</label>
+                        <div class="col-sm-8">
+                            <input name="county" placeholder="请输入所属区县" class="form-control" type="text" maxlength="128">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-12">
+                    <div class="form-group">
+                        <label class="col-xs-2 control-label">详细地址:</label>
+                        <div class="col-xs-10">
+                            <input name="address" placeholder="请输入详细地址" class="form-control" type="text" maxlength="512">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            
+            <h4 class="form-header h4">负责人信息</h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label is-required">负责人姓名:</label>
+                        <div class="col-sm-8">
+                            <input name="chargeName" placeholder="请输入负责人姓名" class="form-control" type="text" maxlength="128" required>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label is-required">负责人电话:</label>
+                        <div class="col-sm-8">
+                            <div class="input-group">
+                                <input name="chargePhone" placeholder="请输入负责人电话" class="form-control" type="text" maxlength="32" required>
+                                <span class="input-group-addon"><i class="fa fa-mobile"></i></span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            
+            <h4 class="form-header h4">场所面积</h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">占地面积:</label>
+                        <div class="col-sm-8">
+                            <div class="input-group">
+                                <input name="placeArea" placeholder="请输入占地面积" class="form-control" type="number" step="0.01">
+                                <span class="input-group-addon">平方米</span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">建筑面积:</label>
+                        <div class="col-sm-8">
+                            <div class="input-group">
+                                <input name="buildingArea" placeholder="请输入建筑面积" class="form-control" type="number" step="0.01">
+                                <span class="input-group-addon">平方米</span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            
+            <h4 class="form-header h4">批准与登记信息</h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">批准设立机关:</label>
+                        <div class="col-sm-8">
+                            <input name="approveOrgan" placeholder="请输入批准设立机关" class="form-control" type="text" maxlength="128">
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">批准设立时间:</label>
+                        <div class="col-sm-8">
+                            <div class="input-group date">
+                                <input name="approveTime" placeholder="yyyy-MM-dd" class="form-control" type="text">
+                                <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">登记机关:</label>
+                        <div class="col-sm-8">
+                            <input name="regOrgan" placeholder="请输入登记机关" class="form-control" type="text" maxlength="128">
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">登记证号:</label>
+                        <div class="col-sm-8">
+                            <input name="regNo" placeholder="请输入登记证号" class="form-control" type="text" maxlength="64">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">登记时间:</label>
+                        <div class="col-sm-8">
+                            <div class="input-group date">
+                                <input name="regTime" placeholder="yyyy-MM-dd" class="form-control" type="text">
+                                <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">统一社会信用代码:</label>
+                        <div class="col-sm-8">
+                            <input name="creditCode" placeholder="请输入统一社会信用代码" class="form-control" type="text" maxlength="32">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            
+            <h4 class="form-header h4">法人资格信息</h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">是否具有法人资格:</label>
+                        <div class="col-sm-8">
+                            <select name="legalQualified" class="form-control">
+                                <option value="">请选择</option>
+                                <option value="0">否</option>
+                                <option value="1">是</option>
+                            </select>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">法人资格取得时间:</label>
+                        <div class="col-sm-8">
+                            <div class="input-group date">
+                                <input name="legalTime" placeholder="yyyy-MM-dd" class="form-control" type="text">
+                                <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            
+            <h4 class="form-header h4">地理位置信息</h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">经度:</label>
+                        <div class="col-sm-8">
+                            <input name="longitude" placeholder="请输入经度" class="form-control" type="number" step="0.000001">
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">纬度:</label>
+                        <div class="col-sm-8">
+                            <input name="latitude" placeholder="请输入纬度" class="form-control" type="number" step="0.000001">
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </form>
+    </div>
+    
+    <div class="row">
+        <div class="col-sm-offset-5 col-sm-10">
+            <button type="button" class="btn btn-sm btn-primary" onclick="submitHandler()"><i class="fa fa-check"></i>保 存</button>&nbsp;
+            <button type="button" class="btn btn-sm btn-danger" onclick="closeItem()"><i class="fa fa-reply-all"></i>关 闭</button>
+        </div>
+    </div>
+    
+    <th:block th:include="include :: footer" />
+    <th:block th:include="include :: datetimepicker-js" />
+    <script th:inline="javascript">
+        var prefix = ctx + "religion/place";
+        
+        $("#form-place-add").validate({
+            onkeyup: false,
+            rules: {
+                placeName: {
+                    required: true,
+                    minlength: 2,
+                    maxlength: 256
+                },
+                religionType: {
+                    required: true
+                },
+                chargeName: {
+                    required: true,
+                    minlength: 2,
+                    maxlength: 128
+                },
+                chargePhone: {
+                    required: true,
+                    isPhone: true
+                },
+                placeArea: {
+                    number: true,
+                    min: 0
+                },
+                buildingArea: {
+                    number: true,
+                    min: 0
+                },
+                longitude: {
+                    number: true,
+                    min: -180,
+                    max: 180
+                },
+                latitude: {
+                    number: true,
+                    min: -90,
+                    max: 90
+                }
+            },
+            messages: {
+                placeName: {
+                    required: "请输入场所名称",
+                    minlength: "场所名称不能少于2个字符",
+                    maxlength: "场所名称不能超过256个字符"
+                },
+                religionType: {
+                    required: "请选择宗教类别"
+                },
+                chargeName: {
+                    required: "请输入负责人姓名",
+                    minlength: "负责人姓名不能少于2个字符",
+                    maxlength: "负责人姓名不能超过128个字符"
+                },
+                chargePhone: {
+                    required: "请输入负责人电话",
+                    isPhone: "请输入正确的手机号码"
+                }
+            },
+            focusCleanup: true
+        });
+        
+        function submitHandler() {
+            if ($.validate.form()) {
+                var data = $("#form-place-add").serializeArray();
+                $.operate.saveTab(prefix + "/add", data);
+            }
+        }
+        
+        $(function() {
+            // 初始化日期选择器
+            $("input[name='approveTime']").datetimepicker({
+                format: "yyyy-mm-dd",
+                minView: "month",
+                autoclose: true,
+                todayBtn: true
+            });
+            
+            $("input[name='regTime']").datetimepicker({
+                format: "yyyy-mm-dd",
+                minView: "month",
+                autoclose: true,
+                todayBtn: true
+            });
+            
+            $("input[name='legalTime']").datetimepicker({
+                format: "yyyy-mm-dd",
+                minView: "month",
+                autoclose: true,
+                todayBtn: true
+            });
+        });
+    </script>
+</body>
+</html>
+

+ 333 - 0
ruoyi-admin/src/main/resources/templates/religion/place/edit.html

@@ -0,0 +1,333 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <th:block th:include="include :: header('修改宗教活动场所')" />
+    <th:block th:include="include :: datetimepicker-css" />
+</head>
+<body>
+    <div class="main-content">
+        <form id="form-place-edit" class="form-horizontal" th:object="${place}">
+            <input name="placeId" type="hidden" th:field="*{placeId}" />
+            
+            <h4 class="form-header h4">基本信息</h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label is-required">场所名称:</label>
+                        <div class="col-sm-8">
+                            <input name="placeName" placeholder="请输入场所名称" class="form-control" type="text" maxlength="256" th:field="*{placeName}" required>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label is-required">宗教类别:</label>
+                        <div class="col-sm-8">
+                            <select name="religionType" class="form-control" required th:with="type=${@dict.getType('religion_type')}">
+                                <option value="">请选择宗教类别</option>
+                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{religionType}"></option>
+                            </select>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">场所类别:</label>
+                        <div class="col-sm-8">
+                            <!--<input name="placeCategory" placeholder="如:寺/观/清真寺/教堂" class="form-control" type="text" maxlength="64" th:field="*{placeCategory}">-->
+                            <select name="placeCategory" class="form-control" required th:with="type=${@dict.getType('category_type')}">
+                                <option value="">请选择场所类别</option>
+                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{placeCategory}"></option>
+                            </select>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">所属区县:</label>
+                        <div class="col-sm-8">
+                            <input name="county" placeholder="请输入所属区县" class="form-control" type="text" maxlength="128" th:field="*{county}">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-12">
+                    <div class="form-group">
+                        <label class="col-xs-2 control-label">详细地址:</label>
+                        <div class="col-xs-10">
+                            <input name="address" placeholder="请输入详细地址" class="form-control" type="text" maxlength="512" th:field="*{address}">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            
+            <h4 class="form-header h4">负责人信息</h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label is-required">负责人姓名:</label>
+                        <div class="col-sm-8">
+                            <input name="chargeName" placeholder="请输入负责人姓名" class="form-control" type="text" maxlength="128" th:field="*{chargeName}" required>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label is-required">负责人电话:</label>
+                        <div class="col-sm-8">
+                            <div class="input-group">
+                                <input name="chargePhone" placeholder="请输入负责人电话" class="form-control" type="text" maxlength="32" th:field="*{chargePhone}" required>
+                                <span class="input-group-addon"><i class="fa fa-mobile"></i></span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            
+            <h4 class="form-header h4">场所面积</h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">占地面积:</label>
+                        <div class="col-sm-8">
+                            <div class="input-group">
+                                <input name="placeArea" placeholder="请输入占地面积" class="form-control" type="number" step="0.01" th:field="*{placeArea}">
+                                <span class="input-group-addon">平方米</span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">建筑面积:</label>
+                        <div class="col-sm-8">
+                            <div class="input-group">
+                                <input name="buildingArea" placeholder="请输入建筑面积" class="form-control" type="number" step="0.01" th:field="*{buildingArea}">
+                                <span class="input-group-addon">平方米</span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            
+            <h4 class="form-header h4">批准与登记信息</h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">批准设立机关:</label>
+                        <div class="col-sm-8">
+                            <input name="approveOrgan" placeholder="请输入批准设立机关" class="form-control" type="text" maxlength="128" th:field="*{approveOrgan}">
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">批准设立时间:</label>
+                        <div class="col-sm-8">
+                            <div class="input-group date">
+                                <input name="approveTime" placeholder="yyyy-MM-dd" class="form-control" type="text" th:value="${#dates.format(place.approveTime, 'yyyy-MM-dd')}">
+                                <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">登记机关:</label>
+                        <div class="col-sm-8">
+                            <input name="regOrgan" placeholder="请输入登记机关" class="form-control" type="text" maxlength="128" th:field="*{regOrgan}">
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">登记证号:</label>
+                        <div class="col-sm-8">
+                            <input name="regNo" placeholder="请输入登记证号" class="form-control" type="text" maxlength="64" th:field="*{regNo}">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">登记时间:</label>
+                        <div class="col-sm-8">
+                            <div class="input-group date">
+                                <input name="regTime" placeholder="yyyy-MM-dd" class="form-control" type="text" th:value="${#dates.format(place.regTime, 'yyyy-MM-dd')}">
+                                <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">统一社会信用代码:</label>
+                        <div class="col-sm-8">
+                            <input name="creditCode" placeholder="请输入统一社会信用代码" class="form-control" type="text" maxlength="32" th:field="*{creditCode}">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            
+            <h4 class="form-header h4">法人资格信息</h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">是否具有法人资格:</label>
+                        <div class="col-sm-8">
+                            <select name="legalQualified" class="form-control" th:field="*{legalQualified}">
+                                <option value="">请选择</option>
+                                <option value="0">否</option>
+                                <option value="1">是</option>
+                            </select>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">法人资格取得时间:</label>
+                        <div class="col-sm-8">
+                            <div class="input-group date">
+                                <input name="legalTime" placeholder="yyyy-MM-dd" class="form-control" type="text" th:value="${#dates.format(place.legalTime, 'yyyy-MM-dd')}">
+                                <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            
+            <h4 class="form-header h4">地理位置信息</h4>
+            <div class="row">
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">经度:</label>
+                        <div class="col-sm-8">
+                            <input name="longitude" placeholder="请输入经度" class="form-control" type="number" step="0.000001" th:field="*{longitude}">
+                        </div>
+                    </div>
+                </div>
+                <div class="col-sm-6">
+                    <div class="form-group">
+                        <label class="col-sm-4 control-label">纬度:</label>
+                        <div class="col-sm-8">
+                            <input name="latitude" placeholder="请输入纬度" class="form-control" type="number" step="0.000001" th:field="*{latitude}">
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </form>
+    </div>
+    
+    <div class="row">
+        <div class="col-sm-offset-5 col-sm-10">
+            <button type="button" class="btn btn-sm btn-primary" onclick="submitHandler()"><i class="fa fa-check"></i>保 存</button>&nbsp;
+            <button type="button" class="btn btn-sm btn-danger" onclick="closeItem()"><i class="fa fa-reply-all"></i>关 闭</button>
+        </div>
+    </div>
+    
+    <th:block th:include="include :: footer" />
+    <th:block th:include="include :: datetimepicker-js" />
+    <script th:inline="javascript">
+        var prefix = ctx + "religion/place";
+        
+        $("#form-place-edit").validate({
+            onkeyup: false,
+            rules: {
+                placeName: {
+                    required: true,
+                    minlength: 2,
+                    maxlength: 256
+                },
+                religionType: {
+                    required: true
+                },
+                chargeName: {
+                    required: true,
+                    minlength: 2,
+                    maxlength: 128
+                },
+                chargePhone: {
+                    required: true,
+                    isPhone: true
+                },
+                placeArea: {
+                    number: true,
+                    min: 0
+                },
+                buildingArea: {
+                    number: true,
+                    min: 0
+                },
+                longitude: {
+                    number: true,
+                    min: -180,
+                    max: 180
+                },
+                latitude: {
+                    number: true,
+                    min: -90,
+                    max: 90
+                }
+            },
+            messages: {
+                placeName: {
+                    required: "请输入场所名称",
+                    minlength: "场所名称不能少于2个字符",
+                    maxlength: "场所名称不能超过256个字符"
+                },
+                religionType: {
+                    required: "请选择宗教类别"
+                },
+                chargeName: {
+                    required: "请输入负责人姓名",
+                    minlength: "负责人姓名不能少于2个字符",
+                    maxlength: "负责人姓名不能超过128个字符"
+                },
+                chargePhone: {
+                    required: "请输入负责人电话",
+                    isPhone: "请输入正确的手机号码"
+                }
+            },
+            focusCleanup: true
+        });
+        
+        function submitHandler() {
+            if ($.validate.form()) {
+                var data = $("#form-place-edit").serializeArray();
+                $.operate.saveTab(prefix + "/edit", data);
+            }
+        }
+        
+        $(function() {
+            // 初始化日期选择器
+            $("input[name='approveTime']").datetimepicker({
+                format: "yyyy-mm-dd",
+                minView: "month",
+                autoclose: true,
+                todayBtn: true
+            });
+            
+            $("input[name='regTime']").datetimepicker({
+                format: "yyyy-mm-dd",
+                minView: "month",
+                autoclose: true,
+                todayBtn: true
+            });
+            
+            $("input[name='legalTime']").datetimepicker({
+                format: "yyyy-mm-dd",
+                minView: "month",
+                autoclose: true,
+                todayBtn: true
+            });
+        });
+    </script>
+</body>
+</html>
+

+ 133 - 0
ruoyi-admin/src/main/resources/templates/religion/place/place.html

@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
+<head>
+    <th:block th:include="include :: header('宗教活动场所列表')" />
+</head>
+<body class="gray-bg">
+    <div class="container-div">
+        <div class="row">
+            <div class="col-sm-12 search-collapse">
+                <form id="place-form">
+                    <div class="select-list">
+                        <ul>
+                            <li>
+                                场所名称:<input type="text" name="placeName" placeholder="请输入场所名称"/>
+                            </li>
+                            <li>
+                                宗教类别:<select name="religionType" th:with="type=${@dict.getType('religion_type')}">
+                                    <option value="">所有</option>
+                                    <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
+                                </select>
+                            </li>
+                            <li>
+                                所属区县:<input type="text" name="county" placeholder="请输入所属区县"/>
+                            </li>
+                            <li>
+                                负责人姓名:<input type="text" name="chargeName" placeholder="请输入负责人姓名"/>
+                            </li>
+                            <li>
+                                <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
+                                <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
+                            </li>
+                        </ul>
+                    </div>
+                </form>
+            </div>
+            
+            <div class="btn-group-sm" id="toolbar" role="group">
+                <a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="religion:place:add">
+                    <i class="fa fa-plus"></i> 新增
+                </a>
+                <a class="btn btn-primary single disabled" onclick="$.operate.edit()" shiro:hasPermission="religion:place:edit">
+                    <i class="fa fa-edit"></i> 修改
+                </a>
+                <a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()" shiro:hasPermission="religion:place:remove">
+                    <i class="fa fa-remove"></i> 删除
+                </a>
+                <a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="religion:place:export">
+                    <i class="fa fa-download"></i> 导出
+                </a>
+            </div>
+            
+            <div class="col-sm-12 select-table table-striped">
+                <table id="bootstrap-table"></table>
+            </div>
+        </div>
+    </div>
+    
+    <th:block th:include="include :: footer" />
+    <script th:inline="javascript">
+        var editFlag = [[${@permission.hasPermi('religion:place:edit')}]];
+        var removeFlag = [[${@permission.hasPermi('religion:place:remove')}]];
+        var prefix = ctx + "religion/place";
+
+        $(function() {
+            var options = {
+                url: prefix + "/list",
+                createUrl: prefix + "/add",
+                updateUrl: prefix + "/edit/{id}",
+                removeUrl: prefix + "/remove",
+                exportUrl: prefix + "/export",
+                sortName: "createTime",
+                sortOrder: "desc",
+                modalName: "宗教活动场所",
+                columns: [{
+                    checkbox: true
+                },
+                {
+                    field: 'placeId',
+                    title: '场所ID',
+                    visible: false
+                },
+                {
+                    field: 'placeName',
+                    title: '场所名称',
+                    sortable: true
+                },
+                {
+                    field: 'religionType',
+                    title: '宗教类别'
+                },
+                {
+                    field: 'placeCategory',
+                    title: '场所类别'
+                },
+                {
+                    field: 'county',
+                    title: '所属区县'
+                },
+                {
+                    field: 'chargeName',
+                    title: '负责人姓名'
+                },
+                {
+                    field: 'chargePhone',
+                    title: '负责人电话'
+                },
+                {
+                    field: 'address',
+                    title: '详细地址',
+                    visible: false
+                },
+                {
+                    field: 'createTime',
+                    title: '创建时间',
+                    sortable: true
+                },
+                {
+                    title: '操作',
+                    align: 'center',
+                    formatter: function(value, row, index) {
+                        var actions = [];
+                        actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="$.operate.edit(\'' + row.placeId + '\')"><i class="fa fa-edit"></i>编辑</a> ');
+                        actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="$.operate.remove(\'' + row.placeId + '\')"><i class="fa fa-remove"></i>删除</a>');
+                        return actions.join('');
+                    }
+                }]
+            };
+            $.table.init(options);
+        });
+    </script>
+</body>
+</html>
+