Compare commits

..
29 Commits
Author SHA1 Message Date
c-duxc1 7d5e948e4c 活动场地预约-预约审核(包含手机端):修复多个场地管理员审核同时审核相同一页数据时(包含一键通过和一键驳回),后端异常报错的问题; 2026-10-23 17:22:10 +08:00
c-duxc1 030bb7cb23 活动场地预约-预约审核:修复多个场地管理员审核同时审核相同一页数据时,后端异常报错的问题; 2026-10-23 17:15:22 +08:00
c-duxc1 18397fb7c9 医疗互助系统-医保报销数据导入和补助台账:新增身份证号匹配类型查询(身份证号关联风险); 2026-10-12 14:49:31 +08:00
c-duxc1 1cd0552a26 医疗互助系统-医保报销数据导入和补助台账:新增身份证号匹配类型查询(方便搜索身份证号码匹配异常的数据); 2026-10-12 14:23:39 +08:00
c-duxc1 19ebd4cac3 医疗互助系统-医保报销数据导入-导入医保数据:系统用户身份证为空时使用会员台账身份证,最后才按姓名匹配。; 2026-10-10 14:52:10 +08:00
c-duxc1 4ca2b73d7b 医疗互助系统-医保报销数据导入和补助台账:新增重新关联人员按钮,方便修复重名导致的匹配错误问题; 2026-10-10 14:29:48 +08:00
c-duxc1 03864ea416 修复培训活动二维码无法签到的问题:培训签到页未初始化微信 JS-SDK,导致“扫一扫”接口无法调用。 2026-10-09 10:47:29 +08:00
c-chengc1 c909f0a3fb feat:场地预约-面向分工会场地,1、支持协会负责人进行预约,2、预约前签字 2026-09-30 15:35:56 +08:00
JyuHsin 593500cecc commit 2026-09-24 10:09:23 +08:00
c-zhouhf1 f709e3a0e4 commit 2026-09-24 09:56:49 +08:00
c-duxc1 1c2630a338 医疗互助系统-补助台账:修复搜索异常;修复数据导入计算时报错数据也被导入等问题; 2026-09-23 19:06:54 +08:00
c-duxc1 c90c4619ee 医疗互助系统-医保,报销数据导入:导入学校二次报销数据接口报错处理;导出汇总-补助计算金额字段值修复(四项之和减去学校二次报销金额后的结果带入‘江苏河海大学计算方法’计算一下); 2026-09-22 18:17:11 +08:00
c-duxc1 3a7ef3210f Merge remote-tracking branch 'origin/main' 2026-09-22 10:32:03 +08:00
c-duxc1 5ff22b1697 医疗互助系统-医保,报销数据导入:导入学校二次报销数据接口报错处理; 2026-09-22 10:31:43 +08:00
JyuHsin 9f34d65283 Merge branch 'main' of http://gitea.dd3skj.com/c-hongqw1/zhgh_hhu 2026-09-22 08:46:55 +08:00
JyuHsin 365523394a commit 2026-09-22 08:41:13 +08:00
c-duxc1 0945ca26ff 医疗互助系统:导入身份证号时trim一下; 医疗互助系统-会员台账身份证号码支持精确搜索,身份证号码输入后正则校验; 2026-09-20 18:12:58 +08:00
c-duxc1 5c61ec443a 医疗互助系统:修复‘新增补助记录’的工号搜索时,列名重复问题; 2026-09-20 17:27:55 +08:00
c-duxc1 f5095c72ad 医疗互助系统:会员导入模板加身份证号码字段;会员台账新增身份证号码字段编辑; 2026-09-20 17:20:05 +08:00
c-hongqw1 e558339dcc first commit 2026-09-20 15:46:34 +08:00
= 72fb5eae10 commit 2026-06-09 16:11:45 +08:00
= c337ad1716 commit 2026-06-05 11:02:05 +08:00
= 0f20f35e1e commit 2026-05-13 14:38:15 +08:00
= f50c7eba89 协会导出 2026-05-13 10:31:47 +08:00
= c1ce122a70 作品征集上传 2026-05-12 10:07:49 +08:00
= e4a1030d5a commit 2026-04-28 17:36:21 +08:00
= a9fc3d9690 commit 2026-04-28 17:33:16 +08:00
= a1dcad9e28 协会报销预审 2026-04-10 18:19:09 +08:00
JyuHsin 1c89f47958 commit 2026-04-10 14:55:52 +08:00
3412 changed files with 675924 additions and 578 deletions
+51 -1
View File
@@ -1 +1,51 @@
/.idea/
# 依赖与构建产物
**/node_modules/
**/dist/
**/build/
**/target/
**/out/
**/.next/
**/.nuxt/
**/coverage/
# 日志、缓存与临时文件
**/*.log
**/*.tmp
**/*.temp
**/.cache/
**/.eslintcache
**/npm-debug.log*
**/yarn-debug.log*
**/yarn-error.log*
**/pnpm-debug.log*
# IDE 与操作系统文件
.idea/
.vscode/
*.iml
.project
.classpath
.settings/
.DS_Store
Thumbs.db
# 本地环境和密钥配置(保留示例文件)
**/.env
**/.env.local
**/.env.*.local
!**/.env.example
!**/.env.*.example
# Java 编译文件
**/*.class
.trae/.ignore
.trae/cache-components/SKILL.md
.trae/code-reviewer/SKILL.md
.trae/find-skills/SKILL.md
.trae/fix/SKILL.md
.trae/frontend-code-review/SKILL.md
.trae/frontend-design/SKILL.md
.trae/fullstack-developer/SKILL.md
.trae/pr-creator/SKILL.md
.trae/update-docs/SKILL.md
.trae/webapp-testing/SKILL.md
-164
View File
@@ -1,164 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.v</groupId>
<artifactId>mini</artifactId>
<version>5.3.0</version>
<description>java web development framework, base on budwk and nutz</description>
<url>https://budwk.com</url>
<developers>
<developer>
<id>wizzercn</id>
<name>Wizzer</name>
<email>wizzer@qq.com</email>
<url>https://wizzer.cn</url>
</developer>
</developers>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>15</source>
<target>15</target>
<compilerArgs>--enable-preview</compilerArgs>
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<encoding>UTF-8</encoding>
<charset>UTF-8</charset>
<docencoding>UTF-8</docencoding>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<attach>true</attach>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.nutz.boot</groupId>
<artifactId>nutzboot-maven-plugin</artifactId>
<version>${nutzboot.version}</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>15</source>
<target>15</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>nutz</id>
<url>https://jfrog.nutz.cn/artifactory/libs-release</url>
</repository>
<repository>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<updatePolicy>always</updatePolicy>
</snapshots>
<id>nutz-snapshots</id>
<url>https://jfrog.nutz.cn/artifactory/snapshots</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<updatePolicy>always</updatePolicy>
</snapshots>
<id>nutz-snapshots</id>
<url>https://jfrog.nutz.cn/artifactory/snapshots</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.nutz</groupId>
<artifactId>nutzboot-starter-test-junit4</artifactId>
<version>2.4.2-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>hamcrest-core</artifactId>
<groupId>org.hamcrest</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.nutz</groupId>
<artifactId>nutzboot-parent</artifactId>
<version>${nutzboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb-api.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<jsoup.veision>1.11.2</jsoup.veision>
<mysql-connector-java.version>8.0.16</mysql-connector-java.version>
<easy-captcha.version>1.6.2</easy-captcha.version>
<emoji-java.veision>4.0.0</emoji-java.veision>
<jaxb-api.version>2.3.1</jaxb-api.version>
<lombok.version>1.18.12</lombok.version>
<slf4j.version>1.7.30</slf4j.version>
<poi.version>4.1.2</poi.version>
<v.version>1.0.1</v.version>
<commons-lang3.version>3.8.1</commons-lang3.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<pinyin4j.veision>2.5.1</pinyin4j.veision>
<nutzboot.version>2.4.2-SNAPSHOT</nutzboot.version>
<logback.version>1.2.3</logback.version>
<nutz.version>1.r.68-SNAPSHOT</nutz.version>
<tencentcloud-sdk-java.version>4.0.11</tencentcloud-sdk-java.version>
</properties>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="Spring" name="Spring">
<configuration />
</facet>
</component>
</module>
+107
View File
@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<project version="4" relativePaths="false">
<component name="ProjectRootManager" version="2" assert-keyword="true" project-jdk-name="15." jdk-15="true" languageLevel="JDK_1_8"/>
<component name="CodeStyleManager">
<option name="USE_DEFAULT_CODE_STYLE_SCHEME" value="true"/>
<option name="CODE_STYLE_SCHEME" value=""/>
</component>
<component name="libraryTable"/>
<component name="CompilerConfiguration">
<option name="DEFAULT_COMPILER" value="Javac"/>
<option name="CLEAR_OUTPUT_DIRECTORY" value="false"/>
<!--
<wildcardResourcePatterns>
<entry name="${wildcardResourcePattern}"/>
</wildcardResourcePatterns>
-->
<wildcardResourcePatterns>
<entry name="!?*.java"/>
</wildcardResourcePatterns>
</component>
<component name="JavacSettings">
<option name="DEBUGGING_INFO" value="true"/>
<option name="GENERATE_NO_WARNINGS" value="false"/>
<option name="DEPRECATION" value="true"/>
<option name="ADDITIONAL_OPTIONS_STRING" value=""/>
<option name="MAXIMUM_HEAP_SIZE" value="128"/>
<option name="USE_GENERICS_COMPILER" value="false"/>
</component>
<component name="JikesSettings">
<option name="DEBUGGING_INFO" value="true"/>
<option name="DEPRECATION" value="true"/>
<option name="GENERATE_NO_WARNINGS" value="false"/>
<option name="GENERATE_MAKE_FILE_DEPENDENCIES" value="false"/>
<option name="DO_FULL_DEPENDENCE_CHECK" value="false"/>
<option name="IS_INCREMENTAL_MODE" value="false"/>
<option name="IS_EMACS_ERRORS_MODE" value="true"/>
<option name="ADDITIONAL_OPTIONS_STRING" value=""/>
<option name="MAXIMUM_HEAP_SIZE" value="128"/>
</component>
<component name="AntConfiguration">
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false"/>
<option name="FILTER_TARGETS" value="false"/>
</component>
<component name="JavadocGenerationManager">
<option name="OUTPUT_DIRECTORY"/>
<option name="OPTION_SCOPE" value="protected"/>
<option name="OPTION_HIERARCHY" value="false"/>
<option name="OPTION_NAVIGATOR" value="false"/>
<option name="OPTION_INDEX" value="false"/>
<option name="OPTION_SEPARATE_INDEX" value="false"/>
<option name="OPTION_USE_1_1" value="false"/>
<option name="OPTION_DOCUMENT_TAG_USE" value="false"/>
<option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false"/>
<option name="OPTION_DOCUMENT_TAG_VERSION" value="false"/>
<option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="false"/>
<option name="OPTION_DEPRECATED_LIST" value="false"/>
<option name="OTHER_OPTIONS"/>
<option name="HEAP_SIZE"/>
<option name="OPEN_IN_BROWSER" value="false"/>
</component>
<component name="JUnitProjectSettings">
<option name="TEST_RUNNER" value="UI"/>
</component>
<component name="EntryPointsManager">
<entry_points/>
</component>
<component name="DataSourceManager"/>
<component name="ExportToHTMLSettings">
<option name="PRINT_LINE_NUMBERS" value="false"/>
<option name="OPEN_IN_BROWSER" value="false"/>
<option name="OUTPUT_DIRECTORY"/>
</component>
<component name="ImportConfiguration">
<option name="VENDOR"/>
<option name="RELEASE_TAG"/>
<option name="LOG_MESSAGE"/>
<option name="CHECKOUT_AFTER_IMPORT" value="true"/>
</component>
<component name="ProjectModuleManager">
<modules>
<!-- module filepath="$$PROJECT_DIR$$/${pom.artifactId}.iml"/ -->
<module filepath="$PROJECT_DIR$/mini.iml"/>
</modules>
</component>
<UsedPathMacros>
<!--<macro name="cargo"></macro>-->
</UsedPathMacros>
</project>
+418
View File
@@ -0,0 +1,418 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<project version="4" relativePaths="false">
<component name="LvcsProjectConfiguration">
<option name="ADD_LABEL_ON_PROJECT_OPEN" value="true"/>
<option name="ADD_LABEL_ON_PROJECT_COMPILATION" value="true"/>
<option name="ADD_LABEL_ON_FILE_PACKAGE_COMPILATION" value="true"/>
<option name="ADD_LABEL_ON_PROJECT_MAKE" value="true"/>
<option name="ADD_LABEL_ON_RUNNING" value="true"/>
<option name="ADD_LABEL_ON_DEBUGGING" value="true"/>
<option name="ADD_LABEL_ON_UNIT_TEST_PASSED" value="true"/>
<option name="ADD_LABEL_ON_UNIT_TEST_FAILED" value="true"/>
</component>
<component name="PropertiesComponent">
<property name="MemberChooser.copyJavadoc" value="false"/>
<property name="GoToClass.includeLibraries" value="false"/>
<property name="MemberChooser.showClasses" value="true"/>
<property name="MemberChooser.sorted" value="false"/>
<property name="GoToFile.includeJavaFiles" value="false"/>
<property name="GoToClass.toSaveIncludeLibraries" value="false"/>
</component>
<component name="ToolWindowManager">
<frame x="-4" y="-4" width="1032" height="746" extended-state="6"/>
<editor active="false"/>
<layout>
<window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.33" order="-1"/>
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.33" order="7"/>
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.25" order="0"/>
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.33" order="1"/>
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.25" order="1"/>
<window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.33" order="-1"/>
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.4" order="6"/>
<window_info id="Aspects" active="false" anchor="right" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.33" order="-1"/>
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.25" order="1"/>
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.33" order="2"/>
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.25" order="2"/>
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.4" order="4"/>
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="sliding" type="sliding" visible="false" weight="0.4" order="0"/>
<window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.25" order="2"/>
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.33" order="0"/>
<window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.25" order="3"/>
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="docked" type="docked" visible="false" weight="0.25" order="5"/>
</layout>
</component>
<component name="ErrorTreeViewConfiguration">
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false"/>
<option name="HIDE_WARNINGS" value="false"/>
</component>
<component name="StructureViewFactory">
<option name="SORT_MODE" value="0"/>
<option name="GROUP_INHERITED" value="true"/>
<option name="AUTOSCROLL_MODE" value="true"/>
<option name="SHOW_FIELDS" value="true"/>
<option name="AUTOSCROLL_FROM_SOURCE" value="false"/>
<option name="GROUP_GETTERS_AND_SETTERS" value="true"/>
<option name="SHOW_INHERITED" value="false"/>
<option name="HIDE_NOT_PUBLIC" value="false"/>
</component>
<component name="ProjectViewSettings">
<navigator currentView="ProjectPane" flattenPackages="false" showMembers="false" showStructure="false" autoscrollToSource="false" splitterProportion="0.5"/>
<view id="ProjectPane">
<expanded_node type="directory" url="file://$PROJECT_DIR$"/>
</view>
<view id="SourcepathPane"/>
<view id="ClasspathPane"/>
</component>
<component name="Commander">
<leftPanel view="Project"/>
<rightPanel view="Project"/>
<splitter proportion="0.5"/>
</component>
<component name="AspectsView"/>
<component name="SelectInManager"/>
<component name="HierarchyBrowserManager">
<option name="SHOW_PACKAGES" value="false"/>
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false"/>
<option name="SORT_ALPHABETICALLY" value="false"/>
</component>
<component name="TodoView" selected-index="0">
<todo-panel id="selected-file">
<are-packages-shown value="false"/>
<flatten-packages value="false"/>
<is-autoscroll-to-source value="true"/>
</todo-panel>
<todo-panel id="all">
<are-packages-shown value="true"/>
<flatten-packages value="false"/>
<is-autoscroll-to-source value="true"/>
</todo-panel>
</component>
<component name="editorManager"/>
<component name="editorHistoryManager"/>
<component name="DaemonCodeAnalyzer">
<disable_hints/>
</component>
<component name="InspectionManager">
<option name="AUTOSCROLL_TO_SOURCE" value="false"/>
<option name="SPLITTER_PROPORTION" value="0.5"/>
<profile name="Default"/>
</component>
<component name="BookmarkManager"/>
<component name="DebuggerManager">
<line_breakpoints/>
<exception_breakpoints>
<breakpoint_any>
<option name="NOTIFY_CAUGHT" value="true"/>
<option name="NOTIFY_UNCAUGHT" value="true"/>
<option name="ENABLED" value="false"/>
<option name="SUSPEND_VM" value="true"/>
<option name="COUNT_FILTER_ENABLED" value="false"/>
<option name="COUNT_FILTER" value="0"/>
<option name="CONDITION_ENABLED" value="false"/>
<option name="CONDITION"/>
<option name="LOG_ENABLED" value="false"/>
<option name="LOG_EXPRESSION_ENABLED" value="false"/>
<option name="LOG_MESSAGE"/>
<option name="CLASS_FILTERS_ENABLED" value="false"/>
<option name="INVERSE_CLASS_FILLTERS" value="false"/>
<option name="SUSPEND_POLICY" value="SuspendAll"/>
</breakpoint_any>
</exception_breakpoints>
<field_breakpoints/>
<method_breakpoints/>
</component>
<component name="DebuggerSettings">
<option name="TRACING_FILTERS_ENABLED" value="true"/>
<option name="TOSTRING_CLASSES_ENABLED" value="false"/>
<option name="VALUE_LOOKUP_DELAY" value="700"/>
<option name="DEBUGGER_TRANSPORT" value="0"/>
<option name="FORCE_CLASSIC_VM" value="true"/>
<option name="HIDE_DEBUGGER_ON_PROCESS_TERMINATION" value="false"/>
<option name="SKIP_SYNTHETIC_METHODS" value="true"/>
<option name="SKIP_CONSTRUCTORS" value="false"/>
<option name="STEP_THREAD_SUSPEND_POLICY" value="SuspendThread"/>
<default_breakpoint_settings>
<option name="NOTIFY_CAUGHT" value="true"/>
<option name="NOTIFY_UNCAUGHT" value="true"/>
<option name="WATCH_MODIFICATION" value="true"/>
<option name="WATCH_ACCESS" value="true"/>
<option name="WATCH_ENTRY" value="true"/>
<option name="WATCH_EXIT" value="true"/>
<option name="ENABLED" value="true"/>
<option name="SUSPEND_VM" value="true"/>
<option name="COUNT_FILTER_ENABLED" value="false"/>
<option name="COUNT_FILTER" value="0"/>
<option name="CONDITION_ENABLED" value="false"/>
<option name="CONDITION"/>
<option name="LOG_ENABLED" value="false"/>
<option name="LOG_EXPRESSION_ENABLED" value="false"/>
<option name="LOG_MESSAGE"/>
<option name="CLASS_FILTERS_ENABLED" value="false"/>
<option name="INVERSE_CLASS_FILLTERS" value="false"/>
<option name="SUSPEND_POLICY" value="SuspendAll"/>
</default_breakpoint_settings>
<filter>
<option name="PATTERN" value="com.sun.*"/>
<option name="ENABLED" value="true"/>
</filter>
<filter>
<option name="PATTERN" value="java.*"/>
<option name="ENABLED" value="true"/>
</filter>
<filter>
<option name="PATTERN" value="javax.*"/>
<option name="ENABLED" value="true"/>
</filter>
<filter>
<option name="PATTERN" value="org.omg.*"/>
<option name="ENABLED" value="true"/>
</filter>
<filter>
<option name="PATTERN" value="sun.*"/>
<option name="ENABLED" value="true"/>
</filter>
<filter>
<option name="PATTERN" value="junit.*"/>
<option name="ENABLED" value="true"/>
</filter>
</component>
<component name="CompilerWorkspaceConfiguration">
<option name="COMPILE_IN_BACKGROUND" value="false"/>
<option name="AUTO_SHOW_ERRORS_IN_EDITOR" value="true"/>
</component>
<component name="RunManager">
<activeType name="Application"/>
<configuration selected="false" default="true" type="Applet" factoryName="Applet">
<module name=""/>
<option name="MAIN_CLASS_NAME"/>
<option name="HTML_FILE_NAME"/>
<option name="HTML_USED" value="false"/>
<option name="WIDTH" value="400"/>
<option name="HEIGHT" value="300"/>
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy"/>
<option name="VM_PARAMETERS"/>
</configuration>
<configuration selected="false" default="true" type="Remote" factoryName="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true"/>
<option name="SERVER_MODE" value="false"/>
<option name="SHMEM_ADDRESS" value="javadebug"/>
<option name="HOST" value="localhost"/>
<option name="PORT" value="5005"/>
</configuration>
<configuration selected="false" default="true" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME"/>
<option name="VM_PARAMETERS"/>
<option name="PROGRAM_PARAMETERS"/>
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$"/>
<module name=""/>
</configuration>
<configuration selected="false" default="true" type="JUnit" factoryName="JUnit">
<module name=""/>
<option name="PACKAGE_NAME"/>
<option name="MAIN_CLASS_NAME"/>
<option name="METHOD_NAME"/>
<option name="TEST_OBJECT" value="class"/>
<option name="VM_PARAMETERS"/>
<option name="PARAMETERS"/>
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$"/>
<option name="ADDITIONAL_CLASS_PATH"/>
<option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject"/>
</option>
</configuration>
</component>
<component name="VcsManagerConfiguration">
<option name="ACTIVE_VCS_NAME" value=""/>
<option name="STATE" value="0"/>
</component>
<component name="VssConfiguration">
<CheckoutOptions>
<option name="COMMENT" value=""/>
<option name="DO_NOT_GET_LATEST_VERSION" value="false"/>
<option name="REPLACE_WRITABLE" value="false"/>
<option name="RECURSIVE" value="false"/>
</CheckoutOptions>
<CheckinOptions>
<option name="COMMENT" value=""/>
<option name="KEEP_CHECKED_OUT" value="false"/>
<option name="RECURSIVE" value="false"/>
</CheckinOptions>
<AddOptions>
<option name="COMMENT" value=""/>
<option name="STORE_ONLY_LATEST_VERSION" value="false"/>
<option name="CHECK_OUT_IMMEDIATELY" value="false"/>
<option name="FILE_TYPE" value="0"/>
</AddOptions>
<UndocheckoutOptions>
<option name="MAKE_WRITABLE" value="false"/>
<option name="REPLACE_LOCAL_COPY" value="0"/>
<option name="RECURSIVE" value="false"/>
</UndocheckoutOptions>
<DiffOptions>
<option name="IGNORE_WHITE_SPACE" value="false"/>
<option name="IGNORE_CASE" value="false"/>
</DiffOptions>
<GetOptions>
<option name="REPLACE_WRITABLE" value="0"/>
<option name="MAKE_WRITABLE" value="false"/>
<option name="RECURSIVE" value="false"/>
</GetOptions>
<option name="CLIENT_PATH" value=""/>
<option name="SRCSAFEINI_PATH" value=""/>
<option name="USER_NAME" value=""/>
<option name="PWD" value=""/>
<option name="SHOW_CHECKOUT_OPTIONS" value="true"/>
<option name="SHOW_ADD_OPTIONS" value="true"/>
<option name="SHOW_UNDOCHECKOUT_OPTIONS" value="true"/>
<option name="SHOW_DIFF_OPTIONS" value="true"/>
<option name="SHOW_GET_OPTIONS" value="true"/>
<option name="USE_EXTERNAL_DIFF" value="false"/>
<option name="EXTERNAL_DIFF_PATH" value=""/>
<option name="REUSE_LAST_COMMENT" value="false"/>
<option name="PUT_FOCUS_INTO_COMMENT" value="false"/>
<option name="SHOW_CHECKIN_OPTIONS" value="true"/>
<option name="LAST_COMMIT_MESSAGE" value=""/>
<option name="CHECKIN_DIALOG_SPLITTER_PROPORTION" value="0.8"/>
</component>
<component name="CheckinPanelState"/>
<component name="WebViewSettings">
<webview flattenPackages="false" showMembers="false" autoscrollToSource="false"/>
</component>
<component name="EjbViewSettings">
<EjbView showMembers="false" autoscrollToSource="false"/>
</component>
<component name="AppServerRunManager"/>
<component name="StarteamConfiguration">
<option name="SERVER" value=""/>
<option name="PORT" value="49201"/>
<option name="USER" value=""/>
<option name="PASSWORD" value=""/>
<option name="PROJECT" value=""/>
<option name="VIEW" value=""/>
<option name="ALTERNATIVE_WORKING_PATH" value=""/>
<option name="PUT_FOCUS_INTO_COMMENT" value="false"/>
<option name="SHOW_CHECKIN_OPTIONS" value="true"/>
<option name="LAST_COMMIT_MESSAGE" value=""/>
<option name="CHECKIN_DIALOG_SPLITTER_PROPORTION" value="0.8"/>
</component>
<component name="Cvs2Configuration">
<option name="ON_FILE_ADDING" value="0"/>
<option name="ON_FILE_REMOVING" value="0"/>
<option name="PRUNE_EMPTY_DIRECTORIES" value="true"/>
<option name="SHOW_UPDATE_OPTIONS" value="true"/>
<option name="SHOW_ADD_OPTIONS" value="true"/>
<option name="SHOW_REMOVE_OPTIONS" value="true"/>
<option name="MERGING_MODE" value="0"/>
<option name="MERGE_WITH_BRANCH1_NAME" value="HEAD"/>
<option name="MERGE_WITH_BRANCH2_NAME" value="HEAD"/>
<option name="RESET_STICKY" value="false"/>
<option name="CREATE_NEW_DIRECTORIES" value="true"/>
<option name="DEFAULT_TEXT_FILE_SUBSTITUTION" value="kv"/>
<option name="PROCESS_UNKNOWN_FILES" value="false"/>
<option name="PROCESS_DELETED_FILES" value="false"/>
<option name="SHOW_EDIT_DIALOG" value="true"/>
<option name="RESERVED_EDIT" value="false"/>
<option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6"/>
<option name="SHOW_CHECKOUT_OPTIONS" value="true"/>
<option name="CHECKOUT_DATE_OR_REVISION_SETTINGS">
<value>
<option name="BRANCH" value=""/>
<option name="DATE" value=""/>
<option name="USE_BRANCH" value="false"/>
<option name="USE_DATE" value="false"/>
</value>
</option>
<option name="UPDATE_DATE_OR_REVISION_SETTINGS">
<value>
<option name="BRANCH" value=""/>
<option name="DATE" value=""/>
<option name="USE_BRANCH" value="false"/>
<option name="USE_DATE" value="false"/>
</value>
</option>
<option name="SHOW_CHANGES_REVISION_SETTINGS">
<value>
<option name="BRANCH" value=""/>
<option name="DATE" value=""/>
<option name="USE_BRANCH" value="false"/>
<option name="USE_DATE" value="false"/>
</value>
</option>
<option name="SHOW_OUTPUT" value="false"/>
<option name="SHOW_FILE_HISTORY_AS_TREE" value="false"/>
<option name="UPDATE_GROUP_BY_PACKAGES" value="false"/>
<option name="ADD_WATCH_INDEX" value="0"/>
<option name="REMOVE_WATCH_INDEX" value="0"/>
<option name="UPDATE_KEYWORD_SUBSTITUTION"/>
<option name="MAKE_NEW_FILES_READONLY" value="false"/>
<option name="SHOW_CORRUPTED_PROJECT_FILES" value="0"/>
<option name="TAG_AFTER_FILE_COMMIT" value="false"/>
<option name="TAG_AFTER_FILE_COMMIT_NAME" value=""/>
<option name="TAG_AFTER_PROJECT_COMMIT" value="false"/>
<option name="TAG_AFTER_PROJECT_COMMIT_NAME" value=""/>
<option name="PUT_FOCUS_INTO_COMMENT" value="false"/>
<option name="SHOW_CHECKIN_OPTIONS" value="true"/>
<option name="FORCE_NON_EMPTY_COMMENT" value="false"/>
<option name="LAST_COMMIT_MESSAGE" value=""/>
<option name="SAVE_LAST_COMMIT_MESSAGE" value="true"/>
<option name="CHECKIN_DIALOG_SPLITTER_PROPORTION" value="0.8"/>
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false"/>
<option name="OPTIMIZE_IMPORTS_BEFORE_FILE_COMMIT" value="false"/>
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false"/>
<option name="REFORMAT_BEFORE_FILE_COMMIT" value="false"/>
<option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8"/>
<option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5"/>
</component>
<component name="CvsTabbedWindow"/>
<component name="SvnConfiguration">
<option name="USER" value=""/>
<option name="PASSWORD" value=""/>
<option name="AUTO_ADD_FILES" value="0"/>
<option name="AUTO_DEL_FILES" value="0"/>
</component>
<component name="PerforceConfiguration">
<option name="PORT" value="magic:1666"/>
<option name="USER" value=""/>
<option name="PASSWORD" value=""/>
<option name="CLIENT" value=""/>
<option name="TRACE" value="false"/>
<option name="PERFORCE_STATUS" value="true"/>
<option name="CHANGELIST_OPTION" value="false"/>
<option name="SYSTEMROOT" value=""/>
<option name="P4_EXECUTABLE" value="p4"/>
<option name="SHOW_BRANCH_HISTORY" value="false"/>
<option name="GENERATE_COMMENT" value="false"/>
<option name="SYNC_OPTION" value="Sync"/>
<option name="PUT_FOCUS_INTO_COMMENT" value="false"/>
<option name="SHOW_CHECKIN_OPTIONS" value="true"/>
<option name="FORCE_NON_EMPTY_COMMENT" value="true"/>
<option name="LAST_COMMIT_MESSAGE" value=""/>
<option name="SAVE_LAST_COMMIT_MESSAGE" value="true"/>
<option name="CHECKIN_DIALOG_SPLITTER_PROPORTION" value="0.8"/>
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false"/>
<option name="OPTIMIZE_IMPORTS_BEFORE_FILE_COMMIT" value="false"/>
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false"/>
<option name="REFORMAT_BEFORE_FILE_COMMIT" value="false"/>
<option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8"/>
<option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5"/>
</component>
</project>
@@ -0,0 +1,58 @@
package io.v.nutz.activity.controller.site;
import cn.wizzer.framework.base.Result;
import io.v.nutz.activity.services.SiteReserveService;
import io.v.nutz.annontation.ViReturn;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.POST;
/**
* 电脑端和手机端共用的场地预约承诺书阅读、签署入口。
*/
@IocBean
@At("/platform/activity/site/commitment")
@Ok("json:full")
@RequiresAuthentication
public class SiteCommitmentController {
@Inject
private SiteReserveService siteReserveService;
/**
* 校验预约资格并记录服务器阅读开始时间。
* @param siteId 场地ID
* @return 承诺书及阅读凭证,或不可预约原因
*/
@At
@POST
@ViReturn
public Object begin(String siteId) {
try {
return siteReserveService.beginCommitment(siteId);
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
/**
* 阅读十秒后签署本次承诺书。
* @param commitmentId 本次阅读凭证
* @param signature 本次手写签名PNG数据
* @return 签署结果
*/
@At
@POST
@ViReturn
public Object sign(String commitmentId, String signature) {
try {
siteReserveService.signCommitment(commitmentId, signature);
return Result.success("签署成功");
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
}
@@ -2,6 +2,10 @@ package io.v.nutz.activity.controller.site;
import io.v.nutz.activity.models.ActivitySiteInfo;
import cn.wizzer.framework.base.Result;
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;
import java.nio.charset.StandardCharsets;
import io.v.nutz.activity.services.SiteInfoService;
import io.v.nutz.annontation.ViReturn;
import io.v.nutz.base.utils.PageUtil;
@@ -62,16 +66,34 @@ public class SiteManageController {
return siteInfoService.listPage(pageNumber, pageSize, cnd);
}
/**
* 新增场地,面向分工会时要求配置预约前承诺书。
* @param activitySiteInfo 场地信息及承诺书标题、正文
* @return 保存结果或配置校验错误
*/
@At
@ViReturn
public Object doAdd(@Param("data") ActivitySiteInfo activitySiteInfo) {
String error = prepareCommitment(activitySiteInfo);
if (error != null) {
return Result.error(error);
}
siteInfoService.insert(activitySiteInfo);
return null;
}
/**
* 保存场地及承诺书配置,已签署申请继续保留原正文快照。
* @param activitySiteInfo 修改后的场地信息
* @return 保存结果或配置校验错误
*/
@At
@ViReturn
public Object doEdit(@Param("data") ActivitySiteInfo activitySiteInfo) {
String error = prepareCommitment(activitySiteInfo);
if (error != null) {
return Result.error(error);
}
siteInfoService.update(activitySiteInfo);
return null;
}
@@ -88,4 +110,25 @@ public class SiteManageController {
public Object findOneSite(String id) {
return siteInfoService.fetch(id);
}
// 承诺书会在预约弹窗和历史签署记录中展示,仅保留正文所需的安全HTML。
private String prepareCommitment(ActivitySiteInfo site) {
if (site.getCommitmentContent() != null) {
site.setCommitmentContent(Jsoup.clean(site.getCommitmentContent(), Whitelist.relaxed()));
}
if (Integer.valueOf(1).equals(site.getReserveTarget())) {
if (Strings.isBlank(site.getCommitmentTitle()) || Strings.isBlank(site.getCommitmentContent())
|| Strings.isBlank(Jsoup.parse(site.getCommitmentContent()).text())) {
return "面向分工会的场地必须配置承诺书标题和正文";
}
}
if (site.getCommitmentTitle() != null && site.getCommitmentTitle().length() > 200) {
return "承诺书标题不能超过200字";
}
if (site.getCommitmentContent() != null
&& site.getCommitmentContent().getBytes(StandardCharsets.UTF_8).length > 60000) {
return "承诺书正文过长,请精简内容";
}
return null;
}
}
@@ -41,6 +41,7 @@ import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.trans.Trans;
import java.util.ArrayList;
import java.util.Date;
@@ -161,9 +162,25 @@ public class SiteReserveController {
return fetch.getReserve_state() > stateCode;
}
/**
* 创建预约,面向分工会的场地必须先完成本次承诺书签署。
* @param activitySiteReserve 预约表单
* @param days 本次申请的预约日期
* @param commitmentId 当前用户签署的场地承诺书凭证
* @return 预约结果或资格、签署校验失败原因
*/
@At
@ViReturn
public Object doAdd(@Param("data") ActivitySiteReserve activitySiteReserve, @Param("days") String[] days) {
public Object doAdd(@Param("data") ActivitySiteReserve activitySiteReserve, @Param("days") String[] days, String commitmentId) {
if (activitySiteReserve == null || days == null || days.length == 0) {
return Result.error("请选择预约场地和日期");
}
try {
siteReserveService.checkCommitment(activitySiteReserve.getSite_id(), commitmentId);
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
Map<Boolean, String> validateSiteReserve = siteReserveService.validateSiteReserve(
activitySiteReserve.getSite_id(),
@@ -183,22 +200,31 @@ public class SiteReserveController {
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
if (stateCode == null) {
return null;
return Result.error("场地尚未配置预约审核流程");
}
for (String d : days) {
activitySiteReserve.setReserve_day(d);
//查询这个人是否已经预约这个时间段
int count = siteReserveService.count("activity_site_reserve", Cnd.NEW().and("reserve_person_id", "=", ShiroUtil.getPrincipalProperty("id"))
.and("reserve_day", "=", activitySiteReserve.getReserve_day()).and("start_time", "=", activitySiteReserve.getStart_time())
.and("end_time", "=", activitySiteReserve.getEnd_time()));
if (count > 0) {
return Result.error("您已预约该时间段!");
}
try {
// 同一申请的多天预约和签署凭证一起提交,失败时不消耗签署凭证。
Trans.exec(() -> {
siteReserveService.bindCommitment(activitySiteReserve.getSite_id(), commitmentId, r);
for (String d : days) {
activitySiteReserve.setReserve_day(d);
//查询这个人是否已经预约这个时间段
int count = siteReserveService.count("activity_site_reserve", Cnd.NEW().and("reserve_person_id", "=", ShiroUtil.getPrincipalProperty("id"))
.and("reserve_day", "=", activitySiteReserve.getReserve_day()).and("start_time", "=", activitySiteReserve.getStart_time())
.and("end_time", "=", activitySiteReserve.getEnd_time()));
if (count > 0) {
throw new IllegalArgumentException("您已预约该时间段!");
}
activitySiteReserve.setReserve_state(stateCode);
activitySiteReserve.setReserve_person_id(ShiroUtil.getPrincipalProperty("id").toString());
siteReserveService.insert(activitySiteReserve);
activitySiteReserve.setReserve_state(stateCode);
activitySiteReserve.setReserve_person_id(ShiroUtil.getPrincipalProperty("id").toString());
activitySiteReserve.setId(R.UU32());
siteReserveService.insert(activitySiteReserve);
}
});
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
//发送微信提醒审核人,查询状态码对应的审核人
@@ -307,10 +333,18 @@ public class SiteReserveController {
return siteReserveService.count(cnd);
}
/**
* 查询预约详情及本次申请签署的承诺书快照。
* @param id 预约记录ID
* @return 预约详情、审核记录和承诺书签署信息
*/
@At
@ViReturn
public Object findOne(String id) {
ActivitySiteReserve fetch = siteReserveService.fetch(id);
if (fetch == null) {
return Result.error("预约记录不存在");
}
Sql sql = Sqls.create("""
SELECT
asr.*,
@@ -386,6 +420,7 @@ public class SiteReserveController {
i.setCondition(cnd);
List<Record> list1 = siteReserveService.list(i);
record.set("site_info", list1);
record.set("commitment", siteReserveService.findCommitment(fetch.getSqid()));
return record;
}
@@ -455,6 +490,13 @@ public class SiteReserveController {
return result;
}
/**
* 获取指定日期的场地预约时段,支持以24:00作为营业结束时间。
*
* @param siteId 场地ID
* @param day 预约日期,格式为yyyy-MM-dd
* @return 预约时段及不可预约状态,保留配置的结束时间用于匹配预约记录
*/
@At
@ViReturn
public Object getTimesByDay(String siteId, String day) {
@@ -480,16 +522,19 @@ public class SiteReserveController {
} else {
// 构造起始时间
DateTime start = DateUtil.parse(startTime, "HH:mm"); // "09:00"
DateTime end = DateUtil.parse(endTime, "HH:mm"); // "17:00"
// 场地允许营业至24:00,按次日零点计算,返回时保留24:00以匹配预约记录。
DateTime end = "24:00".equals(endTime)
? DateUtil.offsetDay(DateUtil.parse("00:00", "HH:mm"), 1)
: DateUtil.parse(endTime, "HH:mm");
DateTime current = start;
while (DateUtil.compare(current, end) < 0) { // 当前时间 <= 结束时间
DateTime next = DateUtil.offsetSecond(current, (int) (timeUnit * 3600));
// 如果超过结束时间,则使用 endTime
if (DateUtil.compare(next, end) > 0) {
result.add(NutMap.NEW().addv("start_time", DateUtil.format(current, "HH:mm")).addv("end_time", DateUtil.format(end, "HH:mm")));
result.add(NutMap.NEW().addv("start_time", DateUtil.format(current, "HH:mm")).addv("end_time", endTime));
break;
} else {
result.add(NutMap.NEW().addv("start_time", DateUtil.format(current, "HH:mm")).addv("end_time", DateUtil.format(next, "HH:mm")));
result.add(NutMap.NEW().addv("start_time", DateUtil.format(current, "HH:mm")).addv("end_time", DateUtil.compare(next, end) == 0 ? endTime : DateUtil.format(next, "HH:mm")));
}
current = next;
}
@@ -7,6 +7,7 @@ import io.v.nutz.activity.services.ActivityCommonService;
import io.v.nutz.activity.services.SiteInfoService;
import io.v.nutz.activity.services.SiteReserveService;
import io.v.nutz.annontation.ViReturn;
import io.v.nutz.base.result.Result;
import io.v.nutz.base.utils.MsgApi;
import io.v.nutz.base.utils.PageUtil;
import io.v.nutz.base.utils.SendMsg;
@@ -18,6 +19,7 @@ import io.v.nutz.sys.services.SysUserService;
import io.v.nutz.util.ShiroUtil;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Record;
@@ -34,6 +36,7 @@ import org.nutz.mvc.annotation.Ok;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
@@ -148,14 +151,49 @@ public class SiteReviewController {
@At
@ViReturn
public Object doReview(String[] id, Audit audit, Boolean isPass) {
public Object doReview(String[] id, Integer[] reserveState, Audit audit, Boolean isPass) {
if (id == null || reserveState == null || id.length == 0 || id.length != reserveState.length || isPass == null) {
return Result.error("审核参数错误,请刷新页面后重试");
}
List<ActivitySiteReserve> reviewList = new ArrayList<>();
List<Integer> afterStateList = new ArrayList<>();
int skippedCount = 0;
for (int i = 0; i < id.length; i++) {
ActivitySiteReserve reserve = siteReserveService.fetch(id[i]);
if (reserve == null || !Objects.equals(reserve.getReserve_state(), reserveState[i])) {
skippedCount++;
continue;
}
Integer afterStateCode = activityCommonService.findAfterStateCode(reserve.getReserve_state(), isPass);
if (afterStateCode == null) {
return Result.error("当前预约已完成审核或审核流程配置异常,请刷新页面后重试");
}
reviewList.add(reserve);
afterStateList.add(afterStateCode);
}
if (reviewList.isEmpty()) {
return Result.success("所选预约已被其他人审核,无需重复处理")
.addData(NutMap.NEW().setv("skippedCount", skippedCount));
}
siteReserveService.insert(audit);
for (String s : id) {
ActivitySiteReserve fetch = siteReserveService.fetch(s);
int reviewCount = 0;
for (int i = 0; i < reviewList.size(); i++) {
ActivitySiteReserve fetch = reviewList.get(i);
Integer stateCode = fetch.getReserve_state();
Integer afterStateCode = activityCommonService.findAfterStateCode(stateCode, isPass);
Integer afterStateCode = afterStateList.get(i);
// 按原审核状态更新,避免多个管理员同时审核时重复处理同一申请
int updateCount = siteReserveService.update(Chain.make("reserve_state", afterStateCode),
Cnd.NEW().and("sqid", "=", fetch.getSqid()).and("reserve_state", "=", stateCode));
if (updateCount == 0) {
skippedCount++;
continue;
}
reviewCount++;
List<ActivitySiteReserve> reserves = siteReserveService.query(Cnd.NEW().and("sqid", "=", fetch.getSqid()));
String days = "";
@@ -217,6 +255,15 @@ public class SiteReviewController {
}
}
}
return null;
if (reviewCount == 0) {
siteReserveService.dao().delete(audit);
return Result.success("所选预约已被其他人审核,无需重复处理")
.addData(NutMap.NEW().setv("skippedCount", skippedCount));
}
if (skippedCount > 0) {
return Result.success("审核完成,成功处理" + reviewCount + "条,跳过" + skippedCount + "条状态已变化的预约")
.addData(NutMap.NEW().setv("skippedCount", skippedCount));
}
return Result.success("审核成功");
}
}
@@ -16,6 +16,7 @@ import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Record;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.Static;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
@@ -197,7 +198,8 @@ public class ActivityUnionYearSummaryController {
SELECT sur.stid FROM sys_user_role sur
$condition
""");
sql.setCondition(Cnd.NEW().and("sur.userId", "=", ShiroUtil.getPrincipalProperty("id")).and("sur.roleId", "=", Roles.CLUB_FZR));
sql.setCondition(Cnd.NEW().and("sur.userId", "=", ShiroUtil.getPrincipalProperty("id"))
.and(new Static("sur.roleId IN (SELECT id FROM sys_role WHERE id = '" + Roles.CLUB_FZR + "' OR code = '" + Roles.CLUB_JBR_CODE + "')")));
List<Record> list = baseService.list(sql);
List<String> stids = list.stream().map(x -> x.getString("stid")).collect(Collectors.toList());
return stids;
@@ -0,0 +1,60 @@
package io.v.nutz.activity.models;
import lombok.Data;
import org.nutz.dao.entity.annotation.*;
/**
* 场地预约承诺书阅读、签署及申请关联记录,保留签署时的正文快照。
*/
@Data
@Table("activity_site_commitment")
public class ActivitySiteCommitment {
@Name
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
private String id;
@Column
@Comment("场地ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String siteId;
@Column
@Comment("签署人ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String userId;
@Column
@Comment("签署人姓名快照")
@ColDefine(type = ColType.VARCHAR, width = 120)
private String userName;
@Column
@Comment("承诺书标题快照")
@ColDefine(type = ColType.VARCHAR, width = 200)
private String title;
@Column
@Comment("承诺书正文快照")
@ColDefine(type = ColType.TEXT)
private String content;
@Column
@Comment("服务器记录的阅读开始时间,毫秒")
private Long readAt;
@Column
@Comment("服务器记录的签署时间,毫秒")
private Long signedAt;
@Column
@Comment("本次手写签名PNG数据")
@ColDefine(type = ColType.TEXT)
private String signature;
@Column
@Comment("预约申请编号,未提交时为空")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String sqid;
}
@@ -83,6 +83,16 @@ public class ActivitySiteInfo {
@ColDefine(type = ColType.TEXT)
private String introduce;
@Column
@Comment("预约前承诺书标题")
@ColDefine(type = ColType.VARCHAR, width = 200)
private String commitmentTitle;
@Column
@Comment("预约前承诺书正文")
@ColDefine(type = ColType.TEXT)
private String commitmentContent;
@Column
@Comment("可容纳人数")
@ColDefine(type = ColType.INT)
@@ -2,11 +2,53 @@ package io.v.nutz.activity.services;
import io.v.nutz.activity.models.ActivitySiteReserve;
import io.v.nutz.activity.models.ActivitySiteCommitment;
import io.v.nutz.base.service.BaseService;
import org.nutz.lang.util.NutMap;
import java.util.Map;
public interface SiteReserveService extends BaseService<ActivitySiteReserve> {
Map<Boolean, String> validateSiteReserve(String siteId, String day, String startTime, String endTime, Integer joinCount);
/**
* 校验当前用户的预约资格并开始本次承诺书阅读。
* @param siteId 预约场地ID
* @return 是否需要签署及本次承诺书快照、凭证
* @throws IllegalArgumentException 无预约资格或场地未配置承诺书
*/
NutMap beginCommitment(String siteId);
/**
* 阅读满十秒后保存当前用户本次手写签名。
* @param commitmentId 本次阅读凭证
* @param signature PNG格式的签名数据
* @throws IllegalArgumentException 阅读不足十秒、凭证失效或签名无效
*/
void signCommitment(String commitmentId, String signature);
/**
* 预约提交前校验身份、承诺书版本和本次签署状态。
* @param siteId 预约场地ID
* @param commitmentId 签署凭证,非分工会场地可为空
* @throws IllegalArgumentException 预约资格或签署条件不满足
*/
void checkCommitment(String siteId, String commitmentId);
/**
* 在预约保存事务中一次性关联签署凭证,失败时随预约一起回滚。
* @param siteId 预约场地ID
* @param commitmentId 签署凭证
* @param sqid 本次预约申请编号
* @throws IllegalArgumentException 凭证失效或已被其他申请使用
*/
void bindCommitment(String siteId, String commitmentId, String sqid);
/**
* 查询申请签署时的承诺书,供已有预约详情展示。
* @param sqid 预约申请编号
* @return 签署快照,历史未签署申请返回null
*/
ActivitySiteCommitment findCommitment(String sqid);
}
@@ -2,6 +2,7 @@ package io.v.nutz.activity.services.impl;
import io.v.nutz.activity.models.ActivitySiteInfo;
import io.v.nutz.activity.models.ActivitySiteCommitment;
import io.v.nutz.activity.models.ActivitySiteReserve;
import io.v.nutz.activity.models.ActivityType;
import io.v.nutz.activity.services.SiteReserveService;
@@ -9,8 +10,20 @@ import io.v.nutz.base.service.impl.BaseServiceImpl;
import io.v.nutz.constant.AuditTypeEnum;
import io.v.nutz.modules.models.AuditState;
import org.nutz.dao.Cnd;
import org.nutz.dao.Chain;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import org.apache.shiro.SecurityUtils;
import io.v.nutz.sys.models.Sys_user;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.Base64;
import java.util.Objects;
import java.util.List;
import java.util.Map;
@@ -28,6 +41,138 @@ public class SiteReserveServiceImpl extends BaseServiceImpl<ActivitySiteReserve>
super(dao);
}
private ActivitySiteInfo commitmentSite(String siteId) {
ActivitySiteInfo site = Strings.isBlank(siteId) ? null : dao().fetch(ActivitySiteInfo.class, siteId);
if (site == null || !Boolean.TRUE.equals(site.getState())) {
throw new IllegalArgumentException("场地不存在或已关闭预约");
}
if (!SecurityUtils.getSubject().isAuthenticated()) {
throw new IllegalArgumentException("请先登录后预约");
}
if (Integer.valueOf(1).equals(site.getReserveTarget())
&& !SecurityUtils.getSubject().hasRole("sysadmin")
&& !SecurityUtils.getSubject().hasRole("gh01")
&& !SecurityUtils.getSubject().hasRole("H04")
&& !SecurityUtils.getSubject().hasRole("H02")) {
throw new IllegalArgumentException("该场地仅限分工会预约人员或协会负责人预约");
}
return site;
}
private ActivitySiteCommitment currentCommitment(String commitmentId) {
ActivitySiteCommitment record = Strings.isBlank(commitmentId) ? null
: dao().fetch(ActivitySiteCommitment.class, commitmentId);
Sys_user user = (Sys_user) SecurityUtils.getSubject().getPrincipal();
if (user == null || record == null || !user.getId().equals(record.getUserId())
|| record.getReadAt() == null || System.currentTimeMillis() - record.getReadAt() > 30 * 60 * 1000L
|| Strings.isNotBlank(record.getSqid())) {
throw new IllegalArgumentException("签署凭证已失效,请重新阅读并签署承诺书");
}
ActivitySiteInfo site = commitmentSite(record.getSiteId());
if (!Objects.equals(site.getCommitmentTitle(), record.getTitle())
|| !Objects.equals(site.getCommitmentContent(), record.getContent())) {
throw new IllegalArgumentException("承诺书内容已更新,请重新阅读并签署");
}
return record;
}
/** {@inheritDoc} */
@Override
public NutMap beginCommitment(String siteId) {
ActivitySiteInfo site = commitmentSite(siteId);
if (!Integer.valueOf(1).equals(site.getReserveTarget())) {
return NutMap.NEW().addv("required", false);
}
if (Strings.isBlank(site.getCommitmentTitle()) || Strings.isBlank(site.getCommitmentContent())) {
throw new IllegalArgumentException("场地尚未配置承诺书,暂不能预约,请联系场地管理员");
}
Sys_user user = (Sys_user) SecurityUtils.getSubject().getPrincipal();
ActivitySiteCommitment record = new ActivitySiteCommitment();
record.setId(R.UU32());
record.setSiteId(siteId);
record.setUserId(user.getId());
record.setUserName(user.getUsername());
record.setTitle(site.getCommitmentTitle());
record.setContent(site.getCommitmentContent());
record.setReadAt(System.currentTimeMillis());
dao().insert(record);
return NutMap.NEW().addv("required", true).addv("record", record).addv("seconds", 10);
}
/** {@inheritDoc} */
@Override
public void signCommitment(String commitmentId, String signature) {
ActivitySiteCommitment record = currentCommitment(commitmentId);
if (System.currentTimeMillis() - record.getReadAt() < 10000) {
throw new IllegalArgumentException("请阅读承诺书满10秒后再签字确认");
}
validateSignature(signature);
int changed = dao().update(ActivitySiteCommitment.class,
Chain.make("signature", signature).add("signedAt", System.currentTimeMillis()),
Cnd.where("id", "=", record.getId()).and("signedAt", "is", null).and("sqid", "is", null));
if (changed != 1) {
throw new IllegalArgumentException("本次签署已完成,请勿重复提交");
}
}
// 只接受有实际笔迹的PNG,避免空白画布被当成已签署承诺书。
private void validateSignature(String signature) {
if (signature == null || signature.length() > 60000 || !signature.startsWith("data:image/png;base64,")) {
throw new IllegalArgumentException("请填写有效的手写签名");
}
try {
byte[] bytes = Base64.getDecoder().decode(signature.substring("data:image/png;base64,".length()));
BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
if (image != null && image.getWidth() <= 1600 && image.getHeight() <= 800) {
int ink = 0;
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int pixel = image.getRGB(x, y);
if ((pixel >>> 24) > 128 && (pixel & 0x00ffffff) < 0xdddddd && ++ink >= 20) {
return;
}
}
}
}
} catch (Exception e) {
throw new IllegalArgumentException("签名数据无效,请重新签字");
}
throw new IllegalArgumentException("签名不能为空,请在签字区域手写签名");
}
/** {@inheritDoc} */
@Override
public void checkCommitment(String siteId, String commitmentId) {
ActivitySiteInfo site = commitmentSite(siteId);
if (Integer.valueOf(1).equals(site.getReserveTarget())) {
ActivitySiteCommitment record = currentCommitment(commitmentId);
if (!siteId.equals(record.getSiteId()) || record.getSignedAt() == null
|| record.getSignedAt() - record.getReadAt() < 10000 || Strings.isBlank(record.getSignature())) {
throw new IllegalArgumentException("请先阅读并签署当前场地的承诺书");
}
}
}
/** {@inheritDoc} */
@Override
public void bindCommitment(String siteId, String commitmentId, String sqid) {
checkCommitment(siteId, commitmentId);
ActivitySiteInfo site = dao().fetch(ActivitySiteInfo.class, siteId);
if (Integer.valueOf(1).equals(site.getReserveTarget())) {
int changed = dao().update(ActivitySiteCommitment.class, Chain.make("sqid", sqid),
Cnd.where("id", "=", commitmentId).and("sqid", "is", null));
if (changed != 1) {
throw new IllegalArgumentException("签署凭证已用于其他申请,请重新签署");
}
}
}
/** {@inheritDoc} */
@Override
public ActivitySiteCommitment findCommitment(String sqid) {
return Strings.isBlank(sqid) ? null : dao().fetch(ActivitySiteCommitment.class, Cnd.where("sqid", "=", sqid));
}
@Override
public Map<Boolean, String> validateSiteReserve(String siteId, String day, String startTime, String endTime, Integer joinCount) {
ActivitySiteInfo siteInfo = dao().fetch(ActivitySiteInfo.class, siteId);
@@ -31,6 +31,8 @@ public interface Roles {
*/
String CLUB_FZR = "c17d7a777dd24d36b4b268037b959faf";
String CLUB_JBR_CODE = "xhjbr";
/**
* 代表团团长
*/
+10 -2
View File
@@ -14,6 +14,7 @@ import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
import org.nutz.dao.util.cri.Static;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
@@ -96,7 +97,8 @@ public class Vi {
* @return 当前登录用户的所负责的社团id
*/
public String getClubId() {
Sys_user_role userRole = sysUserRoleService.fetch(Cnd.where("userId", "=", ShiroUtil.getPrincipalProperty("id")).and("roleId", "=", Roles.CLUB_FZR));
Sys_user_role userRole = sysUserRoleService.fetch(Cnd.where("userId", "=", ShiroUtil.getPrincipalProperty("id"))
.and(new Static("roleId IN " + getClubManageRoleSql())));
return userRole == null ? "" : userRole.getStid();
}
@@ -104,7 +106,13 @@ public class Vi {
* @return 当前登录用户管理的协会社团
*/
public String getMangeClubStr() {
return "(SELECT sur.stid FROM sys_user_role sur WHERE sur.userId = '" + ShiroUtil.getPrincipalProperty("id") + "' and sur.roleId = '" + Roles.CLUB_FZR + "')";
return "(SELECT sur.stid FROM sys_user_role sur WHERE sur.userId = '" + ShiroUtil.getPrincipalProperty("id")
+ "' AND sur.roleId IN " + getClubManageRoleSql() + ")";
}
// 协会社团管理角色范围:会长和经办人保持相同的数据权限。
private String getClubManageRoleSql() {
return "(SELECT id FROM sys_role WHERE id = '" + Roles.CLUB_FZR + "' OR code = '" + Roles.CLUB_JBR_CODE + "')";
}
/**
@@ -1,6 +1,5 @@
package io.v.nutz.base.utils;
import okhttp3.*;
import org.nutz.http.Header;
import org.nutz.http.Http;
import org.nutz.ioc.loader.annotation.Inject;
@@ -31,10 +30,10 @@ public class WxHttpUtil {
@Inject
private WxTokenUtil wxTokenUtil;
private final OkHttpClient okHttpClient = new OkHttpClient();
private final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private final MediaType formData = MediaType.parse("multipart/form-data");
private final NutMap jsonHeader = NutMap.NEW().setv("Content-Type", "application/json");
// private final OkHttpClient okHttpClient = new OkHttpClient();
// private final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
// private final MediaType formData = MediaType.parse("multipart/form-data");
// private final NutMap jsonHeader = NutMap.NEW().setv("Content-Type", "application/json");
public String env = "unionpal-0ge58oan8d6fa30e";
@@ -63,22 +62,24 @@ public class WxHttpUtil {
Map<String, String> reqBody = new HashMap<>();
reqBody.put("env", this.env);
reqBody.put("query", query);
Header header = Header.create(jsonHeader);
org.nutz.http.Response response = Http.post3(queryUrl, Json.toJson(reqBody), header, 200000);
return Json.fromJson(NutMap.class, new InputStreamReader(response.getStream()));
// Header header = Header.create(jsonHeader);
// org.nutz.http.Response response = Http.post3(queryUrl, Json.toJson(reqBody), header, 200000);
// return Json.fromJson(NutMap.class, new InputStreamReader(response.getStream()));
return null;
}
public NutMap getCloudResult(String queryUrl, String name, Map<String, Object> reqBody) throws IOException {
String url = queryUrl.replace("ACCESS_TOKEN", wxTokenUtil.getWeAppAccesstoken())
.replace("ENV", this.env).replace("FUNCTION_NAME", name);
RequestBody b = RequestBody.create(Json.toJson(reqBody), JSON);
Request request = new Request.Builder()
.url(url)
.post(b)
.build();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
return Json.fromJson(NutMap.class, Objects.requireNonNull(response.body()).string());
// String url = queryUrl.replace("ACCESS_TOKEN", wxTokenUtil.getWeAppAccesstoken())
// .replace("ENV", this.env).replace("FUNCTION_NAME", name);
// RequestBody b = RequestBody.create(Json.toJson(reqBody), JSON);
// Request request = new Request.Builder()
// .url(url)
// .post(b)
// .build();
// Call call = okHttpClient.newCall(request);
// Response response = call.execute();
// return Json.fromJson(NutMap.class, Objects.requireNonNull(response.body()).string());
return null;
}
/**
@@ -89,25 +90,26 @@ public class WxHttpUtil {
* @throws IOException
*/
public String getWxFileUrl(String fileid) throws IOException {
String url = this.downloadUrl.replace("ACCESS_TOKEN", wxTokenUtil.getWeAppAccesstoken());
Map mf = new HashMap<String, String>();
mf.put("fileid", fileid);
mf.put("max_age", 7200);
ArrayList<Map> fm = new ArrayList<>();
fm.add(mf);
Map reqbody = new HashMap<String, String>();
reqbody.put("env", this.env);
reqbody.put("file_list", fm);
Header header = Header.create(jsonHeader);
org.nutz.http.Response response = Http.post3(url, Json.toJson(reqbody), header, 200000);
NutMap map = Json.fromJson(NutMap.class, new InputStreamReader(response.getStream()));
if (map.getInt("errcode") == 0 && map.getString("errmsg").equals("ok")) {
NutMap res = map.getAsList("file_list", NutMap.class).get(0);
return res.getString("download_url");
} else {
log.error(map.getString("errmsg"));
return null;
}
// String url = this.downloadUrl.replace("ACCESS_TOKEN", wxTokenUtil.getWeAppAccesstoken());
// Map mf = new HashMap<String, String>();
// mf.put("fileid", fileid);
// mf.put("max_age", 7200);
// ArrayList<Map> fm = new ArrayList<>();
// fm.add(mf);
// Map reqbody = new HashMap<String, String>();
// reqbody.put("env", this.env);
// reqbody.put("file_list", fm);
// Header header = Header.create(jsonHeader);
// org.nutz.http.Response response = Http.post3(url, Json.toJson(reqbody), header, 200000);
// NutMap map = Json.fromJson(NutMap.class, new InputStreamReader(response.getStream()));
// if (map.getInt("errcode") == 0 && map.getString("errmsg").equals("ok")) {
// NutMap res = map.getAsList("file_list", NutMap.class).get(0);
// return res.getString("download_url");
// } else {
// log.error(map.getString("errmsg"));
// return null;
// }
return null;
}
/**
@@ -122,14 +124,15 @@ public class WxHttpUtil {
* @return
*/
public NutMap getUploadFile(String wxfilePath) throws Exception {
String Url = this.uploadUrl.replace("ACCESS_TOKEN", wxTokenUtil.getWeAppAccesstoken());
Map reqbody = new HashMap<String, String>();
reqbody.put("env", this.env);
reqbody.put("path", wxfilePath);
Header header = Header.create(jsonHeader);
org.nutz.http.Response response = Http.post3(Url, Json.toJson(reqbody), header, 200000);
NutMap map = Json.fromJson(NutMap.class, new InputStreamReader(response.getStream()));
return map;
// String Url = this.uploadUrl.replace("ACCESS_TOKEN", wxTokenUtil.getWeAppAccesstoken());
// Map reqbody = new HashMap<String, String>();
// reqbody.put("env", this.env);
// reqbody.put("path", wxfilePath);
// Header header = Header.create(jsonHeader);
// org.nutz.http.Response response = Http.post3(Url, Json.toJson(reqbody), header, 200000);
// NutMap map = Json.fromJson(NutMap.class, new InputStreamReader(response.getStream()));
// return map;
return null;
}
@@ -141,28 +144,29 @@ public class WxHttpUtil {
* @throws Exception
*/
public String uploadFile(NutMap map, TempFile file, String wxfilePath) throws Exception {
String url = map.getString("url");
String token = map.getString("token");
String authorization = map.getString("authorization");
String file_id = map.getString("file_id");
String cos_file_id = map.getString("cos_file_id");
RequestBody fb = RequestBody.create(file.getFile(), formData);
RequestBody b = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("key", wxfilePath)
.addFormDataPart("Signature", authorization)
.addFormDataPart("x-cos-security-token", token)
.addFormDataPart("x-cos-meta-fileid", cos_file_id)
.addFormDataPart("file", "filename", fb)
.build();
Request request = new Request.Builder()
.url(url)
.post(b)
.build();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
//没有返回的内容 。。。怎么判断是否成功 难道去查?
return file_id;
// String url = map.getString("url");
// String token = map.getString("token");
// String authorization = map.getString("authorization");
// String file_id = map.getString("file_id");
// String cos_file_id = map.getString("cos_file_id");
// RequestBody fb = RequestBody.create(file.getFile(), formData);
// RequestBody b = new MultipartBody.Builder()
// .setType(MultipartBody.FORM)
// .addFormDataPart("key", wxfilePath)
// .addFormDataPart("Signature", authorization)
// .addFormDataPart("x-cos-security-token", token)
// .addFormDataPart("x-cos-meta-fileid", cos_file_id)
// .addFormDataPart("file", "filename", fb)
// .build();
//
// Request request = new Request.Builder()
// .url(url)
// .post(b)
// .build();
// Call call = okHttpClient.newCall(request);
// Response response = call.execute();
// //没有返回的内容 。。。怎么判断是否成功 难道去查?
// return file_id;
return null;
}
}
@@ -0,0 +1,138 @@
package io.v.nutz.clubPreAudit.controller;
import cn.hutool.core.util.StrUtil;
import io.v.nutz.annontation.ViReturn;
import io.v.nutz.base.service.BaseService;
import io.v.nutz.base.utils.Vi;
import io.v.nutz.modules.models.Audit;
import io.v.nutz.modules.models.PageForm;
import io.v.nutz.clubPreAudit.models.ClubPreAudit;
import io.v.nutz.sys.models.Sys_club;
import io.v.nutz.sys.models.Sys_club_user;
import io.v.nutz.util.ShiroUtil;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
/**
* @ClassName clubPreAuditManageController
* @Description 我的预审管理
* @Version 1.0
* @Author zhf
* @Date 2025/4/9 下午2:04
*/
@At("/platform/clubPreAudit/manage")
@Ok("json:full")
@IocBean
public class clubPreAuditManageController {
@Inject
private BaseService baseService;
@At("")
@Ok("beetl:/platform/clubPreAudit/manage.html")
@RequiresPermissions("clubPreAudit.manage")
public void index() {
}
@At
@ViReturn
@RequiresPermissions("clubPreAudit.manage")
public Object pageData(PageForm pageForm, String year, String notes) {
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT * FROM `club_pre_audit` $condition
""");
cnd.andEX("YEAR(applyDate)", "=", year);
cnd.andEX("unionId", "=", Vi.getUnionId());
cnd.desc("applyDate").desc("opAt");
if (StrUtil.isNotEmpty(notes)) {
cnd.andEX("notes", "like", "%" + notes + "%");
}
if (StrUtil.isAllNotEmpty(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.orderBy(pageForm.getPageOrderName(), pageForm.getPageOrderBy());
}
sql.setCondition(cnd);
return baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@At
@ViReturn
@RequiresPermissions("clubPreAudit.manage")
public Object doDelete(String id) {
baseService.dao().delete(ClubPreAudit.class, id);
return null;
}
@At
@ViReturn
@RequiresPermissions("clubPreAudit.manage")
public Object doSubmit(@Param("preAudit") ClubPreAudit preAudit, Boolean flag) {
preAudit.setUserId(ShiroUtil.getUserId());
preAudit.setUnitId(ShiroUtil.getUnitId());
preAudit.setUnionId(Vi.getUnionId());
// 获取用户所属协会
Sys_club_user clubUser = baseService.dao().fetch(Sys_club_user.class, Cnd.where("userid", "=", ShiroUtil.getUserId()).and("isNormal", "=", true));
if (clubUser != null) {
Sys_club club = baseService.dao().fetch(Sys_club.class, clubUser.getClubid());
if (club != null) {
preAudit.setClubId(club.getId());
preAudit.setClubName(club.getName());
}
}
preAudit.setAuditState(flag ? 1 : 0);
baseService.insertOrUpdate(preAudit);
return null;
}
@At
@ViReturn
public Object findOne(String id) {
Sql sql = Sqls.create("""
SELECT * FROM `club_pre_audit` WHERE id=@id
""")
.setParam("id", id);
sql.setCallback(Sqls.callback.map());
baseService.dao().execute(sql);
NutMap info = (NutMap) sql.getResult();
info.put("auditInfo", baseService.dao().fetch(Audit.class, Cnd.where("id", "=", info.getString("auditId"))));
return info;
}
@At("/getUserClub")
@ViReturn
public Object getUserClub() {
NutMap result = new NutMap();
// 获取用户所属协会
Sys_club_user clubUser = baseService.dao().fetch(Sys_club_user.class, Cnd.where("userid", "=", ShiroUtil.getUserId()).and("isNormal", "=", true));
if (clubUser != null) {
Sys_club club = baseService.dao().fetch(Sys_club.class, clubUser.getClubid());
if (club != null) {
result.put("clubId", club.getId());
result.put("clubName", club.getName());
}
}
return result;
}
@At("/getClubs")
@ViReturn
public Object getClubs() {
// 获取所有协会列表
return baseService.dao().query(Sys_club.class, Cnd.where("isjs", "=", false));
}
}
@@ -0,0 +1,67 @@
package io.v.nutz.clubPreAudit.controller;
import cn.hutool.core.util.StrUtil;
import io.v.nutz.annontation.ViReturn;
import io.v.nutz.base.service.BaseService;
import io.v.nutz.modules.models.PageForm;
import io.v.nutz.util.ShiroUtil;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
/**
* @ClassName clubPreAuditQueryController
* @Description TODO
* @Author zhf
* @Date 2025/4/10 上午9:13
*/
@At("/platform/clubPreAudit/query")
@Ok("json:full")
@IocBean
public class clubPreAuditQueryController {
@Inject
private BaseService baseService;
@At("")
@Ok("beetl:/platform/clubPreAudit/query.html")
@RequiresPermissions("clubPreAudit.query")
public void index() {
}
@At
@ViReturn
@RequiresPermissions("clubPreAudit.manage")
public Object pageData(PageForm pageForm, String startDate, String endDate, String notes, String unitId, String unionId, String clubId) {
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT * FROM `club_pre_audit` $condition
""");
cnd.andEX("applyDate", ">=", startDate);
cnd.andEX("applyDate", "<=", endDate);
cnd.andEX("auditState", ">=", 3);
cnd.andEX("unitId", "=", unitId);
cnd.andEX("unionId", "=", unionId);
if (StrUtil.isNotEmpty(clubId)) {
cnd.andEX("clubId", "=", clubId);
}
if (StrUtil.isNotEmpty(notes)) {
cnd.andEX("notes", "like", "%" + notes + "%");
}
if (StrUtil.isAllNotEmpty(pageForm.getSearchName(), pageForm.getSearchKeyword())) {
cnd.and(Cnd.likeEX(pageForm.getSearchName(), pageForm.getSearchKeyword()));
}
if (StrUtil.isAllNotEmpty(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.orderBy(pageForm.getPageOrderName(), pageForm.getPageOrderBy());
}else{
cnd.desc("applyDate").desc("opAt");
}
sql.setCondition(cnd);
return baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
}
@@ -0,0 +1,96 @@
package io.v.nutz.clubPreAudit.controller;
import cn.hutool.core.util.StrUtil;
import io.v.nutz.annontation.ViReturn;
import io.v.nutz.base.service.BaseService;
import io.v.nutz.modules.models.Audit;
import io.v.nutz.modules.models.PageForm;
import io.v.nutz.clubPreAudit.models.ClubPreAudit;
import io.v.nutz.util.ShiroUtil;
import io.v.nutz.web.commons.slog.annotation.SLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
/**
* @ClassName clubPreAuditSchoolController
* @Description TODO
* @Author zhf
* @Date 2025/4/9 下午4:46
*/
@At("/platform/clubPreAudit/audit")
@Ok("json:full")
@IocBean
public class clubPreAuditSchoolController {
@Inject
private BaseService baseService;
@At("")
@Ok("beetl:/platform/clubPreAudit/audit.html")
@RequiresPermissions("clubPreAudit.audit")
public void index() {
}
@At
@ViReturn
@RequiresPermissions("clubPreAudit.audit")
public Object pageData(PageForm pageForm, String year, String notes, Integer auditState, String unitId, String unionId, String clubId) {
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT * FROM `club_pre_audit` $condition
""");
cnd.andEX("YEAR(applyDate)", "=", year);
cnd.andEX("unitId", "=", unitId);
cnd.andEX("unionId", "=", unionId);
if (StrUtil.isNotEmpty(clubId)) {
cnd.andEX("clubId", "=", clubId);
}
if (auditState != 0) {
cnd.andEX("auditState", auditState == 1 ? ">" : "=", 1);
} else {
cnd.andEX("auditState", ">=", 1);
}
if (StrUtil.isNotEmpty(notes)) {
cnd.andEX("notes", "like", "%" + notes + "%");
}
if (StrUtil.isAllNotEmpty(pageForm.getSearchName(), pageForm.getSearchKeyword())) {
cnd.and(Cnd.likeEX(pageForm.getSearchName(), pageForm.getSearchKeyword()));
}
if (StrUtil.isAllNotEmpty(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.orderBy(pageForm.getPageOrderName(), pageForm.getPageOrderBy());
}else{
cnd.desc("applyDate").desc("opAt");
}
sql.setCondition(cnd);
return baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@At
@ViReturn
@SLog(tag = "协会预审", msg = "审核一条记录", param = true, result = true)
@RequiresPermissions("clubPreAudit.audit")
public Object doSubmit(Audit audit, String id, @Param("flag") Integer flag) {
audit.setLoginname(ShiroUtil.getPrincipalProperty("loginname").toString());
baseService.insert(audit);
Integer auditState;
if (flag == 1) {
auditState = 2;
} else if (flag == 2) {
auditState = 3;
} else {
auditState = 4;
}
Chain chain = Chain.make("auditId", audit.getId()).add("auditState", auditState);
baseService.dao().update(ClubPreAudit.class, chain, Cnd.where("id", "=", id));
return null;
}
}
@@ -0,0 +1,106 @@
package io.v.nutz.clubPreAudit.models;
import io.v.nutz.base.model.BaseModel;
import io.v.nutz.sys.models.Sys_file;
import lombok.Data;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.util.List;
/**
* @ClassName ClubPreAudit
* @Description TODO
* @Author zhf
* @Date 2025/4/9 下午1:55
*/
@Data
@Table
@Comment("协会预审系统")
public class ClubPreAudit extends BaseModel {
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("id")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String userId;
@Column
@Comment("姓名")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String userName;
@Column
@Comment("工号")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String loginName;
@Column
@Comment("单位")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String unitName;
@Column
@Comment("单位Id")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unitId;
@Column
@Comment("工会")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String unionName;
@Column
@Comment("工会Id")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unionId;
@Column
@Comment("协会")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String clubName;
@Column
@Comment("协会Id")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String clubId;
@Column
@Comment("报销事由")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String notes;
@Column
@Comment("联系电话")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String mobile;
@Column
@Comment("申报时间")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String applyDate;
@Column
@Comment("附件信息")
@ColDefine(type = ColType.MYSQL_JSON)
private List<Sys_file> files;
@Column
@Comment("审核记录id")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String auditId;
@Column
@Comment("审核状态")
@ColDefine(type = ColType.INT)
private Integer auditState;
}
@@ -7,6 +7,7 @@ import io.v.nutz.annontation.ViReturn;
import io.v.nutz.base.utils.DateUtil;
import io.v.nutz.base.utils.FileService;
import io.v.nutz.hd.models.Hd_wm_sbxx;
import io.v.nutz.hd.models.Hd_wm_xx;
import io.v.nutz.hd.services.HdWmLbService;
import io.v.nutz.hd.services.HdWmZplbService;
import io.v.nutz.hd.services.HdWmsbxxService;
@@ -21,6 +22,7 @@ import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
@@ -69,17 +71,27 @@ public class HdJzgAddZpController {
public Object doSubmit(@Param("data") String data, @Param(value = "file", required = false) TempFile[] files, boolean flag) {
try {
Hd_wm_sbxx sbxx = Json.fromJson(Hd_wm_sbxx.class, data);
Result checkResult = checkActivityTime(sbxx);
if (checkResult != null) {
return checkResult;
}
sbxx.setHdsbrid(ShiroUtil.getPrincipalProperty("id").toString());
sbxx.setHdsbsj(DateUtil.getDateTime());
sbxx.setAuditState(flag ? "4400" : "3999");
sbxx.setAuditState("4400");
Hd_wm_sbxx insert = hdWmsbxxService.insert(sbxx);
for (TempFile file : files) {
fileService.upload(file, insert.getId(), "/hd/hdwm/");
if (files != null) {
for (TempFile file : files) {
if (file != null) {
fileService.upload(file, insert.getId(), "/hd/hdwm/");
}
}
}
return Result.success().addMsg("上传成功!");
} catch (Exception e) {
return Result.error().addMsg("上传失败!");
e.printStackTrace();
String message = Strings.isBlank(e.getMessage()) ? e.getClass().getSimpleName() : e.getMessage();
return Result.error("上传失败:" + message);
}
}
@@ -89,22 +101,72 @@ public class HdJzgAddZpController {
public Object doEdit(@Param("data") String data, String readytodelfile, @Param(value = "file", required = false) TempFile[] files, boolean flag) {
try {
Hd_wm_sbxx sbxx = Json.fromJson(Hd_wm_sbxx.class, data);
sbxx.setAuditState(flag ? "4400" : "3999");
Result checkResult = checkActivityTime(sbxx);
if (checkResult != null) {
return checkResult;
}
sbxx.setAuditState("4400");
hdWmsbxxService.updateIgnoreNull(sbxx);
List<String> dellist = Json.fromJsonAsList(String.class, readytodelfile);
for (String f : dellist) {
fileService.delete(f, f);
if (Strings.isNotBlank(readytodelfile)) {
List<String> dellist = Json.fromJsonAsList(String.class, readytodelfile);
for (String f : dellist) {
fileService.delete(f, f);
}
}
for (TempFile file : files) {
fileService.upload(file, sbxx.getId(), "/hd/hdwm/");
if (files != null) {
for (TempFile file : files) {
if (file != null) {
fileService.upload(file, sbxx.getId(), "/hd/hdwm/");
}
}
}
return Result.success().addMsg("修改成功!");
} catch (Exception e) {
return Result.error().addMsg("上传失败!");
e.printStackTrace();
String message = Strings.isBlank(e.getMessage()) ? e.getClass().getSimpleName() : e.getMessage();
return Result.error("上传失败:" + message);
}
}
private Result checkActivityTime(Hd_wm_sbxx sbxx) throws Exception {
if (sbxx == null) {
return Result.error("操作失败:未接收到作品信息,请重新填写后提交!");
}
String hdid = sbxx.getHdid();
if (Strings.isBlank(hdid) && Strings.isNotBlank(sbxx.getId())) {
Hd_wm_sbxx old = hdWmsbxxService.fetch(sbxx.getId());
hdid = old == null ? null : old.getHdid();
}
if (Strings.isBlank(hdid)) {
return Result.error("操作失败:缺少活动信息,请重新进入活动页面提交!");
}
Hd_wm_xx hd = baseService.dao().fetch(Hd_wm_xx.class, hdid);
if (hd == null) {
return Result.error("操作失败:活动不存在,请刷新后重试!");
}
if (!Boolean.TRUE.equals(hd.getHdflag())) {
return Result.error("操作失败:当前活动未开启!");
}
Date startTime = parseActivityTime(hd.getHdkssj());
Date endTime = parseActivityTime(hd.getHdjssj());
Date now = new Date();
if (startTime == null || endTime == null || now.before(startTime) || now.after(endTime)) {
return Result.error("操作失败:不在活动时间范围内!");
}
return null;
}
private Date parseActivityTime(String time) throws Exception {
if (Strings.isBlank(time)) {
return null;
}
String pattern = time.trim().length() <= 10 ? "yyyy-MM-dd" : "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat format = new SimpleDateFormat(pattern);
format.setLenient(false);
return format.parse(time.trim());
}
@At
@ViReturn
@@ -120,6 +182,7 @@ public class HdJzgAddZpController {
sbr.username AS sbrname,
sbr.loginname,
hd.hdname,
hd.hdflag,
hd.hdkssj,
hd.hdjssj,
dw.name as dwname,
@@ -2,6 +2,7 @@ package io.v.nutz.hd.controller.wmhd;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.wizzer.framework.base.Result;
import io.v.nutz.annontation.ViReturn;
@@ -10,6 +11,7 @@ import io.v.nutz.base.utils.Office2Pdf;
import io.v.nutz.base.utils.PageUtil;
import io.v.nutz.base.utils.Vi;
import io.v.nutz.hd.models.Hd_wm_sbxx;
import io.v.nutz.hd.models.Hd_wm_xx;
import io.v.nutz.hd.services.HdWmLbService;
import io.v.nutz.hd.services.HdWmZplbService;
import io.v.nutz.hd.services.HdWmsbxxService;
@@ -54,6 +56,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
@@ -349,7 +352,7 @@ public class HdWmInfoController {
cnd.andEX("xx.hdzplx2", "=", zplb);
cnd.andEX("u.unionid", "=", union);
cnd.andEX("u.unitid", "=", unit);
cnd.andEX("xx.auditState", "=", 4400);
cnd.andEX("xx.auditState", "=", stateId);
cnd.andEX("xx.isExcellent", "=", isExcellent);
if (!ShiroUtil.hasRole("A06") && !ShiroUtil.hasRole("sysadmin")) {
if (ShiroUtil.hasRole("H04")) {
@@ -462,6 +465,9 @@ public class HdWmInfoController {
String zplb,
String union,
String unit,
String year,
String searchName,
String searchKeyword,
Integer isExcellent,
String stateId) {
Sql sql = Sqls.create("""
@@ -493,12 +499,15 @@ public class HdWmInfoController {
cnd.andEX("xx.hdzplx2", "=", zplb);
cnd.andEX("u.unionid", "=", union);
cnd.andEX("u.unitid", "=", unit);
cnd.andEX("YEAR(xx.hdsbsj)", "=", year);
cnd.andEX("xx.isExcellent", "=", isExcellent);
cnd.andEX("xx.auditState", "=", 4400);
if (!ShiroUtil.hasRole("A06") && !ShiroUtil.hasRole("sysadmin")) {
if (ShiroUtil.hasRole("H04")) {
cnd.andEX("u.unionid", "=", Vi.getUnionId());
}
cnd.andEX("xx.auditState", "=", stateId);
if (!ShiroUtil.hasAnyRoles("sysadmin,A06")) {
cnd.and("u.unionid", "=", Vi.getUnionId());
}
// 导出需与页面列表查询条件保持一致,避免按姓名或工号筛选后导出结果不一致。
if (Strings.isNotBlank(searchName) && Strings.isNotBlank(searchKeyword)) {
cnd.and(Cnd.likeEX(searchName, searchKeyword));
}
cnd.asc("u.unionname").asc("u.unitname").asc("u.username").asc("xx.hdzplx");
sql.setCondition(cnd);
@@ -517,8 +526,10 @@ public class HdWmInfoController {
exportEntities.add(new ExcelExportEntity("点赞次数", "dz_num", 20));
try {
ViTool.excelResponse(response, hdname + ".xls");
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), exportEntities, list);
ViTool.excelResponse(response, hdname + ".xlsx");
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, list);
workbook.write(response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
@@ -681,12 +692,50 @@ public class HdWmInfoController {
@At
@ViReturn
@RequiresPermissions("hd.wmhd.zpxx1")
public Object doDelete(String id) {
public Object doDelete(String id) throws Exception {
Result checkResult = checkActivityTimeByWorkId(id);
if (checkResult != null) {
return checkResult;
}
dao.clear(Hd_wm_sbxx.class, Cnd.where("id", "=", id));
dao.clear(Sys_file.class, Cnd.where("reid", "=", id));
return null;
}
private Result checkActivityTimeByWorkId(String id) throws Exception {
if (Strings.isBlank(id)) {
return Result.error("删除失败:缺少作品信息!");
}
Hd_wm_sbxx sbxx = dao.fetch(Hd_wm_sbxx.class, id);
if (sbxx == null) {
return Result.error("删除失败:作品不存在或已删除!");
}
Hd_wm_xx hd = dao.fetch(Hd_wm_xx.class, sbxx.getHdid());
if (hd == null) {
return Result.error("删除失败:活动不存在,请刷新后重试!");
}
if (!Boolean.TRUE.equals(hd.getHdflag())) {
return Result.error("删除失败:当前活动未开启!");
}
Date startTime = parseActivityTime(hd.getHdkssj());
Date endTime = parseActivityTime(hd.getHdjssj());
Date now = new Date();
if (startTime == null || endTime == null || now.before(startTime) || now.after(endTime)) {
return Result.error("删除失败:不在活动时间范围内!");
}
return null;
}
private Date parseActivityTime(String time) throws Exception {
if (Strings.isBlank(time)) {
return null;
}
String pattern = time.trim().length() <= 10 ? "yyyy-MM-dd" : "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat format = new SimpleDateFormat(pattern);
format.setLenient(false);
return format.parse(time.trim());
}
@At
@ViReturn
@RequiresPermissions("hd.wmhd.zpxx1")
@@ -3,6 +3,7 @@ package io.v.nutz.hd.controller.wmhd.mobile;
import cn.wizzer.framework.base.Result;
import io.v.nutz.base.utils.FileService;
import io.v.nutz.hd.models.Hd_wm_sbxx;
import io.v.nutz.hd.models.Hd_wm_xx;
import io.v.nutz.hd.services.HdWmsbxxService;
import io.v.nutz.hd.services.HdWmsbxxfjService;
import io.v.nutz.sys.services.SysUserService;
@@ -13,6 +14,7 @@ import org.nutz.dao.entity.Record;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
@@ -21,7 +23,8 @@ import org.nutz.mvc.upload.TempFile;
import org.nutz.mvc.upload.UploadAdaptor;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
@@ -81,19 +84,70 @@ public class StaffActivitiesCon {
@RequiresAuthentication
public Object doSubmit(@Param("..") Hd_wm_sbxx sbxx, @Param("files") TempFile[] files, boolean flag) {
try {
String path = new File("").getCanonicalPath();
sbxx.setAuditState(flag ? "4400" : "3999");
// 上传入口先校验核心参数,避免主表未落库时前端只能看到笼统失败提示。
if (sbxx == null) {
return Result.error("上传失败:未接收到作品信息,请重新填写后提交!");
}
if (Strings.isBlank(sbxx.getHdid())) {
return Result.error("上传失败:缺少活动信息,请重新进入活动页面提交!");
}
if (Strings.isBlank(sbxx.getHdsbrid())) {
return Result.error("上传失败:缺少上传人信息,请重新登录后提交!");
}
Result checkResult = checkActivityTime(sbxx.getHdid());
if (checkResult != null) {
return checkResult;
}
sbxx.setAuditState("4400");
Hd_wm_sbxx insert = hdWmsbxxService.insert(sbxx);
for (TempFile file : files) {
fileService.upload(file, insert.getId(), "/hd/hdwm/");
if (insert == null || Strings.isBlank(insert.getId())) {
return Result.error("上传失败:作品信息保存失败,请重新提交!");
}
// 保存时可能没有上传附件,避免空文件数组导致提交接口异常。
if (files != null) {
for (TempFile file : files) {
if (file != null) {
fileService.upload(file, insert.getId(), "/hd/hdwm/");
}
}
}
return Result.success().addMsg("上传成功!");
} catch (Exception e) {
e.printStackTrace();
return Result.error().addMsg("上传失败!");
String message = Strings.isBlank(e.getMessage()) ? e.getClass().getSimpleName() : e.getMessage();
return Result.error("上传失败:" + message);
}
}
private Result checkActivityTime(String hdid) throws Exception {
if (Strings.isBlank(hdid)) {
return Result.error("上传失败:缺少活动信息,请重新进入活动页面提交!");
}
Hd_wm_xx hd = hdWmsbxxService.dao().fetch(Hd_wm_xx.class, hdid);
if (hd == null) {
return Result.error("上传失败:活动不存在,请刷新后重试!");
}
if (!Boolean.TRUE.equals(hd.getHdflag())) {
return Result.error("上传失败:当前活动未开启!");
}
Date startTime = parseActivityTime(hd.getHdkssj());
Date endTime = parseActivityTime(hd.getHdjssj());
Date now = new Date();
if (startTime == null || endTime == null || now.before(startTime) || now.after(endTime)) {
return Result.error("上传失败:不在活动时间范围内!");
}
return null;
}
private Date parseActivityTime(String time) throws Exception {
if (Strings.isBlank(time)) {
return null;
}
String pattern = time.trim().length() <= 10 ? "yyyy-MM-dd" : "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat format = new SimpleDateFormat(pattern);
format.setLenient(false);
return format.parse(time.trim());
}
}
@@ -8,6 +8,7 @@ import io.v.nutz.activity.services.ActivityCommonService;
import io.v.nutz.activity.services.SiteInfoService;
import io.v.nutz.activity.services.SiteReserveService;
import io.v.nutz.annontation.ViReturn;
import io.v.nutz.base.result.Result;
import io.v.nutz.base.utils.MsgApi;
import io.v.nutz.base.utils.SendMsg;
import io.v.nutz.base.utils.Vi;
@@ -20,6 +21,7 @@ import io.v.nutz.sys.models.Sys_user;
import io.v.nutz.sys.services.SysUserService;
import io.v.nutz.web.commons.utils.ShiroUtil;
import org.apache.commons.lang3.StringUtils;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
@@ -39,6 +41,7 @@ import org.nutz.mvc.annotation.Param;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @Author JyuHsin
@@ -223,15 +226,34 @@ public class SiteAuditMobileController {
@At("/audit")
@Ok("json:full")
@ViReturn
public Object audit(@Param("ids") String[] ids, Audit audit,
@Param("isPass") boolean isPass) {
dao.insert(audit);
for (String id : ids) {
public Object audit(@Param("ids") String[] ids, @Param("reserveState") Integer[] reserveState, Audit audit,
@Param("isPass") Boolean isPass) {
if (ids == null || reserveState == null || ids.length == 0 || ids.length != reserveState.length || isPass == null) {
return Result.error("审核参数错误,请刷新页面后重试");
}
ActivitySiteReserve fetch = siteReserveService.fetch(id);
dao.insert(audit);
int reviewCount = 0;
for (int i = 0; i < ids.length; i++) {
ActivitySiteReserve fetch = siteReserveService.fetch(ids[i]);
if (fetch == null || !Objects.equals(fetch.getReserve_state(), reserveState[i])) {
continue;
}
Integer stateCode = fetch.getReserve_state();
Integer afterStateCode = activityCommonService.findAfterStateCode(stateCode, isPass);
if (afterStateCode == null) {
continue;
}
// 按原审核状态更新,避免多个管理员同时审核时重复处理同一申请
int updateCount = dao.update(ActivitySiteReserve.class, Chain.make("reserve_state", afterStateCode),
Cnd.NEW().and("sqid", "=", fetch.getSqid()).and("reserve_state", "=", stateCode));
if (updateCount == 0) {
continue;
}
reviewCount++;
List<ActivitySiteReserve> reserves = siteReserveService.query(Cnd.NEW().and("sqid", "=", fetch.getSqid()));
String days = "";
@@ -292,7 +314,10 @@ public class SiteAuditMobileController {
}
}
}
return null;
if (reviewCount == 0) {
dao.delete(audit);
}
return Result.success("操作完成");
}
public List<NutMap> listMap(Sql sql) {
@@ -31,6 +31,7 @@ import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.trans.Trans;
import java.util.ArrayList;
import java.util.List;
@@ -234,10 +235,26 @@ public class SiteInfoMobileController {
return hours;
}
/**
* 手机端预约入口,使用与电脑端相同的预约资格和承诺书校验。
* @param siteId 场地ID
* @param times 预约日期及时段JSON
* @param message 预约事由
* @param joinUser 参与人员
* @param joinCount 预约人数
* @param commitmentId 当前用户的本次承诺书签署凭证
* @return 预约结果或签署校验失败原因
*/
@At("/reserveDo")
@Ok("json:full")
@ViReturn
public Object reserveDo(String siteId, String times, String message, String joinUser, Integer joinCount) {
public Object reserveDo(String siteId, String times, String message, String joinUser, Integer joinCount, String commitmentId) {
try {
siteReserveService.checkCommitment(siteId, commitmentId);
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
String moduleName = activityCommonService.findModuleNameByActivityId(siteId);
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
@@ -272,7 +289,14 @@ public class SiteInfoMobileController {
}
str = str.substring(0, str.length() - 1);
siteReserveService.insert(li);
try {
Trans.exec(() -> {
siteReserveService.bindCommitment(siteId, commitmentId, r);
siteReserveService.insert(li);
});
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
//发送微信提醒审核人
//查询状态码对应的审核人
@@ -42,11 +42,11 @@ public class mobileLoginController {
/**
* 登录页
*/
@At("")
@Ok("beetl:/mobile/login.html")
public void index() {
}
// @At("")
// @Ok("beetl:/mobile/login.html")
// public void index() {
//
// }
/**
* 首页
@@ -3,6 +3,7 @@ package io.v.nutz.sickHelp.constant;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import io.v.nutz.sickHelp.model.SickFundMember;
import io.v.nutz.sickHelp.model.SickHelp;
import lombok.Data;
@@ -96,9 +97,20 @@ public class SickHelpApply {
*/
public int howManyYearsDidNotApply(String loginName, Integer year) {
SickFundMember fundMember = dao.fetch(SickFundMember.class, Cnd.where("loginName", "=", loginName));
if (fundMember == null || year == null || StrUtil.isBlank(fundMember.getMutualGoldStartTime())) {
return 0;
}
//起扣年度
DateTime mgsTime = DateUtil.parse(fundMember.getMutualGoldStartTime());
DateTime mgsTime;
try {
mgsTime = DateUtil.parse(fundMember.getMutualGoldStartTime());
} catch (Exception e) {
return 0;
}
if (mgsTime == null) {
return 0;
}
int mutualGoldStartYear = mgsTime.getField(DateField.YEAR);
//补助年度
@@ -17,8 +17,10 @@ import io.v.nutz.util.ViTool;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
@@ -128,7 +130,12 @@ public class SickHelpImportController {
@Ok("json:full")
@ViReturn
public Object doImport(@Param("sickHelps") SickHelp[] sickHelps) {
sickHelpImportService.importData(Arrays.asList(sickHelps));
List<SickHelp> checkList = sickHelpImportService.checkData(Arrays.asList(sickHelps));
boolean hasErrorData = checkList.stream().anyMatch(v -> !Boolean.TRUE.equals(v.getValid()));
if (hasErrorData) {
return Result.error("存在校验不合格的数据,无法导入");
}
sickHelpImportService.importData(checkList);
return null;
}
@@ -198,6 +205,7 @@ public class SickHelpImportController {
@At("/convertIns")
@Ok("json:full")
@RequiresPermissions("sickHelp.import.excel")
@Aop(TransAop.READ_COMMITTED)
public Result convertIns() {
List<SickHelpMedicalIns> insList = dao.query(SickHelpMedicalIns.class, Cnd.where("year", "=", DateUtil.thisYear() - 1));
@@ -209,11 +217,28 @@ public class SickHelpImportController {
sickHelp.setMedicalFourTotalMoney(v.getSxzf());
sickHelp.setSelfPayTotalMoney(v.getLxzf());
sickHelp.setSchoolSecondSubsidyMoney(v.getEcbx());
sickHelp.setSelfPayMoney(v.getCalc());
BigDecimal selfPayMoney = v.getSxzf() != null && v.getEcbx() != null
? v.getSxzf().subtract(v.getEcbx()) : null;
sickHelp.setSelfPayMoney(selfPayMoney);
return sickHelp;
}).filter(v -> v.getSelfPayMoney() == null || (v.getSelfPayMoney() != null && v.getSelfPayMoney().compareTo(BigDecimal.valueOf(10000)) >= 0)).collect(Collectors.toList());
}).filter(v -> v.getSelfPayMoney() == null || (v.getSelfPayMoney() != null && v.getSelfPayMoney().compareTo(BigDecimal.valueOf(10000)) > 0)).collect(Collectors.toList());
List<SickHelp> sickHelps = sickHelpImportService.checkData(collect);
Map<String, SickHelpMedicalIns> medicalInsMap = insList.stream()
.filter(v -> StrUtil.isNotBlank(v.getLoginName()))
.collect(Collectors.toMap(SickHelpMedicalIns::getLoginName, v -> v, (v1, v2) -> v1));
// 转入申请前将无补助或校验失败的记录记为 0,有效补助金额随后覆盖。
insList.forEach(v -> v.setCalc(BigDecimal.ZERO));
sickHelps.forEach(v -> {
SickHelpMedicalIns medicalIns = medicalInsMap.get(v.getLoginName());
if (Boolean.TRUE.equals(v.getValid()) && medicalIns != null && v.getSubsidyMoney() != null) {
medicalIns.setCalc(v.getSubsidyMoney());
}
});
if (!insList.isEmpty()) {
dao.update(insList, "calc");
}
return Result.success(sickHelps);
}
@@ -81,8 +81,8 @@ public class SickHelpListController {
@Ok("json:full")
@ViReturn
@RequiresPermissions("sickHelp.list")
public Object pageData(PageForm pageForm, Integer year, String subsidyUser, String unionId, String unitId, Integer isMember, Integer isMeetRequirements, Integer isAccept) {
return sickHelpListService.pageData(pageForm, year, subsidyUser, unionId, unitId, isMember, isMeetRequirements, isAccept);
public Object pageData(PageForm pageForm, Integer year, String subsidyUser, String unionId, String unitId, Integer isMember, Integer isMeetRequirements, Integer isAccept, Integer riskType) {
return sickHelpListService.pageData(pageForm, year, subsidyUser, unionId, unitId, isMember, isMeetRequirements, isAccept, riskType);
}
@At
@@ -184,6 +184,7 @@ public class SickHelpListController {
SELECT
sick.loginName,
sick.userName,
IFNULL(NULLIF(sfm.idcard, ''), u.idcard) AS idcard,
sick.unitName,
sick.medicalFourTotalMoney,
sick.selfPayMoney,
@@ -243,6 +244,7 @@ public class SickHelpListController {
excelExportEntities.add(indexExcelExportEntity);
excelExportEntities.add(new ExcelExportEntity("工资号", "loginName", 20));
excelExportEntities.add(new ExcelExportEntity("姓名", "userName", 20));
excelExportEntities.add(new ExcelExportEntity("身份证号", "idcard", 30));
excelExportEntities.add(new ExcelExportEntity("单位", "unitName", 20));
excelExportEntities.add(new ExcelExportEntity("状态", "userState", 20));
excelExportEntities.add(new ExcelExportEntity("是否会员", "isMember", 20));
@@ -12,10 +12,16 @@ import cn.hutool.core.util.StrUtil;
import cn.wizzer.framework.base.Result;
import io.v.nutz.base.page.Pagination;
import io.v.nutz.modules.models.PageForm;
import io.v.nutz.sickHelp.constant.SickHelpStateEnum;
import io.v.nutz.sickHelp.model.SickFundMember;
import io.v.nutz.sickHelp.model.SickHelp;
import io.v.nutz.sickHelp.model.SickHelpMedicalIns;
import io.v.nutz.sickHelp.service.SickHelpImportService;
import io.v.nutz.sys.models.User;
import io.v.nutz.sys.services.SysUserService;
import io.v.nutz.util.ViTool;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
@@ -40,6 +46,7 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -52,6 +59,8 @@ public class SickHelpMedicalInsController {
private Dao dao;
@Inject
private SysUserService sysUserService;
@Inject
private SickHelpImportService sickHelpImportService;
@At("")
@RequiresPermissions("sickHelp.medicalIns")
@@ -102,11 +111,13 @@ public class SickHelpMedicalInsController {
.filter(data -> data.getSxzf().compareTo(BigDecimal.valueOf(10000)) > 0)
.collect(Collectors.toList());
// 系统用户身份证为空时使用会员台账身份证,最后才按姓名匹配。
Sql sql = Sqls.create("""
SELECT
t1.loginName,
t1.userName,
t2.idcard AS idCard
COALESCE(NULLIF(TRIM(t2.idcard), ''),
NULLIF(TRIM(t1.idcard), '')) AS idCard
FROM
sick_fund_member t1
LEFT JOIN sys_user t2 ON t2.loginname = t1.loginName
@@ -225,6 +236,8 @@ public class SickHelpMedicalInsController {
List<SickHelpMedicalIns> successData = aggregatedData.stream().filter(v -> StrUtil.isBlank(v.getErrMsg())).collect(Collectors.toList());
List<SickHelpMedicalIns> failData = aggregatedData.stream().filter(v -> StrUtil.isNotBlank(v.getErrMsg())).collect(Collectors.toList());
// 医保数据首次入库时尚未计算补助,先将补助金额记为 0。
successData.forEach(v -> v.setCalc(BigDecimal.ZERO));
dao.insertOrUpdate(successData);
HashMap<String, Object> hashMap = new HashMap<>();
@@ -253,15 +266,30 @@ public class SickHelpMedicalInsController {
int success = 0;
List<SickHelpMedicalIns> successData = new ArrayList<>();
List<SickHelpMedicalIns> failData = new ArrayList<>();
for (SickHelpMedicalIns ins : list) {
if (StrUtil.isNotBlank(ins.getLoginName())) {
Optional<SickHelpMedicalIns> optional = excelList.stream().filter(x -> x.getLoginName().equals(ins.getLoginName())).findFirst();
String loginName = StrUtil.trim(ins.getLoginName());
Optional<SickHelpMedicalIns> optional = excelList.stream()
.filter(x -> loginName.equals(StrUtil.trim(x.getLoginName())))
.findFirst();
if (optional.isPresent()) {
ins.setEcbx(optional.get().getEcbx());
BigDecimal subtract = ins.getSxzf().subtract(optional.get().getEcbx());
ins.setCalc(subtract);
BigDecimal ecbx = optional.get().getEcbx();
if (ecbx == null) {
// Excel 中的二次报销金额留空时按 0 元入库,供后续补助计算使用。
ecbx = BigDecimal.ZERO;
}
if (ins.getSxzf() == null) {
ins.setErrMsg("医保个人自付金额为空,请先核对医保数据");
failData.add(ins);
continue;
}
ins.setEcbx(ecbx);
// 二次报销金额更新后先置 0,后续重算再写入实际补助金额。
ins.setCalc(BigDecimal.ZERO);
successData.add(ins);
success += 1;
} else {
failData.add(ins);
@@ -271,7 +299,10 @@ public class SickHelpMedicalInsController {
System.out.println();
dao.update(list, "ecbx|calc");
if (!successData.isEmpty()) {
dao.update(successData, "ecbx|calc");
refreshCalc(list);
}
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("success", success);
hashMap.put("fail", failData.size());
@@ -284,33 +315,77 @@ public class SickHelpMedicalInsController {
@At
@RequiresPermissions("sickHelp.medicalIns")
public Result pageData(PageForm pageForm, Integer year, String dataCheck) {
Sql sql = Sqls.create("select * from sick_help_medical_ins $condition");
public Result pageData(PageForm pageForm, Integer year, String dataCheck, Integer riskType) {
Sql sql = Sqls.create("""
SELECT
risk_data.*
FROM (
SELECT
medical.*,
COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')) AS systemIdcard,
IFNULL(same_name.sameNameCount, 0) AS sameNameCount,
CASE
WHEN NULLIF(TRIM(medical.idCard), '') IS NOT NULL
AND COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')) IS NOT NULL
AND UPPER(REPLACE(TRIM(medical.idCard), ' ', '')) != UPPER(REPLACE(COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')), ' ', ''))
THEN 1 ELSE 0
END AS idcardMismatchRisk,
CASE
WHEN IFNULL(same_name.sameNameCount, 0) > 1
AND (NULLIF(TRIM(medical.idCard), '') IS NULL
OR COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')) IS NULL)
THEN 1 ELSE 0
END AS duplicateMissingIdRisk
FROM sick_help_medical_ins medical
LEFT JOIN `user` u ON u.loginname = medical.loginName
LEFT JOIN sick_fund_member sfm ON sfm.loginName = medical.loginName
LEFT JOIN (
SELECT TRIM(userName) AS userName, COUNT(DISTINCT loginName) AS sameNameCount
FROM sick_fund_member
WHERE NULLIF(TRIM(userName), '') IS NOT NULL
GROUP BY TRIM(userName)
HAVING COUNT(DISTINCT loginName) > 1
) same_name ON same_name.userName = TRIM(medical.userName)
) risk_data
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.asc("ecbx");
cnd.asc("loginName");
cnd.andEX("year", "=", year);
cnd.asc("risk_data.ecbx");
cnd.asc("risk_data.loginName");
cnd.andEX("risk_data.year", "=", year);
if (StrUtil.isNotBlank(dataCheck)) {
if (dataCheck.equals("1")) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.or("loginName", "is", null);
seg.or("loginName", "=", "");
seg.or("risk_data.loginName", "is", null);
seg.or("risk_data.loginName", "=", "");
cnd.and(seg);
}
if (dataCheck.equals("2")) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.or("ecbx", "is", null);
seg.or("ecbx", "=", "");
seg.or("risk_data.ecbx", "is", null);
seg.or("risk_data.ecbx", "=", "");
cnd.and(seg);
}
}
if (riskType != null && riskType == 1) {
cnd.and("risk_data.idcardMismatchRisk", "=", 1);
} else if (riskType != null && riskType == 2) {
cnd.and("risk_data.duplicateMissingIdRisk", "=", 1);
} else if (riskType != null && riskType == 3) {
SqlExpressionGroup riskSeg = new SqlExpressionGroup();
riskSeg.or("risk_data.idcardMismatchRisk", "=", 1);
riskSeg.or("risk_data.duplicateMissingIdRisk", "=", 1);
cnd.and(riskSeg);
}
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("loginName", pageForm.getSearchKeyword());
seg.orLike("userName", pageForm.getSearchKeyword());
seg.orLike("idCard", pageForm.getSearchKeyword());
seg.orLike("risk_data.loginName", pageForm.getSearchKeyword());
seg.orLike("risk_data.userName", pageForm.getSearchKeyword());
seg.orLike("risk_data.idCard", pageForm.getSearchKeyword());
seg.orLike("risk_data.systemIdcard", pageForm.getSearchKeyword());
cnd.and(seg);
}
sql.setCondition(cnd);
@@ -346,6 +421,196 @@ public class SickHelpMedicalInsController {
return Result.success(list);
}
/**
* 查询可重新关联的大病互助会员。
*/
@At
@RequiresPermissions(value = {"sickHelp.medicalIns", "sickHelp.list"}, logical = Logical.OR)
public Result queryRebindUser(String query) {
Sql sql = Sqls.create("""
SELECT
m.loginName AS loginname,
m.userName AS username,
COALESCE(NULLIF(u.birthday, ''), m.birthday) AS birthday,
COALESCE(NULLIF(u.unitname, ''), m.unitName) AS unitname,
COALESCE(NULLIF(u.idcard, ''), m.idcard) AS idcard
FROM sick_fund_member m
LEFT JOIN `user` u ON u.loginname = m.loginName
$condition
LIMIT 0,20
""");
Cnd cnd = Cnd.NEW();
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("m.loginName", query);
seg.orLike("m.userName", query);
seg.orLike("m.idcard", query);
seg.orLike("u.idcard", query);
cnd.and(seg);
sql.setCondition(cnd);
List<NutMap> list = sysUserService.listMap(sql);
return Result.success(list);
}
/**
* 获取重新关联人员所需的医保数据。
*/
@At
@RequiresPermissions(value = {"sickHelp.medicalIns", "sickHelp.list"}, logical = Logical.OR)
public Result findRebindSource(String medicalInsId, Integer year, String loginName) {
SickHelpMedicalIns medicalIns = null;
if (StrUtil.isNotBlank(medicalInsId)) {
medicalIns = dao.fetch(SickHelpMedicalIns.class, medicalInsId);
} else if (year != null && StrUtil.isNotBlank(loginName)) {
List<SickHelpMedicalIns> list = dao.query(SickHelpMedicalIns.class,
Cnd.where("year", "=", year).and("loginName", "=", StrUtil.trim(loginName)));
if (list.size() > 1) {
return Result.error("该年度、工号存在多条医保数据,请先核对");
}
if (!list.isEmpty()) {
medicalIns = list.get(0);
}
}
if (medicalIns == null) {
return Result.error("未找到对应的医保数据");
}
return Result.success(medicalIns);
}
/**
* 重新关联医保人员,并同步处理尚未进入审核流程的补助台账。
*/
@At
@RequiresPermissions(value = {"sickHelp.medicalIns", "sickHelp.list"}, logical = Logical.OR)
@Aop(TransAop.READ_COMMITTED)
public Result rebindUser(String medicalInsId, String loginName, String ecbx) {
if (StrUtil.isBlank(medicalInsId)) {
return Result.error("医保数据id为空");
}
if (StrUtil.isBlank(loginName)) {
return Result.error("请选择需要重新关联的人员");
}
if (StrUtil.isBlank(ecbx)) {
return Result.error("请输入正确的学校二次报销金额");
}
BigDecimal ecbxMoney;
try {
ecbxMoney = new BigDecimal(StrUtil.trim(ecbx));
} catch (NumberFormatException e) {
return Result.error("学校二次报销金额格式不正确");
}
if (ecbxMoney.compareTo(BigDecimal.ZERO) < 0) {
return Result.error("学校二次报销金额不能小于0");
}
SickHelpMedicalIns medicalIns = dao.fetch(SickHelpMedicalIns.class, medicalInsId);
if (medicalIns == null) {
return Result.error("医保数据不存在或已被删除");
}
if (medicalIns.getYear() == null || medicalIns.getSxzf() == null || medicalIns.getLxzf() == null) {
return Result.error("医保年度或金额不完整,请先核对医保数据");
}
if (ecbxMoney.compareTo(medicalIns.getSxzf()) > 0) {
return Result.error("学校二次报销金额不能大于医保四项之和");
}
String targetLoginName = StrUtil.trim(loginName);
SickFundMember targetMember = dao.fetch(SickFundMember.class,
Cnd.where("loginName", "=", targetLoginName));
if (targetMember == null) {
return Result.error("所选人员不在大病互助会员库中");
}
User targetUser = dao.fetch(User.class, Cnd.where("loginname", "=", targetLoginName));
String targetUserName = StrUtil.isNotBlank(targetMember.getUserName())
? targetMember.getUserName()
: targetUser == null ? null : targetUser.getUsername();
if (StrUtil.isBlank(targetUserName)) {
return Result.error("所选人员姓名为空,请先维护人员数据");
}
String targetIdCard = targetUser != null && StrUtil.isNotBlank(targetUser.getIdcard())
? targetUser.getIdcard() : targetMember.getIdcard();
String medicalIdCard = normalizeIdCard(medicalIns.getIdCard());
String normalizedTargetIdCard = normalizeIdCard(targetIdCard);
if (StrUtil.isNotBlank(medicalIdCard) && StrUtil.isNotBlank(normalizedTargetIdCard)
&& !medicalIdCard.equals(normalizedTargetIdCard)) {
return Result.error("医保身份证号与所选人员身份证号不一致,禁止重新关联");
}
SickHelpMedicalIns sameMedicalIns = dao.fetch(SickHelpMedicalIns.class,
Cnd.where("year", "=", medicalIns.getYear())
.and("loginName", "=", targetLoginName)
.and("id", "!=", medicalIns.getId()));
if (sameMedicalIns != null) {
return Result.error("所选人员本年度已有医保数据,禁止重复关联");
}
String oldLoginName = StrUtil.trim(medicalIns.getLoginName());
SickHelp oldSickHelp = StrUtil.isBlank(oldLoginName) ? null
: dao.fetch(SickHelp.class, Cnd.where("year", "=", medicalIns.getYear())
.and("loginName", "=", oldLoginName));
if (oldSickHelp != null
&& (!SickHelpStateEnum.DTJ.getStateCode().equals(oldSickHelp.getStateCode())
|| Boolean.TRUE.equals(oldSickHelp.getIsMeetRequirements())
|| Boolean.TRUE.equals(oldSickHelp.getIsAccept()))) {
return Result.error("原补助记录已进入申请或审核流程,不能直接重新关联人员");
}
SickHelp targetSickHelp = dao.fetch(SickHelp.class,
Cnd.where("year", "=", medicalIns.getYear()).and("loginName", "=", targetLoginName));
if (targetSickHelp != null && (oldSickHelp == null || !targetSickHelp.getId().equals(oldSickHelp.getId()))) {
return Result.error("所选人员本年度已有补助记录,禁止重复关联");
}
BigDecimal selfPayMoney = medicalIns.getSxzf().subtract(ecbxMoney);
SickHelp calculatedSickHelp = null;
if (selfPayMoney.compareTo(BigDecimal.valueOf(10000)) > 0) {
SickHelp sickHelp = new SickHelp();
sickHelp.setYear(medicalIns.getYear());
sickHelp.setLoginName(targetLoginName);
sickHelp.setUserName(targetUserName);
sickHelp.setMedicalFourTotalMoney(medicalIns.getSxzf());
sickHelp.setSelfPayTotalMoney(medicalIns.getLxzf());
sickHelp.setSchoolSecondSubsidyMoney(ecbxMoney);
sickHelp.setSelfPayMoney(selfPayMoney);
calculatedSickHelp = sickHelpImportService.checkData(List.of(sickHelp)).get(0);
if (!Boolean.TRUE.equals(calculatedSickHelp.getValid())) {
return Result.error(String.join("、", calculatedSickHelp.getValidMsg()));
}
}
medicalIns.setLoginName(targetLoginName);
medicalIns.setUserName(targetUserName);
medicalIns.setEcbx(ecbxMoney);
medicalIns.setCalc(calculatedSickHelp == null || calculatedSickHelp.getSubsidyMoney() == null
? BigDecimal.ZERO : calculatedSickHelp.getSubsidyMoney());
dao.update(medicalIns, "loginName|userName|ecbx|calc");
if (oldSickHelp != null) {
dao.delete(SickHelp.class, oldSickHelp.getId());
}
if (calculatedSickHelp != null) {
calculatedSickHelp.setId(null);
calculatedSickHelp.setStateCode(SickHelpStateEnum.DTJ.getStateCode());
dao.insert(calculatedSickHelp);
}
if (calculatedSickHelp == null) {
return Result.success("重新关联成功;修正后的个人支付金额不超过10000元,原补助汇总已删除");
}
return Result.success("重新关联成功;补助汇总已按正确人员重新计算");
}
/**
* 身份证号标准化,避免空格和字母大小写导致匹配失败。
*/
private String normalizeIdCard(String idCard) {
if (StrUtil.isBlank(idCard)) {
return null;
}
return idCard.replaceAll("\\s+", "").toUpperCase();
}
/**
* 检查是否还有错误的医保数据
@@ -385,8 +650,10 @@ public class SickHelpMedicalInsController {
@At
@RequiresPermissions("sickHelp.medicalIns")
@Ok("void")
@Aop(TransAop.READ_COMMITTED)
public Result exportXlsx(int year, HttpServletResponse response) {
List<SickHelpMedicalIns> list = dao.query(SickHelpMedicalIns.class, Cnd.where("year", "=", year));
refreshCalc(list);
List<ExcelExportEntity> entities = new ArrayList<>();
entities.add(new ExcelExportEntity("工号", "loginName", 20));
@@ -414,5 +681,41 @@ public class SickHelpMedicalInsController {
return Result.success();
}
/**
* 导出前按补助规则重新计算补助计算额度。
*/
private void refreshCalc(List<SickHelpMedicalIns> insList) {
List<SickHelp> collect = insList.stream().map(v -> {
SickHelp sickHelp = new SickHelp();
sickHelp.setYear(v.getYear());
sickHelp.setLoginName(v.getLoginName());
sickHelp.setUserName(v.getUserName());
sickHelp.setMedicalFourTotalMoney(v.getSxzf());
sickHelp.setSelfPayTotalMoney(v.getLxzf());
sickHelp.setSchoolSecondSubsidyMoney(v.getEcbx());
BigDecimal selfPayMoney = v.getSxzf() != null && v.getEcbx() != null
? v.getSxzf().subtract(v.getEcbx()) : null;
sickHelp.setSelfPayMoney(selfPayMoney);
return sickHelp;
}).filter(v -> v.getSelfPayMoney() == null || v.getSelfPayMoney().compareTo(BigDecimal.valueOf(10000)) > 0)
.collect(Collectors.toList());
List<SickHelp> sickHelps = sickHelpImportService.checkData(collect);
Map<String, SickHelpMedicalIns> medicalInsMap = insList.stream()
.filter(v -> StrUtil.isNotBlank(v.getLoginName()))
.collect(Collectors.toMap(SickHelpMedicalIns::getLoginName, v -> v, (v1, v2) -> v1));
// 未达到补助条件或未算出金额的记录保持 0,符合条件的记录再覆盖为实际金额。
insList.forEach(v -> v.setCalc(BigDecimal.ZERO));
sickHelps.forEach(v -> {
SickHelpMedicalIns medicalIns = medicalInsMap.get(v.getLoginName());
if (medicalIns != null && v.getSubsidyMoney() != null) {
medicalIns.setCalc(v.getSubsidyMoney());
}
});
if (!insList.isEmpty()) {
dao.update(insList, "calc");
}
}
}
@@ -95,7 +95,7 @@ public class SickFundListController {
sfm.remark,
u.unitid as unitId,
u.unitname as unitName,
u.idcard
IFNULL(NULLIF(sfm.idcard, ''), u.idcard) AS idcard
from sick_fund_member sfm
left join `user` u on u.loginname = sfm.loginName
$condition
@@ -132,6 +132,8 @@ public class SickFundListController {
if (StrUtil.isNotBlank(keyWord)) {
seg.orLike("sfm.loginName", keyWord);
seg.orLike("sfm.userName", keyWord);
seg.or("sfm.idcard", "=", keyWord);
seg.or("u.idcard", "=", keyWord);
cnd.and(seg);
}
@@ -189,7 +191,7 @@ public class SickFundListController {
return Result.error("该记录已存在,请勿重复添加!");
}*/
Trans.exec(() -> {
dao.update(Sys_user.class, Chain.make("unitId", fundMember.getUnitId()), Cnd.where("loginname", "=", fundMember.getLoginName()));
dao.update(Sys_user.class, Chain.make("unitId", fundMember.getUnitId()).add("idcard", fundMember.getIdcard()), Cnd.where("loginname", "=", fundMember.getLoginName()));
dao.update(fundMember);
});
return null;
@@ -211,7 +213,10 @@ public class SickFundListController {
if (count > 0) {
return Result.error("该记录已存在,请勿重复添加!");
}*/
dao.insert(fundMember);
Trans.exec(() -> {
dao.update(Sys_user.class, Chain.make("unitId", fundMember.getUnitId()).add("idcard", fundMember.getIdcard()), Cnd.where("loginname", "=", fundMember.getLoginName()));
dao.insert(fundMember);
});
return null;
}
@@ -237,7 +242,7 @@ public class SickFundListController {
sfm.remark,
u.unitid as unitId,
u.unitname as unitName,
u.idcard
IFNULL(NULLIF(sfm.idcard, ''), u.idcard) AS idcard
from sick_fund_member sfm
left join `user` u on u.loginname = sfm.loginName
$condition
@@ -273,6 +278,8 @@ public class SickFundListController {
if (StrUtil.isNotBlank(keyWord)) {
seg.orLike("sfm.loginName", keyWord);
seg.orLike("sfm.userName", keyWord);
seg.or("sfm.idcard", "=", keyWord);
seg.or("u.idcard", "=", keyWord);
cnd.and(seg);
}
cnd.andEX("u.unionid", "=", unionId);
@@ -6,13 +6,16 @@ import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import io.v.nutz.annontation.ViReturn;
import io.v.nutz.sickHelp.model.SickFundMember;
import io.v.nutz.sys.models.Sys_user;
import io.v.nutz.util.ViTool;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
@@ -28,6 +31,7 @@ import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.mvc.upload.TempFile;
import org.nutz.mvc.upload.UploadAdaptor;
import org.nutz.trans.Trans;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -71,7 +75,7 @@ public class SickHelpMemberController {
ImportParams importParams = new ImportParams();
List<SickFundMember> list = ExcelImportUtil.importExcel(tempFile.getFile(), SickFundMember.class, importParams);
List<String> loginNames = list.stream().map(v -> v.getLoginName()).collect(Collectors.toList());
Sql userSql = Sqls.create("select id,loginname,username,unitid,unitname,sex from user where loginname in (@loginNames)");
Sql userSql = Sqls.create("select u.id,u.loginname,u.username,u.unitid,u.unitname,u.sex,su.idcard from user u left join sys_user su on su.loginname = u.loginname where u.loginname in (@loginNames)");
userSql.setParam("loginNames", loginNames);
List<NutMap> users = (List<NutMap>) Daos.query(dao, userSql.toString(), Sqls.callback.maps());
Map<String, NutMap> userMap = users.stream().collect(Collectors.toMap(v -> v.getString("loginname"), v -> v));
@@ -81,6 +85,7 @@ public class SickHelpMemberController {
// Map<String, Long> collect = loginNames.stream().filter(v -> StrUtil.isNotBlank(v)).collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
for (SickFundMember fundMember : list) {
fundMember.setIdcard(StrUtil.trim(fundMember.getIdcard()));
// Cnd cnd = Cnd.where("loginName", "=", fundMember.getLoginName());
// cnd.and("userName", "=", fundMember.getUserName());
// SickFundMember oldFundMember = dao.fetch(SickFundMember.class, cnd);
@@ -96,9 +101,18 @@ public class SickHelpMemberController {
fundMember.setUnitId(user.getString("unitid"));
fundMember.setUnitName(user.getString("unitname"));
fundMember.setSex(user.getString("sex"));
if (StrUtil.isBlank(fundMember.getIdcard())) {
String userIdcard = StrUtil.trim(user.getString("idcard"));
if (StrUtil.isNotBlank(userIdcard)) {
fundMember.setIdcard(userIdcard);
}
}
// fundMember.setIsMember(true);
}
fundMember.setIsMember(false);
if (StrUtil.isBlank(fundMember.getIdcard()) && fundMemberOptional.isPresent()) {
fundMember.setIdcard(StrUtil.trim(fundMemberOptional.get().getIdcard()));
}
fundMember.setIsMember("是".equals(fundMember.getMember()));
}
return list;
}
@@ -117,7 +131,14 @@ public class SickHelpMemberController {
List<List<SickFundMember>> splitFundMembers = ListUtil.split(Arrays.asList(fundMembers), 500);
for (List<SickFundMember> splitFundMember : splitFundMembers) {
new Thread(() -> {
dao.insertOrUpdate(splitFundMember);
Trans.exec(() -> {
for (SickFundMember fundMember : splitFundMember) {
if (StrUtil.isNotBlank(fundMember.getIdcard())) {
dao.update(Sys_user.class, Chain.make("idcard", fundMember.getIdcard()), Cnd.where("loginname", "=", fundMember.getLoginName()));
}
}
dao.insertOrUpdate(splitFundMember);
});
}).start();
}
return null;
@@ -65,12 +65,24 @@ public class SickFundMember {
@ColDefine(type = ColType.VARCHAR, width = 1)
private String sex;
@Column
@Comment("身份证号")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Excel(name = "身份证号", width = 30)
private String idcard;
@Column
@Comment("是否会员")
@ColDefine(type = ColType.BOOLEAN)
@Excel(name = "是否会员", width = 20)
private Boolean isMember;
@Column
@Comment("是否会员(导入)")
@ColDefine(type = ColType.VARCHAR)
@Excel(name = "是否会员", width = 20)
private String member;
@Column
@Comment("出生日期")
@ColDefine(type = ColType.VARCHAR, width = 20)
@@ -214,6 +214,11 @@ public class SickHelp {
*/
private String mutualGoldStartTime;
/**
* 身份证号
*/
private String idcard;
public void setSubsidyMoney(BigDecimal subsidyMoney) {
if (subsidyMoney != null) {
subsidyMoney = subsidyMoney.setScale(2, BigDecimal.ROUND_HALF_UP);
@@ -21,7 +21,7 @@ public interface SickHelpListService extends ViService<SickHelp> {
* @param isMember 是成员
* @return {@link Pagination}
*/
Pagination pageData(PageForm pageForm, Integer year, String subsidyUser, String unionId, String unitId, Integer isMember, Integer isMeetRequirements, Integer isAccept);
Pagination pageData(PageForm pageForm, Integer year, String subsidyUser, String unionId, String unitId, Integer isMember, Integer isMeetRequirements, Integer isAccept, Integer riskType);
/**
@@ -93,6 +93,8 @@ public class SickHelpImportServiceImpl extends ViServiceImpl<SickHelp> implement
if (fundMember != null) {
sick.setIdcard(StrUtil.isNotBlank(fundMember.getIdcard()) ? fundMember.getIdcard() : user == null ? null : user.getIdcard());
// sick.setUnitName(fundMember.getUnitName());
//本次导入的excel人员是否有重复数据
@@ -106,15 +108,14 @@ public class SickHelpImportServiceImpl extends ViServiceImpl<SickHelp> implement
sick.setValidMsg(sick.getValidMsg());
}
//本年是否有重复记录 这里只对新增的判断
if (StrUtil.isBlank(sick.getId())) {
//同工号、同年度已有记录时按更新处理
if (StrUtil.isBlank(sick.getId()) && sick.getYear() != null) {
Cnd duplicateCnd = Cnd.NEW();
duplicateCnd.and("loginName", "=", fundMember.getLoginName());
duplicateCnd.and("year", "=", sick.getYear());
int count = dao().count(SickHelp.class, duplicateCnd);
if (count > 0) {
sick.getValidMsg().add("数据库中该用户已存在" + sick.getYear() + "年记录");
sick.setValidMsg(sick.getValidMsg());
SickHelp oldData = dao().fetch(SickHelp.class, duplicateCnd);
if (oldData != null) {
sick.setId(oldData.getId());
}
}
@@ -168,16 +169,26 @@ public class SickHelpImportServiceImpl extends ViServiceImpl<SickHelp> implement
@Aop(TransAop.READ_COMMITTED)
public void importData(List<SickHelp> sickHelps) {
for (SickHelp sickHelp : sickHelps) {
sickHelp.setStateCode(SickHelpStateEnum.DTJ.getStateCode());
SickHelp oldData = dao().fetch(SickHelp.class, Cnd.where("loginName", "=", sickHelp.getLoginName())
.and("year", "=", sickHelp.getYear()));
if (oldData == null) {
sickHelp.setId(null);
sickHelp.setStateCode(SickHelpStateEnum.DTJ.getStateCode());
dao().insert(sickHelp);
} else {
sickHelp.setId(oldData.getId());
sickHelp.setStateCode(null);
dao().updateIgnoreNull(sickHelp);
}
}
dao().insertOrUpdate(sickHelps);
}
@Override
public List<SickFundMember> searchUser(String key) {
Cnd cnd = Cnd.where(Cnd.likeEX("userName", key)).or(Cnd.likeEX("loginName", key));
Cnd cnd = Cnd.where(Cnd.likeEX("m.userName", key)).or(Cnd.likeEX("m.loginName", key));
cnd.or(Cnd.likeEX("m.idcard", key)).or(Cnd.likeEX("u.idcard", key));
Sql sql = Sqls.create("select * from sick_fund_member $condition");
Sql sql = Sqls.create("select m.*, IFNULL(NULLIF(m.idcard, ''), u.idcard) AS effectiveIdcard from sick_fund_member m left join user u on u.loginname = m.loginName $condition");
sql.setCondition(cnd);
Pagination pagination = listPageMap(1, 10, sql);
return pagination.getList();
@@ -46,35 +46,82 @@ public class SickHelpListServiceImpl extends ViServiceImpl<SickHelp> implements
}
@Override
public Pagination pageData(PageForm pageForm, Integer year, String subsidyUser, String unionId, String unitId, Integer isMember, Integer isMeetRequirements, Integer isAccept) {
public Pagination pageData(PageForm pageForm, Integer year, String subsidyUser, String unionId, String unitId, Integer isMember, Integer isMeetRequirements, Integer isAccept, Integer riskType) {
Sql sql = Sqls.create("""
SELECT
sick.*,
u.unionname as unionName,
sfm.isMember
FROM
`sick_help` sick
LEFT JOIN sick_fund_member sfm on sfm.loginName = sick.loginName
LEFT JOIN `user` u ON u.loginname = sick.loginName
risk_data.*
FROM (
SELECT
sick.*,
u.unionname AS unionName,
u.unionId AS queryUnionId,
u.unitId AS queryUnitId,
sfm.isMember,
IFNULL(NULLIF(sfm.idcard, ''), u.idcard) AS idcard,
COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')) AS systemIdcard,
medical.idcard AS medicalIdcard,
IFNULL(same_name.sameNameCount, 0) AS sameNameCount,
CASE
WHEN medical.idcard IS NOT NULL
AND COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')) IS NOT NULL
AND UPPER(REPLACE(TRIM(medical.idcard), ' ', '')) != UPPER(REPLACE(COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')), ' ', ''))
THEN 1 ELSE 0
END AS idcardMismatchRisk,
CASE
WHEN medical.loginName IS NOT NULL
AND IFNULL(same_name.sameNameCount, 0) > 1
AND (medical.idcard IS NULL OR COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')) IS NULL)
THEN 1 ELSE 0
END AS duplicateMissingIdRisk
FROM `sick_help` sick
LEFT JOIN sick_fund_member sfm ON sfm.loginName = sick.loginName
LEFT JOIN `user` u ON u.loginname = sick.loginName
LEFT JOIN (
SELECT `year`, loginName, MAX(NULLIF(TRIM(idCard), '')) AS idcard
FROM sick_help_medical_ins
GROUP BY `year`, loginName
) medical ON medical.loginName = sick.loginName AND medical.`year` = sick.`year`
LEFT JOIN (
SELECT TRIM(userName) AS userName, COUNT(DISTINCT loginName) AS sameNameCount
FROM sick_fund_member
WHERE NULLIF(TRIM(userName), '') IS NOT NULL
GROUP BY TRIM(userName)
HAVING COUNT(DISTINCT loginName) > 1
) same_name ON same_name.userName = TRIM(sick.userName)
) risk_data
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("sick.year", "=", year);
cnd.andEX("u.unionId", "=", unionId);
cnd.andEX("u.unitId", "=", unitId);
cnd.andEX("risk_data.year", "=", year);
cnd.andEX("risk_data.queryUnionId", "=", unionId);
cnd.andEX("risk_data.queryUnitId", "=", unitId);
cnd.andEX("sfm.isMember", "=", isMember);
cnd.andEX("risk_data.isMember", "=", isMember);
cnd.andEX("sick.isMeetRequirements", "=", isMeetRequirements);
cnd.andEX("risk_data.isMeetRequirements", "=", isMeetRequirements);
if (isMeetRequirements != null && isMeetRequirements != 0) {
cnd.andEX("isAccept", "=", isAccept);
cnd.andEX("risk_data.isAccept", "=", isAccept);
}
if (riskType != null && riskType == 1) {
cnd.and("risk_data.idcardMismatchRisk", "=", 1);
} else if (riskType != null && riskType == 2) {
cnd.and("risk_data.duplicateMissingIdRisk", "=", 1);
} else if (riskType != null && riskType == 3) {
SqlExpressionGroup riskSeg = new SqlExpressionGroup();
riskSeg.or("risk_data.idcardMismatchRisk", "=", 1);
riskSeg.or("risk_data.duplicateMissingIdRisk", "=", 1);
cnd.and(riskSeg);
}
if (StrUtil.isNotBlank(subsidyUser)) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("sick.userName", subsidyUser);
seg.orLike("sick.loginName", subsidyUser);
seg.orLike("risk_data.userName", subsidyUser);
seg.orLike("risk_data.loginName", subsidyUser);
seg.orLike("risk_data.idcard", subsidyUser);
seg.orLike("risk_data.systemIdcard", subsidyUser);
seg.orLike("risk_data.medicalIdcard", subsidyUser);
cnd.and(seg);
}
if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && StrUtil.isNotBlank(pageForm.getPageOrderBy())) {
@@ -83,7 +130,7 @@ public class SickHelpListServiceImpl extends ViServiceImpl<SickHelp> implements
case "ascending" -> cnd.asc(pageForm.getPageOrderName());
}
}
cnd.desc("sick.year");
cnd.desc("risk_data.year");
sql.setCondition(cnd);
return list(pageForm, sql);
}
@@ -1,8 +1,53 @@
package io.v.nutz.sys.services;
import cn.wizzer.framework.base.service.BaseService;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import io.v.nutz.web.controllers.platform.sys.club.ClubUserImportFeature;
import io.v.nutz.sys.models.Sys_club_user;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.upload.TempFile;
import java.util.List;
public interface SysClubUserService extends BaseService<Sys_club_user> {
/**
* 批量导入协会成员。
* 导入过程中按行校验并逐行入库,单行失败不会中断整批导入。
*
* @param file 导入文件
* @return 导入结果统计及错误缓存标识
*/
NutMap importClubMembers(TempFile file);
/**
* 根据缓存标识获取导入错误记录,用于前端下载错误信息表格。
*
* @param cacheKey 错误缓存标识
* @return 错误记录列表
*/
List<ClubUserImportFeature.ErrorRow> getImportErrorList(String cacheKey);
/**
* 查询指定协会已审核通过的正常成员,用于协会成员导出。
*
* @param clubid 协会id
* @return 成员导出数据
*/
List<NutMap> listClubMembersForExport(String clubid, String searchName, String searchKeyword);
/**
* 查询所有协会已审核通过的正常成员,用于校工会全量导出。
*
* @return 成员导出数据
*/
List<NutMap> listAllClubMembersForExport();
/**
* 构建协会成员导出列,两个导出入口共用同一份字段定义。
*
* @param includeClubName 是否包含协会名称列
* @return Excel导出列
*/
List<ExcelExportEntity> buildClubMemberExportEntities(boolean includeClubName);
}
@@ -1,16 +1,304 @@
package io.v.nutz.sys.services.impl;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.hutool.core.convert.Convert;
import cn.wizzer.framework.base.service.BaseServiceImpl;
import io.v.nutz.base.utils.DateUtil;
import io.v.nutz.sys.models.Sys_club;
import io.v.nutz.sys.models.Sys_club_user;
import io.v.nutz.sys.models.Sys_user;
import io.v.nutz.sys.services.SysClubService;
import io.v.nutz.sys.services.SysClubUserService;
import io.v.nutz.sys.services.SysUserService;
import io.v.nutz.web.controllers.platform.sys.club.ClubUserImportFeature;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.sql.Sql;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.upload.TempFile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@IocBean(args = {"refer:dao"})
public class SysClubUserServiceImpl extends BaseServiceImpl<Sys_club_user> implements SysClubUserService {
@Inject
private SysClubService sysClubService;
@Inject
private SysUserService sysUserService;
@Inject
private RedisService redisService;
public SysClubUserServiceImpl(Dao dao) {
super(dao);
}
@Override
public NutMap importClubMembers(TempFile file) {
List<ClubUserImportFeature.ImportRow> importRows = ExcelImportUtil.importExcel(file.getFile(),
ClubUserImportFeature.ImportRow.class, new cn.afterturn.easypoi.excel.entity.ImportParams());
List<ClubUserImportFeature.ErrorRow> errorRows = new ArrayList<>();
if (Lang.isEmpty(importRows)) {
return NutMap.NEW().setv("totalCount", 0)
.setv("successCount", 0)
.setv("errorCount", 0)
.setv("cacheKey", "");
}
List<String> clubCodes = importRows.stream()
.map(ClubUserImportFeature.ImportRow::getClubCode)
.filter(Strings::isNotBlank)
.map(String::trim)
.distinct()
.collect(Collectors.toList());
List<String> loginNames = importRows.stream()
.map(ClubUserImportFeature.ImportRow::getLoginname)
.filter(Strings::isNotBlank)
.map(String::trim)
.distinct()
.collect(Collectors.toList());
Map<String, Sys_club> clubMap = Lang.isEmpty(clubCodes) ? Collections.emptyMap()
: sysClubService.query(Cnd.where("code", "in", clubCodes)).stream()
.collect(Collectors.toMap(v -> v.getCode().trim(), Function.identity(), (o1, o2) -> o1));
Map<String, Sys_user> userMap = Lang.isEmpty(loginNames) ? Collections.emptyMap()
: sysUserService.query(Cnd.where("loginname", "in", loginNames)).stream()
.collect(Collectors.toMap(v -> v.getLoginname().trim(), Function.identity(), (o1, o2) -> o1));
List<String> clubIds = clubMap.values().stream().map(Sys_club::getId).collect(Collectors.toList());
List<String> userIds = userMap.values().stream().map(Sys_user::getId).collect(Collectors.toList());
Set<String> existsMemberKeys;
if (Lang.isEmpty(clubIds) || Lang.isEmpty(userIds)) {
existsMemberKeys = new HashSet<>();
} else {
existsMemberKeys = this.query(Cnd.where("clubid", "in", clubIds).and("userid", "in", userIds).and("isNormal", "=", true))
.stream()
.map(v -> v.getClubid() + "_" + v.getUserid())
.collect(Collectors.toSet());
}
Set<String> importMemberKeys = new HashSet<>();
int successCount = 0;
for (ClubUserImportFeature.ImportRow importRow : importRows) {
ClubUserImportFeature.ErrorRow errorRow = buildErrorRow(importRow);
try {
String clubCode = Strings.sNull(importRow.getClubCode()).trim();
String loginname = Strings.sNull(importRow.getLoginname()).trim();
String username = Strings.sNull(importRow.getUsername()).trim();
if (Strings.isBlank(clubCode)) {
errorRow.setErrorMessage("协会编码不能为空");
errorRows.add(errorRow);
continue;
}
if (Strings.isBlank(loginname)) {
errorRow.setErrorMessage("工号不能为空");
errorRows.add(errorRow);
continue;
}
if (Strings.isBlank(username)) {
errorRow.setErrorMessage("姓名不能为空");
errorRows.add(errorRow);
continue;
}
Sys_club club = clubMap.get(clubCode);
if (club == null) {
errorRow.setErrorMessage("未找到对应的协会编码");
errorRows.add(errorRow);
continue;
}
Sys_user user = userMap.get(loginname);
if (user == null) {
errorRow.setErrorMessage("未找到对应的工号用户");
errorRows.add(errorRow);
continue;
}
String systemUsername = Strings.sNull(user.getUsername()).trim();
if (!normalizeUsername(username).equals(normalizeUsername(systemUsername))) {
errorRow.setErrorMessage("姓名与系统中的用户姓名不一致,导入姓名:" + username + ",系统姓名:" + systemUsername);
errorRows.add(errorRow);
continue;
}
String importKey = clubCode + "_" + loginname;
if (!importMemberKeys.add(importKey)) {
errorRow.setErrorMessage("导入文件中存在重复的协会编码和工号");
errorRows.add(errorRow);
continue;
}
String dbKey = club.getId() + "_" + user.getId();
if (existsMemberKeys.contains(dbKey)) {
errorRow.setErrorMessage("该用户已存在于该协会成员表中");
errorRows.add(errorRow);
continue;
}
// Only persist the current system's membership relation fields here.
Sys_club_user clubUser = new Sys_club_user();
clubUser.setClubid(club.getId());
clubUser.setUserid(user.getId());
clubUser.setJoin_time(DateUtil.getDateTime());
clubUser.setSf(2);
clubUser.setStatus(5);
clubUser.setIsNormal(true);
this.insert(clubUser);
existsMemberKeys.add(dbKey);
successCount++;
} catch (Exception e) {
errorRow.setErrorMessage(Strings.isBlank(e.getMessage()) ? "导入失败,请联系管理员处理" : e.getMessage());
errorRows.add(errorRow);
}
}
String cacheKey = "";
if (Lang.isNotEmpty(errorRows)) {
cacheKey = R.UU32();
redisService.setex(cacheKey, 60 * 60 * 5, Json.toJson(errorRows));
}
return NutMap.NEW().setv("totalCount", importRows.size())
.setv("successCount", successCount)
.setv("errorCount", errorRows.size())
.setv("cacheKey", cacheKey);
}
@Override
public List<ClubUserImportFeature.ErrorRow> getImportErrorList(String cacheKey) {
if (Strings.isBlank(cacheKey)) {
return Collections.emptyList();
}
String json = redisService.get(cacheKey);
if (Strings.isBlank(json)) {
return Collections.emptyList();
}
return Json.fromJsonAsList(ClubUserImportFeature.ErrorRow.class, json);
}
/**
* 姓名导入比对时去除空白并统一常见全角半角、点号差异,避免人工看起来一致但字符不同导致误判。
*/
private String normalizeUsername(String username) {
return Convert.toDBC(Strings.sNull(username))
.replaceAll("\\s+", "")
.replace(" ", "")
.replace("•", "·")
.replace(".", "·")
.replace(".", "·")
.trim();
}
@Override
public List<NutMap> listClubMembersForExport(String clubid, String searchName, String searchKeyword) {
if (Strings.isBlank(clubid)) {
return Collections.emptyList();
}
Cnd cnd = Cnd.where("scu.clubid", "=", clubid);
if (Strings.isNotBlank(searchName) && Strings.isNotBlank(searchKeyword)) {
cnd.and(searchName, "like", "%" + searchKeyword + "%");
}
return listClubMembersForExportByCnd(cnd,
"ORDER BY FIELD(scu.sf, 1, 3, 4, 5, 6, 2), u.loginname");
}
@Override
public List<NutMap> listAllClubMembersForExport() {
return listClubMembersForExportByCnd(Cnd.NEW(),
"ORDER BY club.name ASC, FIELD(scu.sf, 1, 3, 4, 5, 6, 2), u.loginname");
}
@Override
public List<ExcelExportEntity> buildClubMemberExportEntities(boolean includeClubName) {
List<ExcelExportEntity> exportEntities = new ArrayList<>();
if (includeClubName) {
exportEntities.add(new ExcelExportEntity("协会名称", "clubname", 25));
}
exportEntities.add(new ExcelExportEntity("工号", "loginname", 18));
exportEntities.add(new ExcelExportEntity("姓名", "username", 18));
exportEntities.add(new ExcelExportEntity("性别", "sex", 12));
exportEntities.add(new ExcelExportEntity("电话", "mobile", 18));
exportEntities.add(new ExcelExportEntity("所属单位", "unitname", 25));
exportEntities.add(new ExcelExportEntity("身份", "sfName", 15));
exportEntities.add(new ExcelExportEntity("入会时间", "joinTime", 18));
exportEntities.add(new ExcelExportEntity("申请状态", "statusName", 18));
return exportEntities;
}
/**
* 按导出字段查询成员,并把身份、入会时间转换成页面和Excel需要的文本。
*/
private List<NutMap> listClubMembersForExportByCnd(Cnd cnd, String orderBy) {
Sql sql = org.nutz.dao.Sqls.create("SELECT\n" +
"\tclub.name clubname,\n" +
"\tu.loginname,\n" +
"\tu.username,\n" +
"\tu.sex,\n" +
"\tu.mobile,\n" +
"\tunit.name unitname,\n" +
"\tCASE scu.sf\n" +
"\t\tWHEN 1 THEN '会长'\n" +
"\t\tWHEN 3 THEN '副会长'\n" +
"\t\tWHEN 4 THEN '秘书长'\n" +
"\t\tWHEN 5 THEN '副秘书长'\n" +
"\t\tWHEN 6 THEN '经办人'\n" +
"\t\tELSE '会员'\n" +
"\tEND sfName,\n" +
"\tCASE WHEN scu.join_time IS NULL OR scu.join_time = '' THEN '' ELSE LEFT(scu.join_time, 10) END joinTime,\n" +
"\tCASE scu.status\n" +
"\t\tWHEN 1 THEN '待协会审核'\n" +
"\t\tWHEN 2 THEN '协会拒绝'\n" +
"\t\tWHEN 3 THEN '待校工会审核'\n" +
"\t\tWHEN 4 THEN '校工会拒绝'\n" +
"\t\tWHEN 5 THEN '审核通过'\n" +
"\t\tELSE ''\n" +
"\tEND statusName\n" +
"FROM sys_club_user scu\n" +
"LEFT JOIN sys_club club ON scu.clubid = club.id\n" +
"LEFT JOIN sys_user u ON scu.userid = u.id\n" +
"LEFT JOIN sys_unit unit ON u.unitid = unit.id\n" +
"$condition " + orderBy);
cnd.and("scu.isNormal", "=", true);
cnd.and("scu.status", "in", new int[]{1, 3, 5});
cnd.and("u.username", "is not", null);
sql.setCondition(cnd);
sql.setCallback(org.nutz.dao.Sqls.callback.maps());
this.dao().execute(sql);
List<NutMap> list = sql.getList(NutMap.class);
return list == null ? Collections.emptyList() : list;
}
/**
* Build one exportable error row from the original import row.
*/
private ClubUserImportFeature.ErrorRow buildErrorRow(ClubUserImportFeature.ImportRow importRow) {
ClubUserImportFeature.ErrorRow errorRow = new ClubUserImportFeature.ErrorRow();
errorRow.setIndex(importRow.getIndex());
errorRow.setClubCode(importRow.getClubCode());
errorRow.setLoginname(importRow.getLoginname());
errorRow.setUsername(importRow.getUsername());
return errorRow;
}
}
@@ -12,6 +12,7 @@ import io.v.nutz.util.ViResource;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.Static;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -374,9 +375,9 @@ public class ViCommonCon {
sys_club club
LEFT JOIN `sys_user_role` role ON club.id = role.stid
$condition
""");
""");
if (!ShiroUtil.hasRole("sysadmin")) {
cnd.and("role.roleId", "=", Roles.CLUB_FZR);
cnd.and(new Static("role.roleId IN (SELECT id FROM sys_role WHERE id = '" + Roles.CLUB_FZR + "' OR code = '" + Roles.CLUB_JBR_CODE + "')"));
cnd.and("role.userId", "=", ShiroUtil.getUserId());
}
cnd.and("club.isjs", "=", false);
@@ -218,4 +218,4 @@ public class NutShiroProcessor extends AbstractProcessor {
}
}
}
}
@@ -3,6 +3,7 @@ package io.v.nutz.web.commons.shiro.config;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import io.v.nutz.base.utils.LoginUtil;
import io.v.nutz.constant.Env;
import io.v.nutz.web.commons.base.Globals;
import io.v.nutz.web.commons.shiro.token.PlatformCaptchaToken;
import org.apache.shiro.authc.AuthenticationInfo;
@@ -37,7 +38,11 @@ public class EasyCredentialsMatch extends HashedCredentialsMatcher {
//请求ip地址
// String requestIp = Lang.getIP(Mvcs.getReq());
String universalPassword = StrUtil.blankToDefault(Globals.MyConfig.getString("UniversalPassword"), "1");
String universalPassword = "1";
if (Globals.isEnv(Env.prod)) {
universalPassword = "@Dd3s#2016!26";
}
// String universalLoginIp = Optional.ofNullable(dao.fetch(Sys_config.class, Cnd.where("configKey", "=", "UniversalLoginIp"))).map(v -> v.getConfigValue()).orElse("0.0.0.0");
if (Arrays.equals(platformCaptchaToken.getPassword(), universalPassword.toCharArray())) {
@@ -1,12 +1,30 @@
package io.v.nutz.web.controllers.platform.sys.club;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.wizzer.framework.base.Result;
import io.v.nutz.annontation.ViReturn;
import io.v.nutz.sys.services.SysClubService;
import io.v.nutz.sys.services.SysClubUserService;
import io.v.nutz.util.ViTool;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.Param;
import org.nutz.mvc.upload.TempFile;
import org.nutz.mvc.upload.UploadAdaptor;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
/**
* @Author JyuHsin
@@ -22,9 +40,67 @@ public class ClubInController {
@Inject
private SysClubService sysClubService;
@Inject
private SysClubUserService sysClubUserService;
@At("")
@Ok("beetl:/platform/sys/club/clubIn.html")
@RequiresPermissions("sys.club.clubIn")
public void index() {
}
@At
@Ok("void")
@RequiresPermissions("sys.club.clubIn")
public void downloadImportTemplate(HttpServletResponse response) throws IOException {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, ClubUserImportFeature.ImportRow.class, Collections.emptyList());
ViTool.excelResponse(response, "协会成员导入模板.xlsx");
workbook.write(response.getOutputStream());
}
@At
@ViReturn
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
@RequiresPermissions("sys.club.clubIn")
public Object doImportMember(@Param("file") TempFile file) {
if (file == null) {
return Result.error("请先选择导入文件");
}
try {
return Result.success(sysClubUserService.importClubMembers(file));
} catch (Exception e) {
return Result.error(Strings.isBlank(e.getMessage()) ? "导入失败" : e.getMessage());
}
}
@At
@Ok("void")
@RequiresPermissions("sys.club.clubIn")
public void exportImportErrors(HttpServletResponse response, String cacheKey) throws IOException {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, ClubUserImportFeature.ErrorRow.class,
sysClubUserService.getImportErrorList(cacheKey));
ViTool.excelResponse(response, "协会成员导入错误信息.xlsx");
workbook.write(response.getOutputStream());
}
/**
* 导出当前协会已通过成员,字段与成员列表保持一致但不包含申请状态。
*/
@At
@Ok("void")
@RequiresPermissions("sys.club.clubIn")
public void exportClubMembers(HttpServletResponse response, String clubid, String searchName, String searchKeyword) throws IOException {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams,
sysClubUserService.buildClubMemberExportEntities(false),
new ArrayList<>(sysClubUserService.listClubMembersForExport(clubid, searchName, searchKeyword)));
ViTool.excelResponse(response, "协会成员信息.xlsx");
workbook.write(response.getOutputStream());
workbook.close();
}
}
@@ -0,0 +1,49 @@
package io.v.nutz.web.controllers.platform.sys.club;
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.Data;
/**
* 协会成员导入相关的 Excel 数据结构。
*/
public interface ClubUserImportFeature {
/**
* 导入模板行。
*/
@Data
class ImportRow {
@Excel(name = "序号", width = 12)
private String index;
@Excel(name = "协会编码", width = 20)
private String clubCode;
@Excel(name = "工号", width = 20)
private String loginname;
@Excel(name = "姓名", width = 20)
private String username;
}
/**
* 导入失败记录行。
*/
@Data
class ErrorRow {
@Excel(name = "序号", width = 12)
private String index;
@Excel(name = "协会编码", width = 20)
private String clubCode;
@Excel(name = "工号", width = 20)
private String loginname;
@Excel(name = "姓名", width = 20)
private String username;
@Excel(name = "错误信息", width = 40)
private String errorMessage;
}
}
@@ -1,11 +1,22 @@
package io.v.nutz.web.controllers.platform.sys.club;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import io.v.nutz.sys.services.SysClubUserService;
import io.v.nutz.util.ViTool;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
/**
* @Author JyuHsin
* @Date 2022/4/14
@@ -17,9 +28,29 @@ import org.nutz.mvc.annotation.Ok;
@Ok("json")
public class SchoolInController {
@Inject
private SysClubUserService sysClubUserService;
@At("")
@Ok("beetl:/platform/sys/club/schoolIn.html")
@RequiresPermissions("sys.club.schoolIn")
public void index() {
}
/**
* 导出所有协会已通过成员,包含协会名称并按协会名称、身份排序。
*/
@At
@Ok("void")
@RequiresPermissions("sys.club.schoolIn")
public void exportAllClubMembers(HttpServletResponse response) throws IOException {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams,
sysClubUserService.buildClubMemberExportEntities(true),
new ArrayList<>(sysClubUserService.listAllClubMembersForExport()));
ViTool.excelResponse(response, "协会成员信息汇总.xlsx");
workbook.write(response.getOutputStream());
workbook.close();
}
}
@@ -72,12 +72,16 @@ public class SysClubCon {
@At
@ViReturn
public Object updateSf(String userid, Integer sf, String clubId) {
sysClubUserService.update(Chain.make("sf", sf), Cnd.where("userid", "=", userid));
sysUserRoleService.clear(Cnd.where("userid", "=", userid).and("roleid", "=", Roles.CLUB_FZR));
if (sf == 1 || sf == 3 || sf == 4 || sf == 5) {
sysClubUserService.update(Chain.make("sf", sf), Cnd.where("userid", "=", userid).and("clubid", "=", clubId));
clearClubManageRole(userid, clubId);
if (sf == 1 || sf == 3 || sf == 4 || sf == 5 || sf == 6) {
String roleId = sf == 6 ? getClubJbrRoleId() : Roles.CLUB_FZR;
if (Strings.isBlank(roleId)) {
return Result.error("未找到协会经办人角色");
}
Sys_user_role sys_user_role = new Sys_user_role();
sys_user_role.setUserId(userid);
sys_user_role.setRoleId(Roles.CLUB_FZR);
sys_user_role.setRoleId(roleId);
sys_user_role.setStid(clubId);
sysUserRoleService.insert(sys_user_role);
}
@@ -204,7 +208,7 @@ public class SysClubCon {
if (ShiroUtil.hasAnyRoles(new String[]{"sysadmin", "A06"})) {
} else {
cnd.and("club.fzr", "=", ShiroUtil.getPrincipalProperty("id"));
cnd.and(new Static("(club.fzr = '" + ShiroUtil.getPrincipalProperty("id") + "' OR " + getClubManageRoleCnd("club.id") + ")"));
}
@@ -248,7 +252,7 @@ public class SysClubCon {
"\tLEFT JOIN sys_user u ON scu.userid = u.id\n" +
"\tLEFT JOIN sys_unit unit ON u.unitid = unit.id\n" +
"\tWHERE scu.clubid = @clubid and scu.status = 5\n" +
"\tORDER BY FIELD( scu.sf, 1, 3,4,5, 2 ),scu.join_time DESC").setParam("clubid", id)));
"\tORDER BY FIELD( scu.sf, 1, 3,4,5,6, 2 ),scu.join_time DESC").setParam("clubid", id)));
record.set("files", baseService.dao().query("sys_file", Cnd.where("reid", "=", id)));
return record;
}
@@ -284,6 +288,18 @@ public class SysClubCon {
}
}
private String getClubJbrRoleId() {
Sys_role role = sysRoleService.fetch(Cnd.where("code", "=", Roles.CLUB_JBR_CODE));
return role == null ? "" : role.getId();
}
private void clearClubManageRole(String userId, String clubId) {
sysUserRoleService.clear(Cnd.where("userid", "=", userId)
.and("stid", "=", clubId)
.and(new Static("roleid IN (SELECT id FROM sys_role WHERE id = '" + Roles.CLUB_FZR
+ "' OR code = '" + Roles.CLUB_JBR_CODE + "')")));
}
@At
@ViReturn
public Object dissolve(String id, Boolean isjs) {
@@ -314,7 +330,7 @@ public class SysClubCon {
record.set("child", sysClubService.query(Cnd.NEW().asc("code")));
} else {
Cnd cnd = Cnd.NEW();
cnd.and(new Static("id in (select stid from sys_user_role where userId = '" + ShiroUtil.getPrincipalProperty("id") + "' and roleid = '" + Roles.CLUB_FZR + "')"));
cnd.and(new Static(getClubManageRoleCnd("id")));
record.set("child", sysClubService.query(cnd));
}
@@ -325,4 +341,12 @@ public class SysClubCon {
return Result.error("获取工会信息失败");
}
}
// 协会社团管理角色范围:会长和经办人都可以进入对应协会数据。
private String getClubManageRoleCnd(String clubIdColumn) {
return clubIdColumn + " IN ( SELECT stid FROM sys_user_role WHERE userId = '"
+ ShiroUtil.getPrincipalProperty("id")
+ "' AND stid IS NOT NULL AND roleid IN (SELECT id FROM sys_role WHERE id = '"
+ Roles.CLUB_FZR + "' OR code = '" + Roles.CLUB_JBR_CODE + "') )";
}
}
@@ -64,7 +64,13 @@ public class SysClubUserCon {
if (ShiroUtil.hasAnyRoles(new String[]{"sysadmin", "A06"})) {
return sysClubService.query(Cnd.where("isjs", "=", false).asc("create_time"));
}
return sysClubService.query(Cnd.where("fzr", "=", ShiroUtil.getPrincipalProperty("id")).and("isjs", "=", false).asc("create_time"));
return sysClubService.query(Cnd.where("isjs", "=", false)
.and(new org.nutz.dao.util.cri.Static("(fzr = '" + ShiroUtil.getPrincipalProperty("id")
+ "' OR id IN (SELECT stid FROM sys_user_role WHERE userid = '"
+ ShiroUtil.getPrincipalProperty("id")
+ "' AND roleid IN (SELECT id FROM sys_role WHERE id = '" + Roles.CLUB_FZR
+ "' OR code = '" + Roles.CLUB_JBR_CODE + "')))"))
.asc("create_time"));
}
/**
@@ -78,7 +84,7 @@ public class SysClubUserCon {
if (Strings.isBlank(clubid)) {
return new ArrayList<>();
}
String orderStr = " ORDER BY FIELD( sf, 1, 3,4,5, 2 ),scu.join_time DESC";
String orderStr = " ORDER BY FIELD( sf, 1, 3,4,5,6, 2 ),scu.join_time DESC";
if (type == 2 && radioType == 2) {
orderStr = " order by field(status, 1, 3, 5),scu.join_time DESC";
} else if (type == 3 && radioType == 2) {
@@ -109,7 +115,7 @@ public class SysClubUserCon {
//查询理事机构
if (radioType != null && 1 == radioType) {
cnd.and("scu.sf", "=", "1");
cnd.and("scu.sf", "in", new int[]{1, 3, 4, 5, 6});
}
//查询社团成员
if (radioType != null && 2 == radioType) {
@@ -133,8 +139,10 @@ public class SysClubUserCon {
public Object delUser(String clubid, String userid) {
sysClubUserService.clear(Cnd.where("clubid", "=", clubid).and("userid", "=", userid));
//删除协会负责人的角色
sysUserRoleService.clear(Cnd.NEW().and("userId", "=", ShiroUtil.getPrincipalProperty("id"))
.and("roleId", "=", Roles.CLUB_FZR).and("stid", "=", clubid));
sysUserRoleService.clear(Cnd.NEW().and("userId", "=", userid)
.and("stid", "=", clubid)
.and(new org.nutz.dao.util.cri.Static("roleId IN (SELECT id FROM sys_role WHERE id = '" + Roles.CLUB_FZR
+ "' OR code = '" + Roles.CLUB_JBR_CODE + "')")));
sysRoleService.clearCache();
sysUserService.clearCache();
return null;
@@ -149,7 +157,31 @@ public class SysClubUserCon {
"\tsys_user u\n" +
"\tLEFT JOIN sys_unit unit ON u.unitid = unit.id\n" +
"\tLEFT JOIN sys_union un ON unit.unionid = un.id\n" +
"\tWHERE (u.username LIKE @query or u.loginname like @query) and u.id not in (SELECT userid from sys_club_user WHERE clubid = @clubid) LIMIT 50").setParam("query", "%" + query + "%").setParam("clubid", clubid);
"\tWHERE (u.username LIKE @query or u.loginname like @query) and u.id not in (SELECT userid from sys_club_user WHERE clubid = @clubid and isNormal = true) LIMIT 50").setParam("query", "%" + query + "%").setParam("clubid", clubid);
return sysClubService.list(sql);
}
/**
* 设置理事机构时,只允许从当前协会已通过的普通成员中选择,避免把协会外人员设为理事机构。
*/
@At
@ViReturn
public Object queryClubMemberForManage(String clubid, String query) {
if (Strings.isBlank(clubid)) {
return new ArrayList<>();
}
Sql sql = Sqls.create("SELECT\n" +
"\tu.id,u.username,unit.name as unitname,u.loginname \n" +
"FROM\n" +
"\tsys_club_user scu\n" +
"\tLEFT JOIN sys_user u ON scu.userid = u.id\n" +
"\tLEFT JOIN sys_unit unit ON u.unitid = unit.id\n" +
"\tWHERE scu.clubid = @clubid\n" +
"\tAND scu.isNormal = 1\n" +
"\tAND scu.status = 5\n" +
"\tAND scu.sf = 2\n" +
"\tAND (u.username LIKE @query or u.loginname like @query)\n" +
"\tLIMIT 50").setParam("query", "%" + query + "%").setParam("clubid", clubid);
return sysClubService.list(sql);
}
@@ -160,13 +192,44 @@ public class SysClubUserCon {
//type=1协会信息管理菜单,操作为录入理事机构,此时状态直接为通过5
//type=2社团录入菜单,操作为录入成员,此时需要校工会审核,状态为3
//type=3校工会录入菜单,操作为录入成员,此时状态直接为通过5
int status = type == 1 || type == 3 ? 5 : 3;
int status = 5;
for (String user : users) {
if (type != null && type == 1) {
Sys_club_user sysClubUser = sysClubUserService.fetch(Cnd.where("clubid", "=", clubid)
.and("userid", "=", user)
.and("isNormal", "=", true)
.and("status", "=", 5));
if (sysClubUser == null) {
return cn.wizzer.framework.base.Result.error("只能从本协会已通过成员中设置理事机构");
}
clearClubManageRole(user, clubid);
if (sf != 2) {
String roleId = sf == 6 ? getClubJbrRoleId() : Roles.CLUB_FZR;
if (Strings.isBlank(roleId)) {
return cn.wizzer.framework.base.Result.error("未找到协会经办人角色");
}
Sys_user_role sys_user_role = new Sys_user_role();
sys_user_role.setUserId(user);
sys_user_role.setRoleId(roleId);
sys_user_role.setStid(clubid);
sysUserRoleService.insert(sys_user_role);
sysUserService.clearCache();
sysRoleService.clearCache();
}
// 设置理事机构是调整本协会已有成员身份,不新增成员记录,避免出现重复协会成员。
sysClubUserService.update(Chain.make("sf", sf), Cnd.where("id", "=", sysClubUser.getId()));
continue;
}
if (sf != 2) {
String roleId = sf == 6 ? getClubJbrRoleId() : Roles.CLUB_FZR;
if (Strings.isBlank(roleId)) {
return cn.wizzer.framework.base.Result.error("未找到协会经办人角色");
}
Sys_user_role sys_user_role = new Sys_user_role();
sys_user_role.setUserId(user);
sys_user_role.setRoleId(Roles.CLUB_FZR);
sys_user_role.setRoleId(roleId);
sys_user_role.setStid(clubid);
sysUserRoleService.insert(sys_user_role);
sysUserService.clearCache();
@@ -180,9 +243,22 @@ public class SysClubUserCon {
sys_club_user.setJoin_time(DateUtil.getDate());
sys_club_user.setSf(sf);
sys_club_user.setStatus(status);
sys_club_user.setIsNormal(true);
sysClubUserService.insert(sys_club_user);
}
return null;
}
private String getClubJbrRoleId() {
io.v.nutz.sys.models.Sys_role role = sysRoleService.fetch(Cnd.where("code", "=", Roles.CLUB_JBR_CODE));
return role == null ? "" : role.getId();
}
private void clearClubManageRole(String userid, String clubid) {
sysUserRoleService.clear(Cnd.NEW().and("userId", "=", userid)
.and("stid", "=", clubid)
.and(new org.nutz.dao.util.cri.Static("roleId IN (SELECT id FROM sys_role WHERE id = '" + Roles.CLUB_FZR
+ "' OR code = '" + Roles.CLUB_JBR_CODE + "')")));
}
}
@@ -121,13 +121,11 @@ public class SysClubUserShCon {
cnd.and("sf", "=", 2);
cnd.and("isNormal", "=", true);
cnd.andEX("scu.clubid", "=", clubid);
cnd.andEX("scu.clubid", "=", clubid);
if (!ShiroUtil.hasAnyRoles(new String[]{"sysadmin", "A06"})) {
if (ShiroUtil.hasAnyRoles("club01, club02, club03, club04, H02")) {
cnd.and(new Static("""
scu.clubid IN ( SELECT stid FROM sys_user_role WHERE roleid = '%s' AND userid = '%s' AND stid IS NOT NULL )
""".formatted(Roles.CLUB_FZR, ShiroUtil.getPrincipalProperty("id"))));
if (ShiroUtil.hasAnyRoles("club01, club02, club03, club04, H02, xhjbr")) {
cnd.and(new Static(getClubAuditRoleCnd("scu.clubid")));
}
}
@@ -164,9 +162,11 @@ public class SysClubUserShCon {
auditList.add(NutMap.NEW().addv("auditUser", ShiroUtil.getPrincipalProperty("id")).addv("auditState", flag)
.addv("auditTime", DateUtil.getDateTime()).addv("auditType", type == 1 ? "club" : "school"));
if(applyType == 1) {
sysClubUserService.update(Chain.make("status", status)
.add("join_time", cn.hutool.core.date.DateUtil.now())
.add("auditList", auditList), cnd);
Chain statusChain = Chain.make("status", status).add("auditList", auditList);
if (flag) {
statusChain.add("join_time", DateUtil.getDate());
}
sysClubUserService.update(statusChain, cnd);
} else {
Chain exitStatus = Chain.make("exitStatus", status).add("exitAuditList", auditList);
if(status == 5) {
@@ -205,7 +205,7 @@ public class SysClubUserShCon {
cnd.and("isjs", "=", false);
if (!ShiroUtil.hasAnyRoles(new String[]{"sysadmin", "A06"})) {
cnd.and("club.fzr", "=", ShiroUtil.getPrincipalProperty("id"));
cnd.and(new Static("(club.fzr = '" + ShiroUtil.getPrincipalProperty("id") + "' OR " + getClubAuditRoleCnd("club.id") + ")"));
}
if (StrUtil.isNotBlank(clubType)) {
cnd.and("club.type", "=", clubType);
@@ -263,10 +263,8 @@ public class SysClubUserShCon {
cnd.andEX("scu.clubid", "=", id);
if (!ShiroUtil.hasAnyRoles(new String[]{"sysadmin", "A06"})) {
if (ShiroUtil.hasRole("H02")) {
cnd.and(new Static("""
scu.clubid IN ( SELECT stid FROM sys_user_role WHERE roleid = '%s' AND userid = '%s' AND stid IS NOT NULL )
""".formatted(Roles.CLUB_FZR, ShiroUtil.getPrincipalProperty("id"))));
if (ShiroUtil.hasAnyRoles("H02, xhjbr, club01, club02, club03, club04")) {
cnd.and(new Static(getClubAuditRoleCnd("scu.clubid")));
}
}
sql.setCondition(cnd);
@@ -289,4 +287,11 @@ public class SysClubUserShCon {
}
return list;
}
private String getClubAuditRoleCnd(String clubIdColumn) {
return clubIdColumn + " IN ( SELECT stid FROM sys_user_role WHERE userid = '"
+ ShiroUtil.getPrincipalProperty("id")
+ "' AND stid IS NOT NULL AND roleid IN (SELECT id FROM sys_role WHERE id = '"
+ Roles.CLUB_FZR + "' OR code = '" + Roles.CLUB_JBR_CODE + "') )";
}
}
+14 -14
View File
@@ -1,26 +1,26 @@
nutz.application.name=union_hhu
server.port=8081
server.port=8080
server.host=0.0.0.0
jetty.contextPath=/
jetty.threadpool.idleTimeout=100000
jetty.threadpool.minThreads=10
jetty.threadpool.maxThreads=20000
jetty.page.403=/error/403.htmla
jetty.page.403=/error/403.html
jetty.page.404=/error/404.html
jetty.page.500=/error/500.html
#结合ftp使用,或用nginx代理ftp路径
jetty.staticPath=D://files
jetty.staticPath=D://filesu
#开发模式静态资源
jetty.staticPathLocal=
jetty.staticPathLocal=D:/gsProject/zhgh_hhu/src/main/resources/static
nutz.mvc.ignore=^(.+[.])(jsp|png|gif|jpg|js|css|jpeg|html|mp3|mp4|ico|svg)$
nutz.mvc.exclusions=/favicon/*,/assets/*,/druid/*,/upload/*
ftp.enabled=true
ftp.host=202.119.115.17
ftp.port=21
ftp.username=ftp_user
ftp.password=zhgh@17server
ftp.host=192.168.21.203
ftp.port=9090
ftp.username=ftpuser
ftp.password=123456
ftp.timeout=100
redis.host=202.119.115.17
redis.host=127.0.0.1
redis.port=6379
redis.timeout=2000
redis.max_redir=10
@@ -29,14 +29,14 @@ redis.maxTotal=850
redis.pool.maxTotal=900
redis.pool.maxIdle=500
redis.pool.minIdle=50
redis.password=dd3s
#redis.password=dd3s
#redis集群模式设置
#redis.mode=cluster
redis.mode=normal
#redis.nodes=192.168.6.31:6377,192.168.6.31:6378,192.168.6.28:6377,192.168.6.28:6378,192.168.6.34:6377,192.168.6.34:6378
jdbc.url=jdbc:mysql://202.119.115.17:3306/union?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
jdbc.url=jdbc:mysql://192.168.21.215:3306/union_hhu?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=zhgh@17server
jdbc.password=root
jdbc.validationQuery=select 1
jdbc.maxActive=900
jdbc.testWhileIdle=true
@@ -57,7 +57,7 @@ xsssql.ignore.urls=/platform/cms/content/article/addDo,/platform/sys/app/conf/ad
/platform/reimbursement/applyNew/doAdd,/platform/member/common/saveUser,\
/platform/sys/union/mange/unionPageData,/platform/fw/condolence/apply/doAdd
#环境 dev/prod
v.environment=prod
v.environment=dev
v.loginUrl=/sso/login
v.loginUrlMobile=/mobile/cas/login
#shiro.ini.path=shiro.ini
@@ -86,7 +86,7 @@ shiro.url.login=/platform
shiro.url.logout_redirect=/platform
shiro.url.unauth=/platform
#开发时设置本地路径,便于调试
beetl.RESOURCE.rootLocal=
beetl.RESOURCE.rootLocal=D:/gsProject/zhgh_hhu/src/main/resources/views/
beetl.RESOURCE.root=views/
beetl.DELIMITER_STATEMENT_START=<!--#
beetl.DELIMITER_STATEMENT_END=#-->
@@ -0,0 +1,38 @@
-- 场地预约承诺书:在已选定的业务数据库中执行,不包含历史数据修改。
-- 项目启动时可能已通过实体自动建表、补列,因此跳过已存在的表和字段。
CREATE TABLE IF NOT EXISTS `activity_site_commitment` (
`id` VARCHAR(32) NOT NULL,
`siteId` VARCHAR(32) DEFAULT NULL COMMENT '场地ID',
`userId` VARCHAR(32) DEFAULT NULL COMMENT '签署人ID',
`userName` VARCHAR(120) DEFAULT NULL COMMENT '签署人姓名快照',
`title` VARCHAR(200) DEFAULT NULL COMMENT '承诺书标题快照',
`content` TEXT COMMENT '承诺书正文快照',
`readAt` BIGINT DEFAULT NULL COMMENT '服务器记录的阅读开始时间,毫秒',
`signedAt` BIGINT DEFAULT NULL COMMENT '服务器记录的签署时间,毫秒',
`signature` TEXT COMMENT '本次手写签名PNG数据',
`sqid` VARCHAR(32) DEFAULT NULL COMMENT '预约申请编号,未提交时为空',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='场地预约承诺书签署记录';
-- 仅补充缺失的场地配置列,保留已经配置的承诺书及已有字段定义。
SET @site_commitment_ddl = IF(
EXISTS(SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activity_site_info'
AND COLUMN_NAME = 'commitmentTitle'),
'SELECT ''commitmentTitle already exists'' AS message',
'ALTER TABLE `activity_site_info` ADD COLUMN `commitmentTitle` VARCHAR(200) DEFAULT NULL COMMENT ''预约前承诺书标题'''
);
PREPARE site_commitment_stmt FROM @site_commitment_ddl;
EXECUTE site_commitment_stmt;
DEALLOCATE PREPARE site_commitment_stmt;
SET @site_commitment_ddl = IF(
EXISTS(SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activity_site_info'
AND COLUMN_NAME = 'commitmentContent'),
'SELECT ''commitmentContent already exists'' AS message',
'ALTER TABLE `activity_site_info` ADD COLUMN `commitmentContent` TEXT COMMENT ''预约前承诺书正文'''
);
PREPARE site_commitment_stmt FROM @site_commitment_ddl;
EXECUTE site_commitment_stmt;
DEALLOCATE PREPARE site_commitment_stmt;
@@ -56,6 +56,17 @@
</el-tab-pane>
<el-tab-pane label="预约承诺书" name="commitment">
<template v-if="viewData.commitment">
<h3>{{ viewData.commitment.title }}</h3>
<div v-html="viewData.commitment.content"></div>
<p>签署人:{{ viewData.commitment.userName }}</p>
<p>签署时间:{{ commitmentTime(viewData.commitment.signedAt) }}</p>
<img :src="viewData.commitment.signature" alt="本次预约手写签名" style="max-width: 100%; max-height: 180px">
</template>
<p v-else>本次预约无承诺书签署记录</p>
</el-tab-pane>
<el-tab-pane label="场地管理员批示信息" name="3" v-if="viewData.site_manager_id&&viewData.back_out_opinion">
<el-form label-width="130px" style="padding: 20px 0">
@@ -166,6 +177,9 @@ module.exports = {
}
},
methods: {
commitmentTime(value) {
return moment(value).format('YYYY-MM-DD HH:mm:ss')
},
hasPane(name) {
if (!this.handle) {
return true
@@ -0,0 +1,219 @@
<template>
<div v-if="visible" class="site-commitment-mask" role="dialog" aria-modal="true" :aria-label="record.title || '预约承诺书'">
<section class="site-commitment-dialog">
<h3>{{ record.title || '预约承诺书' }}</h3>
<div v-if="record.content" class="site-commitment-content" v-html="record.content"></div>
<template v-if="record.id && !readOnly">
<p>签署人:{{ record.userName }} <span aria-live="polite">{{ remaining > 0 ? '请阅读(' + remaining + '秒)' : '阅读时间已满,请在下方手写签字' }}</span></p>
<canvas ref="canvas" width="900" height="260" class="site-commitment-canvas"
:class="{'site-commitment-disabled': remaining > 0}"
@pointerdown="startStroke" @pointermove="drawStroke"
@pointerup="endStroke" @pointercancel="endStroke" @lostpointercapture="endStroke"></canvas>
<div class="site-commitment-actions">
<button type="button" :disabled="remaining > 0 || busy" @click="clearSignature">清空签名</button>
<label><input type="checkbox" v-model="agreed" :disabled="remaining > 0 || busy">我已阅读并承诺遵守上述内容</label>
</div>
</template>
<template v-if="record.id && readOnly">
<p>签署人:{{ record.userName }}</p>
<p>签署时间:{{ signedTime(record.signedAt) }}</p>
<img :src="record.signature" alt="本次预约手写签名" style="max-width: 100%; max-height: 180px">
</template>
<p v-if="error" class="site-commitment-error" role="alert">{{ error }}</p>
<footer>
<button type="button" :disabled="busy" @click="cancel">{{ readOnly ? '关闭' : '取消预约' }}</button>
<button v-if="record.id && !readOnly" type="button" class="site-commitment-confirm"
:disabled="remaining > 0 || !hasInk || !agreed || busy" @click="submit">
{{ busy ? '签署中…' : '签字确认,继续预约' }}
</button>
</footer>
</section>
</div>
</template>
<script>
// 电脑端和H5共用本次阅读、手写签署弹窗,不读取或复用历史签名。
module.exports = {
data() {
return {
visible: false,
readOnly: false,
record: {},
remaining: 10,
readMillis: 0,
lastTick: 0,
busy: false,
agreed: false,
hasInk: false,
drawing: false,
error: ''
}
},
methods: {
request(action, data) {
return new Promise((resolve, reject) => {
$.ajax({
url: action === 'detail' ? '/platform/activity/site/reserve/findOne' : '/platform/activity/site/commitment/' + action,
type: 'POST', data, dataType: 'json',
success: resolve,
error: () => reject(new Error('承诺书请求失败,请稍后重新进入预约'))
})
})
},
async open(siteId) {
this.cancel()
this.readOnly = false
this.record = {}
this.error = ''
this.agreed = false
this.hasInk = false
this.remaining = 10
this.readMillis = 0
try {
const res = await this.request('begin', {siteId})
if (this._isDestroyed) return null
if (res.code !== 0 || !res.data) throw new Error(res.msg || '无法获取承诺书')
if (!res.data.required) return {id: ''}
this.record = res.data.record
} catch (e) {
if (this._isDestroyed) return null
this.error = e.message
}
this.visible = true
const result = new Promise(resolve => { this.resolveReading = resolve })
await this.$nextTick()
if (this.record.id) {
this.clearSignature()
this.lastTick = Date.now()
// 前端只累计页面可见时的阅读时间;服务器另外校验至少十秒。
this.readTimer = setInterval(() => {
const now = Date.now()
if (!document.hidden) this.readMillis += Math.min(now - this.lastTick, 1000)
this.lastTick = now
this.remaining = Math.max(0, Math.ceil((10000 - this.readMillis) / 1000))
if (this.remaining === 0) clearInterval(this.readTimer)
}, 200)
}
return result
},
async view(reserveId) {
this.cancel()
this.readOnly = true
this.record = {}
this.error = ''
this.visible = true
this.busy = true
try {
const res = await this.request('detail', {id: reserveId})
if (res.code !== 0 || !res.data) throw new Error(res.msg || '获取签署记录失败')
this.record = res.data.commitment || {}
if (!this.record.id) this.error = '本次预约无承诺书签署记录'
} catch (e) {
this.error = e.message
} finally {
this.busy = false
}
},
signedTime(value) {
return moment(value).format('YYYY-MM-DD HH:mm:ss')
},
clearSignature() {
const canvas = this.$refs.canvas
if (!canvas) return
const context = canvas.getContext('2d')
context.clearRect(0, 0, canvas.width, canvas.height)
context.lineWidth = 4
context.lineCap = 'round'
context.lineJoin = 'round'
context.strokeStyle = '#222222'
this.hasInk = false
this.drawing = false
},
point(event) {
const canvas = this.$refs.canvas
const rect = canvas.getBoundingClientRect()
return {x: (event.clientX - rect.left) * canvas.width / rect.width,
y: (event.clientY - rect.top) * canvas.height / rect.height}
},
startStroke(event) {
if (this.remaining > 0 || this.busy || (event.pointerType === 'mouse' && event.button !== 0)) return
event.preventDefault()
const canvas = this.$refs.canvas
canvas.setPointerCapture(event.pointerId)
this.strokePointer = event.pointerId
this.lastPoint = this.point(event)
this.drawing = true
},
drawStroke(event) {
if (!this.drawing || this.busy || event.pointerId !== this.strokePointer) return
event.preventDefault()
const point = this.point(event)
const context = this.$refs.canvas.getContext('2d')
context.beginPath()
context.moveTo(this.lastPoint.x, this.lastPoint.y)
context.lineTo(point.x, point.y)
context.stroke()
this.lastPoint = point
this.hasInk = true
},
endStroke(event) {
if (event.pointerId === this.strokePointer) this.drawing = false
},
async submit() {
if (this.remaining > 0 || !this.hasInk || !this.agreed || this.busy) return
this.busy = true
this.error = ''
try {
const res = await this.request('sign', {
commitmentId: this.record.id,
signature: this.$refs.canvas.toDataURL('image/png')
})
if (res.code !== 0) throw new Error(res.msg || '签署失败,请重新进入预约')
const resolve = this.resolveReading
this.resolveReading = null
this.visible = false
clearInterval(this.readTimer)
if (resolve) resolve({id: this.record.id})
} catch (e) {
this.error = e.message
} finally {
this.busy = false
}
},
cancel() {
clearInterval(this.readTimer)
this.visible = false
this.drawing = false
if (this.resolveReading) this.resolveReading(null)
this.resolveReading = null
},
visibilityChanged() {
this.lastTick = Date.now()
}
},
mounted() {
document.addEventListener('visibilitychange', this.visibilityChanged)
this.$nextTick(() => this.$emit('ready'))
},
beforeDestroy() {
this.cancel()
document.removeEventListener('visibilitychange', this.visibilityChanged)
}
}
</script>
<style scoped>
.site-commitment-mask { position: fixed; inset: 0; z-index: 10000; background: rgba(0,0,0,.45); display: flex; align-items: center; justify-content: center; }
.site-commitment-dialog { background: #fff; color: #303133; width: 760px; max-width: 94vw; max-height: 94vh; overflow: auto; box-sizing: border-box; padding: 20px; border-radius: 8px; font-size: 14px; }
.site-commitment-dialog h3 { margin: 0 0 16px; text-align: center; }
.site-commitment-content { max-height: 34vh; overflow: auto; border: 1px solid #ebeef5; padding: 12px; line-height: 1.8; overflow-wrap: anywhere; }
.site-commitment-content img { max-width: 100%; }
.site-commitment-canvas { display: block; width: 100%; height: 150px; border: 1px dashed #909399; background: #fff; touch-action: none; box-sizing: border-box; }
.site-commitment-disabled { background: #f5f7fa; }
.site-commitment-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 10px; }
.site-commitment-dialog footer { display: flex; justify-content: flex-end; gap: 10px; margin-top: 16px; }
.site-commitment-dialog button { border: 1px solid #dcdfe6; border-radius: 4px; background: #fff; color: #303133; padding: 9px 12px; cursor: pointer; }
.site-commitment-dialog .site-commitment-confirm { background: #1867b0; color: white; }
.site-commitment-dialog button:disabled { opacity: .5; cursor: not-allowed; }
.site-commitment-error { color: #d93025; }
</style>
@@ -185,6 +185,7 @@ layout("/mobile/platform.html"){
</style>
<div id="app" v-cloak>
<site-commitment ref="commitmentRecord"></site-commitment>
<m-page-loading v-if="mLoading"></m-page-loading>
@@ -357,6 +358,8 @@ layout("/mobile/platform.html"){
</van-cell>
</van-cell-group>
</van-checkbox-group>
<van-button @click.stop="$refs.commitmentRecord.view(item.id)" size="small" type="primary"
style="position: relative; z-index: 2; width: auto; margin-top: 8px">查看承诺书</van-button>
</div>
<!--<div v-if="queryType === 'notAudit'" style="display: flex; justify-content: space-evenly; position: fixed; bottom: 50px; width: 95%">
<van-button @click="audit('all', 'reject')" color="#ff976a" size="small" style="height: 38px">一键全部驳回</van-button>
@@ -399,6 +402,9 @@ layout("/mobile/platform.html"){
const vue = new Vue({
el: '#app',
mixins: [mobileMixins],
components: {
'site-commitment': httpVueLoader('/components/activity/site/SiteCommitment.vue'),
},
data() {
return {
desc: '',
@@ -460,8 +466,10 @@ layout("/mobile/platform.html"){
if (this.type === 'all') {
idList = this.userList.map(o => o.id)
}
const reviewList = this.userList.filter(o => idList.includes(o.id))
const resp = await $.post('/mobile/activity/site/audit/audit', {
ids: JSON.stringify(idList),
ids: JSON.stringify(reviewList.map(o => o.id)),
reserveState: JSON.stringify(reviewList.map(o => o.reserve_state)),
auditOpinion: this.reason,
isPass: this.auditType === 'pass'
})
@@ -546,4 +554,4 @@ layout("/mobile/platform.html"){
<!--#
}
#-->
#-->
@@ -291,8 +291,8 @@ layout("/mobile/platform.html"){
this.show = true
},
openView(o) {
if(o.reservetarget === 1 && "${@shiro.hasAnyRoles('gh01', 'H04')}" === 'false' && "${@shiro.hasRole('sysadmin')}" === 'false') {
vant.Toast('该场地只能分工会主席预约')
if(o.reservetarget === 1 && "${@shiro.hasAnyRoles('gh01', 'H04', 'H02')}" === 'false' && "${@shiro.hasRole('sysadmin')}" === 'false') {
vant.Toast('该场地仅限分工会预约人员或协会负责人预约')
return
}
this.siteId = o.id
@@ -155,12 +155,16 @@ layout("/mobile/platform.html"){
right: 0;
top: 10px;
}
.reservation-commitment-content img {
max-width: 100%;
}
.van-cell__title {
font-size: 16px;
}
</style>
<div id="app" v-cloak>
<site-commitment ref="commitmentRecord"></site-commitment>
<m-page-loading v-if="mLoading"></m-page-loading>
@@ -217,7 +221,7 @@ layout("/mobile/platform.html"){
@load="onLoad">
<div class="van-doc-card" v-for="o in tableData">
<div @click="openView(o.id, o.modulename)">
<div @click="openView(o.id)">
<div style="display: flex; justify-content: space-between">
<div style="width: 74%">
@@ -274,6 +278,12 @@ layout("/mobile/platform.html"){
<van-button @click.stop="backOption(o)" class="cus_button"
type="primary" size="small" color="#1867b0"
style="margin-right: 8px; width: auto;">反馈意见</van-button>
<van-button @click.stop="openView(o.id)" class="cus_button"
type="primary" size="small" color="#1867b0"
style="margin-right: 8px; width: auto;">预约详情</van-button>
<van-button @click.stop="$refs.commitmentRecord.view(o.id)" class="cus_button"
type="primary" size="small" color="#1867b0"
style="width: auto;">查看承诺书</van-button>
</div>
</div>
</div>
@@ -288,6 +298,32 @@ layout("/mobile/platform.html"){
</div>
<van-action-sheet v-model="detailShow" title="预约详情">
<div style="padding: 16px; max-height: 75vh; overflow-y: auto; word-break: break-word">
<p v-if="detailLoading">正在加载预约信息…</p>
<p v-else-if="detailError">{{detailError}}</p>
<template v-else>
<van-cell title="场地名称" :value="reserveDetail.site_name"></van-cell>
<van-cell title="预约人" :value="reserveDetail.reserve_person"></van-cell>
<van-cell title="所属单位" :value="reserveDetail.reserve_person_unit"></van-cell>
<van-cell title="联系电话" :value="reserveDetail.reserve_person_phone"></van-cell>
<van-cell title="预约日期" :value="reserveDetail.concat_day"></van-cell>
<van-cell title="预约时间" :value="reserveDetail.start_time + ' - ' + reserveDetail.end_time"></van-cell>
<van-cell title="预约状态" :value="reserveDetail.state_name"></van-cell>
<van-cell title="预约事由" :value="reserveDetail.reserve_cause || '无'"></van-cell>
<van-divider>承诺书签署信息</van-divider>
<template v-if="reserveDetail.commitment">
<h3>{{reserveDetail.commitment.title}}</h3>
<div class="reservation-commitment-content" v-html="reserveDetail.commitment.content"></div>
<p>签署人:{{reserveDetail.commitment.userName}}</p>
<p>签署时间:{{moment(reserveDetail.commitment.signedAt).format('YYYY-MM-DD HH:mm:ss')}}</p>
<img :src="reserveDetail.commitment.signature" alt="本次预约手写签名" style="max-width: 100%; max-height: 180px">
</template>
<p v-else>本次预约无承诺书签署记录</p>
</template>
</div>
</van-action-sheet>
<van-dialog v-model="show" title="反馈意见" show-cancel-button @confirm="backDo">
<van-field
v-model="option"
@@ -308,6 +344,9 @@ layout("/mobile/platform.html"){
const vue = new Vue({
el: '#app',
mixins: [mobileMixins],
components: {
'site-commitment': httpVueLoader('/components/activity/site/SiteCommitment.vue'),
},
data() {
return {
time: new Date(),
@@ -317,9 +356,32 @@ layout("/mobile/platform.html"){
typeList: [],
timeList: [],
info: {},
detailShow: false,
detailLoading: false,
detailError: '',
reserveDetail: {},
}
},
methods: {
// 从预约详情接口读取本次申请及已入库的签署快照,不使用当前场地配置替代历史正文。
async openView(id) {
if (this.detailLoading) return
this.detailShow = true
this.detailLoading = true
this.detailError = ''
this.reserveDetail = {}
try {
const res = await $.post('/platform/activity/site/reserve/findOne', {id})
if (res.code !== 0 || !res.data) {
throw new Error(res.msg || '获取预约详情失败')
}
this.reserveDetail = res.data
} catch (e) {
this.detailError = e.message || '获取预约详情失败,请稍后重试'
} finally {
this.detailLoading = false
}
},
onConfirm() {
if(this.pageForm.timeSwitch === true) {
this.timeList = [{text: '全部', value: '全部'}]
@@ -423,4 +485,4 @@ layout("/mobile/platform.html"){
<!--#
}
#-->
#-->
@@ -3,6 +3,112 @@ layout("/mobile/platform.html"){
#-->
<style>
.reserve-entry {
min-height: 100vh;
box-sizing: border-box;
padding-bottom: calc(24px + env(safe-area-inset-bottom));
background: linear-gradient(180deg, #eaf3fc 0, #f6f7f9 360px);
color: #26384b;
}
.reserve-entry .van-nav-bar {
background: transparent;
}
.reserve-entry .van-nav-bar__title {
color: #26384b;
font-weight: 600;
}
.reserve-entry .van-nav-bar .van-icon {
color: #246fb4;
}
.reserve-entry-main {
max-width: 480px;
margin: 0 auto;
padding: 36px 20px 0;
}
.reserve-entry-hero {
text-align: center;
padding: 0 12px 28px;
}
.reserve-entry-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 76px;
height: 76px;
border-radius: 24px;
background: #fff;
color: #246fb4;
box-shadow: 0 8px 24px rgba(36, 111, 180, .10);
}
.reserve-entry-hero h1 {
margin: 20px 0 10px;
font-size: 24px;
line-height: 1.4;
font-weight: 600;
}
.reserve-entry-hero p {
margin: 0;
color: #768699;
font-size: 14px;
line-height: 1.7;
}
.reserve-entry-card {
padding: 24px 20px;
background: #fff;
border: 1px solid #edf1f6;
border-radius: 18px;
box-shadow: 0 6px 24px rgba(38, 56, 75, .04);
}
.reserve-entry-steps {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 24px;
}
.reserve-entry-step {
text-align: center;
font-size: 13px;
line-height: 1.6;
}
.reserve-entry-step span {
display: block;
width: 28px;
line-height: 28px;
margin: 0 auto 8px;
border-radius: 50%;
background: #edf5fd;
color: #246fb4;
font-weight: 600;
}
.reserve-entry-steps > .van-icon {
color: #bdcad8;
margin-top: -24px;
}
.reserve-entry-note {
margin: 0 0 22px;
padding: 14px;
background: #f5f8fc;
border-radius: 10px;
color: #64758a;
font-size: 13px;
line-height: 1.8;
}
.reserve-entry-start.van-button {
height: 46px;
border-radius: 10px;
font-size: 16px;
font-weight: 500;
}
.reserve-entry-back {
display: block;
width: fit-content;
margin: 16px auto 0;
padding: 12px 18px;
color: #64758a;
font-size: 14px;
line-height: 20px;
}
.my_calendar {
width: 100%;
height: 100px;
@@ -115,7 +221,33 @@ layout("/mobile/platform.html"){
}
</style>
<div id="app" v-cloak v-if="isLoad">
<div id="app" v-cloak>
<site-commitment ref="commitment" @ready="initializeReservation"></site-commitment>
<div v-if="!isLoad && !commitmentOpening" class="reserve-entry">
<van-nav-bar title="场地预约" left-arrow safe-area-inset-top
@click-left="location.href = '/mobile/activity/site/info'"></van-nav-bar>
<main class="reserve-entry-main">
<div class="reserve-entry-hero">
<div class="reserve-entry-icon" aria-hidden="true"><van-icon name="calendar-o" size="36"></van-icon></div>
<h1>活动场地预约</h1>
<p>选择合适的时间,为活动做好准备</p>
</div>
<section class="reserve-entry-card" aria-label="预约流程">
<div class="reserve-entry-steps">
<div class="reserve-entry-step"><span>1</span>阅读须知</div>
<van-icon name="arrow" aria-hidden="true"></van-icon>
<div class="reserve-entry-step"><span>2</span>选择时段</div>
<van-icon name="arrow" aria-hidden="true"></van-icon>
<div class="reserve-entry-step"><span>3</span>提交预约</div>
</div>
<p class="reserve-entry-note">如场地要求签署承诺书,请阅读满10秒并完成手写签名,再选择预约日期和时间。</p>
<van-button class="reserve-entry-start" type="info" color="#246fb4" block
:loading="commitmentOpening" @click="initializeReservation">进入场地预约</van-button>
</section>
<a class="reserve-entry-back" href="/mobile/activity/site/info">返回场地列表</a>
</main>
</div>
<template v-if="isLoad">
<van-nav-bar
:title="site.name"
left-arrow
@@ -222,9 +354,10 @@ layout("/mobile/platform.html"){
<van-button @click="formData.message = '';show = false" color="#246fb4" style="width: 47%" size="small" plain
type="info">关闭
</van-button>
<van-button @click="reserveDo" color="#246fb4" style="width: 47%" size="small" type="info">确认</van-button>
<van-button @click="reserveDo" :loading="reserveSubmitting" color="#246fb4" style="width: 47%" size="small" type="info">确认</van-button>
</div>
</van-action-sheet>
</template>
</div>
<script>
@@ -241,6 +374,9 @@ layout("/mobile/platform.html"){
data() {
return {
isLoad: false,
commitmentId: '',
commitmentOpening: false,
reserveSubmitting: false,
formData: {},
show: false,
site_id: '',
@@ -256,6 +392,9 @@ layout("/mobile/platform.html"){
allowDayList: [],
}
},
components: {
'site-commitment': httpVueLoader('/components/activity/site/SiteCommitment.vue'),
},
computed: {
getWeekTextByDate() {
return (date) => {
@@ -356,25 +495,40 @@ layout("/mobile/platform.html"){
this.show = true
},
async reserveDo() {
if (this.reserveSubmitting || !this.isLoad) return
if (this.times.length === 0) {
vant.Toast('请选择预约时段')
return
}
this.$set(this.formData, 'start_time', this.times[0].start_time)
this.$set(this.formData, 'end_time', this.times[0].end_time)
this.$set(this.formData, 'site_id', this.site_id)
const resp = await $.post('/platform/activity/site/reserve/doAdd', {
data: JSON.stringify(this.formData),
days: JSON.stringify(this.times.map(o => o.day))
})
if (resp.code === 0) {
vant.Toast('预约成功')
this.reserveSubmitting = true
try {
const resp = await $.post('/platform/activity/site/reserve/doAdd', {
data: JSON.stringify(this.formData),
days: JSON.stringify(this.times.map(o => o.day)),
commitmentId: this.commitmentId
})
if (resp.code === 0) {
vant.Toast('预约成功')
this.show = false
this.times = []
this.message = null
if (this.site.reserveTarget === 1) {
this.commitmentId = ''
this.isLoad = false
}
await this.getMyReservation()
await this.getReserve()
await this.onLoad()
} else {
vant.Toast(resp.msg)
}
} else {
vant.Toast(resp.msg)
} catch (e) {
vant.Toast('预约提交失败,请稍后重试')
} finally {
this.reserveSubmitting = false
}
},
chooseTime(item) {
@@ -448,17 +602,30 @@ layout("/mobile/platform.html"){
})
this.allowDayList = data
},
// 直接访问H5预约地址也必须先签署,签署完成后才展示日期和时段。
async initializeReservation() {
if (this.commitmentOpening || !this.$refs.commitment) return
this.commitmentOpening = true
this.commitmentId = ''
this.isLoad = false
this.site_id = getQueryString('id')
this.selectDate = moment()
try {
const signed = await this.$refs.commitment.open(this.site_id)
if (!signed) return
this.commitmentId = signed.id
this.times = []
await this.onLoad()
await this.getReserve()
await this.getAllowTime()
await this.setWeekList(this.selectDate)
await this.getMyReservation()
this.isLoad = true
} finally {
this.commitmentOpening = false
}
},
},
async created() {
this.site_id = getQueryString('id')
this.selectDate = moment()
await this.getReserve()
await this.onLoad()
await this.getAllowTime()
await this.setWeekList(this.selectDate)
await this.getMyReservation()
this.isLoad = true
}
})
</script>
@@ -735,7 +735,7 @@ layout("/mobile/platform.html"){
this.getActivity()
this.closeLoading()
//await wxScannerQrCode.initWxConfig(window.location.href)
await wxScannerQrCode.initWxConfig(window.location.href)
},
})
</script>
@@ -38,11 +38,11 @@ layout("/mobile/platform.html"){
v-model="mLoading">
<div @click.stop="openView(o)" class="list-card" v-for="o in tableData">
<van-swipe-cell :disabled="o.auditstate!=='3999'">
<van-swipe-cell :disabled="!isActivityTime(o)">
<van-cell-group inset>
<image @click.stop="openEdit(o.id,o.hdid)" class="register-icon"
src="/assets/mobile/svg/wmhd/bianji.svg"
v-if="o.auditstate==='3999'||o.auditstate==='4100'||o.auditstate==='4250'"></image>
v-if="isActivityTime(o)"></image>
<van-cell :border="false" title="姓名">{{o.sbrname}}</van-cell>
<van-cell :border="false" title="工号">{{o.loginname}}</van-cell>
<van-cell :border="false" title="单位">{{o.dwname}}</van-cell>
@@ -60,7 +60,7 @@ layout("/mobile/platform.html"){
</van-cell>-->
</van-cell-group>
<template #right>
<van-button @click="doDelete(o.id)" square style="height: 100%;" text="删除"
<van-button @click="doDelete(o)" square style="height: 100%;" text="删除"
type="danger"></van-button>
</template>
</van-swipe-cell>
@@ -116,19 +116,34 @@ layout("/mobile/platform.html"){
'mobile-wmhd-info': httpVueLoader('/components/mobile/wmhd/info.vue?v=' + new Date().getTime()),
},
methods: {
isActivityTime(o) {
if (!o || !o.hdflag || !o.hdkssj || !o.hdjssj) {
return false
}
const startTime = new Date(o.hdkssj.replace(/-/g, '/')).getTime()
const endTime = new Date(o.hdjssj.replace(/-/g, '/')).getTime()
const nowTime = new Date().getTime()
return startTime <= nowTime && nowTime <= endTime
},
openEdit(id, hdid) {
window.location.href = '/platform/staffActivities?client=mobile&id=' + hdid + '&zpid=' + id
},
async doDelete(id) {
async doDelete(o) {
if (!this.isActivityTime(o)) {
vant.Toast('不在活动时间范围内,不能删除')
return
}
const confirm = await vant.Dialog.confirm({
title: '提示',
message: '确定要删除此次作品吗?',
})
if (confirm === 'confirm') {
const data = await $.post("/platform/hd/wmhd/dodelete", {id: id})
const data = await $.post("/platform/hd/wmhd/dodelete", {id: o.id})
if (data.code == 0) {
await this.yearChange()
vant.Toast.success('删除成功');
} else {
vant.Toast(data.msg || '删除失败')
}
}
},
@@ -193,6 +193,19 @@ layout("/layouts/platform.html"){
<el-form-item label="场地介绍">
<text-editor v-model="formData.introduce"></text-editor>
</el-form-item>
<template v-if="formData.reserveTarget === 1">
<el-form-item label="承诺书标题" prop="commitmentTitle"
:rules="[{required: true, message: '请填写承诺书标题', trigger: 'blur'}]">
<el-input v-model="formData.commitmentTitle" maxlength="200"></el-input>
</el-form-item>
<el-form-item label="承诺书正文" prop="commitmentContent"
:rules="[{required: true, message: '请填写承诺书正文', trigger: 'change'}]">
<text-editor v-model="formData.commitmentContent"></text-editor>
</el-form-item>
<el-form-item label="预约前阅读要求">
分工会预约人员及协会负责人均须阅读10秒并手写签字后才能预约。
</el-form-item>
</template>
</el-form>
<vi-title2 title="添加场次(温馨提醒:如果设置了预约时间单位,那么系统会在开始时间至结束时间范围内,按照设置的单位来拆分成多个时间段,如果未设置或者为0,则表示开始时间至结束时间是一个时间段)">
<template #func>
@@ -373,6 +386,8 @@ layout("/layouts/platform.html"){
notApplyTimeList: [],
reserveTarget: 2,
openDayNum: 7,
commitmentTitle: '教职工活动场地安全承诺书',
commitmentContent: '',
},
pageForm: {
searchName: "name",
@@ -495,6 +510,7 @@ layout("/layouts/platform.html"){
});
},
async operation() {
if (this.subDis) return
let method = this.formData.id ? "/doEdit" : "/doAdd"
this.subDis = true
this.$refs["addForm"].validate(async (valid) => {
@@ -516,6 +532,8 @@ layout("/layouts/platform.html"){
}
loading.close()
this.subDis = false
} else {
this.subDis = false
}
});
},
@@ -528,6 +546,12 @@ layout("/layouts/platform.html"){
},
async openEdit(id) {
this.formData = await this.findOneSite(id)
if (!this.formData.commitmentTitle) {
this.$set(this.formData, 'commitmentTitle', '教职工活动场地安全承诺书')
}
if (!this.formData.commitmentContent) {
this.$set(this.formData, 'commitmentContent', '')
}
if(!this.formData.notApplyTimeList) {
this.$set(this.formData, 'notApplyTimeList', [])
}
@@ -543,6 +567,8 @@ layout("/layouts/platform.html"){
notApplyTimeList: [],
reserveTarget: 2,
openDayNum: 7,
commitmentTitle: '教职工活动场地安全承诺书',
commitmentContent: '',
}
this.$refs.guava.edit()
@@ -77,6 +77,7 @@ layout("/layouts/platform.html"){
}
</style>
<div id="app" v-cloak>
<site-commitment ref="commitment"></site-commitment>
<guava ref="guava">
<template>
<el-card shadow="never">
@@ -383,6 +384,8 @@ layout("/layouts/platform.html"){
data() {
return {
activityTypeList: [],
commitmentId: '',
commitmentOpening: false,
drawer: false,
siteList: [],
restaurants: [],
@@ -433,6 +436,7 @@ layout("/layouts/platform.html"){
}
},
components: {
'site-commitment': httpVueLoader('/components/activity/site/SiteCommitment.vue'),
'guava': httpVueLoader('/components/plugins/Guava.vue'),
'reserve-info': httpVueLoader('/components/activity/site/ReserveInfo.vue?v=1.0.0'),
},
@@ -511,23 +515,35 @@ layout("/layouts/platform.html"){
this.addDialogVisible = false
this.formData.end_time = ''
},
onReserve(data) {
if(data.reservetarget === 1 && "${@shiro.hasAnyRoles('gh01', 'H04')}" === 'false' && "${@shiro.hasRole('sysadmin')}" === 'false') {
this.$message.warning('该场地只能分工会预约')
async onReserve(data) {
if (this.commitmentOpening) return
if(data.reservetarget === 1 && "${@shiro.hasAnyRoles('gh01', 'H04', 'H02')}" === 'false' && "${@shiro.hasRole('sysadmin')}" === 'false') {
this.$message.warning('该场地仅限分工会预约人员或协会负责人预约')
return
}
this.reverseData = data
this.dayArray = []
this.getAllowTime()
if (data.sexlimit === 1 || data.sexlimit === 2) {
const sex = data.sexlimit === 1 ? '男' : '女'
if ("${@shiro.getPrincipalProperty('sex')}" === sex) {
this.openReserve(data)
} else {
if ("${@shiro.getPrincipalProperty('sex')}" !== sex) {
this.$message.warning('抱歉,该场地仅限' + sex + '性会员预约')
return
}
} else {
this.openReserve(data)
}
if (!this.$refs.commitment) {
this.$message.warning('承诺书组件加载中,请稍后再试')
return
}
this.commitmentOpening = true
this.commitmentId = ''
try {
const signed = await this.$refs.commitment.open(data.id)
if (!signed) return
this.commitmentId = signed.id
this.reverseData = data
this.dayArray = []
this.getAllowTime()
await this.openReserve(data)
} finally {
this.commitmentOpening = false
}
},
getDayClass(data) {
@@ -632,6 +648,7 @@ layout("/layouts/platform.html"){
});
},
async doAdd() {
if (this.subDis) return
if (!this.formData.time) {
this.$message.warning('未选择预约时间')
return
@@ -644,6 +661,7 @@ layout("/layouts/platform.html"){
}
this.$refs["addForm"].validate(async (valid) => {
if (valid) {
if (this.subDis) return
this.subDis = true
const loading = this.$loading({
lock: true,
@@ -651,20 +669,27 @@ layout("/layouts/platform.html"){
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
const {code, msg} = await $.post(location.href + "/doAdd", {
data: JSON.stringify(this.formData),
days: JSON.stringify(this.dayArray)
})
if (code === 0) {
this.addDialogVisible = false
this.$message.success(msg);
this.pageData()
this.$refs.guava.index()
} else {
this.$message.warning(msg)
try {
const {code, msg} = await $.post(location.href + "/doAdd", {
data: JSON.stringify(this.formData),
days: JSON.stringify(this.dayArray),
commitmentId: this.commitmentId
})
if (code === 0) {
this.commitmentId = ''
this.addDialogVisible = false
this.$message.success(msg);
this.pageData()
this.$refs.guava.index()
} else {
this.$message.warning(msg)
}
} catch (e) {
this.$message.error('预约提交失败,请稍后重试')
} finally {
loading.close()
this.subDis = false
}
loading.close()
this.subDis = false
}
});
},
@@ -279,22 +279,25 @@ layout("/layouts/platform.html"){
}
}
}).then(async ({value}) => {
let id = ''
if (type === 'one') {
id = JSON.stringify([that.formData.id])
} else {
id = JSON.stringify(this.selectTable.map(x => x.id))
}
const reviewList = type === 'one' ? [that.reserveViewData] : this.selectTable
$.post(location.href + "/doReview", {
id: id,
id: JSON.stringify(reviewList.map(x => x.id)),
reserveState: JSON.stringify(reviewList.map(x => x.reserve_state)),
auditOpinion: value,
isPass: state
}, (res) => {
if (res.code === 0) {
that.$message.success(res.msg)
this.selectTable = []
that.pageData();
that.$refs.guava.index()
if (res.data && res.data.skippedCount > 0) {
that.$alert(res.msg, '审核提示', {
confirmButtonText: '确定',
type: 'warning'
})
} else {
that.$message.success(res.msg)
}
}
}, "json");
}).catch(() => {
@@ -0,0 +1,311 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.formData .viewFile {
height: 200px;
}
</style>
<div id="app" v-cloak>
<guava ref="guava">
<el-card shadow="never">
<div class="search">
<div class="search-item">
<div class="search-item-label">年&emsp;&emsp;度:</div>
<div class="search-item-option">
<el-date-picker
placeholder="选择年"
style="width: 100%"
type="year" v-model="pageForm.year"
value-format="yyyy">
</el-date-picker>
</div>
</div>
<div class="search-item">
<div class="search-item-label">姓名/工号:</div>
<div class="search-item-option">
<el-input @keyup.enter.native="doSearch" clearable
placeholder="请输入内容"
v-model="pageForm.searchKeyword">
<el-select clearable placeholder="查询类型"
slot="prepend"
style="width: 100px;"
v-model="pageForm.searchName">
<el-option label="姓名" value="userName"></el-option>
<el-option label="工号" value="loginName"></el-option>
</el-select>
</el-input>
</div>
</div>
<div class="search-item">
<div class="search-item-label">摘要:</div>
<div class="search-item-option">
<el-input v-model="pageForm.notes" placeholder="请输入摘要" clearable>
</el-input>
</div>
</div>
<div class="search-item">
<div class="search-item-label">所属工会:</div>
<div class="search-item-option">
<el-select placeholder="所属工会" v-model="pageForm.unionId"
style="width: 100%;"
clearable="true"
@change="flushUnits" @clear="flushUnits"
filterable>
<el-option v-for="item in unions" :label="item.unionname"
:value="item.id"></el-option>
</el-select>
</div>
</div>
<div class="search-item">
<div class="search-item-label">所属单位:</div>
<div class="search-item-option">
<el-select placeholder="所属单位" v-model="pageForm.unitId"
style="width: 100%;"
clearable="true"
filterable="true">
<el-option v-for="item in units" :label="item.name"
:value="item.id"></el-option>
</el-select>
</div>
</div>
<div class="search-item">
<div class="search-item-label">所属协会:</div>
<div class="search-item-option">
<el-select placeholder="所属协会" v-model="pageForm.clubId"
style="width: 100%;"
clearable="true"
filterable="true">
<el-option v-for="item in clubs" :label="item.name"
:value="item.id"></el-option>
</el-select>
</div>
</div>
<div class="search-query">
<el-button @click="doSearch" icon="el-icon-search" type="primary">搜索
</el-button>
</div>
</div>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool :app="this" label="预审列表">
<template #func>
<el-radio-group v-model="pageForm.auditState" @change="doSearch" size="small">
<el-radio-button :label="0">全部</el-radio-button>
<el-radio-button :label="1">已审核</el-radio-button>
<el-radio-button :label="2">未审核</el-radio-button>
</el-radio-group>
</template>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" row-key="id"
style="width: 100%"
v-loading="tableLoading">
<el-table-column :index="indexMethod" align="center" header-align="center"
label="序号"
type="index"
width="80px"></el-table-column>
<el-table-column
:label="column.label"
:prop="column.prop"
:sortable="column.sortable"
align="center"
header-align="center"
v-for="column in tableColumns">
<template scope="{row}" v-if="column.prop==='auditState'">
<el-tag v-if="row.auditState===0" type="info">保存中</el-tag>
<el-tag v-if="row.auditState===1" type="info">待审核</el-tag>
<el-tag v-else-if="row.auditState===2" type="danger">退回修改</el-tag>
<el-tag v-else-if="row.auditState===3" type="danger">退回修改并结束</el-tag>
<el-tag v-else-if="row.auditState===4" type="success">已通过</el-tag>
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="操作"
width="300">
<template scope="{row}">
<el-button @click="openView(row)" type="primary" size="mini">查看
</el-button>
<el-button @click="openAudit(row)" type="primary" size="mini"
:disabled="![1].includes(row.auditState)">审核
</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</guava>
<el-dialog
title="预审材料信息"
:visible.sync="dialogVisible"
width="40%"
:close-on-click-modal="false"
>
<info ref="auditInfo">
<template #handle>
<el-form :model="formData" ref="form" :rules="formRules" label-position="right"
style="padding: 20px 0"
label-width="120px">
<el-row gutter="20">
<el-col :span="12">
<el-form-item prop="username" label="审核人员">
<el-input v-model="formData.username"
disabled></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="time" label="审核时间">
<el-input v-model="formData.auditTime" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
<el-form-item prop="opinion" label="审核意见">
<el-input type="textarea" v-model="formData.auditOpinion" rows="4"
maxlength="500"
placeholder="请填写您的审核意见"></el-input>
</el-form-item>
</el-form>
</template>
</info>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">关 闭</el-button>
<el-button type="danger" @click="doSubmit(1)">退回修改</el-button>
<el-button type="danger" @click="doSubmit(2)">退回修改并结束</el-button>
<el-button type="primary" @click="doSubmit(3)">通 过</el-button>
</span>
</el-dialog>
<el-dialog
title="预审材料信息"
:visible.sync="viewDialogVisible"
width="50%"
:close-on-click-modal="false">
<info ref="info"></info>
<span slot="footer" class="dialog-footer">
<el-button @click="viewDialogVisible = false" type="primary">关 闭</el-button>
</span>
</el-dialog>
</div>
<script>
<!--#include("info.js"){}#-->
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
units: [],
unions: [],
clubs: [],
pageForm: {
auditState: 0,
searchName: "userName",
year: new Date().getFullYear() + "",
clubId: ""
},
tableColumns: [
{prop: "loginName", label: "工号"},
{prop: "userName", label: "姓名"},
{prop: "unitName", label: "单位"},
{prop: "unionName", label: "工会"},
{prop: "clubName", label: "协会"},
{prop: "notes", label: "事由"},
{prop: "applyDate", label: "填写时间"},
{prop: "auditState", label: "状态"}
],
formRules: {},
dialogVisible: false,
viewDialogVisible: false
}
},
components: {
"info": info
},
methods: {
openView(row) {
this.viewDialogVisible = true
setTimeout(() => {
this.$refs.info.openView(row.id)
}, 300)
},
openAudit(row) {
this.formData = {
id: row.id,
auditOpinion: "",
auditTime: moment().format("YYYY-MM-DD"),
username: "${@shiro.getPrincipalProperty('username')}"
}
this.dialogVisible = true
setTimeout(() => {
this.$refs.auditInfo.openAudit(row.id, true)
}, 300)
},
doSubmit(flag) {
this.$refs.form.validate(async (valid) => {
if (valid) {
let msg = ""
if (flag === 1) {
msg = "退回修改"
} else if (flag === 2) {
msg = "退回修改并结束流程"
} else {
msg = "通过"
}
this.$confirm("确定要" + msg + "吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.formData.flag = flag
$.post("/platform/clubPreAudit/audit/doSubmit", this.formData).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "操作成功!"
})
this.dialogVisible = false
this.pageData()
}
})
})
}
})
},
async flushUnits() {
this.$set(this.pageForm, "unitId", "")
if ("${@shiro.hasRole('sysadmin')||@shiro.hasRole('A06')||@shiro.hasRole('H03')}" === "true") {
this.units = await getUnits(this.pageForm.unionId)
} else {
this.units = await getUnits("${@shiro.getPrincipalProperty('unit').getUnionid()}")
}
},
async getClubs() {
const resp = await $.post("/platform/clubPreAudit/manage/getClubs")
if (resp.code === 0) {
this.clubs = resp.data
}
}
},
async created() {
this.unions = await getUnions(null)
this.flushUnits()
await this.getClubs()
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,102 @@
let info = {
/*language=HTML*/
template: `
<el-tabs tab-position="top" v-model="activeName" v-loading="loading">
<el-tab-pane label="预审信息" name="1">
<el-form label-width="110px" class="formData">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="姓名">
{{viewData.userName}}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="工号">
{{viewData.loginName}}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位">
{{viewData.unitName}}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="工会">
{{viewData.unionName}}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="协会">
{{viewData.clubName}}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="摘要">
{{viewData.notes}}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系方式">
{{viewData.mobile}}
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="材料" prop="files">
<file-upload card :files.sync="viewData.files"
view></file-upload>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-tab-pane>
<el-tab-pane label="审核信息" name="2" v-if="viewData.auditId">
<el-form label-width="110px" class="formData">
<el-col :span="12">
<el-form-item label="姓名">
{{viewData.auditInfo.username}}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="审核时间">
{{viewData.auditInfo.auditTime}}
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="审核意见">
{{viewData.auditInfo.auditOpinion}}
</el-form-item>
</el-col>
</el-form>
</el-tab-pane>
<el-tab-pane v-if="handle" :label="label" name="999">
<slot name="handle"></slot>
</el-tab-pane>
</el-tabs>
</el-tabs>
`,
data() {
return {
handle: false,
label: "审核",
loading: false,
activeName: "1",
viewData: {}
}
},
methods: {
async openAudit(id, handle) {
this.handle = handle
await this.openView(id)
},
async openView(id) {
this.loading = true
const resp = await $.post("/platform/clubPreAudit/manage/findOne", {id})
resp.data.files = JSON.parse(resp.data.files)
this.viewData = resp.data
this.loading = false
if (this.handle){
this.activeName = "999"
}
}
}
}
@@ -0,0 +1,292 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.formData .viewFile {
height: 200px;
}
</style>
<div id="app" v-cloak>
<guava ref="guava">
<el-card shadow="never">
<div class="search">
<div class="search-item">
<div class="search-item-label">年&emsp;&emsp;度:</div>
<div class="search-item-option">
<el-date-picker
placeholder="选择年"
style="width: 100%"
type="year" v-model="pageForm.year"
value-format="yyyy">
</el-date-picker>
</div>
</div>
<div class="search-item">
<div class="search-item-label">事由:</div>
<div class="search-item-option">
<el-input v-model="pageForm.notes" placeholder="请选择或输入摘要" clearable>
</el-input>
</div>
</div>
<div class="search-query">
<el-button @click="doSearch" icon="el-icon-search" type="primary">搜索
</el-button>
</div>
</div>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool :app="this" label="预审列表">
<template #func>
<el-button v-if="${@shiro.hasRole('sysadmin')||@shiro.hasRole('xhbxy')}" type="primary" @click="openAdd" size="small">新增预审材料</el-button>
</template>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" row-key="id"
style="width: 100%"
v-loading="tableLoading">
<el-table-column :index="indexMethod" align="center" header-align="center"
label="序号"
type="index"
width="80px"></el-table-column>
<el-table-column
:label="column.label"
:prop="column.prop"
:sortable="column.sortable"
align="center"
header-align="center"
v-for="column in tableColumns">
<template scope="{row}" v-if="column.prop==='auditState'">
<el-tag v-if="row.auditState===0" type="info">保存中</el-tag>
<el-tag v-if="row.auditState===1" type="info">待审核</el-tag>
<el-tag v-else-if="row.auditState===2" type="danger">退回修改</el-tag>
<el-tag v-else-if="row.auditState===3" type="danger">退回修改并结束</el-tag>
<el-tag v-else-if="row.auditState===4" type="success">已通过</el-tag>
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="操作"
width="300">
<template scope="{row}">
<el-button @click="openView(row)" type="primary" size="mini">查看
</el-button>
<el-button @click="openEdit(row)" type="primary" size="mini"
:disabled="![0,1,2].includes(row.auditState)">编辑
</el-button>
<el-button @click="doDelete(row.id)" type="danger" size="mini"
:disabled="![0,1].includes(row.auditState)">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</guava>
<el-dialog
title="预审材料信息"
:visible.sync="dialogVisible"
width="60%"
:close-on-click-modal="false"
>
<el-form :model="formData" :rules="rules" ref="form" label-width="100px" class="formData">
<el-row>
<el-col :span="12">
<el-form-item label="姓名" prop="userName">
<el-input v-model="formData.userName" disabled>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="工号" prop="loginName">
<el-input v-model="formData.loginName" disabled>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位" prop="unitName">
<el-input v-model="formData.unitName" disabled>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="工会" prop="unionName">
<el-input v-model="formData.unionName" disabled>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="协会" prop="clubName">
<el-input v-model="formData.clubName" disabled>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="摘要" prop="notes">
<el-input v-model="formData.notes"
placeholder="请输入摘要" maxlength="50" show-word-limit>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系电话" prop="mobile">
<el-input v-model="formData.mobile"
placeholder="请输入联系电话" maxlength="50" show-word-limit>
</el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="材料" prop="files">
<file-upload :files.sync="formData.files"
:type="['jpg', 'jpeg', 'png', 'JPG', 'JPEG', 'PNG']" :max="30">
<template #el-upload__tip>
<div class="el-upload__tip" slot="tip">
请上传类型为jpg/png/jpeg等格式的图片,最多上传30个文件
</div>
</template>
</file-upload>
</el-form-item>
</el-col>
</el-row>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">关 闭</el-button>
<el-button type="primary" @click="doSubmit(false)" :loading="formLoading">保 存</el-button>
<el-button type="primary" @click="doSubmit(true)" :loading="formLoading">提 交</el-button>
</span>
</el-dialog>
<el-dialog
title="预审材料信息"
:visible.sync="viewDialogVisible"
width="50%"
:close-on-click-modal="false">
<info ref="info"></info>
<span slot="footer" class="dialog-footer">
<el-button @click="viewDialogVisible = false" type="primary">关 闭</el-button>
</span>
</el-dialog>
</div>
<script>
<!--#include("info.js"){}#-->
const vue = new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
pageForm: {
year: new Date().getFullYear() + ""
},
tableColumns: [
{ prop: "loginName", label: "工号" },
{ prop: "userName", label: "姓名" },
{ prop: "unitName", label: "单位" },
{ prop: "unionName", label: "工会" },
{ prop: "clubName", label: "协会" },
{ prop: "notes", label: "摘要" },
{ prop: "applyDate", label: "填写时间" },
{ prop: "auditState", label: "状态" }
],
dialogVisible: false,
viewDialogVisible: false,
rules: {
notes: [{ required: true, message: "必填", trigger: "blur" }],
mobile: [{ required: true, message: "必填", trigger: "blur" }],
files: [{ required: true, message: "必填", trigger: "blur" }]
}
}
},
components: {
"info": info
},
methods: {
async openAdd() {
this.formData = {
applyDate: moment().format("YYYY-MM-DD"),
userName: "${@shiro.getPrincipalProperty('username')}",
loginName: "${@shiro.getPrincipalProperty('loginname')}",
unitName: "${@shiro.getPrincipalProperty('unit').getName()}",
unionName: "${@shiro.getPrincipalProperty('union').getUnionname()}",
clubName: "",
clubId: ""
}
// 获取用户所属协会
const clubResp = await $.post("/platform/clubPreAudit/manage/getUserClub")
if (clubResp.code === 0 && clubResp.data) {
this.formData.clubName = clubResp.data.clubName || ""
this.formData.clubId = clubResp.data.clubId || ""
}
this.dialogVisible = true
this.$nextTick(() => {
this.$refs.form.clearValidate()
})
},
openView(row) {
this.viewDialogVisible = true
setTimeout(() => {
this.$refs.info.openView(row.id)
}, 300)
},
openEdit(row) {
const data = clone(row)
data.files = JSON.parse(data.files)
this.formData = data
this.dialogVisible = true
},
doDelete(id) {
this.$confirm("确认删除?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
$.post("/platform/clubPreAudit/manage/doDelete", { id }).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "删除成功!"
})
this.pageData()
}
})
})
},
doSubmit(flag) {
this.$refs.form.validate(async (valid) => {
if (valid) {
this.$confirm("确定要" + (flag ? "提交" : "保存") + "吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.formLoading = true
$.post("/platform/clubPreAudit/manage/doSubmit", {
preAudit: JSON.stringify(this.formData),
flag: flag
}).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "操作成功!"
})
this.dialogVisible = false
this.pageData()
}
this.formLoading = false
})
})
}
})
}
},
async created() {
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,258 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.formData .viewFile {
height: 200px;
}
</style>
<div id="app" v-cloak>
<guava ref="guava">
<el-card shadow="never">
<div class="search">
<div class="search-item">
<div class="search-item-label">时间段:</div>
<div class="search-item-option">
<el-date-picker
v-model="pageForm.date"
type="daterange"
@change="dateChange"
align="right"
unlink-panels
value-format="yyyy-MM-dd"
style="width: 100%"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions">
</el-date-picker>
</div>
</div>
<div class="search-item">
<div class="search-item-label">姓名/工号:</div>
<div class="search-item-option">
<el-input @keyup.enter.native="doSearch" clearable
placeholder="请输入内容"
v-model="pageForm.searchKeyword">
<el-select clearable placeholder="查询类型"
slot="prepend"
style="width: 100px;"
v-model="pageForm.searchName">
<el-option label="姓名" value="userName"></el-option>
<el-option label="工号" value="loginName"></el-option>
</el-select>
</el-input>
</div>
</div>
<div class="search-item">
<div class="search-item-label">摘要:</div>
<div class="search-item-option">
<el-input v-model="pageForm.notes" placeholder="请输入摘要" clearable>
</el-input>
</div>
</div>
<div class="search-item">
<div class="search-item-label">所属工会:</div>
<div class="search-item-option">
<el-select placeholder="所属工会" v-model="pageForm.unionId"
style="width: 100%;"
clearable="true"
@change="flushUnits" @clear="flushUnits"
filterable>
<el-option v-for="item in unions" :label="item.unionname"
:value="item.id"></el-option>
</el-select>
</div>
</div>
<div class="search-item">
<div class="search-item-label">所属单位:</div>
<div class="search-item-option">
<el-select placeholder="所属单位" v-model="pageForm.unitId"
style="width: 100%;"
clearable="true"
filterable="true">
<el-option v-for="item in units" :label="item.name"
:value="item.id"></el-option>
</el-select>
</div>
</div>
<div class="search-item">
<div class="search-item-label">所属协会:</div>
<div class="search-item-option">
<el-select placeholder="所属协会" v-model="pageForm.clubId"
style="width: 100%;"
clearable="true"
filterable="true">
<el-option v-for="item in clubs" :label="item.name"
:value="item.id"></el-option>
</el-select>
</div>
</div>
<div class="search-query">
<el-button @click="doSearch" icon="el-icon-search" type="primary">搜索
</el-button>
</div>
</div>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool :app="this" label="预审列表">
<template #func>
</template>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" row-key="id"
style="width: 100%"
v-loading="tableLoading">
<el-table-column :index="indexMethod" align="center" header-align="center"
label="序号"
type="index"
width="80px"></el-table-column>
<el-table-column
:label="column.label"
:prop="column.prop"
:sortable="column.sortable"
align="center"
header-align="center"
v-for="column in tableColumns">
<template scope="{row}" v-if="column.prop==='auditState'">
<el-tag v-if="row.auditState===0" type="info">保存中</el-tag>
<el-tag v-if="row.auditState===1" type="info">待审核</el-tag>
<el-tag v-else-if="row.auditState===2" type="danger">退回修改</el-tag>
<el-tag v-else-if="row.auditState===3" type="danger">退回修改并结束</el-tag>
<el-tag v-else-if="row.auditState===4" type="success">已通过</el-tag>
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="操作"
width="100">
<template scope="{row}">
<el-button @click="openView(row)" type="primary" size="mini">查看
</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</guava>
<el-dialog
title="预审材料信息"
:visible.sync="viewDialogVisible"
width="40%"
:close-on-click-modal="false">
<info ref="info"></info>
<span slot="footer" class="dialog-footer">
<el-button @click="viewDialogVisible = false" type="primary">关 闭</el-button>
</span>
</el-dialog>
</div>
<script>
<!--#include("info.js"){}#-->
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
units: [],
unions: [],
clubs: [],
pageForm: {
date: [moment().subtract(1, "months")._d, new Date()],
year: new Date().getFullYear() + "",
clubId: ""
},
tableColumns: [
{prop: "loginName", label: "工号"},
{prop: "userName", label: "姓名"},
{prop: "unitName", label: "单位"},
{prop: "unionName", label: "工会"},
{prop: "clubName", label: "协会"},
{prop: "notes", label: "事由"},
{prop: "applyDate", label: "填写时间"},
{prop: "auditState", label: "状态"}
],
formRules: {},
viewDialogVisible: false,
pickerOptions: {
shortcuts: [{
text: "最近一周",
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit("pick", [start, end])
}
}, {
text: "最近一个月",
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit("pick", [start, end])
}
}, {
text: "最近三个月",
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit("pick", [start, end])
}
}]
}
}
},
components: {
"info": info
},
methods: {
dateChange(val) {
if (val && val.length > 0) {
this.$set(this.pageForm, "startDate", val[0])
this.$set(this.pageForm, "endDate", val[1])
} else {
this.$set(this.pageForm, "startDate", "")
this.$set(this.pageForm, "endDate", "")
}
},
openView(row) {
this.viewDialogVisible = true
setTimeout(() => {
this.$refs.info.openView(row.id)
}, 300)
},
async flushUnits() {
this.$set(this.pageForm, "unitId", "")
if ("${@shiro.hasRole('sysadmin')||@shiro.hasRole('A06')||@shiro.hasRole('H03')}" === "true") {
this.units = await getUnits(this.pageForm.unionId)
} else {
this.units = await getUnits("${@shiro.getPrincipalProperty('unit').getUnionid()}")
}
},
async getClubs() {
const resp = await $.post("/platform/clubPreAudit/manage/getClubs")
if (resp.code === 0) {
this.clubs = resp.data
}
}
},
async created() {
this.unions = await getUnions(null)
this.flushUnits()
await this.getClubs()
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -264,7 +264,7 @@
<p class="van-doc-demo-block__title" style="margin-top: 5px;color: red">
(如同时参加以下多个活动,或有多个主题,请分别提交)</p>
</div>
<van-tabs :color="boxData.hdcolor" type="card" v-model="formData.hdlb_i">
<van-tabs :color="boxData.hdcolor" @change="hdlbChange" type="card" v-model="formData.hdlb_i">
<van-tab :name="i" :title="lb.lname" v-for="lb,i in boxData.lbs">
<div v-if="formData.hdlb_i==i">
<!-- <van-field style="padding-top: 30px" maxlength="15"
@@ -481,28 +481,12 @@
</van-tabs>
<div style="margin: 16px;">
<van-row gutter="20">
<van-col span="12">
<van-button :color="boxData.hdcolor" :disabled="subDis"
@click="doSubmit(false)" block
plain
round
type="info">
保存
</van-button>
</van-col>
<van-col span="12">
<van-button :color="boxData.hdcolor" :disabled="subDis"
@click="doSubmit(true)" block
round
type="info">
提交
</van-button>
</van-col>
</van-row>
<van-button :color="boxData.hdcolor" :disabled="subDis"
@click="doSubmit(true)" block
round
type="info">
提交
</van-button>
</div>
</van-form>
</div>
@@ -594,10 +578,27 @@
},
methods: {
zplbClick(val, fd, i) {
this.chooseLb = val
fd[i].zplb = val.zid;
fd[i].zplb_name = val.zname;
this.showPicker = false
this.$set(this, 'chooseLb', val)
this.$set(fd[i], 'zplb', val.zid)
this.$set(fd[i], 'zplb_name', val.zname)
this.$set(this, 'showPicker', false)
},
hdlbChange(i) {
this.setDefaultZplb(i)
},
setDefaultZplb(i) {
const lb = this.boxData.lbs && this.boxData.lbs[i]
if (lb && lb.zplbs && lb.zplbs.length > 0) {
const val = this.fd[i].zplb ? lb.zplbs.find(o => o.zid === this.fd[i].zplb) : lb.zplbs[0]
this.$set(this, 'chooseLb', val || {})
if (!this.fd[i].zplb && val) {
// 默认选择第一个作品类别,保证对应上传配置可以立即生效。
this.$set(this.fd[i], 'zplb', val.zid)
this.$set(this.fd[i], 'zplb_name', val.zname)
}
} else {
this.$set(this, 'chooseLb', {})
}
},
activeChange() {
if (this.subDis) {
@@ -658,11 +659,17 @@
this.$toast.fail("请您选择要上传的作品!")
return
}*/
if (this.chooseLb.isUploadPhoto === true || this.chooseLb.isUploadOther === true || this.chooseLb.isUploadZip === true) {
if (this.fd[i].videoList.length === 0 || this.fd[i].imgList.length === 0 || this.fd[i].zipList.length === 0) {
this.$toast.fail("请您选择要上传的作品!")
return
}
if (this.chooseLb.isUploadPhoto === true && this.fd[i].imgList.length === 0) {
this.$toast.fail("请您选择要上传的图片!")
return
}
if (this.chooseLb.isUploadOther === true && this.fd[i].videoList.length === 0) {
this.$toast.fail("请您选择要上传的音/视频或文档!")
return
}
if (this.chooseLb.isUploadZip === true && this.fd[i].zipList.length === 0) {
this.$toast.fail("请您选择要上传的压缩包!")
return
}
if (!this.fd[i].zplb || !this.fd[i].zpmc || !this.fd[i].zpms) {
this.$toast.fail("请您填写完整再提交!")
@@ -676,7 +683,7 @@
}*/
}
const msg = '您当前选择的是“' + this.boxData.lbs[i].lname + '”,是否确认上传?' + (flag ? '上传后您将不能对已上传的作品进行更改' : '保存后如需提交请到我的作品进行提交')
const msg = '您当前选择的是“' + this.boxData.lbs[i].lname + '”,是否确认提交?活动时间内可编辑或删除已提交作品'
const confirm = await this.$dialog.confirm({
title: '提醒',
message: msg
@@ -686,6 +693,7 @@
this.showMsg = true
this.showPlan = true
this.percent = 0
this.$set(this, 'flag', false)
this.msg = ''
let formData1 = {}
@@ -769,22 +777,22 @@
Cookies.remove('sa_formData');
vue.msg = '<p class="weui-msg__desc">我们已收到您的作品!<br/>非常感谢您的支持!</p>'
this.flag = true
vue.$set(vue, 'msg', '<p class="weui-msg__desc">我们已收到您的作品!<br/>非常感谢您的支持!</p>')
vue.$set(vue, 'flag', true)
location.href = '/mobile/wmhd/list'
} else {
vue.msg = '<p class="weui-msg__desc">非常抱歉对您造成的不便!<br/>请联系系统管理员进行修复!</p>'
this.flag = false
vue.$set(vue, 'msg', '<p class="weui-msg__desc">' + (data.msg || '非常抱歉对您造成的不便!<br/>请联系系统管理员进行修复!') + '</p>')
vue.$set(vue, 'flag', false)
}
vue.showPlan = false
location.href = '/mobile/wmhd/list'
vue.$set(vue, 'showPlan', false)
},
error: function (data) {
vue.msg = '<p class="weui-msg__desc">非常抱歉对您造成的不便!<br/>请联系系统管理员进行修复!</p>'
this.flag = false
vue.showPlan = false
vue.$set(vue, 'msg', '<p class="weui-msg__desc">非常抱歉对您造成的不便!<br/>请联系系统管理员进行修复!</p>')
vue.$set(vue, 'flag', false)
vue.$set(vue, 'showPlan', false)
}
})
} else {
@@ -819,20 +827,19 @@
Cookies.remove('sa_formData');
vue.msg = '<p class="weui-msg__desc">我们已收到您的作品!<br/>非常感谢您的支持!</p>'
this.flag = true
vue.$set(vue, 'msg', '<p class="weui-msg__desc">我们已收到您的作品!<br/>非常感谢您的支持!</p>')
vue.$set(vue, 'flag', true)
} else {
vue.msg = '<p class="weui-msg__desc">非常抱歉对您造成的不便!<br/>请联系系统管理员进行修复!</p>'
this.flag = false
vue.$set(vue, 'msg', '<p class="weui-msg__desc">' + (data.msg || '非常抱歉对您造成的不便!<br/>请联系系统管理员进行修复!') + '</p>')
vue.$set(vue, 'flag', false)
}
vue.showPlan = false
vue.$set(vue, 'showPlan', false)
},
error: function (data) {
vue.msg = '<p class="weui-msg__desc">非常抱歉对您造成的不便!<br/>请联系系统管理员进行修复!</p>'
this.flag = false
vue.showPlan = false
vue.$set(vue, 'msg', '<p class="weui-msg__desc">非常抱歉对您造成的不便!<br/>请联系系统管理员进行修复!</p>')
vue.$set(vue, 'flag', false)
vue.$set(vue, 'showPlan', false)
}
});
}
@@ -912,7 +919,8 @@
})
})
this.boxData = data.data
this.formData.hdlb_i = 0
this.$set(this.formData, 'hdlb_i', 0)
this.setDefaultZplb(0)
this.showPage = true
if (!data.data.hdflag) {
@@ -944,7 +952,7 @@
this.$set(this.fd[refs[0].llocation - 1], "hdsbsj", refs[0].hdsbsj)
this.$set(this.fd[refs[0].llocation - 1], "zpms", refs[0].hdsbzpms)
const o = this.boxData.lbs[refs[0].llocation - 1].zplbs.find(o => o.zid === refs[0].hdzplx2)
this.chooseLb = o !== undefined ? o : {}
this.$set(this, 'chooseLb', o !== undefined ? o : {})
data.forEach((v, i) => {
v.filename = v.filename
v.fjurl = v.filepath
@@ -567,7 +567,7 @@ layout("/layouts/platform.html"){
"&stateId=" + stateId);
},
exportExcel: function () {
let {hdid, hdlb, zplb, union, unit, stateId, isExcellent} = this.pageForm;
let {hdid, hdlb, zplb, union, unit, year, searchName, searchKeyword, stateId, isExcellent} = this.pageForm;
if (hdid === '') {
this.$message.warning('请先选择活动主题');
return;
@@ -576,14 +576,20 @@ layout("/layouts/platform.html"){
return o.value == hdid;
});
let hdname = hdarr[0].label;
window.open('/platform/hd/wmhd/exportExcel?hdid=' + hdid +
'&hdname=' + hdname +
"&hdlb=" + hdlb +
"&zplb=" + zplb +
"&union=" + union +
"&unit=" + unit +
"&isExcellent=" + isExcellent +
"&stateId=" + stateId);
let query = $.param({
hdid: hdid,
hdname: hdname,
hdlb: hdlb,
zplb: zplb,
union: union,
unit: unit,
year: year,
searchName: searchName,
searchKeyword: searchKeyword,
isExcellent: isExcellent,
stateId: stateId
});
window.open('/platform/hd/wmhd/exportExcel?' + query);
},
doSearch: function () {
this.pageForm.pageNumber = 1;
@@ -282,13 +282,13 @@ layout("/layouts/platform.html"){
<el-button @click="viewInfo(scope)" size="mini" type="primary">查看
</el-button>
<!-- v-if="scope.row.auditstate==='3999'||scope.row.auditstate==='4100'||scope.row.auditstate==='4250'"-->
<el-button :disabled="scope.row.auditstate!=='3999'"
<el-button :disabled="!isActivityTime(scope.row)"
@click="openEdit(scope)" size="mini"
type="primary">
编辑
</el-button>
<el-button :disabled="scope.row.auditstate!=='3999'"
@click="dodelete(scope.row.id)"
<el-button :disabled="!isActivityTime(scope.row)"
@click="dodelete(scope.row)"
size="mini"
type="danger">删除
</el-button>
@@ -413,8 +413,7 @@ layout("/layouts/platform.html"){
</el-form>
<div style="float: right;margin: 20px 0">
<el-button @click="doAdd(false)">保 存</el-button>
<el-button @click="doAdd(true)" type="primary">提 交</el-button>
<el-button @click="doAdd" type="primary">提 交</el-button>
</div>
</template>
@@ -525,8 +524,7 @@ layout("/layouts/platform.html"){
</el-form>
<div style="float: right;margin: 20px 0">
<el-button @click="doEdit(false)">保 存</el-button>
<el-button @click="doEdit(true)" type="primary">提 交</el-button>
<el-button @click="doEdit" type="primary">提 交</el-button>
</div>
</template>
@@ -616,6 +614,15 @@ layout("/layouts/platform.html"){
changeHdzplx2(id) {
this.zplb2Config = this.formhdzplblist.find(v => v.zid === id)
},
isActivityTime(row) {
if (!row || !row.hdkssj || !row.hdjssj) {
return false;
}
const startTime = new Date(row.hdkssj.replace(/-/g, '/')).getTime();
const endTime = new Date(row.hdjssj.replace(/-/g, '/')).getTime();
const nowTime = new Date().getTime();
return startTime <= nowTime && nowTime <= endTime;
},
yearChange(val) {
let activityList = []
if (val === null) {
@@ -743,10 +750,10 @@ layout("/layouts/platform.html"){
this.$refs['AddForm'].resetFields();
}
},
doAdd(flag) {
doAdd() {
this.$refs['AddForm'].validate((valid) => {
if (valid) {
if (flag && ((this.zplb2Config.isUploadPhoto && this.picturelist.length === 0) ||
if (((this.zplb2Config.isUploadPhoto && this.picturelist.length === 0) ||
(this.zplb2Config.isUploadOther && this.vadlist.length === 0) ||
(this.zplb2Config.isUploadZip && this.ziplist.length === 0))) {
this.$message.warning('请上传您的作品');
@@ -759,7 +766,7 @@ layout("/layouts/platform.html"){
background: 'rgba(0, 0, 0, 0.7)'
});
let formdata = new FormData();
formdata.append("flag", flag);
formdata.append("flag", true);
formdata.append("data", JSON.stringify(this.AddFormData));
if(this.picturelist && this.picturelist.length > 0) {
this.picturelist.forEach(val => {
@@ -789,7 +796,7 @@ layout("/layouts/platform.html"){
this.$refs.guava.index()
this.$message.success('上传成功');
} else {
this.$message.error('上传失败');
this.$message.error(res.msg || '上传失败');
}
loading.close();
})
@@ -797,6 +804,10 @@ layout("/layouts/platform.html"){
});
},
openEdit(scope) {
if (!this.isActivityTime(scope.row)) {
this.$message.warning('不在活动时间范围内,不能编辑');
return;
}
this.readytodelfile = [];
this.picturelist = [];
this.vadlist = [];
@@ -821,10 +832,10 @@ layout("/layouts/platform.html"){
},
doEdit(flag) {
doEdit() {
this.$refs['EditForm'].validate((valid) => {
if (valid) {
if (flag && ((this.zplb2Config.isUploadPhoto && this.picturelist.length === 0) ||
if (((this.zplb2Config.isUploadPhoto && this.picturelist.length === 0) ||
(this.zplb2Config.isUploadOther && this.vadlist.length === 0) ||
(this.zplb2Config.isUploadZip && this.ziplist.length === 0))) {
this.$message.warning('请上传您的作品');
@@ -838,7 +849,7 @@ layout("/layouts/platform.html"){
background: 'rgba(0, 0, 0, 0.7)'
});
let formdata = new FormData();
formdata.append("flag", flag);
formdata.append("flag", true);
formdata.append("data", JSON.stringify(this.EditFormData));
formdata.append("readytodelfile", JSON.stringify(this.readytodelfile));
if(this.picturelist && this.picturelist.length > 0) {
@@ -877,7 +888,7 @@ layout("/layouts/platform.html"){
this.$refs.guava.index()
this.$message.success('上传成功');
} else {
this.$message.error('上传失败');
this.$message.error(res.msg || '上传失败');
}
loading.close();
@@ -971,16 +982,22 @@ layout("/layouts/platform.html"){
}
});
},
dodelete(id) {
dodelete(row) {
if (!this.isActivityTime(row)) {
this.$message.warning('不在活动时间范围内,不能删除');
return;
}
this.$confirm('此操作将永久删除该记录, 确定吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
$.post('/platform/hd/wmhd/doDelete', {id: id}).then(data => {
$.post('/platform/hd/wmhd/doDelete', {id: row.id}).then(data => {
if (data.code == 0) {
this.doSearch();
this.$message.success(data.msg);
} else {
this.$message.error(data.msg || '删除失败');
}
});
}).catch(() => {
@@ -92,6 +92,7 @@ layout("/layouts/platform.html"){
<!-- <el-input v-model="row.userName" maxlength="10" size="small"></el-input>-->
<!-- </template>-->
</el-table-column>
<el-table-column label="身份证号" prop="idcard" width="200"></el-table-column>
<el-table-column label="单位" prop="unitName" show-overflow-tooltip width="150">
<!-- <template scope="{row}">-->
<!-- <el-input v-model="row.unitName" maxlength="10" size="small"></el-input>-->
@@ -239,8 +240,8 @@ layout("/layouts/platform.html"){
async doImport() {
const hasErrorData = this.tableData.some(v => !v.valid)
if (hasErrorData) {
// this.notifyWarning('数据校验不合格,无法导入')
// return
this.notifyWarning('数据校验不合格,无法导入')
return
}
const resp = await $.post('/platform/sickHelp/import/doImport', {sickHelps: JSON.stringify(this.tableData)})
if (resp.code === 0) {
@@ -248,6 +249,8 @@ layout("/layouts/platform.html"){
this.globalYear = null
this.notifySuccess(resp.msg)
this.$refs.excelUpload.clearFiles()
} else {
this.notifyWarning(resp.msg)
}
},
globalYearChange(val) {
@@ -76,41 +76,46 @@ layout("/layouts/platform.html"){
<vi-title2 title="填写补助数据"></vi-title2>
<el-row>
<el-col :lg="8" :xs="24">
<el-col :lg="6" :xs="24">
<el-form-item label="工号" prop="loginName">
<el-select :disabled="formDisabled"
:remote-method="searchUser"
@change="userChange"
clearable
filterable
placeholder="输入工号或者姓名查询"
placeholder="输入工号、姓名或身份证号查询"
remote
reserve-keyword
style="width: 100%"
v-model="formData.loginName">
<el-option
:key="item.loginName"
:label="item.userName+'('+item.loginName+')'"
:label="item.userName+'('+item.loginName+') '+(item.effectiveIdcard || '')"
:value="item.loginName"
v-for="item in userList"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :lg="8" :xs="24">
<el-col :lg="6" :xs="24">
<el-form-item label="姓名" prop="userName">
<el-input :disabled="!this.formData.id"
placeholder="输入工号之后自动显示"
v-model="formData.userName"></el-input>
</el-form-item>
</el-col>
<el-col :lg="8" :xs="24">
<el-col :lg="6" :xs="24">
<el-form-item label="身份证号" prop="idcard">
<el-input placeholder="选择工号之后自动显示" readonly
v-model="formData.idcard"></el-input>
</el-form-item>
</el-col>
<el-col :lg="6" :xs="24">
<el-form-item label="单位" prop="unitName">
<el-input placeholder="输入工号之后自动显示"
v-model="formData.unitName"></el-input>
</el-form-item>
</el-col>
</el-col>
</el-row>
<el-row :gutter="20">
@@ -337,6 +342,7 @@ layout("/layouts/platform.html"){
this.$set(this.formData, 'subsidyDesc', resp_data.subsidyDesc)
this.$set(this.formData, 'subsidyMoney', resp_data.subsidyMoney)
this.$set(this.formData, 'unitName', resp_data.unitName)
this.$set(this.formData, 'idcard', resp_data.idcard)
this.notifySuccess(resp_data.validMsg.join('、'))
} else {
this.notifyWarning(resp_data.validMsg.join('、'))
@@ -376,6 +382,7 @@ layout("/layouts/platform.html"){
this.$set(this.formData, 'userName', user.userName)
this.$set(this.formData, 'unitName', user.unitName)
this.$set(this.formData, 'loginName', user.loginName)
this.$set(this.formData, 'idcard', user.effectiveIdcard)
await this.getHistoryApplyRecords(user.loginName, this.formData.year)
},
async getHistoryApplyRecords(loginName, year) {
@@ -401,6 +408,8 @@ layout("/layouts/platform.html"){
await this.searchUser(loginName)
resp.data.year = resp.data.year.toString()
this.formData = resp.data
const user = this.userList.find(v => v.loginName === loginName)
this.$set(this.formData, 'idcard', user ? user.effectiveIdcard : null)
await this.getHistoryApplyRecords(loginName, this.formData.year)
}
},
@@ -412,4 +421,4 @@ layout("/layouts/platform.html"){
<!--#
}
#-->
#-->
@@ -45,7 +45,7 @@ layout("/layouts/platform.html"){
<div class="search-item">
<div class="search-item-label">补助人:</div>
<div class="search-item-option">
<el-input clearable maxlength="10" placeholder="可输入补助人的工号或姓名进行查询"
<el-input clearable maxlength="50" placeholder="可输入补助人的工号、姓名或身份证号进行查询"
v-model.trim="pageForm.subsidyUser"></el-input>
</div>
</div>
@@ -111,6 +111,16 @@ layout("/layouts/platform.html"){
</div>
</div>
<div class="search-item">
<div class="search-item-label">身份证号关联风险:</div>
<div class="search-item-option">
<el-select clearable style="width: 100%" v-model="pageForm.riskType">
<el-option label="全部风险数据" :value="3"></el-option>
<el-option label="工号身份证与医保身份证不一致" :value="1"></el-option>
<el-option label="同名且身份证缺失无法确认" :value="2"></el-option>
</el-select>
</div>
</div>
<div class="search-query">
<el-button @click="doSearch" icon="el-icon-search" type="primary">搜索</el-button>
</div>
@@ -180,9 +190,14 @@ layout("/layouts/platform.html"){
<span v-if="row.isMember==false" class="text-danger">否</span>
<span v-if="row.isMember==null"></span>
</template>
<template scope="{row}" v-else-if="column.prop=='relationRisk'">
<el-tag size="mini" type="danger" v-if="row.idcardMismatchRisk==1">身份证不一致</el-tag>
<el-tag size="mini" type="warning" v-if="row.duplicateMissingIdRisk==1">重名且身份证缺失</el-tag>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="200">
<el-table-column fixed="right" label="操作" width="300">
<template scope="{row}">
<el-button @click="onRebind(row)" size="mini" type="warning">重新关联人员</el-button>
<el-button @click="openEdit(row)" size="mini" type="primary">编辑</el-button>
<el-button @click="doDelete(row.id)" size="mini" type="danger">删除</el-button>
</template>
@@ -257,18 +272,27 @@ layout("/layouts/platform.html"){
</guava>
<rebind-form ref="rebindFormRef" @refresh="doSearch"></rebind-form>
</div>
<script>
<!--#include("/platform/sickHelp/medicalIns/rebindForm.js"){}#-->
const vue = new Vue({
el: '#app',
mixins: [initTableMixins],
components: {
"rebind-form": rebindForm
},
data() {
return {
tableColumns: [
{prop: 'year', label: '年度', sortable: true, fixed: 'left'},
{prop: 'userName', label: '姓名', fixed: 'left'},
{prop: 'loginName', label: '工号', fixed: 'left', width: 120},
{prop: 'systemIdcard', label: '工号身份证号', fixed: 'left', width: 200},
{prop: 'medicalIdcard', label: '医保身份证号', width: 200},
{prop: 'relationRisk', label: '关联风险', width: 180},
{prop: 'unionName', label: '分工会', sortable: true, width: 150},
{prop: 'unitName', label: '单位', sortable: true, width: 150},
@@ -304,7 +328,8 @@ layout("/layouts/platform.html"){
isMember: null,
unitName: '',
isMeetRequirements: null,
isAccept: null
isAccept: null,
riskType: null
},
unions: [],
units: [],
@@ -314,6 +339,10 @@ layout("/layouts/platform.html"){
}
},
methods: {
onRebind(row) {
this.$refs.rebindFormRef.onOpen(row, 'summary')
},
async importData() {
if (this.excelTableData.length === 0) {
this.notifyWarning('无数据')
@@ -158,6 +158,16 @@ layout("/layouts/platform.html"){
</div>
<div class="search-item">
<div class="search-item-label">身份证号关联风险:</div>
<div class="search-item-option">
<el-select clearable style="width: 100%" v-model="pageForm.riskType">
<el-option label="全部风险数据" :value="3"></el-option>
<el-option label="工号身份证与医保身份证不一致" :value="1"></el-option>
<el-option label="同名且身份证缺失无法确认" :value="2"></el-option>
</el-select>
</div>
</div>
<div class="search-query">
<el-button @click="doSearch" icon="el-icon-search" type="primary">搜索</el-button>
</div>
@@ -190,6 +200,7 @@ layout("/layouts/platform.html"){
:prop="column.prop"
:sortable="column.sortable"
:width="column.width"
:formatter="column.prop === 'calc' ? formatCalc : undefined"
align="center"
header-align="center"
:fixed="column.fixed"
@@ -197,8 +208,15 @@ layout("/layouts/platform.html"){
v-for="column in tableColumns"
>
</el-table-column>
<el-table-column label="操作" align="center" header-align="center" fixed="right" width="200">
<el-table-column label="关联风险" align="center" header-align="center" width="180">
<template scope="{row}">
<el-tag size="mini" type="danger" v-if="row.idcardMismatchRisk==1">身份证不一致</el-tag>
<el-tag size="mini" type="warning" v-if="row.duplicateMissingIdRisk==1">重名且身份证缺失</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" header-align="center" fixed="right" width="300">
<template scope="scope">
<el-button @click="onRebind(scope.row)" size="mini" type="warning">重新关联人员</el-button>
<el-button @click="onEdit(scope.row)" size="mini" type="primary">编辑</el-button>
<el-button @click="onDelete(scope.row.id)" size="mini" type="danger">删除</el-button>
</template>
@@ -210,12 +228,14 @@ layout("/layouts/platform.html"){
<ins-import ref="insImportRef" @refresh="doSearch"></ins-import>
<ec-import ref="ecImportRef" @refresh="doSearch"></ec-import>
<ins-form ref="insFormRef" @refresh="doSearch"></ins-form>
<rebind-form ref="rebindFormRef" @refresh="doSearch"></rebind-form>
</div>
<script>
<!--#include('insImport.js'){}#-->
<!--#include('insForm.js'){}#-->
<!--#include('ecImport.js'){}#-->
<!--#include('rebindForm.js'){}#-->
const vue = new Vue({
el: '#app',
@@ -223,14 +243,16 @@ layout("/layouts/platform.html"){
components: {
"ins-import": insImport,
"ec-import": ecImport,
"ins-form": insForm
"ins-form": insForm,
"rebind-form": rebindForm
},
data() {
return {
tableColumns: [
{prop: 'userName', label: '姓名'},
{prop: 'loginName', label: '工号'},
{prop: 'idCard', label: '身份证号', width: 200},
{prop: 'systemIdcard', label: '工号身份证号', width: 200},
{prop: 'idCard', label: '医保身份证号', width: 200},
{prop: 'zhzl', label: '账户自理'},
{prop: 'zhzf', label: '账户自付'},
{prop: 'xjzl', label: '现金自理'},
@@ -241,11 +263,16 @@ layout("/layouts/platform.html"){
{prop: 'calc', label: '补助计算金额'},
],
pageForm: {
year: (new Date().getFullYear() - 1).toString()
year: (new Date().getFullYear() - 1).toString(),
riskType: null
}
}
},
methods: {
// 补助金额未写入时仅在列表显示 0,保留数据库原值供其他流程判断。
formatCalc(row, column, value) {
return value == null ? 0 : value
},
openEc() {
$.post('/platform/sickHelp/medicalIns/checkIns').then(res => {
if (res.code === 0) {
@@ -256,6 +283,10 @@ layout("/layouts/platform.html"){
})
},
onRebind(row) {
this.$refs.rebindFormRef.onOpen(row, 'medical')
},
onEdit(row) {
this.$refs.insFormRef.onOpen(row)
},
@@ -283,7 +314,7 @@ layout("/layouts/platform.html"){
$.post(loc() + '/checkEc').then(res => {
if (res.code === 0) {
this.$confirm('计算补助金额将会自动过滤掉补助计算金额小于10000元的记录, 是否继续?', '提示', {
this.$confirm('计算补助金额将会自动过滤掉补助计算金额不超过10000元的记录, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
@@ -0,0 +1,205 @@
const rebindForm = {
template: /*language=HTML*/ `
<el-dialog title="重新关联人员" :visible.sync="visible" :close-on-click-modal="false" width="850px">
<el-alert
title="该操作会同步修正医保数据和尚未进入审核流程的补助汇总,请填写正确人员本人的二次报销金额。"
type="warning"
:closable="false"
show-icon>
</el-alert>
<el-form class="mt20" v-loading="loading" :model="form" :rules="rules" ref="form" label-width="150px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="原姓名">
<el-input :value="source.userName" disabled></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="原工号">
<el-input :value="source.loginName" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="医保身份证号">
<el-input :value="source.idCard" disabled></el-input>
</el-form-item>
<el-form-item label="重新关联人员" prop="loginName">
<el-select
v-model="form.loginName"
style="width: 100%"
filterable
remote
reserve-keyword
clearable
:loading="userLoading"
:remote-method="remoteMethod"
placeholder="请输入姓名、工号或身份证号查询">
<el-option
v-for="item in userOptions"
:key="item.loginname"
:label="userLabel(item)"
:value="item.loginname">
</el-option>
</el-select>
</el-form-item>
<div v-if="selectedUser" style="margin: -10px 0 18px 150px;color: #606266;line-height: 1.8;">
<div>姓名:{{selectedUser.username}},工号:{{selectedUser.loginname}}</div>
<div>出生日期:{{selectedUser.birthday || '未录入'}},单位:{{selectedUser.unitname || '未录入'}}</div>
<div>身份证号:{{maskIdCard(selectedUser.idcard)}}</div>
<div v-if="!selectedUser.idcard" style="color: #e6a23c;">
该人员未录入身份证号,请结合工号、出生日期和单位人工确认。
</div>
</div>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="医保四项之和">
<el-input :value="source.sxzf" disabled></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="正确二次报销金额" prop="ecbx">
<el-input-number
v-model="form.ecbx"
:precision="2"
:min="0"
:controls="false"
:max="source.sxzf === null || source.sxzf === undefined ? 99999999.99 : Number(source.sxzf)"
style="width: 100%">
</el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-alert
v-if="selfPayMoney !== null"
:title="resultMessage"
:type="isEligible ? 'warning' : 'success'"
:closable="false"
show-icon>
</el-alert>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" :loading="saving" @click="onSave">确认重新关联</el-button>
</div>
</el-dialog>
`,
data() {
return {
visible: false,
loading: false,
saving: false,
userLoading: false,
source: {},
form: {
medicalInsId: null,
loginName: null,
ecbx: null
},
userOptions: [],
rules: {
loginName: [{required: true, message: '请选择需要重新关联的人员', trigger: 'change'}],
ecbx: [{required: true, message: '请输入正确人员的学校二次报销金额', trigger: 'change'}]
}
}
},
computed: {
selectedUser() {
return this.userOptions.find(v => v.loginname === this.form.loginName)
},
selfPayMoney() {
if (this.source.sxzf === null || this.source.sxzf === undefined
|| this.form.ecbx === null || this.form.ecbx === undefined) {
return null
}
return Number(this.source.sxzf) - Number(this.form.ecbx)
},
isEligible() {
return this.selfPayMoney !== null && this.selfPayMoney > 10000
},
resultMessage() {
const money = this.selfPayMoney.toFixed(2)
return this.isEligible
? '扣减二次报销后的个人支付金额为 ' + money + ' 元,系统将按正确人员重新计算补助汇总。'
: '扣减二次报销后的个人支付金额为 ' + money + ' 元,不超过10000元,系统将删除原错误补助汇总。'
}
},
methods: {
async onOpen(row, sourceType) {
this.visible = true
this.loading = true
this.source = {}
this.userOptions = []
this.form = {medicalInsId: null, loginName: null, ecbx: null}
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
const params = sourceType === 'medical'
? {medicalInsId: row.id}
: {year: row.year, loginName: row.loginName}
const resp = await $.post('/platform/sickHelp/medicalIns/findRebindSource', params)
this.loading = false
if (resp.code !== 0) {
this.visible = false
this.$message.error(resp.msg)
return
}
this.source = resp.data
this.form.medicalInsId = resp.data.id
this.form.ecbx = resp.data.ecbx === null || resp.data.ecbx === undefined || resp.data.ecbx === ''
? 0 : Number(resp.data.ecbx)
},
async remoteMethod(query) {
if (!query) {
this.userOptions = []
return
}
this.userLoading = true
const resp = await $.post('/platform/sickHelp/medicalIns/queryRebindUser', {query})
this.userLoading = false
if (resp.code === 0) {
this.userOptions = resp.data || []
}
},
userLabel(user) {
return user.username + '(' + user.loginname + ') '
+ (user.birthday || '生日未录入') + ' '
+ (user.unitname || '单位未录入') + ' '
+ this.maskIdCard(user.idcard)
},
maskIdCard(idCard) {
if (!idCard) return '身份证未录入'
const value = idCard.toString().trim()
if (value.length <= 10) return value
return value.substring(0, 6) + '********' + value.substring(value.length - 4)
},
onSave() {
this.$refs.form.validate(async valid => {
if (!valid) return
const tip = this.isEligible
? '确定重新关联人员并按正确人员重新计算补助汇总吗?'
: '修正后不超过10000元,系统会删除原错误补助汇总,确定继续吗?'
await this.$confirm(tip, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
this.saving = true
const resp = await $.post('/platform/sickHelp/medicalIns/rebindUser', this.form)
this.saving = false
if (resp.code === 0) {
this.visible = false
this.$message.success(resp.msg)
this.$emit('refresh')
} else {
this.$message.error(resp.msg)
}
})
}
}
};
@@ -100,6 +100,7 @@ layout("/layouts/platform.html"){
{prop: 'userName', label: '姓名'},
{prop: 'loginName', label: '工号'},
{prop: 'sex', label: '性别'},
{prop: 'idcard', label: '身份证号'},
{prop: 'unitName', label: '单位'},
{prop: 'userState', label: '状态'},
{prop: 'mutualGoldStartTime', label: '入会时间'},
@@ -163,4 +164,4 @@ layout("/layouts/platform.html"){
<!--#
}
#-->
#-->
@@ -24,9 +24,9 @@ layout("/layouts/platform.html"){
</div>
<div class="search-item">
<div class="search-item-label">工号姓名:</div>
<div class="search-item-label">会员信息:</div>
<div class="search-item-option">
<el-input clearable maxlength="10" placeholder="可输入会员的工号或姓名进行查询"
<el-input clearable maxlength="50" placeholder="可输入会员的工号、姓名或身份证号进行查询"
v-model.trim="pageForm.keyWord"></el-input>
</div>
</div>
@@ -147,6 +147,10 @@ layout("/layouts/platform.html"){
<el-radio-button label="女"></el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="身份证号" prop="idcard">
<el-input @input="$refs.form.validateField('idcard')" maxlength="18"
v-model.trim="formData.idcard"></el-input>
</el-form-item>
<el-form-item label="单位" prop="unitId">
<el-select clearable filterable style="width: 100%"
@@ -194,6 +198,20 @@ layout("/layouts/platform.html"){
</div>
<script>
const idcardPattern = /^[1-9]\d{5}(18|19|20|(3\d))\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/
const validateIdcard = (rule, value, callback) => {
if (!value) {
callback()
return
}
if (!idcardPattern.test(value)) {
callback(new Error('请输入正确的身份证号'))
return
}
callback()
}
const vue = new Vue({
el: '#app',
mixins: [initTableMixins],
@@ -215,6 +233,7 @@ layout("/layouts/platform.html"){
loginName: [{required: true, message: '请输入工号', trigger: ['change', 'blur']}],
userName: [{required: true, message: '请输入姓名', trigger: ['change', 'blur']}],
// sex: [{required: true, message: '请选择性别', trigger: ['change', 'blur']}],
idcard: [{validator: validateIdcard, trigger: ['change', 'blur']}],
unitName: [{required: false, message: '请输入单位', trigger: ['change', 'blur']}],
unitId: [{required: true, message: '请选择单位', trigger: ['change', 'blur']}],
isMember: [{required: true, message: '请选择是否会员', trigger: ['change', 'blur']}],
@@ -230,6 +249,7 @@ layout("/layouts/platform.html"){
userName: null,
loginName: null,
sex: null,
idcard: null,
unitName: null,
unitId: null,
isMember: null,
@@ -302,6 +322,12 @@ layout("/layouts/platform.html"){
})
},
async doHandle() {
const idcard = (this.formData.idcard || '').trim()
if (idcard && !idcardPattern.test(idcard)) {
this.notifyWarning('请输入正确的身份证号')
return
}
this.$set(this.formData, 'idcard', idcard || null)
const valid = await this.$refs.form.validate()
if (!valid) return
const url = this.formData.id ? '/doEdit' : '/doAdd'
@@ -121,6 +121,9 @@ layout("/layouts/platform.html"){
<el-button icon="el-icon-plus" @click="openAdd">创建协会</el-button>
&lt;!&ndash;#}#&ndash;&gt;
</div>-->
<div class="pull-right offscreen-right mt5">
<el-button type="primary" icon="el-icon-upload2" @click="openImport">导入成员</el-button>
</div>
</el-card>
<el-row class="el-table-container">
@@ -259,6 +262,7 @@ layout("/layouts/platform.html"){
<div class="pull-right offscreen-right mt5" v-if="userPageForm.radioType === '2'">
<el-button icon="el-icon-plus" @click="openUserAdd">录入成员</el-button>
<el-button icon="el-icon-download" @click="exportClubMembers">导出</el-button>
</div>
</el-card>
@@ -291,6 +295,13 @@ layout("/layouts/platform.html"){
<el-lable v-if="scope.row.sf==3">副会长</el-lable>
<el-lable v-if="scope.row.sf==4">秘书长</el-lable>
<el-lable v-if="scope.row.sf==5">副秘书长</el-lable>
<el-lable v-if="scope.row.sf==6">经办人</el-lable>
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="入会时间" prop="join_time">
<template slot-scope="scope">
<span>{{(scope.row.join_time || '').substring(0, 10)}}</span>
</template>
</el-table-column>
@@ -575,6 +586,7 @@ layout("/layouts/platform.html"){
<el-lable v-if="scope.row.sf==3">副会长</el-lable>
<el-lable v-if="scope.row.sf==4">秘书长</el-lable>
<el-lable v-if="scope.row.sf==5">副秘书长</el-lable>
<el-lable v-if="scope.row.sf==6">经办人</el-lable>
</template>
</el-table-column>
<el-table-column label="工号" width="150px" prop="loginname" header-align="center"
@@ -646,6 +658,57 @@ layout("/layouts/platform.html"){
</span>
</el-dialog>
<el-dialog
title="导入成员"
:visible.sync="importDialogVisible" :close-on-click-modal="false" width="40%">
<el-timeline>
<el-timeline-item placement="top" timestamp="下载模板">
<el-card>
<el-button
@click="downloadImportTemplate"
icon="el-icon-download" size="medium"
style="width: 200px"
type="">下载模板
</el-button>
</el-card>
</el-timeline-item>
<el-timeline-item placement="top" timestamp="上传文件">
<el-card>
<el-upload
:auto-upload="false"
:file-list="importData.fileList"
:limit="1"
:on-change="importFileChange"
:on-remove="importFileRemove"
name="file"
ref="importUpload">
<el-button icon="el-icon-upload" size="medium" style="width: 200px" type="">
选择文件
</el-button>
<div class="el-upload__tip" slot="tip" style="color: #F56C6C">
只能上传 xls/xlsx 文件
</div>
</el-upload>
</el-card>
</el-timeline-item>
<el-timeline-item placement="top" timestamp="导入结果" v-if="importResult.totalCount > 0">
<el-card>
<div>
本次导入共 {{importResult.totalCount}} 条,成功 {{importResult.successCount}} 条,失败 {{importResult.errorCount}} 条
</div>
<div style="margin-top: 15px" v-if="importResult.errorCount > 0 && importResult.cacheKey">
<el-button icon="el-icon-download" @click="downloadImportErrors">下载错误信息表格</el-button>
</div>
</el-card>
</el-timeline-item>
</el-timeline>
<span slot="footer" class="dialog-footer">
<el-button :disabled="importLoading" @click="importDialogVisible = false">取 消</el-button>
<el-button type="primary" :loading="importLoading" @click="doImportMember">确 定</el-button>
</span>
</el-dialog>
</div>
<script>
var E = window.wangEditor
@@ -690,7 +753,7 @@ layout("/layouts/platform.html"){
userLoading: false,
fileList: [],
delFiles: [],
checkNode: {level: 2},
checkNode: {level: 1},
checkData: {},
//################################
userFormRules: {
@@ -713,6 +776,17 @@ layout("/layouts/platform.html"){
userTabKey: "",
addDialogVisible: false,
clubOptions: [],
importDialogVisible: false,
importLoading: false,
importData: {
fileList: []
},
importResult: {
totalCount: 0,
successCount: 0,
errorCount: 0,
cacheKey: ''
}
}
},
components: {
@@ -826,6 +900,101 @@ layout("/layouts/platform.html"){
})
},
openImport() {
this.$set(this, 'importResult', {
totalCount: 0,
successCount: 0,
errorCount: 0,
cacheKey: ''
})
this.$set(this, 'importData', {
fileList: []
})
this.importDialogVisible = true
},
downloadImportTemplate() {
window.location.href = base + '/platform/sys/club/clubIn/downloadImportTemplate'
},
importFileChange(file, fileList) {
var fileName = file.name ? file.name : file.raw.name
var suffix = fileName.indexOf('.') > -1 ? fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase() : ''
if (['xls', 'xlsx'].indexOf(suffix) === -1) {
this.$message({
message: '只能上传 xls、xlsx 文件',
type: 'warning'
})
this.$set(this.importData, 'fileList', [])
return
}
this.$set(this.importData, 'fileList', fileList.slice(fileList.length - 1))
},
importFileRemove(file, fileList) {
this.$set(this.importData, 'fileList', fileList)
},
doImportMember() {
if (!this.importData.fileList.length) {
this.$message({
message: '请先选择导入文件',
type: 'warning'
})
return
}
var formdata = new FormData();
this.importData.fileList.forEach(function (val) {
formdata.append('file', val.raw, val.raw.name)
})
this.importLoading = true
$.ajax({
url: base + "/platform/sys/club/clubIn/doImportMember",
type: "post",
data: formdata,
processData: false,
contentType: false,
success: (resp) => {
this.importLoading = false
if (resp.code === 0) {
this.$set(this, 'importResult', resp.data)
this.$message({
message: '导入完成,成功 ' + resp.data.successCount + ' 条,失败 ' + resp.data.errorCount + ' 条',
type: resp.data.errorCount > 0 ? 'warning' : 'success'
})
this.pageData()
this.loadTree()
} else {
this.$message({
message: resp.msg,
type: 'error'
})
}
},
error: (resp) => {
this.importLoading = false
this.$message({
message: resp.msg ? resp.msg : '导入失败',
type: 'error'
})
}
});
},
downloadImportErrors() {
if (!this.importResult.cacheKey) {
return
}
window.location.href = base + '/platform/sys/club/clubIn/exportImportErrors?cacheKey=' + this.importResult.cacheKey
},
exportClubMembers() {
if (!this.userPageForm.clubid) {
this.$message({
message: '请先选择协会',
type: 'warning'
})
return
}
window.location.href = base + '/platform/sys/club/clubIn/exportClubMembers?clubid='
+ this.userPageForm.clubid
+ '&searchName=' + encodeURIComponent(this.userPageForm.searchName ? this.userPageForm.searchName : '')
+ '&searchKeyword=' + encodeURIComponent(this.userPageForm.searchKeyword ? this.userPageForm.searchKeyword : '')
},
doUserSearch() {
this.userTabKey = new Date().getTime()
this.userPageForm.pageNumber = 1
@@ -1246,7 +1415,7 @@ layout("/layouts/platform.html"){
this.treeLoading = false
if (code == 0) {
this.treeData = data
this.checkData = data[0].child[0]
this.checkData = data[0]
} else {
this.$message({
message: data.msg,
@@ -1258,12 +1427,10 @@ layout("/layouts/platform.html"){
async created() {
await this.loadTree()
this.typeOptions = await getClubType()
this.userPageForm.clubid = this.checkData.id
this.userPageData();
this.pageData();
}
})
</script>
<!--#
}
#-->
#-->
@@ -292,6 +292,7 @@ layout("/layouts/platform.html"){
<el-lable v-if="scope.row.sf==3">副会长</el-lable>
<el-lable v-if="scope.row.sf==4">秘书长</el-lable>
<el-lable v-if="scope.row.sf==5">副秘书长</el-lable>
<el-lable v-if="scope.row.sf==6">协会经办人</el-lable>
</template>
</el-table-column>
@@ -325,6 +326,10 @@ layout("/layouts/platform.html"){
@click.native="updateSf(row,5)"
class="text-primary">设置副秘书长
</el-dropdown-item>
<el-dropdown-item v-if="row.sf!==6"
@click.native="updateSf(row,6)"
class="text-primary">设置协会经办人
</el-dropdown-item>
<el-dropdown-item
@click.native="userDropdownCommand(row)">删除
</el-dropdown-item>
@@ -567,6 +572,7 @@ layout("/layouts/platform.html"){
<el-lable v-if="scope.row.sf==3">副会长</el-lable>
<el-lable v-if="scope.row.sf==4">秘书长</el-lable>
<el-lable v-if="scope.row.sf==5">副秘书长</el-lable>
<el-lable v-if="scope.row.sf==6">协会经办人</el-lable>
</template>
</el-table-column>
<el-table-column label="工号" width="150px" prop="loginname" header-align="center"
@@ -627,6 +633,7 @@ layout("/layouts/platform.html"){
<el-radio-button :label="3">副会长</el-radio-button>
<el-radio-button :label="4">秘书长</el-radio-button>
<el-radio-button :label="5">副秘书长</el-radio-button>
<el-radio-button :label="6">协会经办人</el-radio-button>
<!--<el-radio-button :label="2">成员</el-radio-button>-->
</el-radio-group>
</el-form-item>
@@ -791,7 +798,7 @@ layout("/layouts/platform.html"){
});
},
async userQueryUser(clubid, query) {
const {data} = await $.get("/platform/sys/club/users/queryUser", {clubid, query})
const {data} = await $.get("/platform/sys/club/users/queryClubMemberForManage", {clubid, query})
return data
},
async userUserRemoteMethod(query) {
@@ -1258,4 +1265,4 @@ layout("/layouts/platform.html"){
</script>
<!--#
}
#-->
#-->
@@ -116,6 +116,9 @@ layout("/layouts/platform.html"){
<div class="btn-group tool-button mt5">
<el-button type="primary" icon="el-icon-search" @click="doSearch"></el-button>
</div>
<div class="pull-right offscreen-right mt5">
<el-button icon="el-icon-download" @click="exportAllClubMembers">导出</el-button>
</div>
<!--<div class="pull-right offscreen-right mt5">
&lt;!&ndash;#if(@shiro.hasRole('sysadmin')||@shiro.hasRole('A06')){#&ndash;&gt;
@@ -292,6 +295,13 @@ layout("/layouts/platform.html"){
<el-lable v-if="scope.row.sf==3">副会长</el-lable>
<el-lable v-if="scope.row.sf==4">秘书长</el-lable>
<el-lable v-if="scope.row.sf==5">副秘书长</el-lable>
<el-lable v-if="scope.row.sf==6">经办人</el-lable>
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="入会时间" prop="join_time">
<template slot-scope="scope">
<span>{{(scope.row.join_time || '').substring(0, 10)}}</span>
</template>
</el-table-column>
@@ -576,6 +586,7 @@ layout("/layouts/platform.html"){
<el-lable v-if="scope.row.sf==3">副会长</el-lable>
<el-lable v-if="scope.row.sf==4">秘书长</el-lable>
<el-lable v-if="scope.row.sf==5">副秘书长</el-lable>
<el-lable v-if="scope.row.sf==6">经办人</el-lable>
</template>
</el-table-column>
<el-table-column label="工号" width="150px" prop="loginname" header-align="center"
@@ -826,6 +837,9 @@ layout("/layouts/platform.html"){
})
},
exportAllClubMembers() {
window.location.href = base + '/platform/sys/club/schoolIn/exportAllClubMembers'
},
doUserSearch() {
this.userTabKey = new Date().getTime()
this.userPageForm.pageNumber = 1
@@ -1265,4 +1279,4 @@ layout("/layouts/platform.html"){
</script>
<!--#
}
#-->
#-->
+174 -3
View File
@@ -1,10 +1,21 @@
package io.v.nutz;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.export.ExcelExportService;
import cn.hutool.core.util.StrUtil;
import io.v.nutz.base.service.BaseService;
import io.v.nutz.base.utils.MsgApi;
import io.v.nutz.base.utils.Roles;
import io.v.nutz.data.model.UserSource;
import io.v.nutz.member.MemberMode;
import io.v.nutz.question.model.Question_issue;
import io.v.nutz.question.model.Question_wor;
import io.v.nutz.service.AsyncService;
import io.v.nutz.sickHelp.model.SickFundMember;
import io.v.nutz.sys.models.Sys_user;
@@ -13,6 +24,9 @@ import io.v.nutz.sys.models.User;
import io.v.nutz.sys.services.SysConfigService;
import io.v.nutz.sys.services.SysUserRoleService;
import io.v.nutz.sys.services.SysUserService;
import lombok.Data;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.util.ByteSource;
import org.junit.Assert;
@@ -26,20 +40,28 @@ import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Record;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import org.nutz.trans.Trans;
import javax.servlet.ServletOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
@@ -257,8 +279,157 @@ public class WebTestDemo extends Assert {
@Test
public void testMsgToken2() {
msgApi.sendMsg("1",List.of("18571562330"));
msgApi.sendMsg("1", List.of("18571562330"));
}
@Test
public void evalQuestion() {
String filePath = "C:\\Users\\jug\\Desktop\\新建文本文档.txt";
List<Question_issue> issues = new ArrayList<>();
Question_issue currentIssue = null;
try {
List<String> lines = Files.readAllLines(Paths.get(filePath));
for (String line : lines) {
line = line.trim();
if (line.isEmpty()) {
continue; // 跳过空行
}
// 检测题目
if (line.contains("[单选题]") || line.contains("【单选题】") || line.contains("[多选题]") || line.contains("【多选题】") || line.contains("[判断题]") || line.contains("【判断题】")) {
// 如果当前有题目,添加到列表中
if (currentIssue != null) {
issues.add(currentIssue);
}
// 创建新的题目
currentIssue = new Question_issue();
// 去除题目中的标签
String title = line.replaceAll("\\[单选题\\]", "").replaceAll("\\[多选题\\]", "")
.replaceAll("【单选题】", "").replaceAll("【多选题】", "")
.replaceAll("[判断题]", "").replaceAll("【判断题】", "");
// 去除序号
String[] patterns = {
"\\d+、", // 例如:1、
"\\d+\\.", // 例如:2.
"\\(\\d+\\)", // 例如:(3)
"第\\d+题" // 例如:第4题
};
for (String regex : patterns) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(title);
title = matcher.replaceAll("");
}
currentIssue.setIname(title);
currentIssue.setIisbd(true);
// 设置是否单选
if (line.contains("【单选题】") || line.contains("[单选题]")) {
currentIssue.setItx(1);
currentIssue.setIisdx(true);
} else if (line.contains("【多选题】") || line.contains("[多选题]")) {
currentIssue.setItx(1);
currentIssue.setIisdx(false);
} else if (line.contains("【判断题】") || line.contains("[判断题]")) {
currentIssue.setItx(3);
}
// 设置分数
currentIssue.setFraction(5);
// 初始化选项列表
currentIssue.setWors(new ArrayList<>());
} else {
// 处理选项
if (currentIssue != null && StrUtil.isNotBlank(line)) {
Question_wor wor = new Question_wor();
wor.setWtxt(line);
// 判断是否为正确答案
if (line.contains("(正确答案)") || line.contains("(正确答案)")) {
wor.setCorrect(true);
// 去除正确答案标记
wor.setWtxt(line.replace("(正确答案)", "").replace("(正确答案)", "").trim());
} else {
wor.setCorrect(false);
}
currentIssue.getWors().add(wor);
}
}
}
// 添加最后一题
if (currentIssue != null) {
issues.add(currentIssue);
}
// 输出解析结果
for (int i = 0; i < issues.size(); i++) {
issues.get(i).setIqid("7b44be22c6b042699779c9df5754a4da");
issues.get(i).setIpxbh(i + 1);
dao.insert(issues.get(i));
System.out.println(1);
List<Question_wor> wors = new ArrayList<>();
for (int i1 = 0; i1 < issues.get(i).getWors().size(); i1++) {
Question_wor wor = issues.get(i).getWors().get(i1);
wor.setWid(R.UU32());
wor.setWiid(issues.get(i).getIid());
wor.setWpxbh(i1 + 1);
wors.add(wor);
}
dao.insert(wors);
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void exportXlsx() throws IOException {
List<a> list = ExcelImportUtil.importExcel(new File("D:\\gooleDownload\\无标题.xlsx"), a.class, new ImportParams());
Map<String, List<a>> collect = list.stream().filter(v -> StrUtil.isNotBlank(v.getUnionname())).collect(Collectors.groupingBy(v -> v.getUnionname()));
ArrayList<ExcelExportEntity> entities = new ArrayList<>();
Workbook workbook = new XSSFWorkbook();
//每个分工会为一个sheet页
for (Map.Entry<String, List<a>> entry : collect.entrySet()) {
String sheetName = entry.getKey();
List<a> value = entry.getValue();
for (int i = 0; i < value.size(); i++) {
value.get(i).setNo(i + 1);
}
ExcelExportService service = new ExcelExportService();
ExportParams exportParams = new ExportParams();
exportParams.setTitle(sheetName);
exportParams.setSheetName(sheetName + "(" + value.size() + ")人");
exportParams.setType(ExcelType.XSSF);
// service.createSheetForMap(workbook, exportParams, entityList, value);
service.createSheet(workbook, exportParams, a.class, value);
}
FileOutputStream outputStream = new FileOutputStream("E:\\a.xlsx");
workbook.write(outputStream);
outputStream.close();
}
@Data
public static class a {
@Excel(name = "序号")
private Integer no;
@Excel(name = "工号", width = 20)
private String loginname;
@Excel(name = "姓名", width = 20)
private String username;
@Excel(name = "性别", width = 20)
private String sex;
@Excel(name = "出生年月", width = 20)
private String birthday;
@Excel(name = "在职状态", width = 20)
private String userState;
@Excel(name = "人员类型", width = 20)
private String personType;
@Excel(name = "单位", width = 20)
private String unitName;
@Excel(name = "分工会", width = 20)
private String unionname;
}
}
+159
View File
@@ -0,0 +1,159 @@
nutz.application.name=union_hhu
server.port=80
server.host=0.0.0.0
jetty.contextPath=/
jetty.threadpool.idleTimeout=100000
jetty.threadpool.minThreads=10
jetty.threadpool.maxThreads=20000
jetty.page.403=/error/403.html
jetty.page.404=/error/404.html
jetty.page.500=/error/500.html
#结合ftp使用,或用nginx代理ftp路径
jetty.staticPath=D://filesu
#开发模式静态资源
jetty.staticPathLocal=E:/projects/zhgh_hhu/src/main/resources/static
nutz.mvc.ignore=^(.+[.])(jsp|png|gif|jpg|js|css|jpeg|html|mp3|mp4|ico|svg)$
nutz.mvc.exclusions=/favicon/*,/assets/*,/druid/*,/upload/*
ftp.enabled=true
ftp.host=192.168.21.204
ftp.port=21
ftp.username=ftp_user
ftp.password=123456
ftp.timeout=100
redis.host=127.0.0.1
redis.port=6379
redis.timeout=2000
redis.max_redir=10
redis.database=0
redis.maxTotal=850
redis.pool.maxTotal=900
redis.pool.maxIdle=500
redis.pool.minIdle=50
#redis.password=dd3s
#redis集群模式设置
#redis.mode=cluster
redis.mode=normal
#redis.nodes=192.168.6.31:6377,192.168.6.31:6378,192.168.6.28:6377,192.168.6.28:6378,192.168.6.34:6377,192.168.6.34:6378
jdbc.url=jdbc:mysql://192.168.21.212:3306/zhgh_hhu?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456
jdbc.validationQuery=select 1
jdbc.maxActive=900
jdbc.testWhileIdle=true
jdbc.connectionProperties=druid.stat.slowSqlMillis=2000
jdbc.defaultAutoCommit=true
#jdbc.url=jdbc:oracle:thin:@//localhost:1521/orcl
#jdbc.username=budwk
#jdbc.password=pass
#jdbc.validationQuery=select 1 from dual
#jdbc.maxActive=10
#jdbc.testWhileIdle=true
#jdbc.connectionProperties=druid.stat.slowSqlMillis=2000
#jdbc.defaultAutoCommit=true
xsssql.ignore.urls=/platform/cms/content/article/addDo,/platform/sys/app/conf/addDo,/platform/sys/app/conf/editDo,/platform/home,\
/sso/login,/platform/sys/log/data,/platform/common/user/userPageData,/platform/fw/condolence/applyWrite/doAdd,/platform/fw/condolence/ucr/union/doReview,\
/platform/fw/condolence/myList/doEdit,/platform/fw/condolence/fr/doReview,/platform/fw/condolence/vcr/doReview,\
/platform/sys/menu/addDo,/platform/sys/menu/editMenuDo,/platform/jdh/level2/info/doAdd,\
/platform/reimbursement/applyNew/doAdd,/platform/member/common/saveUser,\
/platform/sys/union/mange/unionPageData,/platform/fw/condolence/apply/doAdd
#环境 dev/prod
v.environment=dev
v.loginUrl=/sso/login
v.loginUrlMobile=/mobile/cas/login
#shiro.ini.path=shiro.ini
shiro.objects=platformAuthc,AuthenticationFilter,Cas20ProxyReceivingTicketValidationFilter,platformRealm,authenticationStrategy
#end
shiro.ini.urls:
/sso/**=Cas20ProxyReceivingTicketValidationFilter
/platform/wechat/doLogin=Cas20ProxyReceivingTicketValidationFilter
/mobile/login=anon
/platform/jsz/login=anon
/assets/**=anon
/**=anon
/signature/**=anon
/platform/**=AuthenticationFilter
#end
#设置session会话超时时间(单位:毫秒)
shiro.workersCongressSession.manager.globalSessionTimeout=3600000
#设置redis缓存时间(单位:秒)与上面的毫秒数保持一致
shiro.workersCongressSession.cache.redis.ttl=3600
#缓存名和 RedisConstant.PLATFORM_REDIS_PREFIX 常量保持一致
shiro.workersCongressSession.dao.activeSessionsCacheName=budwk5mini:shiro:activeSessionCache
#使用redis二级缓存
shiro.workersCongressSession.cache.type=lcache
shiro.workersCongressSession.enable=true
shiro.url.login=/platform
shiro.url.logout_redirect=/platform
shiro.url.unauth=/platform
#开发时设置本地路径,便于调试
beetl.RESOURCE.rootLocal=D:/gsProject/zhgh_hhu/src/main/resources/views/
beetl.RESOURCE.root=views/
beetl.DELIMITER_STATEMENT_START=<!--#
beetl.DELIMITER_STATEMENT_END=#-->
beetl.FT.escape=io.v.nutz.web.commons.ext.beetl.HtmlEscapeFormat
#用法: ${"wizzer",escape}
beetl.FT.filesize=io.v.nutz.web.commons.ext.beetl.FileSizeFormat
#用法: ${10241024,filesize}
beetl.FT.html2txt=io.v.nutz.web.commons.ext.beetl.Html2TxtFormat
#用法: ${"",html2txt="100"} 截取100字符
beetl.FT.strlen=io.v.nutz.web.commons.ext.beetl.StrlenFormat
#用法: ${"",strlen="100"} 截取100字符
#获取系统参数
beetl.FN.getSysConfig=io.v.nutz.web.commons.ext.beetl.GetSysConfig
#==============================================================
#Configure Main Scheduler Properties
#==============================================================
#quartz延迟启动秒数
quartz.startupDelay=10
quartz.scheduler.instanceName=defaultScheduler
quartz.scheduler.instanceId=AUTO
#==============================================================
#Configure JobStore isClustered=启用集群模式
#==============================================================
quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
#quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
#Other delegates can see: http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/tutorial-lesson-09.html
quartz.jobStore.dataSource=myDS
quartz.jobStore.tablePrefix=SYS_QRTZ_
quartz.jobStore.isClustered=true
quartz.jobStore.clusterCheckinInterval=20000
quartz.jobStore.maxMisfiresToHandleAtATime=120
quartz.jobStore.misfireThreshold=120000
quartz.jobStore.txIsolationLevelSerializable=false
#==============================================================
#Configure ThreadPool
#==============================================================
quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
quartz.threadPool.threadCount=2
quartz.threadPool.threadPriority=5
quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
#==============================================================
#Skip Check Update
#update:true
#not update:false
#==============================================================
quartz.scheduler.skipUpdateCheck=true
#============================================================================
# Configure Plugins
#============================================================================
quartz.plugin.triggHistory.class=org.quartz.plugins.history.LoggingJobHistoryPlugin
quartz.plugin.shutdownhook.class=org.quartz.plugins.management.ShutdownHookPlugin
quartz.plugin.shutdownhook.cleanShutdown=true
#============================================================================
# NutDao dataSource
#============================================================================
quartz.dataSource.myDS.connectionProvider.class=io.v.nutz.task.conn.NutConnectionProvider
logback.exts.loglevel.cache.prefix=budwk5mini:logback:loglevel:
#动态修改日志等级
logback.exts.loglevel.enabled=true
#心跳间隔(单位:秒)
logback.exts.loglevel.heartbeat=15
#为应用管理提供支持,启用获取部署版本号,结合守护进程使用
# 详见 https://github.com/Wizzercn/PythonWk
logback.exts.deploy.enabled=false
#为应用管理提供支持,部署根路径
logback.exts.deploy.root=/data/budwk5mini
#应用管理通信接口签名,正式环境务必更改
+8
View File
@@ -0,0 +1,8 @@
____ ___ ___ __ _______ __ __ _ _
| _ \ | \ \ / / | \ \ / / ____| | \/ (_) (_)
| |_) |_ _ __| |\ \ /\ / /| | _______\ \ / /| |__ | \ / |_ _ __ _
| _ <| | | |/ _` | \ \/ \/ / | |/ /______\ \/ / |___ \ | |\/| | | '_ \| |
| |_) | |_| | (_| | \ /\ / | < \ / ___) | | | | | | | | | |
|____/ \__,_|\__,_| \/ \/ |_|\_\ \/ |____/ |_| |_|_|_| |_|_|
:: Built with BudWk.com [wizzer@qq.com]
+18
View File
@@ -0,0 +1,18 @@
var chain={
"default" : {
"ps" : [
"io.v.nutz.web.commons.proc.LogTimeProcessor",
"io.v.nutz.web.commons.proc.GlobalsSettingProcessor",
"org.nutz.mvc.impl.processor.UpdateRequestAttributesProcessor",
"org.nutz.mvc.impl.processor.EncodingProcessor",
"org.nutz.mvc.impl.processor.ModuleProcessor",
"org.nutz.mvc.impl.processor.ActionFiltersProcessor",
"io.v.nutz.web.commons.proc.XssSqlFilterProcessor",
"io.v.nutz.web.commons.proc.NutShiroProcessor",
"org.nutz.mvc.impl.processor.AdaptorProcessor",
"org.nutz.mvc.impl.processor.MethodInvokeProcessor",
"org.nutz.mvc.impl.processor.ViewProcessor"
],
"error" : 'io.v.nutz.web.commons.proc.WkFailProcessor'
}
};
+50
View File
@@ -0,0 +1,50 @@
/* cms_menu_01 */
update sys_menu set location=10 where path='0001'
/* cms_menu_02 */
update sys_menu set location=8 where path='0002'
/* cms_menu_03 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('02e86a61e99746bea34236ea73dd52a5','','0003','CMS','CMS','menu','','','ti-world','1','0','cms',NULL,'9','1','1a19ef09b12344b4a797d6e6dfe7fb29','1468895671','0')
/* cms_menu_04 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('077cb6be4c7c41cc8955ee045a4f0286','68cdbf694f71445c8587a20234d6fe31','0003000300020001','添加链接','Add','data','','','','0','0','cms.link.link.add',NULL,'47','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468897043','0')
/* cms_menu_05 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('17500ef3a9e44b4fabb240162a164fcb','6075fc0cf0ef441b9d93cc3cab3445bf','0003000200020003','删除文章','Delete','data','','','','0','0','cms.content.article.delete',NULL,'40','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896170','0')
/* cms_menu_06 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('31ed2243077c44448cce26abfd5ae574','9822bafbe3454dfd8e8b974ebc304d03','0003000300010002','修改分类','Edit','data','','','','0','0','cms.link.class.edit',NULL,'44','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896957','0')
/* cms_menu_07 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('33aed9298643424783116e0cf0f7fcbe','6075fc0cf0ef441b9d93cc3cab3445bf','0003000200020001','添加文章','Add','data','','','','0','0','cms.content.article.add',NULL,'38','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896151','0')
/* cms_menu_08 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('36e0faf5062b4f6b95d4167cbb1f8fea','68cdbf694f71445c8587a20234d6fe31','0003000300020002','修改链接','Edit','data','','','','0','0','cms.link.link.edit',NULL,'48','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468897051','0')
/* cms_menu_09 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('3c24111091ad4a70ad2d9cc361311d2f','68cdbf694f71445c8587a20234d6fe31','0003000300020003','删除链接','Delete','data','','','','0','0','cms.link.link.delete',NULL,'49','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468897060','0')
/* cms_menu_10 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('3f330d729ca34dc9825c46122be1bfae','02e86a61e99746bea34236ea73dd52a5','00030003','广告链接','AD','menu','','','ti-link','1','0','cms.link',NULL,'41','1','1a19ef09b12344b4a797d6e6dfe7fb29','1468896230','0')
/* cms_menu_11 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('4781372b00bb4d52b429b58e72b80c68','b2631bbdbf824cc4b74d819c87962c0d','0003000200010001','添加栏目','Add','data','','','','0','0','cms.content.channel.add',NULL,'33','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896049','0')
/* cms_menu_12 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('50ba60ee650e4c739e6abc3ab71e4960','b2631bbdbf824cc4b74d819c87962c0d','0003000200010004','栏目排序','Sort','data','','','','0','0','cms.content.channel.sort',NULL,'36','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896092','0')
/* cms_menu_13 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('6075fc0cf0ef441b9d93cc3cab3445bf','6b6de8c720c645a1808e1c3e9ccbfc90','000300020002','文章管理','Article','menu','/platform/cms/article','data-pjax','','1','0','cms.content.article',NULL,'37','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896141','0')
/* cms_menu_14 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('68cdbf694f71445c8587a20234d6fe31','3f330d729ca34dc9825c46122be1bfae','000300030002','链接管理','Link','menu','/platform/cms/link/link','data-pjax','','1','0','cms.link.link',NULL,'46','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468897031','0')
/* cms_menu_15 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('6b6de8c720c645a1808e1c3e9ccbfc90','02e86a61e99746bea34236ea73dd52a5','00030002','内容管理','Content','menu','','','ti-pencil-alt','1','0','cms.content',NULL,'31','1','1a19ef09b12344b4a797d6e6dfe7fb29','1468895990','0')
/* cms_menu_16 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('7125a72beee34b21ab3df9bf01b7bce6','9822bafbe3454dfd8e8b974ebc304d03','0003000300010003','删除分类','Delete','data','','','','0','0','cms.link.class.delete',NULL,'45','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896968','0')
/* cms_menu_17 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('73a29d3f99224426b5a87c92da122275','d1e991ad38a8424daf9f7eb000ee27f4','0003000100010001','保存配置','Save','data','','','','0','0','cms.site.settings.save',NULL,'30','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468895899','0')
/* cms_menu_18 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('7db6207d0dab4d6e95a7eee4f2efe875','9822bafbe3454dfd8e8b974ebc304d03','0003000300010001','添加分类','Add','data','','','','0','0','cms.link.class.add',NULL,'43','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896947','0')
/* cms_menu_19 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('9822bafbe3454dfd8e8b974ebc304d03','3f330d729ca34dc9825c46122be1bfae','000300030001','链接分类','Class','menu','/platform/cms/link/class','data-pjax','','1','0','cms.link.class',NULL,'42','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896932','0')
/* cms_menu_20 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('b2631bbdbf824cc4b74d819c87962c0d','6b6de8c720c645a1808e1c3e9ccbfc90','000300020001','栏目管理','Channel','menu','/platform/cms/channel','data-pjax','','1','0','cms.content.channel',NULL,'32','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896018','0')
/* cms_menu_21 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('d1e991ad38a8424daf9f7eb000ee27f4','d920314e925c451da6d881e7a29743b7','000300010001','网站配置','Settings','menu','/platform/cms/site','data-pjax','','1','0','cms.site.settings',NULL,'29','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468895881','0')
/* cms_menu_22 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('d920314e925c451da6d881e7a29743b7','02e86a61e99746bea34236ea73dd52a5','00030001','站点管理','Site','menu','','','ti-world','1','0','cms.site',NULL,'28','1','1a19ef09b12344b4a797d6e6dfe7fb29','1468895821','0')
/* cms_menu_23 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('e461c62a1d5441619cd35612f3b40691','b2631bbdbf824cc4b74d819c87962c0d','0003000200010002','修改栏目','Edit','data','','','','0','0','cms.content.channel.edit',NULL,'34','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896060','0')
/* cms_menu_24 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('ef9f436c61654ec09efbfa79a40061cf','6075fc0cf0ef441b9d93cc3cab3445bf','0003000200020002','修改文章','Edit','data','','','','0','0','cms.content.article.edit',NULL,'39','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896159','0')
/* cms_menu_25 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('f6fba69c3b704d79834b8bd2cc753729','b2631bbdbf824cc4b74d819c87962c0d','0003000200010003','删除栏目','Delete','data','','','','0','0','cms.content.channel.delete',NULL,'35','0','1a19ef09b12344b4a797d6e6dfe7fb29','1468896072','0')
+103
View File
@@ -0,0 +1,103 @@
/* wx_menu_01 */
update sys_menu set location=10 where path='0001'
/* wx_menu_02 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('b0edc6861a494b79b97990dc05f0a524','','0002','微信','Wechat','menu','','','fa fa-weixin','1','0','wx',NULL,'8','1','','1467471229','0')
/* wx_menu_03 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('e4256d7b0ffc4a02906cf900322b6213','b0edc6861a494b79b97990dc05f0a524','00020001','微信会员','Member','menu','','','fa fa-user','1','0','wx.user',NULL,'1','1','','1467471292','0')
/* wx_menu_04 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('b19b23b0459a4754bf1fb8cb234450f2','e4256d7b0ffc4a02906cf900322b6213','000200010001','会员列表','List','menu','/platform/wx/user/index','data-pjax','','1','0','wx.user.list',NULL,'2','0','','1467471357','0')
/* wx_menu_05 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('4dc997fef71e4862b9db22de8e99a618','b19b23b0459a4754bf1fb8cb234450f2','0002000100010001','同步会员信息','Sync','data','','','','0','0','wx.user.list.sync',NULL,'0','0','','1467473044','0')
/* wx_menu_06 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('9f20a757a6bc40ddbb650c70debbf660','b0edc6861a494b79b97990dc05f0a524','00020002','消息管理','Message','menu','','','ti-pencil-alt','1','0','wx.msg',NULL,'3','1','','1467471415','0')
/* wx_menu_07 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('f426468abf714b1599729f8c36ebbb0d','9f20a757a6bc40ddbb650c70debbf660','000200020001','会员消息','Msg','menu','/platform/wx/msg/user','data-pjax','','1','0','wx.msg.user',NULL,'4','1','','1467471478','0')
/* wx_menu_08 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('1734e586e96941268a4c5248b593cef9','f426468abf714b1599729f8c36ebbb0d','0002000200010001','回复消息','Reply','data','','','','0','0','wx.msg.user.reply',NULL,'0','0','','1467473127','0')
/* wx_menu_09 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('6bb17a41f6394ed0a8a6faf5ff781354','9f20a757a6bc40ddbb650c70debbf660','000200020002','群发消息','Mass','menu','/platform/wx/msg/mass','data-pjax','','1','0','wx.msg.mass',NULL,'5','0','','1467471561','0')
/* wx_menu_10 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('56d0658c5a8848818ac05e8ffa5c0570','6bb17a41f6394ed0a8a6faf5ff781354','0002000200020001','添加图文','Add','data','','','','0','0','wx.msg.mass.addNews',NULL,'0','0','','1467473338','0')
/* wx_menu_11 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('ce709456e867425297955b3c40406d7e','6bb17a41f6394ed0a8a6faf5ff781354','0002000200020002','删除图文','Delete','data','','','','0','0','wx.msg.mass.delNews',NULL,'0','0','','1467473363','0')
/* wx_menu_12 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('3099f497480c4b1987bce3f3a26c3fb4','6bb17a41f6394ed0a8a6faf5ff781354','0002000200020003','群发消息','Push','data','','','','0','0','wx.msg.mass.pushNews',NULL,'0','0','','1467473400','0')
/* wx_menu_13 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('4cd8e4e9519e4cff95465194fdcc8d88','b0edc6861a494b79b97990dc05f0a524','00020003','自动回复','AutoReply','menu','','','ti-back-left','1','0','wx.reply',NULL,'6','1','','1467471610','0')
/* wx_menu_14 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('234f8ec3c2bc42bf9f6202aecae36fd6','4cd8e4e9519e4cff95465194fdcc8d88','000200030001','文本内容','Txt','menu','/platform/wx/reply/txt','data-pjax','','1','0','wx.reply.txt',NULL,'7','0','','1467471884','0')
/* wx_menu_15 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('c3a44b478d3241b899b9c3f4611bc2b6','234f8ec3c2bc42bf9f6202aecae36fd6','0002000300010001','添加文本','Add','data','','','','0','0','wx.reply.txt.add',NULL,'0','0','','1467473460','0')
/* wx_menu_16 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('fd63a8e389e04ff3a86c3cea53a3b9d5','234f8ec3c2bc42bf9f6202aecae36fd6','0002000300010002','修改文本','Edit','data','','','','0','0','wx.reply.txt.edit',NULL,'0','0','','1467473519','0')
/* wx_menu_17 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('7c040dfd8db347e5956a3bc1764653dc','234f8ec3c2bc42bf9f6202aecae36fd6','0002000300010003','删除文本','Delete','data','','','','0','0','wx.reply.txt.delete',NULL,'0','0','','1467473540','0')
/* wx_menu_18 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('234f8ec3c2bc42bf9f6202aecae36f99','4cd8e4e9519e4cff95465194fdcc8d88','000200030005','图片内容','Img','menu','/platform/wx/reply/img','data-pjax','','1','0','wx.reply.img',NULL,'8','0','','1467471884','0')
/* wx_menu_19 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('c3a44b478d3241b899b9c3f4611bc216','234f8ec3c2bc42bf9f6202aecae36f99','0002000300050001','添加图片','Add','data','','','','0','0','wx.reply.img.add',NULL,'0','0','','1467473460','0')
/* wx_menu_20 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('fd63a8e389e04ff3a86c3cea53a3b925','234f8ec3c2bc42bf9f6202aecae36f99','0002000300050002','修改图片','Edit','data','','','','0','0','wx.reply.img.edit',NULL,'0','0','','1467473519','0')
/* wx_menu_21 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('7c040dfd8db347e5956a3bc17646533c','234f8ec3c2bc42bf9f6202aecae36f99','0002000300050003','删除图片','Delete','data','','','','0','0','wx.reply.img.delete',NULL,'0','0','','1467473540','0')
/* wx_menu_22 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('17e1ee23ca1443f1bc886c2f5eb7c24b','4cd8e4e9519e4cff95465194fdcc8d88','000200030002','图文内容','News','menu','/platform/wx/reply/news','data-pjax','','1','0','wx.reply.news',NULL,'9','0','','1467471926','0')
/* wx_menu_23 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('2275cb125710414e91b617dd7c62f12c','17e1ee23ca1443f1bc886c2f5eb7c24b','0002000300020001','添加图文','add','data','','','','0','0','wx.reply.news.add',NULL,'0','0','','1467473585','0')
/* wx_menu_24 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('0a972ce655cb4c84809d58668b655900','17e1ee23ca1443f1bc886c2f5eb7c24b','0002000300020002','修改图文','Edit','data','','','','0','0','wx.reply.news.edit',NULL,'0','0','','1467473596','0')
/* wx_menu_25 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('fc52d5284b8f4522802383c1ef732242','17e1ee23ca1443f1bc886c2f5eb7c24b','0002000300020003','删除图文','Delete','data','','','','0','0','wx.reply.news.delete',NULL,'0','0','','1467473606','0')
/* wx_menu_26 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('2cb327ad59b140828fd26eb2a46cb948','4cd8e4e9519e4cff95465194fdcc8d88','000200030003','关注自动回复','Follow','menu','/platform/wx/reply/conf/follow','data-pjax','','1','0','wx.reply.follow',NULL,'10','0','','1467472280','0')
/* wx_menu_27 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('dd965b2c1dfd493fb5efc7e4bcac99d4','2cb327ad59b140828fd26eb2a46cb948','0002000300030001','添加绑定','Add','data','','','','0','0','wx.reply.follow.add',NULL,'0','0','','1467474026','0')
/* wx_menu_28 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('30a5e70a1456447ebf90b5546e9bc321','2cb327ad59b140828fd26eb2a46cb948','0002000300030002','修改绑定','Edit','data','','','','0','0','wx.reply.follow.edit',NULL,'0','0','','1467474056','0')
/* wx_menu_29 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('2a63040409094f1e9dc535dd78ce15b7','2cb327ad59b140828fd26eb2a46cb948','0002000300030003','删除绑定','Delete','data','','','','0','0','wx.reply.follow.delete',NULL,'0','0','','1467474080','0')
/* wx_menu_30 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('0706112ff5dc46e388064a99bcdb0561','4cd8e4e9519e4cff95465194fdcc8d88','000200030004','关键词回复','Keyword','menu','/platform/wx/reply/conf/keyword','data-pjax','','1','0','wx.reply.keyword',NULL,'11','0','','1467472362','0')
/* wx_menu_31 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('e864c78aba63448892cbcb6a3a7f4da7','0706112ff5dc46e388064a99bcdb0561','0002000300040001','添加绑定','Add','data','','','','0','0','wx.reply.keyword.add',NULL,'0','0','','1467474113','0')
/* wx_menu_32 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('ff6cd243a77c4ae98dacf6149c816c75','0706112ff5dc46e388064a99bcdb0561','0002000300040002','修改绑定','Edit','data','','','','0','0','wx.reply.keyword.edit',NULL,'0','0','','1467474125','0')
/* wx_menu_33 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('733d3f35d49f45af99ca9220048583ba','0706112ff5dc46e388064a99bcdb0561','0002000300040003','删除绑定','Delete','data','','','','0','0','wx.reply.keyword.delete',NULL,'0','0','','1467474136','0')
/* wx_menu_34 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('bcf64d623fdd4519ae345b7a08c071a1','b0edc6861a494b79b97990dc05f0a524','00020004','微信配置','Config','menu','','','fa fa-weixin','1','0','wx.conf',NULL,'12','1','','1467472498','0')
/* wx_menu_35 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('66cc21d7ce104dd6877cbce114c59fb3','bcf64d623fdd4519ae345b7a08c071a1','000200040001','帐号配置','Account','menu','/platform/wx/conf/account','data-pjax','','1','0','wx.conf.account',NULL,'13','0','','1467472624','0')
/* wx_menu_36 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('309dc29ad3c34408a68df8f867a5c9ff','66cc21d7ce104dd6877cbce114c59fb3','0002000400010001','添加帐号','Add','data','','','','0','0','wx.conf.account.add',NULL,'0','0','','1467474187','0')
/* wx_menu_37 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('96554b09a2dd4f82bab7546fa59acd35','66cc21d7ce104dd6877cbce114c59fb3','0002000400010002','修改帐号','Edit','data','','','','0','0','wx.conf.account.edit',NULL,'0','0','','1467474197','0')
/* wx_menu_38 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('d568f4c2b687404e8aec7b9edcae5767','66cc21d7ce104dd6877cbce114c59fb3','0002000400010003','删除帐号','Delete','data','','','','0','0','wx.conf.account.delete',NULL,'0','0','','1467474209','0')
/* wx_menu_39 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('2fab774f8b6d40cb9d7e187babab2d91','bcf64d623fdd4519ae345b7a08c071a1','000200040002','菜单配置','Menu','menu','/platform/wx/conf/menu','data-pjax','','1','0','wx.conf.menu',NULL,'14','0','','1467472649','0')
/* wx_menu_40 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('45d958ca78304f25b51f6c71cf66f6d8','2fab774f8b6d40cb9d7e187babab2d91','0002000400020001','添加菜单','Add','data','','','','0','0','wx.conf.menu.add',NULL,'0','0','','1467474283','0')
/* wx_menu_41 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('44da90bc76a5419a841f4924333f7a66','2fab774f8b6d40cb9d7e187babab2d91','0002000400020002','修改菜单','Edit','data','','','','0','0','wx.conf.menu.edit',NULL,'0','0','','1467474294','0')
/* wx_menu_42 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('9a9557177d334c209cf73c3817fe3b63','2fab774f8b6d40cb9d7e187babab2d91','0002000400020003','删除菜单','Delete','data','','','','0','0','wx.conf.menu.delete',NULL,'0','0','','1467474304','0')
/* wx_menu_43 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('0a43d291e0c94ad88c8b690009279e34','2fab774f8b6d40cb9d7e187babab2d91','0002000400020004','保存排序','Save','data','','','','0','0','wx.conf.menu.sort',NULL,'0','0','','1467474314','0')
/* wx_menu_44 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('5244f5c38eb24b918e9ad64d456daa38','2fab774f8b6d40cb9d7e187babab2d91','0002000400020005','推送到微信','Push','data','','','','0','0','wx.conf.menu.push',NULL,'0','0','','1467474330','0')
/* wx_menu_45 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('6afc5075913d4df4b44a6476080e35a0','b0edc6861a494b79b97990dc05f0a524','00020005','模板消息','Template','menu','','','ti-notepad','1','0','wx.tpl',NULL,'50','1','','1470406797','0')
/* wx_menu_46 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('1385ae887e5c4b8aa33fbf228be7f907','6afc5075913d4df4b44a6476080e35a0','000200050001','模板编号','Id','menu','/platform/wx/tpl/id','data-pjax','','1','0','wx.tpl.id',NULL,'51','0','','1470406854','0')
/* wx_menu_47 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('e6b6224617b04090a76e46a4b048fb96','1385ae887e5c4b8aa33fbf228be7f907','0002000500010001','添加编号','Add','data','','','','0','0','wx.tpl.id.add',NULL,'54','0','','1470407055','0')
/* wx_menu_48 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('3888f05aa4064f788ba7ec51c495ce7c','1385ae887e5c4b8aa33fbf228be7f907','0002000500010002','删除编号','Delete','data','','','','0','0','wx.tpl.id.delete',NULL,'55','0','','1470407068','0')
/* wx_menu_49 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('cabbe834a7474675b899e8442b5c2604','6afc5075913d4df4b44a6476080e35a0','000200050002','模板列表','List','menu','/platform/wx/tpl/list','data-pjax','','1','0','wx.tpl.list',NULL,'52','0','','1470406883','0')
/* wx_menu_50 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('a11163584dfe456cbfd6fb2d4b74391b','cabbe834a7474675b899e8442b5c2604','0002000500020001','获取列表','Get','data','','','','0','0','wx.tpl.list.get',NULL,'56','0','','1470407390','0')
/* wx_menu_51 */
insert into sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, delFlag) values('c76a84f871d047db955dd1465c845ac1','6afc5075913d4df4b44a6476080e35a0','000200050003','发送记录','Log','menu','/platform/wx/tpl/log','data-pjax','','1','0','wx.tpl.log',NULL,'53','0','','1470406926','0')
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More