chenwen 3 lat temu
commit
458046d8a8
100 zmienionych plików z 8941 dodań i 0 usunięć
  1. 8 0
      .classpath
  2. 1 0
      .gitignore
  3. 23 0
      .project
  4. 2 0
      .settings/org.eclipse.core.resources.prefs
  5. 5 0
      .settings/org.eclipse.jdt.core.prefs
  6. 7 0
      .settings/org.maven.ide.eclipse.prefs
  7. 208 0
      pom.xml
  8. 118 0
      src/main/java/com/jpsoft/proj/api/controller/AuthWeiXinController.java
  9. 193 0
      src/main/java/com/jpsoft/proj/api/controller/MobileAuthController.java
  10. 127 0
      src/main/java/com/jpsoft/proj/api/controller/SeekOtherController.java
  11. 104 0
      src/main/java/com/jpsoft/proj/api/controller/SeekerIndexController.java
  12. 296 0
      src/main/java/com/jpsoft/proj/api/controller/SeekerRescueController.java
  13. 17 0
      src/main/java/com/jpsoft/proj/api/controller/VerifyCodeUtils.java
  14. 87 0
      src/main/java/com/jpsoft/proj/api/controller/VolunteerIndexController.java
  15. 237 0
      src/main/java/com/jpsoft/proj/api/controller/VolunteerOtherController.java
  16. 203 0
      src/main/java/com/jpsoft/proj/api/controller/VolunteerRescueController.java
  17. 91 0
      src/main/java/com/jpsoft/proj/auth/controller/AuthController.java
  18. 121 0
      src/main/java/com/jpsoft/proj/auth/controller/LoginController.java
  19. 101 0
      src/main/java/com/jpsoft/proj/auth/controller/MenuController.java
  20. 63 0
      src/main/java/com/jpsoft/proj/auth/controller/RequestValidateExceptionHandler.java
  21. 115 0
      src/main/java/com/jpsoft/proj/auth/service/AuthService.java
  22. 143 0
      src/main/java/com/jpsoft/proj/auth/service/MenuService.java
  23. 59 0
      src/main/java/com/jpsoft/proj/model/Code2Session.java
  24. 20 0
      src/main/java/com/jpsoft/proj/model/FileUseForEm.java
  25. 70 0
      src/main/java/com/jpsoft/proj/model/MatchedVolunteer.java
  26. 140 0
      src/main/java/com/jpsoft/proj/model/MemberPO.java
  27. 14 0
      src/main/java/com/jpsoft/proj/model/MemberVO.java
  28. 109 0
      src/main/java/com/jpsoft/proj/model/MenuPO.java
  29. 82 0
      src/main/java/com/jpsoft/proj/model/RescueAcceptPO.java
  30. 76 0
      src/main/java/com/jpsoft/proj/model/RescueAcceptVO.java
  31. 138 0
      src/main/java/com/jpsoft/proj/model/RescueApplyPO.java
  32. 153 0
      src/main/java/com/jpsoft/proj/model/RescueApplyVO.java
  33. 31 0
      src/main/java/com/jpsoft/proj/model/RescueFlow.java
  34. 95 0
      src/main/java/com/jpsoft/proj/model/RescueFlowPO.java
  35. 53 0
      src/main/java/com/jpsoft/proj/model/SmsCodeCache.java
  36. 137 0
      src/main/java/com/jpsoft/proj/model/SortCodePO.java
  37. 68 0
      src/main/java/com/jpsoft/proj/model/User.java
  38. 190 0
      src/main/java/com/jpsoft/proj/model/VolunteerPO.java
  39. 74 0
      src/main/java/com/jpsoft/proj/model/VolunteerVO.java
  40. 27 0
      src/main/java/com/jpsoft/proj/model/WxSessionCache.java
  41. 100 0
      src/main/java/com/jpsoft/proj/newsinfo/controller/AdController.java
  42. 154 0
      src/main/java/com/jpsoft/proj/newsinfo/controller/NewsInfoController.java
  43. 60 0
      src/main/java/com/jpsoft/proj/newsinfo/controller/NoticeController.java
  44. 38 0
      src/main/java/com/jpsoft/proj/newsinfo/controller/SuggestController.java
  45. 64 0
      src/main/java/com/jpsoft/proj/newsinfo/service/AdService.java
  46. 113 0
      src/main/java/com/jpsoft/proj/newsinfo/service/NewsInfoService.java
  47. 99 0
      src/main/java/com/jpsoft/proj/newsinfo/service/NoticeService.java
  48. 71 0
      src/main/java/com/jpsoft/proj/newsinfo/service/SuggestService.java
  49. 61 0
      src/main/java/com/jpsoft/proj/rescue/controller/AEDController.java
  50. 26 0
      src/main/java/com/jpsoft/proj/rescue/controller/MemberController.java
  51. 124 0
      src/main/java/com/jpsoft/proj/rescue/controller/RescueConfigController.java
  52. 127 0
      src/main/java/com/jpsoft/proj/rescue/controller/RescueController.java
  53. 89 0
      src/main/java/com/jpsoft/proj/rescue/service/AEDService.java
  54. 104 0
      src/main/java/com/jpsoft/proj/rescue/service/CallNearbyTask.java
  55. 36 0
      src/main/java/com/jpsoft/proj/rescue/service/CallVolunteerExecutor.java
  56. 114 0
      src/main/java/com/jpsoft/proj/rescue/service/CallVolunteerTask.java
  57. 71 0
      src/main/java/com/jpsoft/proj/rescue/service/MemberService.java
  58. 77 0
      src/main/java/com/jpsoft/proj/rescue/service/RescueConfigService.java
  59. 25 0
      src/main/java/com/jpsoft/proj/rescue/service/RescueEquipmentService.java
  60. 57 0
      src/main/java/com/jpsoft/proj/rescue/service/RescueFlowService.java
  61. 228 0
      src/main/java/com/jpsoft/proj/rescue/service/RescueSeekerService.java
  62. 87 0
      src/main/java/com/jpsoft/proj/rescue/service/RescueService.java
  63. 327 0
      src/main/java/com/jpsoft/proj/rescue/service/RescueVolunteerService.java
  64. 42 0
      src/main/java/com/jpsoft/proj/sms/service/SMSErrorCoder.java
  65. 60 0
      src/main/java/com/jpsoft/proj/sms/service/SmsProvider.java
  66. 266 0
      src/main/java/com/jpsoft/proj/sms/service/SmsSender.java
  67. 238 0
      src/main/java/com/jpsoft/proj/sms/service/SmsService.java
  68. 46 0
      src/main/java/com/jpsoft/proj/sysdata/controller/AutoCompleteController.java
  69. 27 0
      src/main/java/com/jpsoft/proj/sysdata/controller/ErrorsController.java
  70. 53 0
      src/main/java/com/jpsoft/proj/sysdata/controller/OptionsController.java
  71. 83 0
      src/main/java/com/jpsoft/proj/sysdata/controller/SortCodeController.java
  72. 15 0
      src/main/java/com/jpsoft/proj/sysdata/service/AutoCompleteService.java
  73. 151 0
      src/main/java/com/jpsoft/proj/sysdata/service/SortCodeService.java
  74. 67 0
      src/main/java/com/jpsoft/proj/sysdata/service/UsErrorService.java
  75. 92 0
      src/main/java/com/jpsoft/proj/sysdata/service/UserService.java
  76. 126 0
      src/main/java/com/jpsoft/proj/utils/AESEncryptUtils.java
  77. 38 0
      src/main/java/com/jpsoft/proj/utils/APIControllerInterceptor.java
  78. 11 0
      src/main/java/com/jpsoft/proj/utils/BusinessException.java
  79. 45 0
      src/main/java/com/jpsoft/proj/utils/CodeConstants.java
  80. 49 0
      src/main/java/com/jpsoft/proj/utils/CustomMethodArgumentResolver.java
  81. 132 0
      src/main/java/com/jpsoft/proj/utils/DataAuthUtils.java
  82. 25 0
      src/main/java/com/jpsoft/proj/utils/DataUtils.java
  83. 66 0
      src/main/java/com/jpsoft/proj/utils/DateEditor.java
  84. 102 0
      src/main/java/com/jpsoft/proj/utils/EncryptUtil.java
  85. 125 0
      src/main/java/com/jpsoft/proj/utils/JsonOutUtils.java
  86. 100 0
      src/main/java/com/jpsoft/proj/utils/LayGridJsonUtils.java
  87. 52 0
      src/main/java/com/jpsoft/proj/utils/LayGridResp.java
  88. 13 0
      src/main/java/com/jpsoft/proj/utils/MVCConstants.java
  89. 106 0
      src/main/java/com/jpsoft/proj/utils/MapUtils.java
  90. 38 0
      src/main/java/com/jpsoft/proj/utils/MySessionUser.java
  91. 96 0
      src/main/java/com/jpsoft/proj/utils/OkhttpUtils.java
  92. 40 0
      src/main/java/com/jpsoft/proj/utils/RequestParams.java
  93. 25 0
      src/main/java/com/jpsoft/proj/utils/RequestUtils.java
  94. 69 0
      src/main/java/com/jpsoft/proj/utils/RespVO.java
  95. 32 0
      src/main/java/com/jpsoft/proj/utils/RespVOBuilder.java
  96. 140 0
      src/main/java/com/jpsoft/proj/utils/SessionTimeoutFilter.java
  97. 106 0
      src/main/java/com/jpsoft/proj/utils/UploadUtils.java
  98. 22 0
      src/main/java/com/jpsoft/proj/utils/WebListener.java
  99. 52 0
      src/main/java/com/jpsoft/proj/volunteer/controller/VolunteerBaseController.java
  100. 133 0
      src/main/java/com/jpsoft/proj/volunteer/service/VolunteerService.java

+ 8 - 0
.classpath

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src/main/java"/>
+	<classpathentry kind="src" path="src/main/resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+target

+ 23 - 0
.project

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>volunteerRescue</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.maven.ide.eclipse.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+	</natures>
+</projectDescription>

+ 2 - 0
.settings/org.eclipse.core.resources.prefs

@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding//src/main/resources/config/CustomValidationMessages.properties=UTF-8

+ 5 - 0
.settings/org.eclipse.jdt.core.prefs

@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.7

+ 7 - 0
.settings/org.maven.ide.eclipse.prefs

@@ -0,0 +1,7 @@
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1

+ 208 - 0
pom.xml

@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>com.jpsoft</groupId>
+  <artifactId>volunteerRescue</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <packaging>war</packaging>
+
+  <name>volunteerRescue Maven Webapp</name>
+  <!-- FIXME change it to the project's website -->
+  <url>http://www.example.com</url>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <maven.compiler.source>1.7</maven.compiler.source>
+    <maven.compiler.target>1.7</maven.compiler.target>
+  </properties>
+
+  <dependencies>
+    <dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>servlet-api</artifactId>
+			<version>2.5</version>
+			<scope>provided</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>javax.servlet.jsp</groupId>
+			<artifactId>jsp-api</artifactId>
+			<version>2.1</version>
+			<scope>provided</scope>
+		</dependency>
+		
+       <dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-webmvc</artifactId>
+			<version>4.3.21.RELEASE</version>
+		</dependency>
+		
+			<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-tx</artifactId>
+			<version>4.3.21.RELEASE</version>
+		</dependency>
+
+		<dependency>
+	      <groupId>commons-dbcp</groupId>
+	      <artifactId>commons-dbcp</artifactId>
+	      <version>1.4</version>
+       </dependency>
+
+
+		<dependency>
+			<groupId>org.aspectj</groupId>
+			<artifactId>aspectjweaver</artifactId>
+			<version>1.7.1</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-jdbc</artifactId>
+			<version>4.3.21.RELEASE</version>
+		</dependency>
+		
+		<dependency>
+			<groupId>commons-beanutils</groupId>
+			<artifactId>commons-beanutils</artifactId>
+			<version>1.9.4</version>
+		</dependency>
+		
+		<dependency>
+		    <groupId>org.apache.commons</groupId>
+		    <artifactId>commons-lang3</artifactId>
+		    <version>3.4</version>
+		</dependency>
+		
+		<dependency>
+			<groupId>commons-lang</groupId>
+			<artifactId>commons-lang</artifactId>
+			<version>2.6</version>
+		</dependency>
+		
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>1.7.2</version>
+		</dependency>
+
+		<dependency>
+			<groupId>ch.qos.logback</groupId>
+			<artifactId>logback-core</artifactId>
+			<version>1.0.7</version>
+		</dependency>
+
+		<dependency>
+			<groupId>ch.qos.logback</groupId>
+			<artifactId>logback-classic</artifactId>
+			<version>1.0.7</version>
+		</dependency>
+		
+		<dependency>
+		    <groupId>com.fasterxml.jackson.core</groupId>
+		    <artifactId>jackson-databind</artifactId>
+		    <version>2.9.2</version>
+		</dependency>
+
+		
+		
+		<dependency>
+		    <groupId>com.alibaba</groupId>
+		    <artifactId>fastjson</artifactId>
+		    <version>1.2.72</version>
+		</dependency>
+		
+		<dependency>
+         	<groupId>com.jpsoft</groupId>
+       	 	<artifactId>framework</artifactId>
+         	<version>4.3</version>
+       </dependency>
+       
+       <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+            <version>2.0.1.Final</version>
+        </dependency>
+        
+       <dependency>
+		    <groupId>org.hibernate.validator</groupId>
+		    <artifactId>hibernate-validator</artifactId>
+		    <version>6.0.15.Final</version>
+		</dependency>
+		
+		<dependency>
+		    <groupId>org.glassfish</groupId>
+		    <artifactId>javax.el</artifactId>
+		    <version>3.0.1-b11</version>
+		</dependency>
+		
+		
+		
+		<dependency>
+		    <groupId>com.squareup.okhttp3</groupId>
+		    <artifactId>okhttp</artifactId>
+		    <version>3.10.0</version>
+		</dependency>
+		
+		<dependency>
+		    <groupId>commons-codec</groupId>
+		    <artifactId>commons-codec</artifactId>
+		    <version>1.15</version>
+		</dependency>
+		
+		<dependency>
+		    <groupId>mysql</groupId>
+		    <artifactId>mysql-connector-java</artifactId>
+		    <version>5.1.25</version>
+		</dependency>
+		
+		 <dependency>
+			<groupId>net.sourceforge</groupId>
+			<artifactId>pinyin4j</artifactId>
+			<version>2.5.0</version>
+		</dependency>
+		
+		<dependency>
+		  <groupId>commons-fileupload</groupId>
+		  <artifactId>commons-fileupload</artifactId>
+		  <version>1.3</version>
+	    </dependency>
+		
+  </dependencies>
+
+  <build>
+    <finalName>volunteerRescue</finalName>
+    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
+      <plugins>
+        <plugin>
+				<groupId>org.apache.tomcat.maven</groupId>
+				<artifactId>tomcat7-maven-plugin</artifactId>
+				<version>2.2</version>
+				<configuration>
+					<url>http://localhost:8081/manager/text</url>
+					<path>/rescue</path>
+					<uriEncoding>UTF-8</uriEncoding>
+					<server>tomcat</server>
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>2.3.2</version> 
+				<configuration>
+					<encoding>utf8</encoding>
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-war-plugin</artifactId>
+				<version>2.4</version>
+				<configuration>
+					<includeEmptyDirectories>true</includeEmptyDirectories>
+				</configuration>
+			</plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+</project>

+ 118 - 0
src/main/java/com/jpsoft/proj/api/controller/AuthWeiXinController.java

@@ -0,0 +1,118 @@
+package com.jpsoft.proj.api.controller;
+
+import javax.validation.constraints.NotBlank;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import com.jpsoft.framework.util.SysConfigUtil;
+import com.jpsoft.proj.model.Code2Session;
+import com.jpsoft.proj.model.MemberPO;
+import com.jpsoft.proj.model.MemberVO;
+import com.jpsoft.proj.model.WxSessionCache;
+import com.jpsoft.proj.rescue.service.MemberService;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.OkhttpUtils;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+/**
+ * 微信小程序端登录用户 信息绑定
+ */
+@RestController
+@RequestMapping("/**/api/seeker/auth")
+@Validated
+public class AuthWeiXinController {
+	
+	public static Logger logger=LoggerFactory.getLogger(AuthWeiXinController.class);
+	
+
+	
+	@Autowired
+	private MemberService memberService;
+
+	/**
+	 * 
+	 * @param code  wx.login 用户登录凭证(有效期五分钟)
+	 * @param response
+	 */
+	@RequestMapping("/getOpenid")
+	public RespVO getOpenid(@NotBlank(message = "小程序登录凭证【code】不能为空") String code){
+		if(WxSessionCache.getOpenid(code)==null){
+			
+			String code2SessionUrl="https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
+			code2SessionUrl=String.format(code2SessionUrl, SysConfigUtil.getConfig("wxappid"),SysConfigUtil.getConfig("wxsecret"),code);
+			String resp=OkhttpUtils.get(code2SessionUrl);
+			Code2Session wxResp=JSON.parseObject(resp,new TypeReference<Code2Session>() {});
+			logger.debug(resp);
+			if(wxResp.getErrcode()==0){ //请求成功
+				WxSessionCache.putOpenid(code,wxResp.getOpenid());
+			}
+			else{
+				logger.warn("code2session request failed:"+wxResp.getErrmsg());
+				return RespVOBuilder.error("身份验证失败");
+			}
+		}
+		
+		
+		MemberVO member=memberService.getByOpenid(WxSessionCache.getOpenid(code));
+		if(member!=null){
+			return RespVOBuilder.ok(member);
+		}
+		else{
+			return RespVOBuilder.ok(MapUtils.builder("openid",WxSessionCache.getOpenid(code)));
+		}
+		
+	}
+	
+	/**
+	 * 首次使用,完善个人信息
+	 * @param userName 姓名
+	 * @param phone  移动电话
+	 * @param response
+	 */
+	@RequestMapping("/bindUserInfo")
+	public RespVO bindUserInfo(@Validated MemberPO  member){
+	  
+		/*MemberPO dbMember=memberService.getByPhone(member.getPhone());
+		if(dbMember!=null&&!(member.getRealName().equals(dbMember.getRealName())) ){
+			return RespVOBuilder.error("该手机号已被使用,请更换");
+		}*/
+		MemberVO dbmember=memberService.getByOpenid(member.getOpenid());
+		if(dbmember!=null){
+			member.setMemberId(dbmember.getMemberId());
+			memberService.updateMemberByOpenid(member);
+		}
+		else{
+			memberService.addMember(member);
+			
+		}
+		return RespVOBuilder.ok();
+		
+	}
+	
+	
+	@RequestMapping("/getBindUserInfo")
+	public RespVO getBindUserInfo(@NotBlank(message = "用户微信识别码【openid】不能为空") String openid){
+		return RespVOBuilder.ok(memberService.getByOpenid(openid));
+	}
+	
+	
+
+	/**
+	 * 注销账号
+	 * @param openid
+	 * @return
+	 */
+	@RequestMapping("/unregister")
+	public RespVO unregister(@NotBlank(message = "用户微信识别码【openid】不能为空") String openid){
+		memberService.delForUnregister(openid);
+		return RespVOBuilder.ok();
+	}
+}

+ 193 - 0
src/main/java/com/jpsoft/proj/api/controller/MobileAuthController.java

@@ -0,0 +1,193 @@
+package com.jpsoft.proj.api.controller;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.proj.model.FileUseForEm;
+import com.jpsoft.proj.model.SmsCodeCache;
+import com.jpsoft.proj.model.VolunteerPO;
+import com.jpsoft.proj.rescue.service.RescueVolunteerService;
+import com.jpsoft.proj.sms.service.SmsSender;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+import com.jpsoft.proj.utils.UploadUtils;
+import com.jpsoft.proj.volunteer.service.VolunteerService;
+
+
+@RestController
+@RequestMapping("/**/api/volunteer/auth")
+@Validated
+public class MobileAuthController {
+
+	@Autowired
+	private VolunteerService service;
+	
+	@Autowired
+	private RescueVolunteerService  rescueVolunteerService;
+	
+	
+	/**
+	 * 志愿者手机端登录(账号密码方式)
+	 * @param phone
+	 * @param pwd md5加密
+	 * @return
+	 */
+	@RequestMapping("/login")
+	public RespVO login(@NotBlank(message = "手机号不能为空") String phone,@NotBlank(message = "密码不能为空") String pwd){
+		VolunteerPO volunteer=service.getVolunteerByPhone(phone);
+		if(volunteer==null){
+			return RespVOBuilder.error("该用户不存在,请先注册");
+		}
+		if(!pwd.equals(volunteer.getPwd())){
+			return RespVOBuilder.error("密码不正确");
+		}
+		volunteer.setPwd(null);
+		return RespVOBuilder.ok(volunteer);
+	}
+	
+	
+	/**
+	 * 志愿者个人信息完善
+	 * @param volunteer
+	 * @return
+	 */
+	@RequestMapping("/bindVolunteerInfo")
+	public RespVO bindVolunteerInfo(@Validated VolunteerPO  volunteer){
+		VolunteerPO dbvolunteer=service.getVolunteerByPhone(volunteer.getPhone());
+		if(dbvolunteer==null){
+			return RespVOBuilder.error("该用户已不存在");
+		}
+		dbvolunteer.setProfession(volunteer.getProfession());
+		dbvolunteer.setOwnEquipments(volunteer.getOwnEquipments());
+		dbvolunteer.setRescueSort(volunteer.getRescueSort());
+		dbvolunteer.setHeadPhoto(volunteer.getHeadPhoto());
+		dbvolunteer.setRealName(volunteer.getRealName());
+		dbvolunteer.setRegAddress(volunteer.getRegAddress());
+		dbvolunteer.setRegLat(volunteer.getRegLat());
+		dbvolunteer.setRegLon(volunteer.getRegLon());
+		service.updateVolunteer(dbvolunteer);
+		return RespVOBuilder.ok();
+	}
+	
+	/**
+	 * 一键登录验证
+	 * @param phone
+	 * @return
+	 */
+	@RequestMapping("/checkUniverify")
+	public RespVO checkUniverify(@NotBlank(message = "手机号不能为空") String phone){
+		return RespVOBuilder.ok(service.getVolunteerByPhone(phone));
+	}
+	
+	
+	
+	/**
+	 * 注册
+	 * @param phone
+	 * @return
+	 */
+	@RequestMapping("/reg")
+	public RespVO reg(@Validated({VolunteerPO.Create.class}) VolunteerPO  volunteer){
+		VolunteerPO  dbVolunteer=service.getVolunteerByPhone(volunteer.getPhone());
+		if(dbVolunteer!=null){
+			return RespVOBuilder.error("该手机号已被使用,请更换");
+		}
+		volunteer.setModifier(volunteer.getPhone());
+		service.addVolunteer(volunteer);
+		return RespVOBuilder.ok(volunteer);
+	}
+	
+	/**
+	 * 设置密码
+	 * @param phone
+	 * @param newpwd
+	 * @param validCode
+	 * @return
+	 */
+	@RequestMapping("/setPwd")
+	public RespVO setPwd(@NotBlank(message = "手机号不能为空") String phone,@NotBlank(message = "新密码不能为空") String newpwd,@NotNull(message = "验证码不能为空") Long validCode){
+		if(!SmsCodeCache.valid(phone, validCode)){
+			return RespVOBuilder.error("验证码错误或无效");
+		}
+		VolunteerPO volunteer=service.getVolunteerByPhone(phone);
+		if(volunteer==null){
+			return RespVOBuilder.error("该用户已不存在");
+		}
+		volunteer.setPwd(newpwd);
+		service.updateVolunteer(volunteer);
+		return RespVOBuilder.ok();
+	}
+	
+	
+	/**
+	 * 通过手机号获得志愿者个人信息
+	 * @param phone
+	 * @return
+	 */
+	@RequestMapping("/getInfoByPhone")
+	public RespVO getInfoByPhone(@NotBlank(message = "手机号不能为空") String phone){
+		Map<String,Object> info=service.getInfoByPhone(phone);
+		if(info!=null){
+			info.put("pwd",null);
+			return RespVOBuilder.ok(info);
+		}
+		else{
+			return RespVOBuilder.error("数据不存在","nodata");
+		}
+		
+	}
+	
+	
+	/**
+	 * 上传照片
+	 * @param request
+	 * @return
+	 * @throws IOException
+	 */
+	@SuppressWarnings("unchecked")
+	@RequestMapping("/uploadPhoto")
+	public RespVO uploadPhoto(HttpServletRequest request)throws IOException{
+		RespVO rst=UploadUtils.saveFile(request, "photo");
+		Map<String,Object>  rstMap=(Map<String,Object>)rst.getData();
+		rstMap=MapUtils.trackMap(rstMap, "volunteerId","fileName","filePath","accessPath","fileSort");
+		rstMap.put("useFor", FileUseForEm.HEAD_PHOTO.getName());
+		String fileId=service.addVolunteerFile(rstMap);
+		return RespVOBuilder.ok(fileId);
+	}
+	
+	
+	/**
+	 * 获取短信验证码
+	 * @param phone
+	 * @return
+	 */
+	@RequestMapping("/getSmsCode")
+	public RespVO  getSmsCode(@NotBlank(message = "手机号不能为空") String phone){
+		if(!SmsCodeCache.next(phone)){
+			return RespVOBuilder.error("短时间内请勿重复获取");
+		}
+		Long validCode=SmsCodeCache.getNext(phone);
+		SmsSender.sendForValidCode(phone,String.format("[\"%d\"]",validCode));
+		return RespVOBuilder.ok();
+	}
+	
+	/**
+	 * 加载个人数据--app 我的 页面
+	 * @param volunteerId
+	 * @return
+	 */
+	@RequestMapping("/loadForMe")
+	public RespVO loadForMe(@NotBlank(message = "志愿者编号【volunteerId】不能为空") String volunteerId){
+		return RespVOBuilder.ok(MapUtils.builder("myInfo",service.getById(volunteerId),"rescueRpt",rescueVolunteerService.getJoinRescueRpt(volunteerId)));
+	}
+}

+ 127 - 0
src/main/java/com/jpsoft/proj/api/controller/SeekOtherController.java

@@ -0,0 +1,127 @@
+package com.jpsoft.proj.api.controller;
+
+import java.util.Date;
+import java.util.Map;
+
+import javax.validation.constraints.NotBlank;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.proj.newsinfo.service.NewsInfoService;
+import com.jpsoft.proj.newsinfo.service.NoticeService;
+import com.jpsoft.proj.newsinfo.service.SuggestService;
+import com.jpsoft.proj.rescue.service.RescueSeekerService;
+import com.jpsoft.proj.sysdata.service.SortCodeService;
+import com.jpsoft.proj.utils.CodeConstants;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+@RestController
+@RequestMapping("/**/api/seeker/other")
+@Validated
+public class SeekOtherController {
+
+	@Autowired
+	private SuggestService suggestService;
+	
+	@Autowired
+	private RescueSeekerService rescueService;
+	
+	@Autowired
+	private NewsInfoService  newsInfoService;
+	
+	@Autowired
+	private SortCodeService  codeService;
+	
+	@Autowired
+	private NoticeService  noticeService;
+	
+	/**
+	 * 加载新闻资讯下的特点类别:社会救援
+	 * @return
+	 */
+	@RequestMapping("/getSocietyRescueInfo")
+	public RespVO  getSocietyRescueInfo(){
+		return RespVOBuilder.ok(newsInfoService.getNewsInfoBySort(CodeConstants.SOCIETY_RESCUE_SORT));
+	}
+	
+	/**
+	 * 加载新闻类别
+	 * @return
+	 */
+	@RequestMapping("/loadNewsSort")
+	public RespVO loadNewsSort(){
+		return RespVOBuilder.ok(codeService.queryCodeNodes(CodeConstants.INFO_SORT));
+	}
+	
+	/**
+	 * 分页加载新闻资讯
+	 * @param infoSort
+	 * @param page
+	 * @param limit
+	 * @return
+	 */
+	@RequestMapping("/loadMoreNewsInfo")
+	public RespVO loadMoreNewsInfo(String infoSort,@RequestParam(value="pageNum",defaultValue="1") Integer pageNum,@RequestParam(value="pageSize",defaultValue="20") Integer pageSize){
+		return RespVOBuilder.ok(newsInfoService.queryNewsInfo("seeker",infoSort,pageNum, pageSize));
+	}
+	
+	@RequestMapping("/getNewsInfo")
+	public RespVO  getNewsInfo(@NotBlank(message = "新闻资讯记录编号【infoId】不能为空") String infoId){
+		newsInfoService.addReadCount(infoId);
+		return RespVOBuilder.ok(newsInfoService.getNewsInfo(infoId));
+	}
+	
+	
+	@RequestMapping("/addSuggest")
+	public RespVO addSuggest(@NotBlank(message = "反馈意见【suggest】不能为空") String suggest,@NotBlank(message = "意见反馈人【seekerId】不能为空") String seekerId){
+		Map<String,Object> lastSuggest=suggestService.getLast(seekerId);
+		if(lastSuggest!=null&&lastSuggest.get("createTime")!=null){
+			Date  lastTime=(Date)lastSuggest.get("createTime");
+			long diff=(new Date()).getTime()-lastTime.getTime();
+			if(diff<3600000){
+				return RespVOBuilder.error("休息会,再反馈吧");
+			}
+		}
+		return RespVOBuilder.ok(suggestService.add(MapUtils.builder("suggestCon",suggest,"suggesterId",seekerId,"fromPlatform","求助者")));
+	}
+	
+	
+	@RequestMapping("/rptMyRescue")
+	public RespVO  rptMyRescue(@NotBlank(message = "求救人编号【seekerId】不能为空") String seekerId){
+		return RespVOBuilder.ok(rescueService.rptMyRescue(seekerId));
+	}
+	
+	/**
+	 * 加载公告
+	 * @param seeker
+	 * @param pageNum
+	 * @param pageSize
+	 * @return
+	 */
+	@RequestMapping("/loadNotices")
+	public RespVO loadNotices(String seekerId,@RequestParam(value="pageNum",defaultValue="1") Integer pageNum,@RequestParam(value="pageSize",defaultValue="20") Integer pageSize){
+		return RespVOBuilder.ok(noticeService.load(seekerId, "seeker",pageNum, pageSize));
+	}
+	
+	/**
+	 * 标记已阅读消息(目前只有公告)
+	 * @param userId
+	 * @param msgId
+	 * @param msgSort
+	 * @return
+	 */
+	@RequestMapping("/addMsgReadedFlag")
+	public RespVO addMsgReadedFlag(String userId,String msgId,String msgSort){
+		if(noticeService.hadRead(userId, msgId)){
+			return RespVOBuilder.ok();
+		}
+		noticeService.addMsgReadedFlag(userId, msgId, msgSort);
+		return RespVOBuilder.ok();
+	}
+}

+ 104 - 0
src/main/java/com/jpsoft/proj/api/controller/SeekerIndexController.java

@@ -0,0 +1,104 @@
+package com.jpsoft.proj.api.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.proj.newsinfo.service.AdService;
+import com.jpsoft.proj.newsinfo.service.NewsInfoService;
+import com.jpsoft.proj.newsinfo.service.NoticeService;
+import com.jpsoft.proj.rescue.service.RescueEquipmentService;
+import com.jpsoft.proj.utils.CodeConstants;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+import com.jpsoft.proj.volunteer.service.VolunteerService;
+
+
+@RestController
+@RequestMapping("/**/api/seeker/index")
+@Validated
+public class SeekerIndexController {
+
+	
+	
+	@Autowired
+	private NoticeService   noticeService;
+	
+	@Autowired
+	private VolunteerService   volunteerService;
+	
+	
+	@Autowired
+	private NewsInfoService  newsInfoService;
+	
+	@Autowired
+	private AdService  adService;
+	
+	@Autowired
+	private RescueEquipmentService  equService;
+	
+	
+	@RequestMapping("/loadForIndex")
+	public RespVO loadForIndex(
+			@RequestParam(value = "noticeCount", defaultValue = "10") Integer noticeCount,
+			@RequestParam(value = "infoCount", defaultValue = "10") Integer infoCount,
+			@RequestParam(value = "adCount", defaultValue = "3") Integer adCount,
+			@RequestParam(value = "equCount", defaultValue = "3") Integer equCount) {
+		
+		List<Map<String,Object>> ads=adService.loadForMobileIndex("seeker",adCount);
+		List<Map<String,Object>> notices=noticeService.loadForMobile("seeker", noticeCount);
+		
+		Map<String,Object> rst=MapUtils.builder("volunteerCount",volunteerService.getVolunteerCount(),"ads",ads,"notices",notices);
+		rst.put("news", newsInfoService.loadIndexSortInfo(CodeConstants.NEWSONLY_SORT, "seeker", infoCount));
+		rst.put("onlineStudy", newsInfoService.loadIndexSortInfo(CodeConstants.ONLINE_STUDY_SORT, "seeker", infoCount));
+		rst.put("rescueKnowledge", newsInfoService.loadIndexSortInfo(CodeConstants.RESCUE_KNOWLEDGE_SORT, "seeker", infoCount));
+		rst.put("selfRescue", newsInfoService.loadIndexSortInfo(CodeConstants.SELF_RESCUE_SORT, "seeker", infoCount));
+		rst.put("seekRescue", newsInfoService.loadIndexSortInfo(CodeConstants.SEEK_RESCUE_SORT, "seeker", infoCount));
+		
+		rst.put("rescueEqu",equService.loadSortEquipments(CodeConstants.EQUIPMENT_RESCUE,equCount));
+		rst.put("intelligentEqu",equService.loadSortEquipments(CodeConstants.EQUIPMENT_INTELLIGENT,equCount));
+		return RespVOBuilder.ok(rst);
+	}
+	
+	
+	/**
+	 * 主页  公告、注册志愿者人数加载
+	 * @param adCount
+	 * @return
+	 */
+	@RequestMapping("/loadIndexNotice")
+	public RespVO loadIndexNotice(@RequestParam(value="noticeCount",defaultValue="3") Integer noticeCount){
+		List<Map<String,Object>> notices=noticeService.loadForMobile("seeker", noticeCount);
+		return RespVOBuilder.ok(MapUtils.builder("volunteerCount",volunteerService.getVolunteerCount(),"notices",notices));
+	}
+	
+	
+	/**
+	 * 主页  加载新闻资讯
+	 * @param infoCount
+	 * @return
+	 */
+	@RequestMapping("/loadIndexNewsInfo")
+	public RespVO loadIndexNewsInfo(@RequestParam(value="infoCount",defaultValue="10") Integer infoCount){
+		return RespVOBuilder.ok(newsInfoService.loadIndexNewsInfo("seeker",infoCount));
+	}
+	
+	/**
+	 * 主页  加载轮播广告
+	 * @param adCount
+	 * @return
+	 */
+	@RequestMapping("/loadIndexAd")
+	public RespVO loadIndexAd(@RequestParam(value="adCount",defaultValue="3") Integer adCount){
+		return RespVOBuilder.ok(adService.loadForMobileIndex("seeker",adCount));
+	}
+	
+	
+	
+}

+ 296 - 0
src/main/java/com/jpsoft/proj/api/controller/SeekerRescueController.java

@@ -0,0 +1,296 @@
+package com.jpsoft.proj.api.controller;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.constraints.NotBlank;
+
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.io.FileUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.framework.util.DateUtil;
+import com.jpsoft.framework.util.SysConfigUtil;
+import com.jpsoft.proj.model.RescueApplyPO;
+import com.jpsoft.proj.model.RescueApplyVO;
+import com.jpsoft.proj.model.RescueFlowPO;
+import com.jpsoft.proj.rescue.service.CallNearbyTask;
+import com.jpsoft.proj.rescue.service.CallVolunteerExecutor;
+import com.jpsoft.proj.rescue.service.CallVolunteerTask;
+import com.jpsoft.proj.rescue.service.MemberService;
+import com.jpsoft.proj.rescue.service.RescueConfigService;
+import com.jpsoft.proj.rescue.service.RescueFlowService;
+import com.jpsoft.proj.rescue.service.RescueSeekerService;
+import com.jpsoft.proj.rescue.service.RescueService;
+import com.jpsoft.proj.utils.CodeConstants;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RequestParams;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+import com.jpsoft.proj.utils.UploadUtils;
+
+@RestController
+@RequestMapping("/**/api/seeker/rescue")
+@Validated
+public class SeekerRescueController {
+
+	@Autowired
+	private RescueService  rescueService;
+	
+	@Autowired
+	private RescueSeekerService  rescueSeekerService;
+	
+	@Autowired
+	private RescueConfigService  rescueConfigService;
+	
+	@Autowired
+	private MemberService  memberService;
+	
+	@Autowired
+	private RescueFlowService  flowService;
+	
+	/**
+	 * 关闭求援记录
+	 * @param rescueId
+	 * @return
+	 */
+	@RequestMapping("/closeRescueApply")
+	public RespVO closeRescueApply(@NotBlank(message = "求援记录编号【rescueId】不能为空") String rescueId){
+		RescueApplyVO rescue=rescueSeekerService.getPublishedRescue(rescueId);
+		rescueSeekerService.updateForCloseRescue(rescueId,rescue.getPublisherName());
+		return RespVOBuilder.ok();
+	}
+	
+	/**
+	 * 求助明细(求助端)
+	 * @param rescueId
+	 * @return
+	 */
+	@RequestMapping("/getRescueDtl")
+	public RespVO getRescueDtl(@NotBlank(message = "求援记录编号【rescueId】不能为空") String rescueId){
+		RescueApplyVO rescue=rescueSeekerService.getPublishedRescue(rescueId);
+		if(rescue==null){
+			return RespVOBuilder.error("数据不存在,刷新后再试");
+		}
+		List<RescueFlowPO> flows=flowService.loadRescueFlow(rescueId);
+		List<Map<String,Object>> files=rescueService.loadRescueFiles(rescueId);
+		List<Map<String,Object>> joiners=rescueService.loadRescueJoiners(rescueId);
+		return RespVOBuilder.ok(MapUtils.builder("rescue",rescue,"flows",flows,"files",files,"joiners",joiners));
+	}
+	
+	/**
+	 * 加载我发布的求救记录
+	 * @param seekerId
+	 * @param pageNum
+	 * @param pageSize
+	 * @return
+	 */
+	@RequestMapping("/loadMyRescues")
+	public RespVO loadMyRescues(@NotBlank(message = "求救人编号【seekerId】不能为空") String seekerId,@RequestParam(value="pageNum",defaultValue="1") Integer pageNum,@RequestParam(value="pageSize",defaultValue="20") Integer pageSize){
+		RequestParams  params=new RequestParams();
+		params.put("publisherId", seekerId);
+		PageModel<RescueApplyVO> pagedData=rescueSeekerService.queryMyPublishedRescue(params, pageNum, pageSize);
+		if(pagedData!=null&&pagedData.getData()!=null){  //数据转换处理,流程步骤代码转名称
+			Map<String,String> stepMap=CodeConstants.loadRescueFlowStepMap();
+			for(RescueApplyVO itm : pagedData.getData()){
+				itm.setStepName(stepMap.get(itm.getStep()));
+			}
+		}
+		return RespVOBuilder.ok(pagedData);
+	}
+	
+	/**
+	 * 发布求救信息
+	 * @param apply
+	 * @param fileIds 上传文件编号(后台返回的编号)
+	 * @return
+	 */
+	@PostMapping("/publishRescue")
+	public RespVO publishRescue(@Validated RescueApplyPO apply,String fileIds){
+		Map<String,Object> seeker=memberService.get(apply.getPublisher());
+		String rescueId=rescueSeekerService.createRescuePublish(apply,fileIds,seeker!=null?((String)seeker.get("realName")):null);
+		
+		RescueApplyVO  rescueApply=rescueSeekerService.getPublishedRescue(rescueId);
+		
+		if(rescueApply.getNeedVolunteerCount()==null){
+			rescueApply.setNeedVolunteerCount(5); //未设置时,默认召集5个志愿者
+		}
+		
+		CallVolunteerExecutor.execute(new CallVolunteerTask(rescueApply));  //异步短信召集志愿者
+		
+		CallVolunteerExecutor.execute(new CallNearbyTask(rescueApply));  //异步短信通知附近用户
+		
+		return RespVOBuilder.ok(rescueId);
+	}
+	
+	
+	/**
+	 * 重新呼救(第一次呼叫后参与的志愿者人数不足,且与第一次呼叫时间间隔1分钟,最多重复5次)
+	 * @param rescueId
+	 * @return
+	 */
+	@RequestMapping("/recallVolunteer")
+	public RespVO recallVolunteer(@Validated RescueApplyPO recallApply,String fileIds){
+		
+		//先更新求救信息,有错误消息返回,取消后续操作
+		String rst=rescueSeekerService.updateRescue(recallApply,fileIds);
+		if(rst!=null){
+			return RespVOBuilder.error(rst);
+		}
+		
+		//再重新发出通知
+		RescueApplyVO  rescueApply=rescueSeekerService.getPublishedRescue(recallApply.getRescueId());
+		if(rescueApply==null){
+			return RespVOBuilder.error("呼救记录不存在");
+		}
+		
+		if(rescueApply.getCallCount()>=5){ //已经达到5次 
+			return RespVOBuilder.error("超过最大呼救次数5");
+		}
+		
+		long diffTime=90000; //大于60000即可
+		long now=(new Date()).getTime();
+		if(rescueApply.getLastCallTime()!=null){
+			diffTime=now-rescueApply.getLastCallTime().getTime();
+		}
+		if(diffTime<60000){ //小于1分钟重复呼救忽略
+			return RespVOBuilder.error("距离上次呼救还不到1分钟,请稍等");
+		}
+		
+		diffTime=now-rescueApply.getPublishTime().getTime();
+		if(diffTime>86400000){ //求救发布超过24小时
+			return RespVOBuilder.error("求救发布已超过24小时,如需呼救请重新发布");
+		}
+		
+		if(rescueApply.getNeedVolunteerCount()==null){
+			rescueApply.setNeedVolunteerCount(5); //未设置时,默认召集5个志愿者
+		}
+		
+		if(rescueApply.getAcceptCount()!=null&&rescueApply.getAcceptCount().intValue()>=rescueApply.getNeedVolunteerCount().intValue()){
+			return RespVOBuilder.error("已经有足够的志愿者正赶来,无需重复呼救");
+		}
+		
+		//重复呼救时,呼救人数为规定数*次数
+		rescueApply.setNeedVolunteerCount(rescueApply.getNeedVolunteerCount()*(rescueApply.getCallCount()+1));
+		CallVolunteerExecutor.execute(new CallVolunteerTask(rescueApply));  //异步短信召集志愿者
+		
+		return RespVOBuilder.ok(recallApply.getRescueId());
+	}
+	
+	/**
+	 * 加载突发事件类别
+	 * @return
+	 */
+	@RequestMapping("/loadEmergencySort")
+	public RespVO loadEmergencySort(){
+		List<Map<String,Object>>  emergencys=rescueConfigService.loadEmergencySort();
+		String icon=null;
+		for(Map<String,Object> itm : emergencys){
+			icon=(String)itm.get("icon");
+			if(StringUtils.isEmpty(icon)){
+				icon="/upload/icon/def.png";
+			}
+			itm.put("icon",SysConfigUtil.getConfig("accessPath")+icon);
+			
+		}
+		emergencys.add(MapUtils.builder("icon",SysConfigUtil.getConfig("accessPath")+"/upload/icon/more.png","emergencySort","more"));
+		return RespVOBuilder.ok(emergencys);
+	}
+	
+	/**
+	 * 加载常用求援场景描述语
+	 * @return
+	 */
+	@RequestMapping("/loadSceneDescSort")
+	public RespVO loadSceneDescSort(){
+		return RespVOBuilder.ok(rescueConfigService.loadSceneDescSort());
+	}
+	
+	/**
+	 * 加载求援基础数据(突发事件类别、常用现场描述语)
+	 * @return
+	 */
+	@RequestMapping("/loadBaseForPublish")
+	public RespVO loadBaseForPublish(){
+		List<Map<String,Object>>  emergencys=rescueConfigService.loadEmergencySort();
+		String icon=null;
+		for(Map<String,Object> itm : emergencys){
+			icon=(String)itm.get("icon");
+			if(StringUtils.isEmpty(icon)){
+				icon="/upload/icon/def.png";
+			}
+			itm.put("icon",SysConfigUtil.getConfig("accessPath")+icon);
+		}
+		emergencys.add(MapUtils.builder("icon",SysConfigUtil.getConfig("accessPath")+"/upload/icon/more.png","emergencySort","more"));
+		
+		return RespVOBuilder.ok(MapUtils.builder("emergencys",emergencys,"sceneDescs",rescueConfigService.loadSceneDescSort()));
+	}
+	
+	/**
+	 * 发布救援信息时上传的照片、视频文件
+	 * @param request
+	 * @return
+	 * @throws IOException
+	 */
+	@RequestMapping("/uploadRescueFile")
+	public RespVO uploadRescueFile(HttpServletRequest request) throws IOException{
+		Map<String,Object> uploadDatas=UploadUtils.process(request);
+		if(uploadDatas==null||uploadDatas.size()==0){
+			return RespVOBuilder.error("未获取到上传数据");
+		}
+		String basePath=request.getSession().getServletContext().getRealPath("");
+		String relPath="upload"+File.separator+"rescue"+File.separator+DateUtil.format(new Date(), "yyyyMM");
+		basePath=basePath+File.separator+relPath;
+		
+		FileUtils.forceMkdir(new File(basePath));
+		
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		String newFileName=uuid.generate()+"."+uploadDatas.get("fileSort");
+		
+		FileUtils.writeByteArrayToFile(new File(basePath+File.separator+newFileName), ((FileItem)uploadDatas.get("fileCon")).get());
+		
+		//不能使用客户端地址
+		//String accessPath=request.getScheme()+"://"+request.getLocalAddr()+":"+request.getLocalPort()+request.getContextPath()+"/upload/rescue/"+DateUtil.format(new Date(), "yyyyMM")+"/"+newFileName;
+		String accessPath=SysConfigUtil.getConfig("accessPath")+"/upload/rescue/"+DateUtil.format(new Date(), "yyyyMM")+"/"+newFileName;
+		
+		Map<String,Object>  fileInfo=MapUtils.builder("fileSort",uploadDatas.get("fileSort"),"fileName",newFileName,"filePath",relPath,"accessPath",accessPath);
+		
+		String fileId=rescueSeekerService.addSeekRescueFile(fileInfo);
+		
+		return RespVOBuilder.ok(MapUtils.builder("fileId",fileId,"filePath",accessPath));
+	}
+	
+	/**
+	 * 删除求救是上传的文件
+	 * @param fileId
+	 * @return
+	 */
+	@RequestMapping("/delRescueFile")
+	public RespVO  delRescueFile(@NotBlank(message = "文件编号【fileId】不能为空") String fileId,HttpServletRequest request){
+		Map<String,Object> fileInfo=rescueService.getRescueFile(fileId);
+		if(fileInfo==null||fileInfo.size()==0||StringUtils.isEmpty((String)fileInfo.get("fileName"))){
+			return RespVOBuilder.ok();
+		}
+		//删除文件记录
+		rescueSeekerService.delRescueFile(fileId);
+		
+		//删除物理文件
+		String fileName=(String)fileInfo.get("fileName");
+		String filePath=(String)fileInfo.get("filePath");
+		String basePath=request.getSession().getServletContext().getRealPath("");
+		FileUtils.deleteQuietly(new File(basePath+File.separator+filePath+File.separator+fileName));
+		return RespVOBuilder.ok();
+	}
+}

+ 17 - 0
src/main/java/com/jpsoft/proj/api/controller/VerifyCodeUtils.java

@@ -0,0 +1,17 @@
+package com.jpsoft.proj.api.controller;
+
+public class VerifyCodeUtils {
+
+	public static int generate(){
+		int code=(int)Math.floor(Math.random()*1000000);
+		if(code<100000){ //保证为6位
+			code=code*10;
+		}
+		return code;
+	}
+	
+	public static void main(String[] args){
+		System.out.println(String.format("[\"%d\"]",VerifyCodeUtils.generate()));
+		//System.out.println(String.format("%06d", 47879));
+	}
+}

+ 87 - 0
src/main/java/com/jpsoft/proj/api/controller/VolunteerIndexController.java

@@ -0,0 +1,87 @@
+package com.jpsoft.proj.api.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.validation.constraints.NotBlank;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.proj.newsinfo.service.AdService;
+import com.jpsoft.proj.newsinfo.service.NewsInfoService;
+import com.jpsoft.proj.newsinfo.service.NoticeService;
+import com.jpsoft.proj.rescue.service.RescueService;
+import com.jpsoft.proj.rescue.service.RescueVolunteerService;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+@RestController
+@RequestMapping("/**/api/volunteer/index")
+@Validated
+public class VolunteerIndexController {
+
+	@Autowired
+	private RescueService  rescueService;
+	
+	@Autowired
+	private RescueVolunteerService  rescueVolunteerService;
+	
+	
+	@Autowired
+	private NewsInfoService  newsInfoService;
+	
+	@Autowired
+	private NoticeService  noticeService;
+	
+	@Autowired
+	private AdService  adService;
+	
+	@RequestMapping("/loadForIndex")
+	public RespVO loadForIndex(
+			@NotBlank(message = "志愿者编号【volunteerId】不能为空") String volunteerId,
+			@RequestParam(value = "infoCount", defaultValue = "10") Integer infoCount,
+			@RequestParam(value = "adCount", defaultValue = "3") Integer adCount) {
+		Map<String,Object> rescueRpt=rescueVolunteerService.getJoinRescueRpt(volunteerId);
+		List<Map<String,Object>> news=newsInfoService.loadIndexNewsInfo("volunteer",infoCount);
+		List<Map<String,Object>> ads=adService.loadForMobileIndex("volunteer",adCount);
+		return RespVOBuilder.ok(MapUtils.builder("rescueRpt",rescueRpt,"news",news,"ads",ads,"msgCount",noticeService.getUnReadNoticeCount(volunteerId)));
+	}
+	
+	/**
+	 * 志愿者手机端主页,救援统计,总救援数、本月救援数、漏单数
+	 * @param volunteerId
+	 * @return
+	 */
+	@RequestMapping("/getRescueRpt")
+	public RespVO getRescueRpt(@NotBlank(message = "志愿者编号【volunteerId】不能为空") String volunteerId){
+		return RespVOBuilder.ok(rescueVolunteerService.getJoinRescueRpt(volunteerId));
+	}
+	
+	
+	/**
+	 * 志愿者手机端主页  加载新闻资讯
+	 * @param infoCount
+	 * @return
+	 */
+	@RequestMapping("/loadIndexNewsInfo")
+	public RespVO loadIndexNewsInfo(@RequestParam(value="infoCount",defaultValue="10") Integer infoCount){
+		return RespVOBuilder.ok(newsInfoService.loadIndexNewsInfo("volunteer",infoCount));
+	}
+	
+	/**
+	 * 志愿者手机端主页  加载轮播广告
+	 * @param adCount
+	 * @return
+	 */
+	@RequestMapping("/loadIndexAd")
+	public RespVO loadIndexAd(@RequestParam(value="adCount",defaultValue="3") Integer adCount){
+		return RespVOBuilder.ok(adService.loadForMobileIndex("volunteer",adCount));
+	}
+	
+	
+}

+ 237 - 0
src/main/java/com/jpsoft/proj/api/controller/VolunteerOtherController.java

@@ -0,0 +1,237 @@
+package com.jpsoft.proj.api.controller;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.constraints.NotBlank;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.util.DateUtil;
+import com.jpsoft.proj.model.FileUseForEm;
+import com.jpsoft.proj.newsinfo.service.NewsInfoService;
+import com.jpsoft.proj.newsinfo.service.NoticeService;
+import com.jpsoft.proj.newsinfo.service.SuggestService;
+import com.jpsoft.proj.rescue.service.AEDService;
+import com.jpsoft.proj.sysdata.service.SortCodeService;
+import com.jpsoft.proj.sysdata.service.UsErrorService;
+import com.jpsoft.proj.utils.CodeConstants;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RequestParams;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+import com.jpsoft.proj.utils.UploadUtils;
+import com.jpsoft.proj.volunteer.service.VolunteerService;
+
+@RestController
+@RequestMapping("/**/api/volunteer/other")
+@Validated
+public class VolunteerOtherController {
+
+	@Autowired
+	private NewsInfoService  newsInfoService;
+	
+	@Autowired
+	private SortCodeService  codeService;
+	
+	@Autowired
+	private UsErrorService  errorService;
+	
+	@Autowired
+	private NoticeService  noticeService;
+	
+	@Autowired
+	private SuggestService suggestService;
+	
+	@Autowired
+	private VolunteerService volunteerService;
+	
+	@Autowired
+	private AEDService  aedService;
+	
+	/**
+	 * 加载新闻资讯下的特定类别:社会救援
+	 * @return
+	 */
+	@RequestMapping("/getSocietyRescueInfo")
+	public RespVO  getSocietyRescueInfo(){
+		return RespVOBuilder.ok(newsInfoService.getNewsInfoBySort(CodeConstants.SOCIETY_RESCUE_SORT));
+	}
+	
+	@RequestMapping("/rptError")
+	public RespVO  rptError(String deviceInfo,String error,String errorSort){
+		if(StringUtils.isBlank(error)){
+			return RespVOBuilder.ok();
+		}
+		errorService.addUsError(deviceInfo, error,errorSort);
+		return RespVOBuilder.ok();
+	}
+	
+	@RequestMapping("/loadNewsSort")
+	public RespVO loadNewsSort(){
+		return RespVOBuilder.ok(codeService.queryCodeNodes(CodeConstants.INFO_SORT));
+	}
+	
+	/**
+	 * 分页加载新闻资讯
+	 * @param infoSort
+	 * @param pageNum
+	 * @param pageSize
+	 * @return
+	 */
+	@RequestMapping("/loadMoreNewsInfo")
+	public RespVO loadMoreNewsInfo(String infoSort,@RequestParam(value="pageNum",defaultValue="1") Integer pageNum,@RequestParam(value="pageSize",defaultValue="20") Integer pageSize){
+		return RespVOBuilder.ok(newsInfoService.queryNewsInfo("volunteer",infoSort,pageNum, pageSize));
+	}
+	
+	@RequestMapping("/getNewsInfo")
+	public RespVO getNewsInfo(@NotBlank(message = "资讯编号【infoId】不能为空") String infoId){
+		newsInfoService.addReadCount(infoId);
+		return RespVOBuilder.ok(newsInfoService.getNewsInfo(infoId));
+	}
+	
+	/**
+	 * 救援能力类别
+	 * @return
+	 */
+	@RequestMapping("/loadRescueSort")
+	public RespVO loadRescueSort(){
+		return RespVOBuilder.ok(codeService.queryCodeNodes(CodeConstants.RESCUE_SORT));
+	}
+	
+	/**
+	 * 加载公告
+	 * @param volunteerId
+	 * @param pageNum
+	 * @param pageSize
+	 * @return
+	 */
+	@RequestMapping("/loadNotices")
+	public RespVO loadNotices(String volunteerId,@RequestParam(value="pageNum",defaultValue="1") Integer pageNum,@RequestParam(value="pageSize",defaultValue="20") Integer pageSize){
+		return RespVOBuilder.ok(noticeService.load(volunteerId, "volunteer",pageNum, pageSize));
+	}
+	
+	/**
+	 * 标记已阅读消息(目前只有公告)
+	 * @param userId
+	 * @param msgId
+	 * @param msgSort
+	 * @return
+	 */
+	@RequestMapping("/addMsgReadedFlag")
+	public RespVO addMsgReadedFlag(String userId,String msgId,String msgSort){
+		if(noticeService.hadRead(userId, msgId)){
+			return RespVOBuilder.ok();
+		}
+		noticeService.addMsgReadedFlag(userId, msgId, msgSort);
+		return RespVOBuilder.ok();
+	}
+	
+	/**
+	 * 反馈意见
+	 * @param suggest
+	 * @param suggesterId
+	 * @return
+	 */
+	@RequestMapping("/addSuggest")
+	public RespVO addSuggest(@NotBlank(message = "反馈意见【suggest】不能为空") String suggest,@NotBlank(message = "意见反馈人【suggesterId】不能为空") String suggesterId){
+		Map<String,Object> lastSuggest=suggestService.getLast(suggesterId);
+		if(lastSuggest!=null&&lastSuggest.get("createTime")!=null){
+			Date  lastTime=(Date)lastSuggest.get("createTime");
+			long diff=(new Date()).getTime()-lastTime.getTime();
+			if(diff<3600000){ //1小时内禁止重复提交
+				return RespVOBuilder.error("休息会,再反馈吧");
+			}
+		}
+		return RespVOBuilder.ok(suggestService.add(MapUtils.builder("suggestCon",suggest,"suggesterId",suggesterId,"fromPlatform","志愿者")));
+	}
+	
+	/**
+	 * 加载我的意见反馈
+	 * @param suggesterId
+	 * @param pageNum
+	 * @param pageSize
+	 * @return
+	 */
+	@RequestMapping("/loadMySuggest")
+	public RespVO loadMySuggest(@NotBlank(message = "意见反馈人【suggesterId】不能为空") String suggesterId,@RequestParam(value="pageNum",defaultValue="1") Integer pageNum,@RequestParam(value="pageSize",defaultValue="20") Integer pageSize){
+		return RespVOBuilder.ok(suggestService.loadMySuggest(suggesterId, pageNum, pageSize));
+	}
+	
+	/**
+	 * 志愿者上报位置数据
+	 * @param volunteerId
+	 * @param lat
+	 * @param lon
+	 * @return
+	 */
+	@RequestMapping("/submitLocation")
+	public RespVO submitLocation(@NotBlank(message = "上报人【volunteerId】不能为空")  String volunteerId,String lat ,String lon){
+		volunteerService.updateLocation(volunteerId, lat, lon);
+		return RespVOBuilder.ok();
+	}
+	
+	/**
+	 * 获取志愿者位置数据
+	 * @param volunteerId
+	 * @return
+	 */
+	@RequestMapping("/getLocation")
+	public RespVO getLocation(@NotBlank(message = "上报人【volunteerId】不能为空")  String volunteerId){
+		return RespVOBuilder.ok(volunteerService.getLocation(volunteerId));
+	}
+	
+	
+	/**
+	 * 上传志愿者认证资料
+	 * @param request
+	 * @return
+	 * @throws IOException
+	 */
+	@SuppressWarnings("unchecked")
+	@RequestMapping("/uploadIdentify")
+	public RespVO uploadIdentify(HttpServletRequest request)throws IOException{
+		String month=DateUtil.format(new Date(), "yyyyMM");
+		RespVO rst=UploadUtils.saveFile(request, "identify/"+month);
+		Map<String,Object>  rstMap=(Map<String,Object>)rst.getData();
+		rstMap=MapUtils.trackMap(rstMap, "volunteerId","fileName","filePath","accessPath","fileSort");
+		rstMap.put("useFor", FileUseForEm.VOLUNTEER_IDENTIFY.getName());
+		String fileId=volunteerService.addVolunteerFile(rstMap);
+		return RespVOBuilder.ok(fileId);
+	}
+	
+	
+	@RequestMapping("/loadIdentify")
+	public RespVO loadIdentify(@NotBlank(message = "用户【volunteerId】不能为空")  String volunteerId){
+		return RespVOBuilder.ok(volunteerService.loadIdentifyArchives(volunteerId));
+	}
+	
+	
+	@RequestMapping("/deleteIdentify")
+	public RespVO deleteIdentify(HttpServletRequest request,@NotBlank(message = "文件编号【fileId】不能为空")  String fileId){
+		Map<String,Object> fileInfo=volunteerService.deleteVolunteerFile(fileId);
+		if(fileInfo!=null&&StringUtils.isNoneBlank((String)fileInfo.get("filePath"))){
+			UploadUtils.removeFile(request, fileInfo.get("filePath")+File.separator+fileInfo.get("fileName"));
+		}
+		return RespVOBuilder.ok();
+	}
+	
+	
+	@RequestMapping("/loadAeds")
+	public RespVO loadAeds(String northEast,String southWest){
+		RequestParams requestParams=new RequestParams();
+		requestParams.put("northEast", northEast);
+		requestParams.put("southWest", southWest);
+		PageModel<Map<String,Object>> aeds=aedService.query(requestParams,1, 10000);
+		return RespVOBuilder.ok(aeds!=null?aeds.getData():null);
+	}
+}

+ 203 - 0
src/main/java/com/jpsoft/proj/api/controller/VolunteerRescueController.java

@@ -0,0 +1,203 @@
+package com.jpsoft.proj.api.controller;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.proj.model.RescueAcceptPO;
+import com.jpsoft.proj.model.RescueAcceptVO;
+import com.jpsoft.proj.model.RescueApplyVO;
+import com.jpsoft.proj.model.RescueFlow;
+import com.jpsoft.proj.model.RescueFlowPO;
+import com.jpsoft.proj.rescue.service.RescueFlowService;
+import com.jpsoft.proj.rescue.service.RescueService;
+import com.jpsoft.proj.rescue.service.RescueVolunteerService;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RequestParams;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+import com.jpsoft.proj.volunteer.service.VolunteerService;
+
+@RestController
+@RequestMapping("/**/api/volunteer/rescue")
+@Validated
+public class VolunteerRescueController {
+
+	@Autowired
+	private RescueVolunteerService  rescueVolunteerService;
+	
+	@Autowired
+	private RescueService  rescueService;
+	
+	@Autowired
+	private RescueFlowService  flowService;
+	
+	@Autowired
+	private VolunteerService  volunteerService;
+	
+	/**
+	 * 求助处理(参与或忽略或结束)
+	 * @param accept
+	 * @return
+	 */
+	@RequestMapping("/respondRescueApply")
+	public RespVO respondRescueApply(
+			@NotBlank(message = "志愿者编号【volunteerId】不能为空") String volunteerId,
+			@NotBlank(message = "求助记录编号【rescueId】不能为空") String rescueId,
+			@NotNull(message = "求助处理结果【respondCode】不能为空") String respondCode) {
+		
+		RescueAcceptPO accept=new RescueAcceptPO();
+		accept.setVolunteerId(volunteerId);
+		accept.setRescueId(rescueId);
+		
+		RescueAcceptPO  dbbaccept=rescueVolunteerService.getRescueAcceptInfo(volunteerId,rescueId);
+		String caller = volunteerService.getRealName(accept.getVolunteerId());
+		
+		if(respondCode.equalsIgnoreCase("join")){ // 参与
+			if(rescueVolunteerService.hadEnoughVolunteer(rescueId)){
+				return RespVOBuilder.error("已经有足够的志愿者参与,期待您下次参与");
+			}
+			if (rescueVolunteerService.isInRescuing(volunteerId)) {
+				return RespVOBuilder.error("有未结束的救援,请先结束");
+			}
+			if(dbbaccept!=null){
+				return RespVOBuilder.error("该求援你已参与过,勿重复");
+			}
+			accept.setAcceptIf(1);
+			rescueVolunteerService.addRescueApplyAccept(accept);
+			flowService.addFlow(RescueFlow.RESCUING.getCode(), "参与救援",
+					accept.getRescueId(), caller);
+		}
+		else if(respondCode.equalsIgnoreCase("ignore")){  //忽略
+			if(dbbaccept!=null){
+				return RespVOBuilder.error("该求助你已响应过,勿重复");
+			}
+			accept.setAcceptIf(0);
+			rescueVolunteerService.addRescueApplyAccept(accept);
+		}
+		else if(respondCode.equalsIgnoreCase("finish")){  //结束
+			if(dbbaccept==null){
+				return RespVOBuilder.error("还未参与救援");
+			}
+			RescueApplyVO rescue=rescueService.getRescue(rescueId);
+			if(rescue.getPublishTime()!=null){
+				long now=(new Date()).getTime();
+				if(now-rescue.getPublishTime().getTime()<600000){  //离发布时间小于10分钟
+					return RespVOBuilder.error("离事发时间太短");
+				}
+			}
+			
+			rescueVolunteerService.updateForCloseSelfRescue(volunteerId, rescueId, caller);
+			
+			/*dbbaccept.setFinishTime(new Date());
+			dbbaccept.setNote("结束自己的救援");
+			rescueVolunteerService.updateRescueApplyAccept(dbbaccept);
+			flowService.addFlow(RescueFlow.RESCUING.getCode(), "结束自己的救援",
+					dbbaccept.getRescueId(), caller);*/
+		}
+		
+		
+		return RespVOBuilder.ok();
+	}
+	
+	/**
+	 * 加载正在发布的求助信息(匹配当前志愿者能力的信息,按距离排序)
+	 * @param volunteerId
+	 * @param latitude
+	 * @param longitude
+	 * @param pageNum  页码
+	 * @param pageSize  每页记录数
+	 * @return
+	 */
+	@RequestMapping("/loadActiveRescue")
+	public RespVO loadActiveRescue(
+			@NotBlank(message = "志愿者编号【volunteerId】不能为空") String volunteerId,
+			String latitude,
+			String longitude,
+			String sortField,
+			String sortAction,
+			@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
+			@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
+		return RespVOBuilder.ok(rescueVolunteerService.loadCanJoinRescueApply(
+				volunteerId, longitude, latitude, pageNum, pageSize, sortField,
+				sortAction));
+	}
+	
+	
+	/**
+	 * 求助明细(志愿者端,用于参与救援,或回看已参与救援明细)
+	 * @param volunteerId
+	 * @param rescueId
+	 * @param latitude 志愿者当前定位(回看历史数据时不需要提供)
+	 * @param longitude
+	 * @return
+	 */
+	@RequestMapping("/getRescueDtl")
+	public RespVO getRescueDtl(@NotBlank(message = "志愿者编号【volunteerId】不能为空") String volunteerId,@NotBlank(message = "救援记录编号【rescueId】不能为空") String rescueId,String latitude,String longitude){
+		RescueApplyVO rescue=rescueVolunteerService.getRescueApplyForJoin(rescueId,longitude, latitude);
+		if(rescue==null){
+			return RespVOBuilder.error("数据不存在,刷新后再试");
+		}
+		List<RescueFlowPO> flows=flowService.loadRescueFlow(rescueId);
+		List<Map<String,Object>> files=rescueService.loadRescueFiles(rescueId);
+		List<Map<String,Object>> joiners=rescueService.loadRescueJoiners(rescueId);
+		RescueAcceptPO  accept=rescueVolunteerService.getRescueAcceptInfo(volunteerId,rescueId);
+		Map<String,Object> acceptInfo=null;
+		if(accept==null){ //未参与
+			acceptInfo=MapUtils.builder("acceptIf",false);
+		}
+		else{
+			acceptInfo=MapUtils.builder("acceptIf",accept.getAcceptIf().intValue()==1,"finishIf",accept.getFinishTime()!=null);
+		}
+		return RespVOBuilder.ok(MapUtils.builder("rescue",rescue,"accept",acceptInfo,"flows",flows,"files",files,"joiners",joiners));
+	}
+	
+	
+	/**
+	 * 加载我参与的救援记录
+	 * @param volunteerId
+	 * @param joinStatus
+	 * @param pageNum
+	 * @param pageSize
+	 * @return
+	 */
+	@RequestMapping("/loadMyJoinRescues")
+	public RespVO loadMyJoinRescues(
+			@NotBlank(message = "志愿者编号【volunteerId】不能为空") String volunteerId,
+			@RequestParam(value = "joinStatus", defaultValue = "") String joinStatus,
+			@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
+			@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
+		if(joinStatus.equalsIgnoreCase("rescuing")){
+			joinStatus=RescueFlow.RESCUING.getCode();
+		}
+		else if(joinStatus.equalsIgnoreCase("complete")||joinStatus.equalsIgnoreCase("finish")){
+			joinStatus=RescueFlow.COMPLETE.getCode();
+		}
+		else{
+			joinStatus="";
+		}
+		RequestParams params = new RequestParams();
+		params.put("joinStatus", joinStatus);
+		params.put("volunteerId", volunteerId);
+		
+		PageModel<RescueAcceptVO> pagedData = rescueVolunteerService.queryMyJoinedRescues(params, pageNum, pageSize);
+		
+		return RespVOBuilder.ok(pagedData);
+	}
+	
+	
+	@RequestMapping("/checkSoundNotice")
+	public RespVO  checkSoundNotice(@NotBlank(message = "志愿者编号【volunteerId】不能为空") String volunteerId){
+		return RespVOBuilder.ok(rescueVolunteerService.updateForNeedSound(volunteerId));
+	}
+}

+ 91 - 0
src/main/java/com/jpsoft/proj/auth/controller/AuthController.java

@@ -0,0 +1,91 @@
+package com.jpsoft.proj.auth.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.NotBlank;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.framework.util.SessionThreadLocal;
+import com.jpsoft.framework.util.SessionUser;
+import com.jpsoft.proj.auth.service.AuthService;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+
+@RestController
+@RequestMapping("/**/auth")
+@Validated
+public class AuthController {
+
+	@Autowired
+	private AuthService service;
+	
+	/**
+	 * 主页菜单(只基于角色权限加载)
+	 * @param response
+	 */
+	@RequestMapping("/loadMyMenus")
+	public RespVO loadMyMenus(){
+		SessionUser su=SessionThreadLocal.getSessionUser();
+		if(su==null){
+			return RespVOBuilder.error("缺少登录信息");
+		}
+		List<Map<String,Object>> menus=null;
+		if(su.isSysAdmin()){
+			menus=service.loadAllMenus();
+		}
+		else{
+			menus=service.loadMyRoleMenus(su.getUserId());
+		}
+		return RespVOBuilder.ok(menus);
+		
+	}
+	
+	
+	/**
+	 * 可分配的权限节点(菜单,功能点暂时不需要)
+	 * @param response
+	 */
+	@RequestMapping("/loadAuthNodes")
+	public RespVO loadAuthNodes(){
+		List<Map<String,Object>> nodes=service.loadAllMenuNodes();
+		if(nodes==null||nodes.size()==0){
+			return RespVOBuilder.error("未找到数据");
+			
+		}
+		return RespVOBuilder.ok(nodes);
+	}
+	
+	@RequestMapping("/loadUsAuth")
+	public RespVO loadUsAuth(@NotBlank(message = "缺少用户信息") String userId){
+		
+		return RespVOBuilder.ok(service.loadUserAuth(userId));
+	}
+	
+	
+	@RequestMapping("/save")
+	public RespVO save(@NotBlank(message = "缺少用户信息") String userId,@NotBlank(message = "缺少已分配数据") String authIds){
+		
+		service.saveUserAuth(userId, authIds);
+		return RespVOBuilder.ok();
+	}
+	
+	@RequestMapping("/loadRoleAuth")
+	public RespVO loadRoleAuth(@NotBlank(message = "缺少角色信息") String roleId,HttpServletResponse response){
+		return RespVOBuilder.ok(service.loadRoleAuth(roleId));
+	}
+	
+	
+	@RequestMapping("/saveRoleAuth")
+	public RespVO saveRoleAuth(@NotBlank(message = "缺少角色信息") String roleId,@NotBlank(message = "缺少已分配数据") String authIds){
+		
+		service.saveRoleAuth(roleId, authIds);
+		return RespVOBuilder.ok();
+	}
+}

+ 121 - 0
src/main/java/com/jpsoft/proj/auth/controller/LoginController.java

@@ -0,0 +1,121 @@
+package com.jpsoft.proj.auth.controller;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.validation.constraints.NotBlank;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.framework.util.SessionUser;
+import com.jpsoft.framework.util.SysAdminHandler;
+import com.jpsoft.proj.sysdata.service.UserService;
+import com.jpsoft.proj.utils.EncryptUtil;
+import com.jpsoft.proj.utils.MVCConstants;
+import com.jpsoft.proj.utils.MySessionUser;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+
+@RestController
+@RequestMapping("/**/auth")
+@Validated
+public class LoginController {
+
+	public static Logger logger=LoggerFactory.getLogger(LoginController.class);
+	
+	@Autowired
+	private UserService userService;
+	
+	
+
+	
+	/**
+	 * 登录验证
+	 * @param loginId  实际输入的是工号或手机号,系统管理员为登录账号
+	 * @param pwd
+	 * @param remenberMe
+	 * @return
+	 */
+	@PostMapping("/login")
+	public RespVO login(@NotBlank(message = "账号不能为空") String loginId,@NotBlank(message = "密码不能为空") String pwd,HttpServletRequest request){
+		
+		String pwdStr="";
+		try {
+			pwdStr =EncryptUtil.encrypt(pwd);
+		} catch (Exception e) {	
+			e.printStackTrace();
+		}
+		MySessionUser su=null;
+		if(SysAdminHandler.isSysAdminIgnoreCase(loginId)){  //系统管理员登陆验证
+			
+			if(SysAdminHandler.validateIgnoreCase(pwdStr)){				
+				su=new MySessionUser();
+				su.setLoginId("admin");
+				su.setUserName("系统管理员");
+				su.setUserId("admin");
+				su.setSysAdmin(true);
+				afterSuccessLogin(request,su);
+				return RespVOBuilder.ok(su);
+			}
+			else{
+				return RespVOBuilder.error("账号或密码不正确");
+			}		
+		}
+		else{
+			Map<String,Object> us=userService.getUserForLogin(loginId);
+			if(us==null||us.size()==0){
+				return RespVOBuilder.error("用户不存在");
+			}
+			if(!pwdStr.equals((String)us.get("pwd"))){
+				return RespVOBuilder.error("密码错误");
+			}
+			else{
+				
+				su=new MySessionUser();
+				su.setLoginId((String)us.get("loginId"));
+				su.setUserName((String)us.get("realName"));
+				su.setUserId((String)us.get("userId"));
+				su.setUserType((String)us.get("userType"));
+				//su.setOrgId((String)us.get("belongOrg"));
+				//su.setOrgName((String)us.get("orgName"));
+				//su.setOrgAssistCode((String)us.get("assistCode"));
+				afterSuccessLogin(request,su);
+				return RespVOBuilder.ok(su);
+				
+			}
+		}
+		
+	}
+	
+	private void afterSuccessLogin(HttpServletRequest request,SessionUser su){
+		request.getSession(true).setAttribute(MVCConstants.CUR_USER_SESSION, su);
+		//addCookies(response,"usId",remenberMe?su.getLoginId():null,!remenberMe);
+	}
+	
+	/*private void addCookies(HttpServletResponse response,String key ,String value,boolean delIf){
+		 Cookie ck=new Cookie(key,value);
+		 ck.setMaxAge(delIf?-1:(60*60*24*30));
+		 ck.setPath("/");
+         response.addCookie(ck);
+	}*/
+	
+	
+	@RequestMapping("/logout")
+	public RespVO loginout(HttpServletRequest request){
+		HttpSession session=request.getSession();
+		session.removeAttribute(MVCConstants.CUR_USER_SESSION);
+		session.invalidate();
+		return RespVOBuilder.ok();
+	}
+	
+	
+}

+ 101 - 0
src/main/java/com/jpsoft/proj/auth/controller/MenuController.java

@@ -0,0 +1,101 @@
+package com.jpsoft.proj.auth.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.NotBlank;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.framework.util.SessionThreadLocal;
+import com.jpsoft.framework.util.SessionUser;
+import com.jpsoft.proj.auth.service.MenuService;
+import com.jpsoft.proj.model.MenuPO;
+import com.jpsoft.proj.utils.JsonOutUtils;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+@RestController
+@RequestMapping("/**/menu")
+@Validated
+public class MenuController {
+
+	@Autowired
+	private MenuService service;
+	
+	@GetMapping("/get")
+	public RespVO get(@NotBlank(message = "菜单编号不能为空") String menuId){
+		
+		return RespVOBuilder.ok(service.getMenu(menuId));
+	}
+	
+	@PostMapping("/add")
+	public RespVO add(@Validated MenuPO  menu){
+		
+		if(StringUtils.isEmpty(menu.getFatherMenuId())){
+			menu.setFatherMenuId("0");
+		}
+		String newAssistCode = service.generateLeveledCode(menu.getFatherMenuId());
+		if(newAssistCode==null){
+			return RespVOBuilder.error("指定的父菜单不存在");
+		}
+		menu.setAssistCode(newAssistCode);
+		SessionUser su=SessionThreadLocal.getSessionUser();
+		menu.setModifier(su!=null?su.getUserName():"unknow");
+		return RespVOBuilder.ok(service.addMenu(menu));
+	}
+	
+	
+	@PostMapping("/update")
+	public RespVO update(@Validated MenuPO  menu,String oldFatherMenuId){
+		if(StringUtils.isEmpty(menu.getFatherMenuId())){
+			menu.setFatherMenuId("0");
+		}
+		SessionUser su=SessionThreadLocal.getSessionUser();
+		menu.setModifier(su!=null?su.getUserName():"unknow");
+		String fatherMenuId=menu.getFatherMenuId();
+		if (!oldFatherMenuId.equals(fatherMenuId)) { // 隶属关系变动后,修正assistCode
+			String newAssistCode =service.generateLeveledCode(fatherMenuId);
+			if(newAssistCode==null){
+				return RespVOBuilder.error("指定的父菜单不存在");
+			}
+			service.updateMenu(menu,newAssistCode);
+		}
+		else{
+			service.updateMenu(menu,null);
+		}
+		return RespVOBuilder.ok();
+	}
+	
+	
+	
+	@GetMapping("/delete")
+	public RespVO delete(@NotBlank(message = "菜单编号不能为空") String menuId){
+		service.deleteMenu(menuId);
+		return RespVOBuilder.ok();
+	}
+	
+	/**
+	 * 获得菜单子节点(ztree格式)
+	 * @param superId
+	 * @param targetDepth
+	 * @param response
+	 */
+	@PostMapping("/treeNode")
+	public List<Map<String,Object>> treeNode(String superId,HttpServletResponse response){
+		List<Map<String,Object>> nodes=service.queryTreeNodeBySuperId(StringUtils.isEmpty(superId)?"0":superId);
+		if(nodes==null){
+			return null;
+		}
+		JsonOutUtils.preprocessTreeNodes(nodes, null);
+		return nodes;
+	}
+	
+}

+ 63 - 0
src/main/java/com/jpsoft/proj/auth/controller/RequestValidateExceptionHandler.java

@@ -0,0 +1,63 @@
+package com.jpsoft.proj.auth.controller;
+
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.validation.BindException;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+@RestControllerAdvice
+public class RequestValidateExceptionHandler {
+	
+	private static final Logger logger=LoggerFactory.getLogger(RequestValidateExceptionHandler.class);
+
+	//处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException
+	@ExceptionHandler(ConstraintViolationException.class)
+	public RespVO constraintViolationExceptionHandler(ConstraintViolationException e) {
+		//e.printStackTrace();
+		Set<ConstraintViolation<?>> constraintViolations=e.getConstraintViolations();
+		StringBuilder error=new StringBuilder();
+		for(ConstraintViolation<?> itm : constraintViolations) {
+			//System.out.println(itm.getInvalidValue()+":"+itm.getPropertyPath()+":"+itm.getMessage());
+			error.append(itm.getMessage()+";");
+		}
+		//jdk8  写法:String message = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining());
+		return RespVOBuilder.error(error.toString());//
+	}
+	
+	@ExceptionHandler(BindException.class)
+	public RespVO validationExceptionHandler(BindException e) {
+		//e.printStackTrace();
+		BindingResult bindingResult = e.getBindingResult();
+		StringBuilder error=new StringBuilder();
+		for (FieldError fieldError : bindingResult.getFieldErrors()) {
+			error.append(fieldError.getDefaultMessage()+";");
+		}
+		return RespVOBuilder.error(error.toString());
+	}
+	
+	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+	public RespVO methodUnsupportExceptionHandler(HttpRequestMethodNotSupportedException e) {
+		logger.error("不支持该请求方式",e);
+		return RespVOBuilder.error("不支持该请求方式");
+		
+	}
+	
+	@ExceptionHandler(Exception.class)
+	public RespVO otherExceptionHandler(Exception e) {
+		logger.error("服务出错",e);
+		return RespVOBuilder.error("服务出错");
+		
+	}
+}

+ 115 - 0
src/main/java/com/jpsoft/proj/auth/service/AuthService.java

@@ -0,0 +1,115 @@
+package com.jpsoft.proj.auth.service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.framework.util.SessionThreadLocal;
+import com.jpsoft.framework.util.SessionUser;
+
+@Service
+public class AuthService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	public List<Map<String,Object>> loadMyMenus(String usId){
+		StringBuilder sql=new StringBuilder();
+		sql.append("select * from t_menu m");
+		sql.append(" where m.menu_id in ( ");
+		sql.append(" select menu_id from t_user_menu um where um.user_id=? ");
+		
+		//sql.append(" select menu_id from t_user_function uf inner join t_function f on uf.function_id=f.function_id where uf.user_id=? ");
+		sql.append(" )  order by m.display_num");
+		return dao.queryForListMap(sql.toString(), usId);
+	}
+	
+	public List<Map<String,Object>> loadMyRoleMenus(String usId){
+		StringBuilder sql=new StringBuilder();
+		
+		sql.append("select distinct tm.* from t_menu tm  inner join  ( ");
+		sql.append("select rm.menu_id,m.menu_name,m.assist_code from t_user_role ur  ");
+		sql.append("inner join  t_role_menu rm on ur.role_id=rm.role_id ");
+		sql.append("inner join t_menu m  on rm.menu_id=m.menu_id ");
+		sql.append("where ur.user_id=? ");
+		sql.append(") tf on tf.assist_code like concat(tm.assist_code,'%') ");
+		sql.append("order by display_num ");
+				
+		return dao.queryForListMap(sql.toString(), usId);
+	}
+	
+	public List<Map<String,Object>> loadAllMenus(){
+		StringBuilder sql=new StringBuilder();
+		sql.append("select * from t_menu m order by m.display_num");
+		return dao.queryForListMap(sql.toString());
+	}
+	
+	public List<Map<String,Object>>  loadAllMenuNodes(){
+		StringBuilder sql=new StringBuilder();
+		sql.append("select menu_id id,menu_name name,father_menu_id p_id,'menu' node_type,length(menu_link) link_tag from t_menu m order by display_num");
+		return dao.queryForListMap(sql.toString());
+	}
+	
+	public List<Map<String,Object>> loadAllFunNodes(){
+		StringBuilder sql=new StringBuilder();
+		sql.append("select function_id id,function_name name,menu_id p_id,'fun' node_type from t_function f");
+		return dao.queryForListMap(sql.toString());
+	}
+	
+	public List<Map<String,Object>>  loadUserAuth(String userId){
+		String sql="select menu_id id from t_user_menu where user_id=?";
+		return dao.queryForListMap(sql,userId);
+	}
+	
+	public void saveUserAuth(String userId,String authIds){
+		String sql="delete from t_user_menu where user_id=?";
+		dao.getJdbcTemplate().update(sql, userId);
+		String[] ids=authIds.split(",");
+		List<Map<String,Object>> datas=new ArrayList<Map<String,Object>>(ids.length);
+		UUIDHexGenerator uuid=UUIDHexGenerator.getInstance();
+		Map<String,Object> d=null;
+		SessionUser su=SessionThreadLocal.getSessionUser();
+		for(String id : ids){
+			d=new HashMap<String,Object>();
+			d.put("recordId", uuid.generate());
+			d.put("userId", userId);
+			d.put("menuId", id);
+			d.put("createTime", new Date());
+			d.put("creator", su!=null?su.getUserName():null);
+			datas.add(d);
+		}
+		dao.batchInsert(datas, "t_user_menu");
+	}
+	
+	public List<Map<String,Object>>  loadRoleAuth(String roleId){
+		String sql="select menu_id id from t_role_menu where role_id=?";
+		return dao.queryForListMap(sql,roleId);
+	}
+	
+	public void saveRoleAuth(String roleId,String authIds){
+		String sql="delete from t_role_menu where role_id=?";
+		dao.getJdbcTemplate().update(sql, roleId);
+		String[] ids=authIds.split(",");
+		List<Map<String,Object>> datas=new ArrayList<Map<String,Object>>(ids.length);
+		UUIDHexGenerator uuid=UUIDHexGenerator.getInstance();
+		Map<String,Object> d=null;
+		SessionUser su=SessionThreadLocal.getSessionUser();
+		for(String id : ids){
+			d=new HashMap<String,Object>();
+			d.put("recordId", uuid.generate());
+			d.put("roleId", roleId);
+			d.put("menuId", id);
+			d.put("modifyTime", new Date());
+			d.put("modifier", su!=null?su.getUserName():null);
+			datas.add(d);
+		}
+		dao.batchInsert(datas, "t_role_menu");
+	}
+}

+ 143 - 0
src/main/java/com/jpsoft/proj/auth/service/MenuService.java

@@ -0,0 +1,143 @@
+package com.jpsoft.proj.auth.service;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.framework.util.PinyinFromHanzi;
+import com.jpsoft.proj.model.MenuPO;
+
+@Service
+public class MenuService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	
+	public List<Map<String,Object>> queryTreeNodeBySuperId(String fatherMenuId) {
+		StringBuilder sql=new StringBuilder();
+		sql.append("select m.menu_id id,m.menu_name name,m.father_menu_id p_id,menu_link, ");
+		sql.append("(select count(1) from t_menu cm where cm.father_menu_id=m.menu_id) child_count");
+		sql.append(" from t_menu  m where m.father_menu_id=?");
+		sql.append(" order by m.display_num");
+		return dao.queryForListMap(sql.toString(),fatherMenuId);
+	}
+	
+	public Map<String,Object> getMenu(String menuId){
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select menu.*,");
+		sql.append("(select sm.menu_name from t_menu sm where sm.menu_id=menu.father_menu_id) father_menu_name");
+		sql.append(" from t_menu menu where menu.menu_id=?");
+		return dao.queryForSingleMap(sql.toString(), menuId);
+	}
+	
+	public String  addMenu(MenuPO menu){
+		UUIDHexGenerator uuid=new UUIDHexGenerator();
+		menu.setModifyTime(new Date());
+		menu.setMenuId(uuid.generate());
+		if(StringUtils.isEmpty(menu.getFatherMenuId())){
+			menu.setFatherMenuId("0");
+		}
+		if(menu.getDisplayNum()==null){
+			Number num=getMenuMaxDispNum(menu.getFatherMenuId());
+			menu.setDisplayNum(num==null?0:(num.intValue()+1));
+		}
+		dao.insertPojo(menu, "t_menu");
+		
+		//增加菜单的时候,自动增加浏览功能项
+		Map<String,Object>  fun=new HashMap<String,Object>();
+		fun.put("functionId", uuid.generate());
+		fun.put("createTime", new Date());
+		fun.put("menuId",menu.getMenuId());
+		fun.put("creator", menu.getModifier());
+		fun.put("functionCode", PinyinFromHanzi.getPinyin(menu.getMenuName()).toUpperCase()+"_BROWSE");
+		fun.put("functionName", "浏览");
+		
+		dao.insert(fun, "t_function");
+		
+		return menu.getMenuId();
+	}
+	
+	public boolean deleteMenu(String menuId){
+		String sql="delete from t_menu where menu_id=?";
+		dao.getJdbcTemplate().update(sql, menuId);
+		
+		//级联删除分配给所有用户的该的功能
+		sql="delete from t_user_function where function_id in (select function_id from t_function where menu_id=?)";
+		dao.getJdbcTemplate().update(sql,menuId);
+				
+		sql="delete from t_function where menu_id=?";
+		dao.getJdbcTemplate().update(sql,menuId);
+		return true;
+	}
+	
+	
+	
+	public boolean updateMenu(MenuPO menu,String newAssistCode){
+		if(StringUtils.isNotEmpty(newAssistCode)){
+			updateAssistCode(menu.getAssistCode(),newAssistCode);
+			menu.setAssistCode(newAssistCode);
+		}
+		menu.setModifyTime(new Date());
+		dao.updatePojo(menu, "t_menu", "menu_id");
+		return true;
+	}
+	
+	/**
+	 * 获得指定级别下的最大assistCode
+	 * @param superId
+	 * @return
+	 */
+	public String generateLeveledCode(String superId) {
+		String superAssistCode=null;
+		if("0".equals(superId)){  //顶级父节点
+			superAssistCode="C";
+		}
+		else{
+			Map<String,Object> superMenu=getMenu(superId);
+			if(superMenu==null){
+				return null;
+			}
+			superAssistCode=(String)superMenu.get("assistCode");
+			if(StringUtils.isEmpty(superAssistCode)){
+				superAssistCode="C";
+			}
+		}
+		String subMaxAssistCode =getMaxSubAssistCode(superId);
+		if (subMaxAssistCode == null || "null".equalsIgnoreCase(subMaxAssistCode)) { // 没有子编码时
+			return superAssistCode + "000";
+		}
+		String subSerial = subMaxAssistCode.replaceFirst(superAssistCode, ""); // 子编码流水号
+
+		int nextNum = Integer.parseInt(subSerial) + 1;
+		subSerial = String.format("%1$03d", nextNum);
+		return superAssistCode + subSerial;
+	}
+	
+	private String getMaxSubAssistCode(String superId) {
+		String sql = "select max(assist_code) assist_code from t_menu where father_menu_id=?";
+		Map<String,Object> rst=dao.queryForSingleMap(sql, superId);
+		return rst!=null?(String)rst.get("assistCode"):null;
+	}
+	
+	private boolean updateAssistCode(String assistCode, String newAssistCode){
+		StringBuilder sql = new StringBuilder();
+		sql.append("update t_menu set assist_code=replace(assist_code,?,?)");
+		sql.append(" where assist_code like ?");
+		dao.getJdbcTemplate().update(sql.toString(), assistCode, newAssistCode, assistCode + "%");
+		return true;
+	}
+	
+	private Number getMenuMaxDispNum(String fatherMenuId){
+		String sql="select max(m.display_num) num from t_menu m where m.father_menu_id=?";
+		Map<String,Object> rst=dao.queryForSingleMap(sql, fatherMenuId);
+		return rst!=null?((Number)rst.get("num")):null;
+	}
+}

+ 59 - 0
src/main/java/com/jpsoft/proj/model/Code2Session.java

@@ -0,0 +1,59 @@
+package com.jpsoft.proj.model;
+/**
+ * 通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用Code2Session接口完成登录流程返回的数据
+ * @author hb
+ *
+ */
+public class Code2Session {
+
+	private String openid;
+	
+	private String session_key;
+	
+	private String unionid;
+	
+	private int errcode;
+	
+	private String errmsg;
+
+	public String getOpenid() {
+		return openid;
+	}
+
+	public void setOpenid(String openid) {
+		this.openid = openid;
+	}
+
+	public String getSession_key() {
+		return session_key;
+	}
+
+	public void setSession_key(String session_key) {
+		this.session_key = session_key;
+	}
+
+	public String getUnionid() {
+		return unionid;
+	}
+
+	public void setUnionid(String unionid) {
+		this.unionid = unionid;
+	}
+
+	public int getErrcode() {
+		return errcode;
+	}
+
+	public void setErrcode(int errcode) {
+		this.errcode = errcode;
+	}
+
+	public String getErrmsg() {
+		return errmsg;
+	}
+
+	public void setErrmsg(String errmsg) {
+		this.errmsg = errmsg;
+	}
+
+}

+ 20 - 0
src/main/java/com/jpsoft/proj/model/FileUseForEm.java

@@ -0,0 +1,20 @@
+package com.jpsoft.proj.model;
+
+public enum FileUseForEm {
+
+	HEAD_PHOTO("个人头像"),VOLUNTEER_IDENTIFY("志愿者认证");
+	
+	private String name;
+	
+	private FileUseForEm(String name){
+		this.name=name;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+}

+ 70 - 0
src/main/java/com/jpsoft/proj/model/MatchedVolunteer.java

@@ -0,0 +1,70 @@
+package com.jpsoft.proj.model;
+
+public class MatchedVolunteer {
+	
+	private String volunteerId;
+
+	private String phone;
+	
+	private String emergencyAddress;
+	
+	private String emergencysortName;
+	
+	private String rescueSort;
+	
+	private Integer personAmount;
+
+	public String getPhone() {
+		return phone;
+	}
+
+	public void setPhone(String phone) {
+		this.phone = phone;
+	}
+
+	public String getEmergencyAddress() {
+		return emergencyAddress;
+	}
+
+	public void setEmergencyAddress(String emergencyAddress) {
+		this.emergencyAddress = emergencyAddress;
+	}
+
+	public String getEmergencysortName() {
+		return emergencysortName;
+	}
+
+	public void setEmergencysortName(String emergencysortName) {
+		this.emergencysortName = emergencysortName;
+	}
+
+	public String getRescueSort() {
+		return rescueSort;
+	}
+
+	public void setRescueSort(String rescueSort) {
+		this.rescueSort = rescueSort;
+	}
+
+	public Integer getPersonAmount() {
+		return personAmount;
+	}
+
+	public void setPersonAmount(Integer personAmount) {
+		this.personAmount = personAmount;
+	}
+
+	public String getVolunteerId() {
+		return volunteerId;
+	}
+
+	public void setVolunteerId(String volunteerId) {
+		this.volunteerId = volunteerId;
+	}
+	
+	
+	
+	
+	
+	
+}

+ 140 - 0
src/main/java/com/jpsoft/proj/model/MemberPO.java

@@ -0,0 +1,140 @@
+package com.jpsoft.proj.model;
+
+import javax.validation.constraints.NotBlank;
+
+public class MemberPO {
+
+	private String memberId;
+	
+	@NotBlank(message = "姓名不能为空")
+	private String realName;
+	
+	private String namePy;
+	
+	@NotBlank(message = "手机号不能为空")
+	private String phone;
+	
+	@NotBlank(message = "openid不能为空")
+	private String openid;
+	
+	private String address;
+	
+	private String lat;
+	
+	private String lon;
+	
+	private String healthStatus;
+	
+	private String firstContact;
+	
+	private String firstContactPhone;
+	
+	private String beguardedPerson;
+	
+	private String beguardedHealth;
+
+	public String getMemberId() {
+		return memberId;
+	}
+
+	public void setMemberId(String memberId) {
+		this.memberId = memberId;
+	}
+
+	public String getRealName() {
+		return realName;
+	}
+
+	public void setRealName(String realName) {
+		this.realName = realName;
+	}
+
+	public String getNamePy() {
+		return namePy;
+	}
+
+	public void setNamePy(String namePy) {
+		this.namePy = namePy;
+	}
+
+	public String getPhone() {
+		return phone;
+	}
+
+	public void setPhone(String phone) {
+		this.phone = phone;
+	}
+
+	public String getAddress() {
+		return address;
+	}
+
+	public void setAddress(String address) {
+		this.address = address;
+	}
+
+	public String getLat() {
+		return lat;
+	}
+
+	public void setLat(String lat) {
+		this.lat = lat;
+	}
+
+	public String getLon() {
+		return lon;
+	}
+
+	public void setLon(String lon) {
+		this.lon = lon;
+	}
+
+	public String getHealthStatus() {
+		return healthStatus;
+	}
+
+	public void setHealthStatus(String healthStatus) {
+		this.healthStatus = healthStatus;
+	}
+
+	public String getFirstContact() {
+		return firstContact;
+	}
+
+	public void setFirstContact(String firstContact) {
+		this.firstContact = firstContact;
+	}
+
+	public String getFirstContactPhone() {
+		return firstContactPhone;
+	}
+
+	public void setFirstContactPhone(String firstContactPhone) {
+		this.firstContactPhone = firstContactPhone;
+	}
+
+	public String getBeguardedPerson() {
+		return beguardedPerson;
+	}
+
+	public void setBeguardedPerson(String beguardedPerson) {
+		this.beguardedPerson = beguardedPerson;
+	}
+
+	public String getBeguardedHealth() {
+		return beguardedHealth;
+	}
+
+	public void setBeguardedHealth(String beguardedHealth) {
+		this.beguardedHealth = beguardedHealth;
+	}
+
+	public String getOpenid() {
+		return openid;
+	}
+
+	public void setOpenid(String openid) {
+		this.openid = openid;
+	}
+	
+}

+ 14 - 0
src/main/java/com/jpsoft/proj/model/MemberVO.java

@@ -0,0 +1,14 @@
+package com.jpsoft.proj.model;
+
+public class MemberVO extends MemberPO {
+
+	private String openid;
+
+	public String getOpenid() {
+		return openid;
+	}
+
+	public void setOpenid(String openid) {
+		this.openid = openid;
+	}
+}

+ 109 - 0
src/main/java/com/jpsoft/proj/model/MenuPO.java

@@ -0,0 +1,109 @@
+package com.jpsoft.proj.model;
+
+import java.util.Date;
+
+import javax.validation.constraints.NotNull;
+
+public class MenuPO {
+
+	private String menuId;
+	
+	private String fatherMenuId;
+	
+	@NotNull(message="{menu.menuname.is.notnull}")
+	private String menuName;
+	
+	private String menuIcon;
+	
+	private String menuLink;
+	
+	private String modifier;
+	
+	private Date modifyTime;
+	
+	private Integer displayNum;
+	
+	private String  targetDepth;
+	
+	private String assistCode;
+
+	public String getMenuId() {
+		return menuId;
+	}
+
+	public void setMenuId(String menuId) {
+		this.menuId = menuId;
+	}
+
+	public String getFatherMenuId() {
+		return fatherMenuId;
+	}
+
+	public void setFatherMenuId(String fatherMenuId) {
+		this.fatherMenuId = fatherMenuId;
+	}
+
+	public String getMenuName() {
+		return menuName;
+	}
+
+	public void setMenuName(String menuName) {
+		this.menuName = menuName;
+	}
+
+	public String getMenuIcon() {
+		return menuIcon;
+	}
+
+	public void setMenuIcon(String menuIcon) {
+		this.menuIcon = menuIcon;
+	}
+
+	public String getMenuLink() {
+		return menuLink;
+	}
+
+	public void setMenuLink(String menuLink) {
+		this.menuLink = menuLink;
+	}
+
+	public Integer getDisplayNum() {
+		return displayNum;
+	}
+
+	public void setDisplayNum(Integer displayNum) {
+		this.displayNum = displayNum;
+	}
+
+	public String getTargetDepth() {
+		return targetDepth;
+	}
+
+	public void setTargetDepth(String targetDepth) {
+		this.targetDepth = targetDepth;
+	}
+
+	public String getAssistCode() {
+		return assistCode;
+	}
+
+	public void setAssistCode(String assistCode) {
+		this.assistCode = assistCode;
+	}
+
+	public String getModifier() {
+		return modifier;
+	}
+
+	public void setModifier(String modifier) {
+		this.modifier = modifier;
+	}
+
+	public Date getModifyTime() {
+		return modifyTime;
+	}
+
+	public void setModifyTime(Date modifyTime) {
+		this.modifyTime = modifyTime;
+	}
+}

+ 82 - 0
src/main/java/com/jpsoft/proj/model/RescueAcceptPO.java

@@ -0,0 +1,82 @@
+package com.jpsoft.proj.model;
+
+import java.util.Date;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+public class RescueAcceptPO {
+
+	private String acceptId;
+	
+	@NotBlank(message = "求助记录编号【rescueId】不能为空")
+	private String rescueId;
+	
+	@NotBlank(message = "志愿者编号【volunteerId】不能为空")
+	private String volunteerId;
+	
+	private Date acceptTime;
+	
+	@NotNull(message = "求助处理结果【acceptIf,1:接受,0:忽略】不能为空")
+	private Integer acceptIf;
+	
+	private Date finishTime;
+	
+	private String note;
+
+	public String getAcceptId() {
+		return acceptId;
+	}
+
+	public void setAcceptId(String acceptId) {
+		this.acceptId = acceptId;
+	}
+
+	public String getRescueId() {
+		return rescueId;
+	}
+
+	public void setRescueId(String rescueId) {
+		this.rescueId = rescueId;
+	}
+
+	public String getVolunteerId() {
+		return volunteerId;
+	}
+
+	public void setVolunteerId(String volunteerId) {
+		this.volunteerId = volunteerId;
+	}
+
+	public Date getAcceptTime() {
+		return acceptTime;
+	}
+
+	public void setAcceptTime(Date acceptTime) {
+		this.acceptTime = acceptTime;
+	}
+
+	public Integer getAcceptIf() {
+		return acceptIf;
+	}
+
+	public void setAcceptIf(Integer acceptIf) {
+		this.acceptIf = acceptIf;
+	}
+
+	public Date getFinishTime() {
+		return finishTime;
+	}
+
+	public void setFinishTime(Date finishTime) {
+		this.finishTime = finishTime;
+	}
+
+	public String getNote() {
+		return note;
+	}
+
+	public void setNote(String note) {
+		this.note = note;
+	}
+}

+ 76 - 0
src/main/java/com/jpsoft/proj/model/RescueAcceptVO.java

@@ -0,0 +1,76 @@
+package com.jpsoft.proj.model;
+
+import java.util.Date;
+
+import com.jpsoft.framework.util.DateUtil;
+
+public class RescueAcceptVO extends RescueApplyVO {
+
+	private String volunteerId;
+	
+	private String volunteerName;
+	
+	private Integer acceptIf;
+	
+	private Date finishTime;
+	
+	private Date acceptTime;
+	
+	private String acceptTimeFmt;
+	
+	private Boolean finishIf;
+
+	public String getVolunteerId() {
+		return volunteerId;
+	}
+
+	public void setVolunteerId(String volunteerId) {
+		this.volunteerId = volunteerId;
+	}
+
+	public Integer getAcceptIf() {
+		return acceptIf;
+	}
+
+	public void setAcceptIf(Integer acceptIf) {
+		this.acceptIf = acceptIf;
+	}
+
+	public Date getFinishTime() {
+		return finishTime;
+	}
+
+	public void setFinishTime(Date finishTime) {
+		this.finishTime = finishTime;
+		this.finishIf=finishTime!=null;
+	}
+
+	public Date getAcceptTime() {
+		return acceptTime;
+	}
+
+	public void setAcceptTime(Date acceptTime) {
+		this.acceptTime = acceptTime;
+		if(acceptTime!=null){
+			this.acceptTimeFmt=DateUtil.format(acceptTime, "yyyy-MM-dd HH:mm:ss");
+		}
+	}
+
+	public Boolean getFinishIf() {
+		return finishIf;
+	}
+
+	public String getVolunteerName() {
+		return volunteerName;
+	}
+
+	public void setVolunteerName(String volunteerName) {
+		this.volunteerName = volunteerName;
+	}
+
+	public String getAcceptTimeFmt() {
+		return acceptTimeFmt;
+	}
+
+	
+}

+ 138 - 0
src/main/java/com/jpsoft/proj/model/RescueApplyPO.java

@@ -0,0 +1,138 @@
+package com.jpsoft.proj.model;
+
+import java.util.Date;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Pattern;
+
+import org.hibernate.validator.constraints.Length;
+
+public class RescueApplyPO {
+
+	private String rescueId;
+	
+	@NotBlank(message = "求救人编号不能为空")
+	private String publisher;
+	
+	private Date publishTime;
+	
+	@NotBlank(message = "求救类型(突发事件)不能为空")
+	private String emergencySort;
+	
+	private String lat;
+	
+	private String lon;
+	
+	@NotBlank(message = "求救地址不能为空")
+	@Length(min=1,max=200)
+	private String address;
+	
+	@Pattern(regexp="^.+[\\u4e00-\\u9fa5].+$",message = "求救简化地址不能为空且应该有中文描述且不超过100字符")
+	@Length(min=1,max=100)
+	private String shortAddress;
+	
+	private String sceneDesc;
+	
+	private String flowId;
+	
+	private Date lastCallTime;
+	
+	private Integer callCount;
+
+	public String getRescueId() {
+		return rescueId;
+	}
+
+	public void setRescueId(String rescueId) {
+		this.rescueId = rescueId;
+	}
+
+	public String getPublisher() {
+		return publisher;
+	}
+
+	public void setPublisher(String publisher) {
+		this.publisher = publisher;
+	}
+
+	public Date getPublishTime() {
+		return publishTime;
+	}
+
+	public void setPublishTime(Date publishTime) {
+		this.publishTime = publishTime;
+	}
+
+	public String getEmergencySort() {
+		return emergencySort;
+	}
+
+	public void setEmergencySort(String emergencySort) {
+		this.emergencySort = emergencySort;
+	}
+
+	public String getLat() {
+		return lat;
+	}
+
+	public void setLat(String lat) {
+		this.lat = lat;
+	}
+
+	public String getLon() {
+		return lon;
+	}
+
+	public void setLon(String lon) {
+		this.lon = lon;
+	}
+
+	public String getAddress() {
+		return address;
+	}
+
+	public void setAddress(String address) {
+		this.address = address;
+	}
+
+	public String getSceneDesc() {
+		return sceneDesc;
+	}
+
+	public void setSceneDesc(String sceneDesc) {
+		this.sceneDesc = sceneDesc;
+	}
+
+	public String getFlowId() {
+		return flowId;
+	}
+
+	public void setFlowId(String flowId) {
+		this.flowId = flowId;
+	}
+
+	public String getShortAddress() {
+		return shortAddress;
+	}
+
+	public void setShortAddress(String shortAddress) {
+		this.shortAddress = shortAddress;
+	}
+
+	public Date getLastCallTime() {
+		return lastCallTime;
+	}
+
+	public void setLastCallTime(Date lastCallTime) {
+		this.lastCallTime = lastCallTime;
+	}
+
+	public Integer getCallCount() {
+		return callCount;
+	}
+
+	public void setCallCount(Integer callCount) {
+		this.callCount = callCount;
+	}
+	
+}

+ 153 - 0
src/main/java/com/jpsoft/proj/model/RescueApplyVO.java

@@ -0,0 +1,153 @@
+package com.jpsoft.proj.model;
+
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.jpsoft.framework.util.DateUtil;
+import com.jpsoft.proj.utils.CodeConstants;
+import com.jpsoft.proj.utils.DataUtils;
+
+public class RescueApplyVO extends RescueApplyPO {
+
+	private String publishTimeFmt;
+	
+	private String distanceFmt;
+	
+	private Double distance;
+	
+	private String emergencyName;
+	
+	private String publisherName;
+	
+	private String publisherPhone;
+	
+	private String step;
+	
+	private String stepName;
+	
+	private Integer  acceptCount;
+	
+	private Integer acceptIf;
+	
+	private Double nearbyDistance;   //特定突发事件向附近用户短信通知时,通知的距离范围(编码表中配置,有则通知否则不通知)
+	
+	private Integer needVolunteerCount; //需要的志愿者数量
+	
+	public void setPublishTime(Date publishTime){
+		super.setPublishTime(publishTime);
+		if(publishTime!=null){
+			this.publishTimeFmt=DateUtil.format(publishTime, "yyyy-MM-dd HH:mm:ss");
+		}
+	}
+	
+	public void setDistance(Double distance){
+		this.distance=distance;
+		if(distance!=null&&!Double.isNaN(distance)){
+			this.distanceFmt=distance>1000?(DataUtils.format(distance/1000, 1)+"公里"):(DataUtils.format(distance, 0)+"米");
+		}
+		
+	}
+	
+
+	public String getPublishTimeFmt() {
+		return publishTimeFmt;
+	}
+
+	public void setPublishTimeFmt(String publishTimeFmt) {
+		this.publishTimeFmt = publishTimeFmt;
+	}
+
+	public String getDistanceFmt() {
+		return distanceFmt;
+	}
+
+	public void setDistanceFmt(String distanceFmt) {
+		this.distanceFmt = distanceFmt;
+	}
+
+	public Double getDistance() {
+		return distance;
+	}
+
+	public String getEmergencyName() {
+		return emergencyName;
+	}
+
+	public void setEmergencyName(String emergencyName) {
+		this.emergencyName = emergencyName;
+	}
+
+	public String getPublisherName() {
+		return publisherName;
+	}
+
+	public void setPublisherName(String publisherName) {
+		this.publisherName = publisherName;
+	}
+
+	public String getPublisherPhone() {
+		return publisherPhone;
+	}
+
+	public void setPublisherPhone(String publisherPhone) {
+		this.publisherPhone = publisherPhone;
+	}
+
+	public String getStep() {
+		return step;
+	}
+
+	public void setStep(String step) {
+		this.step = step;
+		if(StringUtils.isNoneBlank(step)){
+			Map<String,String> mapping=CodeConstants.loadRescueFlowStepMap();
+			this.stepName=mapping.get(step);
+		}
+	}
+
+	public Integer getAcceptCount() {
+		return acceptCount;
+	}
+
+	public void setAcceptCount(Integer acceptCount) {
+		this.acceptCount = acceptCount;
+	}
+
+	public String getStepName() {
+		return stepName;
+	}
+
+	public void setStepName(String stepName) {
+		this.stepName = stepName;
+	}
+
+	public Integer getAcceptIf() {
+		return acceptIf;
+	}
+
+	public void setAcceptIf(Integer acceptIf) {
+		this.acceptIf = acceptIf;
+	}
+
+	
+
+	public Double getNearbyDistance() {
+		return nearbyDistance;
+	}
+
+	public void setNearbyDistance(Double nearbyDistance) {
+		this.nearbyDistance = nearbyDistance;
+	}
+
+	public Integer getNeedVolunteerCount() {
+		return needVolunteerCount;
+	}
+
+	public void setNeedVolunteerCount(Integer needVolunteerCount) {
+		this.needVolunteerCount = needVolunteerCount;
+	}
+	
+	
+}

+ 31 - 0
src/main/java/com/jpsoft/proj/model/RescueFlow.java

@@ -0,0 +1,31 @@
+package com.jpsoft.proj.model;
+
+public enum RescueFlow {
+	
+	PUBLISHED("published","发布求助"),RECALL("recall","呼救"),WAITRESCUE("wait_rescue","等待救援"),RESCUING("rescuing","救援中"),COMPLETE("complete","完成");
+
+    private String name;
+	
+	private String code;
+	
+	private RescueFlow(String code,String name){
+		this.code=code;
+		this.name=name;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+}

+ 95 - 0
src/main/java/com/jpsoft/proj/model/RescueFlowPO.java

@@ -0,0 +1,95 @@
+package com.jpsoft.proj.model;
+
+import java.util.Date;
+import java.util.Map;
+
+import com.jpsoft.framework.util.DateUtil;
+import com.jpsoft.proj.utils.CodeConstants;
+
+public class RescueFlowPO {
+
+	private String flowId;
+	
+	private Date createTime;
+	
+	private String createTimeFmt;
+	
+	private String creator;
+	
+	private String step;
+	
+	private String note;
+	
+	private String dataId;
+	
+	private String stepName;
+
+	public String getFlowId() {
+		return flowId;
+	}
+
+	public void setFlowId(String flowId) {
+		this.flowId = flowId;
+	}
+
+	public Date getCreateTime() {
+		return createTime;
+	}
+
+	public void setCreateTime(Date createTime) {
+		this.createTime = createTime;
+		if(createTime!=null){
+			this.createTimeFmt=DateUtil.format(createTime, "yyyy-MM-dd HH:mm:ss");
+		}
+	}
+
+	public String getCreator() {
+		return creator;
+	}
+
+	public void setCreator(String creator) {
+		this.creator = creator;
+	}
+
+	public String getStep() {
+		return step;
+	}
+
+	public void setStep(String step) {
+		this.step = step;
+		Map<String,String> stepMap=CodeConstants.loadRescueFlowStepMap();
+		this.stepName=stepMap.get(step);
+	}
+
+	public String getNote() {
+		return note;
+	}
+
+	public void setNote(String note) {
+		this.note = note;
+	}
+
+	public String getDataId() {
+		return dataId;
+	}
+
+	public void setDataId(String dataId) {
+		this.dataId = dataId;
+	}
+
+	public String getStepName() {
+		return stepName;
+	}
+
+	public void setStepName(String stepName) {
+		this.stepName = stepName;
+	}
+
+	public String getCreateTimeFmt() {
+		return createTimeFmt;
+	}
+
+	public void setCreateTimeFmt(String createTimeFmt) {
+		this.createTimeFmt = createTimeFmt;
+	}
+}

+ 53 - 0
src/main/java/com/jpsoft/proj/model/SmsCodeCache.java

@@ -0,0 +1,53 @@
+package com.jpsoft.proj.model;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.jpsoft.proj.api.controller.VerifyCodeUtils;
+
+public class SmsCodeCache {
+
+	private static final Map<String,Long[]> codes=new HashMap<String,Long[]>();
+	
+	public static void put(String phone,Long code){
+		codes.put(phone, new Long[]{code,(new Date()).getTime()+300000});  //5分钟有效期
+	}
+	
+	public static boolean valid(String phone,Long code){
+		if(codes.get(phone)==null){
+			return false;
+		}
+		Long[] vals=codes.get(phone);
+		if((new Date()).getTime()>vals[1]){  //验证码过期
+			return false;
+		}
+		if(vals[0].longValue()==code.longValue()){
+			codes.remove(phone);  //使用后删除,避免重复使用
+			return true;
+		}
+		return false;
+	}
+	
+	
+	/**
+	 * 是否可以获取下个验证码(1分钟内禁止重复获取验证码)
+	 * @param phone
+	 * @return
+	 */
+	public static boolean next(String phone){
+		if(codes.get(phone)!=null){
+			Long[] vals=codes.get(phone);
+			if((new Date()).getTime()<(vals[1]+60000)){  //1分钟内
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	public static Long getNext(String phone){
+		Long newCode=VerifyCodeUtils.generate()+0L;
+		put(phone,newCode);
+		return newCode;
+	}
+}

+ 137 - 0
src/main/java/com/jpsoft/proj/model/SortCodePO.java

@@ -0,0 +1,137 @@
+package com.jpsoft.proj.model;
+
+import java.util.Date;
+
+public class SortCodePO {
+
+	private String codeId;
+	
+	private String codeName;
+	
+	private String fatherCodeId;
+	
+	private Integer displayNum;
+	
+	private String assistCode;
+	
+	private String domainCode;
+	
+	private String py;
+	
+	private String spare1;
+	
+	private String spare2;
+	
+	private String spare3;
+	
+	private String note;
+	
+	private String modifier;
+	
+	private Date modifyTime;
+
+	public String getCodeId() {
+		return codeId;
+	}
+
+	public void setCodeId(String codeId) {
+		this.codeId = codeId;
+	}
+
+	public String getCodeName() {
+		return codeName;
+	}
+
+	public void setCodeName(String codeName) {
+		this.codeName = codeName;
+	}
+
+	public String getFatherCodeId() {
+		return fatherCodeId;
+	}
+
+	public void setFatherCodeId(String fatherCodeId) {
+		this.fatherCodeId = fatherCodeId;
+	}
+
+	public Integer getDisplayNum() {
+		return displayNum;
+	}
+
+	public void setDisplayNum(Integer displayNum) {
+		this.displayNum = displayNum;
+	}
+
+	public String getAssistCode() {
+		return assistCode;
+	}
+
+	public void setAssistCode(String assistCode) {
+		this.assistCode = assistCode;
+	}
+
+	public String getDomainCode() {
+		return domainCode;
+	}
+
+	public void setDomainCode(String domainCode) {
+		this.domainCode = domainCode;
+	}
+
+	public String getPy() {
+		return py;
+	}
+
+	public void setPy(String py) {
+		this.py = py;
+	}
+
+	public String getSpare1() {
+		return spare1;
+	}
+
+	public void setSpare1(String spare1) {
+		this.spare1 = spare1;
+	}
+
+	public String getSpare2() {
+		return spare2;
+	}
+
+	public void setSpare2(String spare2) {
+		this.spare2 = spare2;
+	}
+
+	public String getSpare3() {
+		return spare3;
+	}
+
+	public void setSpare3(String spare3) {
+		this.spare3 = spare3;
+	}
+
+	public String getNote() {
+		return note;
+	}
+
+	public void setNote(String note) {
+		this.note = note;
+	}
+
+	public String getModifier() {
+		return modifier;
+	}
+
+	public void setModifier(String modifier) {
+		this.modifier = modifier;
+	}
+
+	public Date getModifyTime() {
+		return modifyTime;
+	}
+
+	public void setModifyTime(Date modifyTime) {
+		this.modifyTime = modifyTime;
+	}
+	
+}

+ 68 - 0
src/main/java/com/jpsoft/proj/model/User.java

@@ -0,0 +1,68 @@
+package com.jpsoft.proj.model;
+
+public class User {
+
+	private String userName;
+	
+	private String openid;
+	
+	private String phone;
+	
+	private String orgName;
+	
+	private Integer userType;
+	
+	private String userId;
+
+	public String getUserName() {
+		return userName;
+	}
+
+	public void setUserName(String userName) {
+		this.userName = userName;
+	}
+
+	
+	public String getPhone() {
+		return phone;
+	}
+
+	public void setPhone(String phone) {
+		this.phone = phone;
+	}
+
+	public String getOrgName() {
+		return orgName;
+	}
+
+	public void setOrgName(String orgName) {
+		this.orgName = orgName;
+	}
+
+	public Integer getUserType() {
+		return userType;
+	}
+
+	public void setUserType(Integer userType) {
+		this.userType = userType;
+	}
+
+	public String getOpenid() {
+		return openid;
+	}
+
+	public void setOpenid(String openid) {
+		this.openid = openid;
+	}
+
+	public String getUserId() {
+		return userId;
+	}
+
+	public void setUserId(String userId) {
+		this.userId = userId;
+	}
+
+	
+
+}

+ 190 - 0
src/main/java/com/jpsoft/proj/model/VolunteerPO.java

@@ -0,0 +1,190 @@
+package com.jpsoft.proj.model;
+
+import java.util.Date;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.jpsoft.framework.util.PinyinFromHanzi;
+
+public class VolunteerPO {
+	
+	public static interface Create {};
+
+	private String volunteerId;
+	
+	@NotBlank(groups=Create.class,message = "姓名不能为空")
+	private String realName;
+	
+	private String namePy;
+	
+	@Pattern(regexp="^1\\d{10}",message = "手机号码不符合规范")
+	private String phone;
+	
+	private String profession;
+	
+	private Date modifyTime;
+	
+	private String modifier;
+	
+	private String pwd;
+	
+	private String rescueSort;  //救援能力
+	
+	private Integer verifyStatus;
+	
+	private String ownEquipments;
+	
+	private String headPhoto;
+	
+	private String lastLat;
+	
+	private String lastLon;
+	
+	private String regLat;
+	
+	private String regLon;
+	
+	private String regAddress;
+
+	public String getVolunteerId() {
+		return volunteerId;
+	}
+
+	public void setVolunteerId(String volunteerId) {
+		this.volunteerId = volunteerId;
+	}
+
+	public String getRealName() {
+		return realName;
+	}
+
+	public void setRealName(String realName) {
+		this.realName = realName;
+		if(StringUtils.isNotBlank(realName)){
+			this.namePy=PinyinFromHanzi.getPinyin(realName);
+		}
+	}
+
+	public String getPhone() {
+		return phone;
+	}
+
+	public void setPhone(String phone) {
+		this.phone = phone;
+	}
+
+	public String getProfession() {
+		return profession;
+	}
+
+	public void setProfession(String profession) {
+		this.profession = profession;
+	}
+
+	public Date getModifyTime() {
+		return modifyTime;
+	}
+
+	public void setModifyTime(Date modifyTime) {
+		this.modifyTime = modifyTime;
+	}
+
+	public String getModifier() {
+		return modifier;
+	}
+
+	public void setModifier(String modifier) {
+		this.modifier = modifier;
+	}
+
+	public String getNamePy() {
+		return namePy;
+	}
+
+	public void setNamePy(String namePy) {
+		this.namePy = namePy;
+	}
+
+	public String getPwd() {
+		return pwd;
+	}
+
+	public void setPwd(String pwd) {
+		this.pwd = pwd;
+	}
+
+	public String getRescueSort() {
+		return rescueSort;
+	}
+
+	public void setRescueSort(String rescueSort) {
+		this.rescueSort = rescueSort;
+	}
+
+	public Integer getVerifyStatus() {
+		return verifyStatus;
+	}
+
+	public void setVerifyStatus(Integer verifyStatus) {
+		this.verifyStatus = verifyStatus;
+	}
+
+	public String getOwnEquipments() {
+		return ownEquipments;
+	}
+
+	public void setOwnEquipments(String ownEquipments) {
+		this.ownEquipments = ownEquipments;
+	}
+
+	public String getHeadPhoto() {
+		return headPhoto;
+	}
+
+	public void setHeadPhoto(String headPhoto) {
+		this.headPhoto = headPhoto;
+	}
+
+	public String getLastLat() {
+		return lastLat;
+	}
+
+	public void setLastLat(String lastLat) {
+		this.lastLat = lastLat;
+	}
+
+	public String getLastLon() {
+		return lastLon;
+	}
+
+	public void setLastLon(String lastLon) {
+		this.lastLon = lastLon;
+	}
+
+	public String getRegLat() {
+		return regLat;
+	}
+
+	public void setRegLat(String regLat) {
+		this.regLat = regLat;
+	}
+
+	public String getRegLon() {
+		return regLon;
+	}
+
+	public void setRegLon(String regLon) {
+		this.regLon = regLon;
+	}
+
+	public String getRegAddress() {
+		return regAddress;
+	}
+
+	public void setRegAddress(String regAddress) {
+		this.regAddress = regAddress;
+	}
+}

+ 74 - 0
src/main/java/com/jpsoft/proj/model/VolunteerVO.java

@@ -0,0 +1,74 @@
+package com.jpsoft.proj.model;
+
+public class VolunteerVO {
+
+	private String volunteerId;
+	
+	private String realName;
+	
+	private String phone;
+	
+	private String profession;
+	
+	private Integer verifyStatus;
+	
+	private String ownEquipments;
+	
+	private String headPhotoPath;
+
+	public String getVolunteerId() {
+		return volunteerId;
+	}
+
+	public void setVolunteerId(String volunteerId) {
+		this.volunteerId = volunteerId;
+	}
+
+	public String getRealName() {
+		return realName;
+	}
+
+	public void setRealName(String realName) {
+		this.realName = realName;
+	}
+
+	public String getPhone() {
+		return phone;
+	}
+
+	public void setPhone(String phone) {
+		this.phone = phone;
+	}
+
+	public String getProfession() {
+		return profession;
+	}
+
+	public void setProfession(String profession) {
+		this.profession = profession;
+	}
+
+	public Integer getVerifyStatus() {
+		return verifyStatus;
+	}
+
+	public void setVerifyStatus(Integer verifyStatus) {
+		this.verifyStatus = verifyStatus;
+	}
+
+	public String getOwnEquipments() {
+		return ownEquipments;
+	}
+
+	public void setOwnEquipments(String ownEquipments) {
+		this.ownEquipments = ownEquipments;
+	}
+
+	public String getHeadPhotoPath() {
+		return headPhotoPath;
+	}
+
+	public void setHeadPhotoPath(String headPhotoPath) {
+		this.headPhotoPath = headPhotoPath;
+	}
+}

+ 27 - 0
src/main/java/com/jpsoft/proj/model/WxSessionCache.java

@@ -0,0 +1,27 @@
+package com.jpsoft.proj.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class WxSessionCache {
+
+	private static final Map<String,String> wxSession=new HashMap<String,String>(200);
+	
+	private static final Map<String,String> openidCache=new HashMap<String,String>(200);
+	
+	public static void put(String openid,String sessionKey){
+		wxSession.put(openid, sessionKey);
+	}
+	
+	public static String getSessionKey(String openid){
+		return wxSession.get(openid);
+	}
+	
+	public static void putOpenid(String code,String openid){
+		openidCache.put(code, openid);
+	}
+	
+	public static String getOpenid(String code){
+		return openidCache.get(code);
+	}
+}

+ 100 - 0
src/main/java/com/jpsoft/proj/newsinfo/controller/AdController.java

@@ -0,0 +1,100 @@
+package com.jpsoft.proj.newsinfo.controller;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.constraints.NotBlank;
+
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.io.FileUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.framework.util.DateUtil;
+import com.jpsoft.framework.util.SessionThreadLocal;
+import com.jpsoft.framework.util.SessionUser;
+import com.jpsoft.framework.util.SysConfigUtil;
+import com.jpsoft.proj.newsinfo.service.AdService;
+import com.jpsoft.proj.utils.LayGridResp;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RequestParams;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+import com.jpsoft.proj.utils.UploadUtils;
+
+
+@RestController
+@RequestMapping("/**/ad")
+@Validated
+public class AdController {
+
+	@Autowired
+	private AdService service;
+	
+	@RequestMapping("/query")
+	public LayGridResp query(RequestParams requestParams,@RequestParam(value="page",defaultValue="1") Integer pageNum,@RequestParam(value="limit",defaultValue="20") Integer pageSize){
+		return RespVOBuilder.getLayGridResp(service.query(requestParams,pageNum, pageSize));
+	}
+	
+	@RequestMapping("/add")
+	public RespVO add(@NotBlank(message = "缺少公告内容") String noticeCon,SessionUser su,RequestParams requestParams){
+		Map<String,Object> args=requestParams.getObjectMap();
+		args.put("creator", su!=null?su.getUserName():"unknow");
+		service.add(args);
+ 		return RespVOBuilder.ok();
+	}
+	
+	@RequestMapping("/del")
+	public RespVO  del(@NotBlank(message = "缺少广告记录编号") String adId,HttpServletRequest request){
+		Map<String,Object>  ad=service.get(adId);
+		if(ad==null||ad.size()==0){
+			return RespVOBuilder.error("数据已不存在");
+		}
+		service.del(adId);
+		delRealFile(ad,request);
+		return RespVOBuilder.ok();
+	}
+	
+	@RequestMapping("/upload")
+	public RespVO upload(HttpServletRequest request) throws IOException{
+		Map<String,Object> uploadDatas=UploadUtils.process(request);
+		if(uploadDatas==null||uploadDatas.size()==0){
+			return RespVOBuilder.error("未获取到上传数据");
+		}
+		String basePath=request.getSession().getServletContext().getRealPath("");
+		String relPath="upload"+File.separator+"ad"+File.separator+DateUtil.format(new Date(), "yyyyMM");
+		basePath=basePath+File.separator+relPath;
+		
+		FileUtils.forceMkdir(new File(basePath));
+		
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		String newFileName=uuid.generate()+"."+uploadDatas.get("fileSort");
+		
+		FileUtils.writeByteArrayToFile(new File(basePath+File.separator+newFileName), ((FileItem)uploadDatas.get("fileCon")).get());
+		
+		//String accessPath=request.getScheme()+"://"+request.getLocalAddr()+":"+request.getLocalPort()+request.getContextPath()+"/upload/ad/"+DateUtil.format(new Date(), "yyyyMM")+"/"+newFileName;
+		String accessPath=SysConfigUtil.getConfig("accessPath")+"/upload/ad/"+DateUtil.format(new Date(), "yyyyMM")+"/"+newFileName;
+		
+		Map<String,Object>  fileInfo=MapUtils.trackMap(uploadDatas, "adNum","showIndex","showPlatform");  //MapUtils.builder("fileSort",uploadDatas.get("fileSort"),"fileName",newFileName,"filePath",relPath,"accessPath",accessPath);
+		fileInfo.put("accessPath", accessPath);
+		fileInfo.put("filePath", relPath+File.separator+newFileName);
+		SessionUser su=SessionThreadLocal.getSessionUser();
+		fileInfo.put("modifier", su!=null?su.getUserName():"unknow");
+		String fileId=service.add(fileInfo);
+		
+		return RespVOBuilder.ok(MapUtils.builder("fileId",fileId,"accessPath",accessPath));
+	}
+	
+	private void delRealFile(Map<String,Object>  fileInfo,HttpServletRequest request){
+		String filePath=(String)fileInfo.get("filePath");
+		String basePath=request.getSession().getServletContext().getRealPath("");
+		FileUtils.deleteQuietly(new File(basePath+File.separator+filePath));
+	}
+}

+ 154 - 0
src/main/java/com/jpsoft/proj/newsinfo/controller/NewsInfoController.java

@@ -0,0 +1,154 @@
+package com.jpsoft.proj.newsinfo.controller;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.constraints.NotBlank;
+
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.framework.util.DateUtil;
+import com.jpsoft.framework.util.SessionThreadLocal;
+import com.jpsoft.framework.util.SessionUser;
+import com.jpsoft.framework.util.SysConfigUtil;
+import com.jpsoft.proj.newsinfo.service.NewsInfoService;
+import com.jpsoft.proj.utils.LayGridResp;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RequestParams;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+import com.jpsoft.proj.utils.UploadUtils;
+
+@RestController
+@RequestMapping("/**/newsinfo")
+@Validated
+public class NewsInfoController {
+
+	@Autowired
+	private NewsInfoService  service;
+	
+	@RequestMapping("/query")
+	public LayGridResp query(RequestParams requestParams,@RequestParam(value="page",defaultValue="1") Integer page,@RequestParam(value="limit",defaultValue="20") Integer limit){
+		return RespVOBuilder.getLayGridResp(service.queryNewsInfo(requestParams,page, limit));
+	}
+	
+	@RequestMapping("/get")
+	public RespVO get(@NotBlank(message = "缺少记录编号参数") String infoId){
+		return RespVOBuilder.ok(service.getNewsInfo(infoId));
+	}
+	
+	@RequestMapping("/add")
+	public RespVO add(@NotBlank(message = "缺少资讯标题") String infoTitle,@NotBlank(message = "缺少资讯内容") String infoCon,SessionUser su,RequestParams requestParams){
+		Map<String,Object> args=requestParams.getObjectMap();
+		args.put("publisher", su!=null?su.getUserName():"unknow");
+		args.put("modifier",args.get("publisher"));
+		service.addNewsInfo(args);
+ 		return RespVOBuilder.ok();
+	}
+	
+	@RequestMapping("/del")
+	public RespVO  del(@NotBlank(message = "缺少资讯记录编号") String infoId){
+		service.delNewsIfo(infoId);
+		return RespVOBuilder.ok();
+	}
+	
+	@RequestMapping("/update")
+	public RespVO update(@NotBlank(message = "缺少资讯记录编号") String infoId,@NotBlank(message = "缺少资讯标题") String infoTitle,@NotBlank(message = "缺少资讯内容") String infoCon,SessionUser su,RequestParams requestParams){
+		Map<String,Object> args=requestParams.getObjectMap();
+		Map<String,Object>  news=MapUtils.trackMap(args, "infoId","infoSort","infoTitle","infoCon","showIndex","showPlatform");  
+		news.put("modifier",su!=null?su.getUserName():"unknow");
+		service.updateNewsInfo(news);
+ 		return RespVOBuilder.ok();
+	}
+	
+	@RequestMapping("/uploadNewsFile")
+	public Map<String,Object> uploadNewsFile(HttpServletRequest request){
+		try {
+			Map<String,Object> uploadDatas=UploadUtils.process(request);
+			if(uploadDatas==null||uploadDatas.size()==0){
+				return MapUtils.builder("error","未获取到上传数据");
+			}
+			String basePath=request.getSession().getServletContext().getRealPath("");
+			String relPath="upload"+File.separator+"news"+File.separator+DateUtil.format(new Date(), "yyyyMM");
+			basePath=basePath+File.separator+relPath;
+			
+			FileUtils.forceMkdir(new File(basePath));
+			
+			UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+			String newFileName=uuid.generate()+"."+uploadDatas.get("fileSort");
+			
+			FileUtils.writeByteArrayToFile(new File(basePath+File.separator+newFileName), ((FileItem)uploadDatas.get("fileCon")).get());
+			
+			//String accessPath=request.getScheme()+"://"+request.getLocalAddr()+":"+request.getLocalPort()+request.getContextPath()+"/upload/news/"+DateUtil.format(new Date(), "yyyyMM")+"/"+newFileName;
+			String accessPath=SysConfigUtil.getConfig("accessPath")+"/upload/news/"+DateUtil.format(new Date(), "yyyyMM")+"/"+newFileName;
+			
+			return MapUtils.builder("location",accessPath);
+			
+		} catch (IOException e) {
+			e.printStackTrace();
+			return MapUtils.builder("error","上传处理出现错误");
+		}
+	}
+	
+	
+	/**
+	 * 以上传方式发布新闻
+	 * @param request
+	 * @return
+	 * @throws IOException
+	 */
+	@RequestMapping("/uploadForPublish")
+	public RespVO uploadForPublish(HttpServletRequest request) throws IOException{
+		Map<String,Object> uploadDatas=UploadUtils.process(request);
+		if(uploadDatas==null||uploadDatas.size()==0){
+			return RespVOBuilder.error("未获取到上传数据");
+		}
+		if(StringUtils.isBlank((String)uploadDatas.get("infoTitle"))){
+			return RespVOBuilder.error("缺少资讯标题");
+		}
+		if(StringUtils.isBlank((String)uploadDatas.get("infoCon"))){
+			return RespVOBuilder.error("缺少资讯内容");
+		}
+		
+		String basePath=request.getSession().getServletContext().getRealPath("");
+		String relPath="upload"+File.separator+"news"+File.separator+DateUtil.format(new Date(), "yyyyMM");
+		basePath=basePath+File.separator+relPath;
+		
+		FileUtils.forceMkdir(new File(basePath));
+		
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		String newFileName=uuid.generate()+"."+uploadDatas.get("fileSort");
+		
+		FileUtils.writeByteArrayToFile(new File(basePath+File.separator+newFileName), ((FileItem)uploadDatas.get("fileCon")).get());
+		
+		//String accessPath=request.getScheme()+"://"+request.getLocalAddr()+":"+request.getLocalPort()+request.getContextPath()+"/upload/news/"+DateUtil.format(new Date(), "yyyyMM")+"/"+newFileName;
+		String accessPath=SysConfigUtil.getConfig("accessPath")+"/upload/news/"+DateUtil.format(new Date(), "yyyyMM")+"/"+newFileName;
+		
+		Map<String,Object>  news=MapUtils.trackMap(uploadDatas, "infoId","infoSort","infoTitle","infoCon","showIndex","showPlatform");  //MapUtils.builder("fileSort",uploadDatas.get("fileSort"),"fileName",newFileName,"filePath",relPath,"accessPath",accessPath);
+		news.put("coverImg", accessPath);
+		
+		SessionUser su=SessionThreadLocal.getSessionUser();
+		news.put("modifier", su!=null?su.getUserName():"unknow");
+		
+		if(StringUtils.isBlank((String)news.get("infoId"))){
+			news.put("publisher",news.get("modifier"));
+			service.addNewsInfo(news);
+		}
+		else{
+			service.updateNewsInfo(news);
+		}
+		
+		return RespVOBuilder.ok();
+	}
+}

+ 60 - 0
src/main/java/com/jpsoft/proj/newsinfo/controller/NoticeController.java

@@ -0,0 +1,60 @@
+package com.jpsoft.proj.newsinfo.controller;
+
+import java.util.Calendar;
+import java.util.Map;
+
+import javax.validation.constraints.NotBlank;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.framework.util.DateUtil;
+import com.jpsoft.framework.util.SessionUser;
+import com.jpsoft.proj.newsinfo.service.NoticeService;
+import com.jpsoft.proj.utils.LayGridResp;
+import com.jpsoft.proj.utils.RequestParams;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+
+@RestController
+@RequestMapping("/**/notice")
+@Validated
+public class NoticeController {
+
+	@Autowired
+	private NoticeService service;
+	
+	@RequestMapping("/query")
+	public LayGridResp query(RequestParams requestParams,@RequestParam(value="page",defaultValue="1") Integer pageNum,@RequestParam(value="limit",defaultValue="20") Integer pageSize){
+		return RespVOBuilder.getLayGridResp(service.query(requestParams,pageNum, pageSize));
+	}
+	
+	@RequestMapping("/add")
+	public RespVO add(@NotBlank(message = "缺少公告内容") String noticeCon,SessionUser su,RequestParams requestParams){
+		Map<String,Object> args=requestParams.getObjectMap();
+		String expireTime=requestParams.get("expireTime");
+		Calendar ca=Calendar.getInstance();
+		if(StringUtils.isBlank(expireTime)){
+			ca.add(Calendar.DAY_OF_MONTH, 3);
+			args.put("expireTime", ca.getTime());
+		}
+		else if(expireTime.compareTo(DateUtil.format(ca.getTime(), "yyyy-MM-dd"))<=0){
+			return RespVOBuilder.error("失效时间设置太早");
+		}
+		
+		args.put("creator", su!=null?su.getUserName():"unknow");
+		service.add(args);
+ 		return RespVOBuilder.ok();
+	}
+	
+	@RequestMapping("/del")
+	public RespVO  del(@NotBlank(message = "缺少公告记录编号") String noticeId){
+		service.del(noticeId);
+		return RespVOBuilder.ok();
+	}
+}

+ 38 - 0
src/main/java/com/jpsoft/proj/newsinfo/controller/SuggestController.java

@@ -0,0 +1,38 @@
+package com.jpsoft.proj.newsinfo.controller;
+
+import javax.validation.constraints.NotBlank;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.framework.util.SessionUser;
+import com.jpsoft.proj.newsinfo.service.SuggestService;
+import com.jpsoft.proj.utils.LayGridResp;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RequestParams;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+
+@RestController
+@RequestMapping("/**/suggest")
+@Validated
+public class SuggestController {
+
+	@Autowired
+	private SuggestService service;
+	
+	@RequestMapping("/query")
+	public LayGridResp query(RequestParams requestParams,@RequestParam(value="page",defaultValue="1") Integer pageNum,@RequestParam(value="limit",defaultValue="20") Integer pageSize){
+		return RespVOBuilder.getLayGridResp(service.query(requestParams,pageNum, pageSize));
+	}
+	
+	@RequestMapping("/reply")
+	public RespVO reply(@NotBlank(message="未指定要回复的对应记录") String suggestId,@NotBlank(message="回复内容不能为空") String reply){
+		service.createReply(suggestId,reply);
+		return RespVOBuilder.ok();
+	}
+}

+ 64 - 0
src/main/java/com/jpsoft/proj/newsinfo/service/AdService.java

@@ -0,0 +1,64 @@
+package com.jpsoft.proj.newsinfo.service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.proj.utils.RequestParams;
+
+@Service
+public class AdService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	
+	public List<Map<String,Object>> loadForMobileIndex(String showPlatform,Integer adCount){
+		String sql="select access_path,ad_link from t_ad where show_index=1 and (show_platform='both' or show_platform=?)  order by ad_num limit ?";
+		return dao.queryForListMap(sql, showPlatform.toLowerCase(),adCount);
+	}
+	
+	public PageModel<Map<String,Object>> query(RequestParams args,int pageNum,int pageSize){
+		List<Object> sqlParams=new ArrayList<Object>();
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select  ad.*,date_format(modify_time,'%Y-%m-%d %H:%i:%s') modify_time_fmt");
+		sql.append(" from t_ad ad");
+		sql.append(" where 1=1");
+		if(StringUtils.isNoneBlank(args.get("startDate"))){
+			sql.append(" and date_format(modify_time,'%Y-%m-%d')>=?");
+			sqlParams.add(args.get("startDate"));
+		}
+		if(StringUtils.isNoneBlank(args.get("endDate"))){
+			sql.append(" and date_format(modify_time,'%Y-%m-%d')<=?");
+			sqlParams.add(args.get("endDate"));
+		}
+		Object[] sqlArgs=sqlParams.size()>0?sqlParams.toArray():null;
+		return dao.queryForPagedListMap(sql.toString(),pageNum, pageSize,sqlArgs);
+	}
+	
+	public String add(Map<String,Object> ad){
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		ad.put("adId", uuid.generate());
+		ad.put("modifyTime",new Date());
+	
+		dao.insert(ad, "t_ad");
+		return (String)ad.get("adId");
+	}
+	
+	public Map<String,Object>  get(String adId){
+		return dao.queryForSingleMap("select * from t_ad  where ad_id=?", adId);
+	}
+	
+	
+	public void del(String adId){
+		dao.getJdbcTemplate().update("delete from t_ad  where ad_id=?", adId);
+	}
+}

+ 113 - 0
src/main/java/com/jpsoft/proj/newsinfo/service/NewsInfoService.java

@@ -0,0 +1,113 @@
+package com.jpsoft.proj.newsinfo.service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.proj.utils.RequestParams;
+
+@Service
+public class NewsInfoService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	public List<Map<String,Object>> loadIndexSortInfo(String sort ,String showPlatform,Integer infoCount){
+		StringBuilder sql=new StringBuilder();
+		sql.append("select info_id,info_title,cover_img,publish_time,date_format(publish_time,'%Y-%m-%d %H:%i:%s') publish_time_fmt ");
+		sql.append(" from t_news_info");
+		sql.append(" where show_index=1 and info_sort=? and (show_platform='both' or show_platform=?)");
+		sql.append(" order by publish_time desc limit ?");
+		return dao.queryForListMap(sql.toString(), sort,showPlatform.toLowerCase(),infoCount);
+	}
+	
+	public List<Map<String,Object>> loadIndexNewsInfo(String showPlatform,Integer infoCount){
+		String sql="select info_id,info_title,cover_img,publish_time,date_format(publish_time,'%Y-%m-%d %H:%i:%s') publish_time_fmt  from t_news_info where show_index=1 and (show_platform='both' or show_platform=?) order by publish_time desc limit ?";
+		return dao.queryForListMap(sql, showPlatform.toLowerCase(),infoCount);
+	}
+	
+	public PageModel<Map<String,Object>> queryNewsInfo(String showPlatform, String infoSort,int pageNo,int pageSize){
+		String sql="select info_id,info_title,cover_img,publish_time,date_format(publish_time,'%Y-%m-%d %H:%i:%s') publish_time_fmt  from t_news_info where info_sort=? and (show_platform='both' or show_platform=?) order by publish_time desc";
+		return dao.queryForPagedListMap(sql, pageNo, pageSize, infoSort,showPlatform.toLowerCase());
+	}
+	
+	public PageModel<Map<String,Object>> queryNewsInfo(RequestParams  params,int pageNum,int pageSize){
+		List<Object>  sqlParams=new ArrayList<Object>();
+		StringBuilder sql=new StringBuilder();
+		sql.append("select d.code_name info_sort_name,cover_img,info_id,info_title,publisher,date_format(publish_time,'%Y-%m-%d %H:%i:%s') publish_time,");
+		sql.append(" show_index,show_platform");
+		sql.append(" from t_news_info n ");
+		sql.append(" left join t_sort_code d  on n.info_sort=d.code_id");
+		sql.append(" where 1=1");
+		
+		if(StringUtils.isNoneBlank(params.get("infoSort"))){
+			sql.append(" and info_sort=? ");
+			sqlParams.add(params.get("infoSort"));
+		}
+		if(StringUtils.isNoneBlank(params.get("infoTitle"))){
+			sql.append(" and info_title like ? ");
+			sqlParams.add("%"+params.get("infoTitle")+"%");
+		}
+		
+		if(StringUtils.isNoneBlank(params.get("startDate"))){
+			sql.append(" and date_format(publish_time,'%Y-%m-%d') >= ? ");
+			sqlParams.add(params.get("startDate"));
+		}
+		
+		if(StringUtils.isNoneBlank(params.get("endDate"))){
+			sql.append(" and date_format(publish_time,'%Y-%m-%d') <= ? ");
+			sqlParams.add(params.get("endDate"));
+		}
+		sql.append(" order by publish_time desc");
+		
+		return dao.queryForPagedListMap(sql.toString(), pageNum, pageSize,sqlParams.size()>0?sqlParams.toArray():null);
+	}
+	
+	public Map<String,Object> getNewsInfo(String infoId){
+		
+		return dao.queryForSingleMap("select n.*,date_format(publish_time,'%Y-%m-%d %H:%i:%s') publish_time_fmt from t_news_info n where info_id=?", infoId);
+	}
+	
+	//更新阅读次数
+	public void addReadCount(String infoId){
+		String sql="update t_news_info set read_count=ifnull(read_count,0)+1 where info_id=?";
+		dao.getJdbcTemplate().update(sql, infoId);
+	}
+	
+	
+	
+	public void addNewsInfo(Map<String,Object> news){
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		news.put("infoId", uuid.generate());
+		news.put("publishTime", new Date());
+		news.put("modifyTime", new Date());
+		dao.insert(news, "t_news_info");
+		
+	}
+	
+	
+	
+	public void updateNewsInfo(Map<String,Object> news){
+		news.put("modifyTime", new Date());
+		dao.update(news, "t_news_info", "info_id");
+	}
+	
+	public void delNewsIfo(String infoId){
+		String sql="delete from t_news_info where info_id=?";
+		dao.getJdbcTemplate().update(sql, infoId);
+	}
+	
+	
+	public Map<String,Object> getNewsInfoBySort(String sortId){
+		
+		return dao.queryForSingleMap("select n.*,date_format(publish_time,'%Y-%m-%d %H:%i:%s') publish_time_fmt from t_news_info n where info_sort=? limit 1", sortId);
+	}
+}

+ 99 - 0
src/main/java/com/jpsoft/proj/newsinfo/service/NoticeService.java

@@ -0,0 +1,99 @@
+package com.jpsoft.proj.newsinfo.service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RequestParams;
+
+@Service
+public class NoticeService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	public List<Map<String,Object>> loadForMobile(String showPlatform,Integer noticeCount){
+		String sql="select notice_con,link_page from t_notice where (show_platform='both' or show_platform=?) and UNIX_TIMESTAMP(now())<=UNIX_TIMESTAMP(expire_time) order by create_time desc limit ?";
+		return dao.queryForListMap(sql, showPlatform.toLowerCase(),noticeCount);
+	}
+	
+	public PageModel<Map<String,Object>> query(RequestParams args,int pageNum,int pageSize){
+		List<Object> sqlParams=new ArrayList<Object>();
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select  n.*,date_format(create_time,'%Y-%m-%d %H:%i:%s') create_time_fmt,date_format(expire_time,'%Y-%m-%d %H:%i:%s') expire_time_fmt");
+		sql.append(" from t_notice n");
+		sql.append(" where del_if=0");
+		if(StringUtils.isNoneBlank(args.get("noticeKey"))){
+			sql.append(" and notice_con like ?");
+			sqlParams.add("%"+args.get("noticeKey")+"%");
+		}
+		if(StringUtils.isNoneBlank(args.get("startDate"))){
+			sql.append(" and date_format(create_time,'%Y-%m-%d')>=?");
+			sqlParams.add(args.get("startDate"));
+		}
+		if(StringUtils.isNoneBlank(args.get("endDate"))){
+			sql.append(" and date_format(create_time,'%Y-%m-%d')<=?");
+			sqlParams.add(args.get("endDate"));
+		}
+		
+		if(StringUtils.isNoneBlank(args.get("showPlatform"))){
+			sql.append(" and (show_platform=? or show_platform='both')");
+			sqlParams.add(args.get("showPlatform"));
+		}
+		
+		Object[] sqlArgs=sqlParams.size()>0?sqlParams.toArray():null;
+		return dao.queryForPagedListMap(sql.toString(),pageNum, pageSize,sqlArgs);
+	}
+	
+	public String add(Map<String,Object> notice){
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		notice.put("noticeId", uuid.generate());
+		notice.put("createTime",new Date());
+		notice.put("delIf",0); 
+		
+		dao.insert(notice, "t_notice");
+		return (String)notice.get("noticeId");
+	}
+	
+	
+	public void del(String noticeId){
+		dao.getJdbcTemplate().update("update t_notice set del_if=1 where notice_id=?", noticeId);
+	}
+	
+	public void addMsgReadedFlag(String userId,String msgId,String msgSort){
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		Map<String,Object> readLog=MapUtils.builder("readId",uuid.generate(),"msgId",msgId,"userId",userId,"msgSort",msgSort,"createTime",new Date());
+		dao.insert(readLog, "t_msg_read_log");
+	}
+	
+	public Integer getUnReadNoticeCount(String userId){
+		String sql="select count(1) msg_count from t_notice n left join t_msg_read_log log on n.notice_id=log.msg_id and log.user_id=? where n.del_if=0 and read_id is null";
+		Map<String,Object> rst=dao.queryForSingleMap(sql, userId);
+		return rst==null||rst.size()==0?0:((Number)rst.get("msgCount")).intValue();
+	}
+	
+	public PageModel<Map<String,Object>> load(String userId,String showPlatform,int pageNum,int pageSize){
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select  n.notice_id msg_id,'通知公告' msg_sort_name,'notice' msg_sort,n.notice_con msg_con,date_format(n.create_time,'%m-%d %H:%i') create_time,isnull(read_id) is_new");
+		sql.append(" from t_notice n");
+		sql.append(" left join t_msg_read_log log on n.notice_id=log.msg_id and log.user_id=?");
+		sql.append(" where n.del_if=0 and (show_platform=? or show_platform='both')");
+		sql.append(" order by n.create_time desc");
+		
+		return dao.queryForPagedListMap(sql.toString(),pageNum, pageSize,userId,showPlatform);
+	}
+	
+	public boolean hadRead(String userId,String msgId){
+		List<Map<String,Object>> rst=dao.queryForListMap("select read_id from t_msg_read_log where msg_id=? and user_id=?", msgId,userId);
+		return rst!=null&&rst.size()>0;
+	}
+}

+ 71 - 0
src/main/java/com/jpsoft/proj/newsinfo/service/SuggestService.java

@@ -0,0 +1,71 @@
+package com.jpsoft.proj.newsinfo.service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.proj.utils.RequestParams;
+
+@Service
+public class SuggestService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	public PageModel<Map<String,Object>> loadMySuggest(String suggesterId,int pageNum,int pageSize){
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select  s.suggest_con,s.reply_con,date_format(create_time,'%m-%d %H:%i') create_time");
+		sql.append(" from t_suggest s");
+		sql.append(" where suggester_id=?");
+		
+		return dao.queryForPagedListMap(sql.toString(), pageNum, pageSize, suggesterId);
+	}
+	
+	public PageModel<Map<String,Object>> query(RequestParams args,int pageNum,int pageSize){
+		List<Object> sqlParams=new ArrayList<Object>();
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select  s.*,date_format(create_time,'%Y-%m-%d %H:%i:%s') create_time_fmt,ifnull(m.real_name,v.real_name) suggester_name");
+		sql.append(" from t_suggest s");
+		sql.append(" left join t_member m on s.suggester_id=m.member_id");
+		sql.append(" left join t_volunteer v on s.suggester_id=v.volunteer_id");
+		sql.append(" where 1=1");
+		if(StringUtils.isNoneBlank(args.get("suggestKey"))){
+			sql.append(" and suggest_con like ?");
+			sqlParams.add("%"+args.get("suggestKey")+"%");
+		}
+		if(StringUtils.isNoneBlank(args.get("startDate"))){
+			sql.append(" and date_format(create_time,'%Y-%m-%d')>=?");
+			sqlParams.add(args.get("startDate"));
+		}
+		if(StringUtils.isNoneBlank(args.get("endDate"))){
+			sql.append(" and date_format(create_time,'%Y-%m-%d')<=?");
+			sqlParams.add(args.get("endDate"));
+		}
+		Object[] sqlArgs=sqlParams.size()>0?sqlParams.toArray():null;
+		return dao.queryForPagedListMap(sql.toString(),pageNum, pageSize,sqlArgs);
+	}
+	
+	public String add(Map<String,Object> suggest){
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		suggest.put("suggestId", uuid.generate());
+		suggest.put("createTime",new Date());
+		dao.insert(suggest, "t_suggest");
+		return (String)suggest.get("suggestId");
+	}
+	
+	public void createReply(String suggestId,String reply){
+		dao.getJdbcTemplate().update("update t_suggest set reply_con=? where suggest_id=?", reply,suggestId);
+	}
+	
+	public Map<String,Object>  getLast(String suggestId){
+		return dao.queryForSingleMap("select create_time from t_suggest where suggester_id=? order by create_time desc limit 1", suggestId);
+	}
+}

+ 61 - 0
src/main/java/com/jpsoft/proj/rescue/controller/AEDController.java

@@ -0,0 +1,61 @@
+package com.jpsoft.proj.rescue.controller;
+
+import java.util.Map;
+
+import javax.validation.constraints.NotBlank;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.proj.rescue.service.AEDService;
+import com.jpsoft.proj.utils.LayGridResp;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RequestParams;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+@RestController
+@RequestMapping("/**/aed")
+@Validated
+public class AEDController {
+
+	@Autowired
+	private AEDService  service;
+	
+	@RequestMapping("/query")
+	public LayGridResp query(RequestParams requestParams,@RequestParam(value="page",defaultValue="1") Integer page,@RequestParam(value="limit",defaultValue="20") Integer limit){
+		return RespVOBuilder.getLayGridResp(service.query(requestParams,page, limit));
+	}
+	
+	@RequestMapping("/add")
+	public RespVO add(@NotBlank(message = "缺少AED存放地址") String address,RequestParams requestParams){
+		/*if(service.checkIfSameAED(requestParams.get("longitude"), requestParams.get("latitude"), 100)){
+			return RespVOBuilder.error("附近100米范围内已经有AED");
+		}*/
+		Map<String,Object> args=requestParams.getObjectMap();
+		return RespVOBuilder.ok(service.add(args));
+	}
+	
+	@RequestMapping("/delete")
+	public RespVO  delete(@NotBlank(message = "缺少AED记录编号") String aedId){
+		service.delete(aedId);
+		return RespVOBuilder.ok();
+	}
+	
+	@RequestMapping("/update")
+	public RespVO update(@NotBlank(message = "缺少AED记录编号") String aedId,RequestParams requestParams){
+		Map<String,Object> args=requestParams.getObjectMap();
+		Map<String,Object>  aed=MapUtils.trackMap(args, "aedId","note");  
+		service.update(aed);
+ 		return RespVOBuilder.ok();
+	}
+	
+	@RequestMapping("/get")
+	public RespVO  get(@NotBlank(message = "缺少AED记录编号") String aedId){
+		return RespVOBuilder.ok(service.get(aedId));
+	}
+	
+}

+ 26 - 0
src/main/java/com/jpsoft/proj/rescue/controller/MemberController.java

@@ -0,0 +1,26 @@
+package com.jpsoft.proj.rescue.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.proj.rescue.service.MemberService;
+import com.jpsoft.proj.utils.LayGridResp;
+import com.jpsoft.proj.utils.RequestParams;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+@RestController
+@RequestMapping("/**/member")
+@Validated
+public class MemberController {
+
+	@Autowired
+	private MemberService  service;
+	
+	@RequestMapping("/query")
+	public LayGridResp query(RequestParams requestParams,@RequestParam(value="page",defaultValue="1") Integer page,@RequestParam(value="limit",defaultValue="20") Integer limit){
+		return RespVOBuilder.getLayGridResp(service.query(requestParams,page, limit));
+	}
+}

+ 124 - 0
src/main/java/com/jpsoft/proj/rescue/controller/RescueConfigController.java

@@ -0,0 +1,124 @@
+package com.jpsoft.proj.rescue.controller;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.constraints.NotBlank;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.hibernate.validator.constraints.Range;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.TypeReference;
+import com.jpsoft.framework.util.SessionUser;
+import com.jpsoft.framework.util.SysConfigUtil;
+import com.jpsoft.proj.model.SortCodePO;
+import com.jpsoft.proj.rescue.service.RescueConfigService;
+import com.jpsoft.proj.sysdata.service.SortCodeService;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+@RestController
+@RequestMapping("/**/rescue")
+@Validated
+public class RescueConfigController {
+	
+	@Autowired
+	private RescueConfigService service;
+	
+	@Autowired
+	private SortCodeService codeService;
+
+	/**
+	 * 删除突发事件类别及相关救援类别配置
+	 * @param emergencySort
+	 * @return
+	 */
+	@PostMapping("/delEmergencySort")
+	public RespVO delEmergencySort(@NotBlank(message = "缺少突发事件类别参数") String emergencySort){
+		service.delEmergencySortAndRescues(emergencySort);
+		return RespVOBuilder.ok();
+	}
+	
+	/**
+	 * 增加突发事件类别、救援类别
+	 * @param superSortId
+	 * @param sortName
+	 * @return
+	 */
+	@PostMapping("/addSort")
+	public RespVO addSort(@NotBlank(message = "缺少父类别参数") String superSortId,@NotBlank(message = "缺少类别名称") String sortName){
+		SortCodePO code=new SortCodePO();
+		code.setFatherCodeId(superSortId);
+		code.setCodeName(sortName);
+		if(codeService.existsCode(superSortId,sortName)){
+			return RespVOBuilder.error("该类别已经存在");
+		}
+		return RespVOBuilder.ok(codeService.addSortCode(code));
+	}
+	
+	/**
+	 * 加载某一突发事件对应的救援配置
+	 * @param emergencySort
+	 * @return
+	 */
+	@PostMapping("/loadRescueConfig")
+	public RespVO loadRescueConfig(@NotBlank(message = "缺少突发事件类别参数") String emergencySort){
+		//String icon=service.getEmergencyIcon(emergencySort);
+		SortCodePO emergencyCode=service.getEmergencyCode(emergencySort);
+		if(StringUtils.isNotBlank(emergencyCode.getSpare1())){
+			emergencyCode.setSpare1(SysConfigUtil.getConfig("accessPath")+emergencyCode.getSpare1());
+		}
+		return RespVOBuilder.ok(MapUtils.builder("emergencyCode",emergencyCode,"configs",service.loadRescueConfig(emergencySort)));
+	}
+	
+	/**
+	 * 保存突发事件和救援的映射配置
+	 * @param su
+	 * @param emergencySort
+	 * @param rescueJson
+	 * @return
+	 */
+	@PostMapping("/saveRescueConfig")
+	public RespVO saveRescueConfig(SessionUser su,
+			@NotBlank(message = "缺少突发事件类别参数") String emergencySort,
+			String emergencyIcon, 
+			Double nearbyDistance, 
+			@Range(min=2,max=100,message = "无效的救援人数[2-100]") Integer volunteerCount,
+			@NotBlank(message = "缺少救援配置数据") String rescueJson){
+		//不存储全路径,界面显示时再拼接
+		if(StringUtils.isBlank(emergencyIcon)){
+			emergencyIcon="/upload/icon/def.png";
+		}
+		else{
+			emergencyIcon=emergencyIcon.substring(emergencyIcon.indexOf("/upload/icon"));
+		}
+		List<Map<String,Object>> rescueConfigs=JSON.parseObject(rescueJson,new TypeReference<List<Map<String,Object>>>() {});
+		service.saveRescueConfig(emergencySort,emergencyIcon,nearbyDistance,volunteerCount,rescueConfigs, su!=null?su.getUserName():"know");
+		return RespVOBuilder.ok();
+	}
+	
+	@PostMapping("/loadEmergencyIcon")
+	public RespVO loadEmergencyIcon(HttpServletRequest request){
+		String basePath=request.getSession().getServletContext().getRealPath("");
+		basePath=basePath+File.separator+"upload"+File.separator+"icon";
+		Iterator<File> fileIterator=FileUtils.iterateFiles(new File(basePath),new String[]{"png","jpeg","jpg"}, false);
+		List<String> imgs=new ArrayList<String>();
+		String accessPath=SysConfigUtil.getConfig("accessPath")+"/upload/icon/";
+		while(fileIterator.hasNext()){
+			imgs.add(accessPath+fileIterator.next().getName());
+		}
+		return RespVOBuilder.ok(imgs);
+	}
+}

+ 127 - 0
src/main/java/com/jpsoft/proj/rescue/controller/RescueController.java

@@ -0,0 +1,127 @@
+package com.jpsoft.proj.rescue.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.validation.constraints.NotBlank;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.util.SessionUser;
+import com.jpsoft.proj.model.RescueAcceptVO;
+import com.jpsoft.proj.model.RescueApplyVO;
+import com.jpsoft.proj.model.RescueFlowPO;
+import com.jpsoft.proj.rescue.service.RescueFlowService;
+import com.jpsoft.proj.rescue.service.RescueSeekerService;
+import com.jpsoft.proj.rescue.service.RescueService;
+import com.jpsoft.proj.rescue.service.RescueVolunteerService;
+import com.jpsoft.proj.utils.CodeConstants;
+import com.jpsoft.proj.utils.LayGridResp;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RequestParams;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+/**
+ * 后台web端 救援管理
+ */
+
+@RestController
+@RequestMapping("/**/rescue")
+@Validated
+public class RescueController {
+
+	@Autowired
+	private RescueSeekerService rescueSeekerService;
+	
+	@Autowired
+	private RescueVolunteerService  rescueVolunteerService;
+	
+	@Autowired
+	private RescueFlowService  flowService;
+	
+	@Autowired
+	private RescueService  rescueService;
+	
+	/**
+	 * 加载求助者发布的求助记录
+	 * @param requestParams
+	 * @param pageNum
+	 * @param pageSize
+	 * @return
+	 */
+	@RequestMapping("/queryMyPublishedRescue")
+	public LayGridResp queryMyPublishedRescue(RequestParams requestParams,@RequestParam(value="page",defaultValue="1") Integer pageNum,@RequestParam(value="limit",defaultValue="20") Integer pageSize){
+		PageModel<RescueApplyVO> pagedData=rescueSeekerService.queryMyPublishedRescue(requestParams, pageNum, pageSize);
+		if(pagedData!=null&&pagedData.getData()!=null){  //数据转换处理,流程步骤代码转名称
+			Map<String,String> stepMap=CodeConstants.loadRescueFlowStepMap();
+			for(RescueApplyVO itm : pagedData.getData()){
+				itm.setStepName(stepMap.get(itm.getStep()));
+			}
+		}
+		return RespVOBuilder.getLayGridResp(pagedData);
+	}
+	
+	/**
+	 * 求助明细(求助端)
+	 * @param rescueId
+	 * @return
+	 */
+	@RequestMapping("/getRescueDtl")
+	public RespVO getRescueDtl(@NotBlank(message = "求助记录编号【rescueId】不能为空") String rescueId){
+		RescueApplyVO rescue=rescueSeekerService.getPublishedRescue(rescueId);
+		if(rescue==null){
+			return RespVOBuilder.error("数据不存在,刷新后再试");
+		}
+		List<RescueFlowPO> flows=flowService.loadRescueFlow(rescueId);
+		List<Map<String,Object>> files=rescueService.loadRescueFiles(rescueId);
+		List<Map<String,Object>> joiners=rescueService.loadRescueJoiners(rescueId);
+		return RespVOBuilder.ok(MapUtils.builder("rescue",rescue,"flows",flows,"files",files,"joiners",joiners));
+	}
+	
+	/**
+	 * 结束求助记录
+	 * @param rescueId
+	 * @return
+	 */
+	@RequestMapping("/closeRescue")
+	public RespVO closeRescue(SessionUser  su,@NotBlank(message = "求助记录编号【rescueId】不能为空") String rescueId){
+		rescueSeekerService.updateForCloseRescue(rescueId,su!=null?su.getUserName():"unknow");
+		return RespVOBuilder.ok();
+	}
+	
+	/**
+	 * 加载我参与的救援记录
+	 * @param requestParams
+	 * @param pageNum
+	 * @param pageSize
+	 * @return
+	 */
+	@RequestMapping("/queryMyJoinedRescues")
+	public LayGridResp queryMyJoinedRescues(RequestParams requestParams,@RequestParam(value="page",defaultValue="1") Integer pageNum,@RequestParam(value="limit",defaultValue="20") Integer pageSize){
+		PageModel<RescueAcceptVO> pagedData=rescueVolunteerService.queryMyJoinedRescues(requestParams, pageNum, pageSize);
+		if(pagedData!=null&&pagedData.getData()!=null){  //数据转换处理,流程步骤代码转名称
+			Map<String,String> stepMap=CodeConstants.loadRescueFlowStepMap();
+			for(RescueAcceptVO itm : pagedData.getData()){
+				itm.setStepName(stepMap.get(itm.getStep()));
+			}
+		}
+		return RespVOBuilder.getLayGridResp(pagedData);
+	}
+	
+	/**
+	 * 结束志愿者个人的救援
+	 * @param rescueId
+	 * @return
+	 */
+	@RequestMapping("/closeVolunteerRescue")
+	public RespVO closeVolunteerRescue(SessionUser  su,@NotBlank(message = "志愿者编号【volunteerId】不能为空") String volunteerId,@NotBlank(message = "求助记录编号【rescueId】不能为空") String rescueId){
+		rescueVolunteerService.updateForCloseSelfRescue(volunteerId,rescueId,su!=null?su.getUserName():"unknow");
+		return RespVOBuilder.ok();
+	}
+}

+ 89 - 0
src/main/java/com/jpsoft/proj/rescue/service/AEDService.java

@@ -0,0 +1,89 @@
+package com.jpsoft.proj.rescue.service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.proj.utils.RequestParams;
+
+@Service
+public class AEDService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	public PageModel<Map<String,Object>> query(RequestParams args,int pageNo,int pageSize){
+		List<Object> sqlParams=new ArrayList<Object>();
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select d.*,date_format(modify_time,'%Y-%d-%d %H:%i:%s') modify_time_fmt from t_aed d");
+		sql.append(" where 1=1 ");
+		
+		//按地址模糊查询
+		if(StringUtils.isNoneEmpty(args.get("address"))){
+			sql.append(" and ( lower(short_address) like ? or lower(address) like ?)");
+			sqlParams.add("%"+args.get("address").toLowerCase()+"%");
+			sqlParams.add("%"+args.get("address").toLowerCase()+"%");
+		}
+		
+		if(StringUtils.isNoneEmpty(args.get("cityCode"))){
+			sql.append(" and city_code=?");
+			sqlParams.add(args.get("cityCode").trim());
+		}
+		
+		//通过地图可视区域过滤
+		if(StringUtils.isNoneEmpty(args.get("northEast"))){
+			String[] vals=args.get("northEast").split(",");
+			sql.append(" and longitude<=?  and latitude<=? ");
+			sqlParams.add(vals[0]);
+			sqlParams.add(vals[1]);
+		}
+		
+		if(StringUtils.isNoneEmpty(args.get("southWest"))){
+			String[] vals=args.get("southWest").split(",");
+			sql.append(" and longitude>=?  and latitude>=? ");
+			sqlParams.add(vals[0]);
+			sqlParams.add(vals[1]);
+		}
+		
+		sql.append(" order by modify_time desc");
+		Object[] sqlArgs=sqlParams.size()>0?sqlParams.toArray():null;
+		return dao.queryForPagedListMap(sql.toString(),pageNo, pageSize,sqlArgs);
+	}
+	
+	public Map<String,Object> get(String aedId){
+		return dao.queryForSingleMap("select * from t_aed where aed_id=?", aedId);
+	}
+	
+	
+	public String add(Map<String,Object>  newAED){
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		newAED.put("aedId",uuid.generate());
+		newAED.put("modifyTime", new Date());
+		dao.insert(newAED, "t_aed");
+		return (String)newAED.get("aedId");
+	}
+	
+	public void update(Map<String,Object>  aed){
+		aed.put("modifyTime", new Date());
+		dao.update(aed, "t_aed", "aed_id");
+	}
+	
+	public void delete(String aedId){
+		dao.getJdbcTemplate().update("delete from t_aed where aed_id=?", aedId);
+	}
+	
+	public boolean checkIfSameAED(String longitude,String latitude,double dis){
+		dis=dis<=0?50:dis;
+		String sql="select  distance from t_aed d where ST_Distance_sphere(point(d.longitud,d.latitude), point(?,?))<? ";
+		List<Map<String,Object>>  rst=dao.queryForListMap(sql, longitude,latitude,dis);
+		return rst!=null&&rst.size()>0;
+	}
+}

+ 104 - 0
src/main/java/com/jpsoft/proj/rescue/service/CallNearbyTask.java

@@ -0,0 +1,104 @@
+package com.jpsoft.proj.rescue.service;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.alibaba.fastjson.JSON;
+import com.jpsoft.framework.util.ApplicationContextUtils;
+import com.jpsoft.framework.util.DateUtil;
+import com.jpsoft.proj.model.MemberPO;
+import com.jpsoft.proj.model.RescueApplyVO;
+import com.jpsoft.proj.sms.service.SmsSender;
+import com.jpsoft.proj.sysdata.service.UsErrorService;
+
+public class CallNearbyTask implements Runnable {
+
+	public static Logger logger=LoggerFactory.getLogger(CallNearbyTask.class);
+	
+	private RescueApplyVO  rescueApply;
+	
+	public CallNearbyTask(RescueApplyVO apply){
+		this.rescueApply=apply;
+	}
+			
+	@Override
+	public void run() {
+		if(rescueApply==null){
+			logger.warn("未提供突发事件求助申请数据,取消召集");
+			return;
+		}
+		logger.debug("发生[{}],开始通知附近人",rescueApply.getEmergencyName());
+		Double nearbyDis=rescueApply.getNearbyDistance();
+		if(nearbyDis==null||nearbyDis.doubleValue()==0){
+			logger.warn("未设置附近通知距离,取消通知");
+			return;
+		}
+		RescueSeekerService service=ApplicationContextUtils.getBean("rescueSeekerService",RescueSeekerService.class);
+		List<MemberPO> noticeMembers=service.loadNearbyMembers(rescueApply.getLat(), rescueApply.getLon(), nearbyDis);
+		if(noticeMembers==null||noticeMembers.size()==0){
+			logger.warn("未找到需要通知的附近用户,取消通知");
+			return;
+		}
+		startNotice(noticeMembers,rescueApply.getPublisherPhone());
+	    logger.debug("结束附近人通知");
+	}
+	
+	
+	private void startNotice(List<MemberPO> members,String excludePhone){
+		StringBuilder phones=new StringBuilder(members.size()*12);
+		for(MemberPO m : members){
+			if(StringUtils.isBlank(m.getPhone())||excludePhone.equals(m.getPhone())){  //排除求助者自身的短信发送
+				continue;
+			}
+			phones.append(","+m.getPhone());
+		}
+		if(phones.length()==0){
+			logger.debug("未找到有效的手机号,取消通知");
+			return;
+		}
+		
+		logger.debug(String.format("求助【%s】通知附近用户:%s", rescueApply.getRescueId(),phones.substring(1)));
+		
+		rptNoticeErrors(SmsSender.sendForNearbyNotice(phones.substring(1), getSmsParams()));
+	}
+	
+	
+	/**
+	 * 上报短信发送失败信息(主要是欠费,账号问题等,其它错误信息查看日志)
+	 * @param smsRsts
+	 */
+	private void rptNoticeErrors(List<String> smsRsts){
+		if(smsRsts==null||smsRsts.size()==0){
+			return;
+		}
+		String  code=null;
+		StringBuilder errors=new StringBuilder();
+		for(String rst : smsRsts){
+			code=(String)(JSON.parseObject(rst, Map.class)).get("code");
+			if(SmsSender.errorCodeMap.containsKey(code)){
+				errors.append("求助Id【"+rescueApply.getRescueId()+"】发送短信到附近人时,错误码【"+code+"】"+SmsSender.errorCodeMap.get(code)+";");
+			}
+		}
+		
+		if(errors.length()==0){
+			return;
+		}
+		
+		UsErrorService errorService=ApplicationContextUtils.getBean("usErrorService",UsErrorService.class);
+		errorService.addUsError("后台短信发送模块", errors.toString(),"sms");
+	}
+	
+	private String getSmsParams(){
+		String address=rescueApply.getShortAddress();
+		address=StringUtils.isBlank(address)?"本市":address.substring(0, Math.min(25, address.length())); //限定地址字符数,避免短信内容超过70字符
+		String emergency=rescueApply.getEmergencyName();
+		emergency=StringUtils.isBlank(emergency)?"紧急事故":emergency.substring(0, Math.min(8, emergency.length()));
+		String timestr=DateUtil.format(rescueApply.getPublishTime(), "HH:mm");
+		return String.format("[\"%s\",\"%s\",\"%s\"]", address,timestr,emergency);
+	}
+
+}

+ 36 - 0
src/main/java/com/jpsoft/proj/rescue/service/CallVolunteerExecutor.java

@@ -0,0 +1,36 @@
+package com.jpsoft.proj.rescue.service;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 召集志愿者服务组件
+ * @author cwen
+ *
+ */
+public class CallVolunteerExecutor {
+	
+	public static Logger logger=LoggerFactory.getLogger(CallVolunteerExecutor.class);
+
+	private static final ThreadPoolExecutor  executor=new ThreadPoolExecutor(2, 4,
+            10L, TimeUnit.SECONDS,
+            new LinkedBlockingQueue<Runnable>());
+	
+	
+	static{
+		executor.prestartCoreThread();  // 预启动所有核心线程
+	}
+	
+	public static void execute(Runnable task){
+		executor.execute(task);
+	}
+	
+	public static void shutdown(){
+		executor.shutdown();
+		logger.debug("close the CallVolunteerExecutor");
+	}
+}

+ 114 - 0
src/main/java/com/jpsoft/proj/rescue/service/CallVolunteerTask.java

@@ -0,0 +1,114 @@
+package com.jpsoft.proj.rescue.service;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.alibaba.fastjson.JSON;
+import com.jpsoft.framework.util.ApplicationContextUtils;
+import com.jpsoft.framework.util.DateUtil;
+import com.jpsoft.proj.model.MatchedVolunteer;
+import com.jpsoft.proj.model.RescueApplyVO;
+import com.jpsoft.proj.sms.service.SmsSender;
+import com.jpsoft.proj.sysdata.service.UsErrorService;
+
+public class CallVolunteerTask implements Runnable {
+	
+	public static Logger logger=LoggerFactory.getLogger(CallVolunteerTask.class);
+	
+	private RescueApplyVO  rescueApply;
+	
+	public CallVolunteerTask(RescueApplyVO apply){
+		this.rescueApply=apply;
+	}
+
+	/**
+	 * 通过短信方式召集愿者参与
+	 */
+	@Override
+	public void run() {
+		if(rescueApply==null){
+			logger.warn("未提供突发事件求助申请数据,取消召集");
+			return;
+		}
+		logger.debug("开始召集志愿者参与,发布人:[{}]",rescueApply.getPublisher());
+		
+		//发送短信召集,能力匹配且无任务
+		RescueVolunteerService service=ApplicationContextUtils.getBean("rescueVolunteerService",RescueVolunteerService.class);
+		List<MatchedVolunteer> volunteers=service.findMatchedVolunteer(rescueApply);
+		if(volunteers==null||volunteers.size()==0){
+			logger.warn("未召集到匹配的志愿者");
+			return;
+		}
+		startNotice(volunteers);
+		
+		//更新呼救次数、最后呼救时间
+		RescueSeekerService service2=ApplicationContextUtils.getBean("rescueSeekerService",RescueSeekerService.class);
+		service2.updateForRecall(rescueApply.getRescueId(), rescueApply.getCallCount()+1, rescueApply.getPublisherName());
+		
+		//增加召集记录
+		service.addCallLog(volunteers, rescueApply);
+	    logger.debug("结束召集志愿者");
+	}
+	
+	private void startNotice(List<MatchedVolunteer> volunteers){
+		StringBuilder phones=new StringBuilder(volunteers.size()*12);
+		for(MatchedVolunteer v : volunteers){
+			if(StringUtils.isBlank(v.getPhone())){
+				continue;
+			}
+			phones.append(","+v.getPhone());
+		}
+		if(phones.length()==0){
+			logger.debug("未召集到有效的手机号,取消通知");
+			return;
+		}
+		
+		logger.debug(String.format("求助【%s】召集到志愿者:%s", rescueApply.getRescueId(),phones.substring(1)));
+		
+		rptNoticeErrors(SmsSender.sendForNotice(phones.substring(1), getSmsParams()));
+	}
+	
+	
+	/**
+	 * 上报短信发送失败信息(主要是欠费,账号问题等,其它错误信息查看日志)
+	 * @param smsRsts
+	 */
+	private void rptNoticeErrors(List<String> smsRsts){
+		if(smsRsts==null||smsRsts.size()==0){
+			return;
+		}
+		String  code=null;
+		StringBuilder errors=new StringBuilder();
+		for(String rst : smsRsts){
+			code=(String)(JSON.parseObject(rst, Map.class)).get("code");
+			if(SmsSender.errorCodeMap.containsKey(code)){
+				errors.append("求助Id【"+rescueApply.getRescueId()+"】发送短信时,错误码【"+code+"】"+SmsSender.errorCodeMap.get(code)+";");
+			}
+		}
+		
+		if(errors.length()==0){
+			return;
+		}
+		
+		UsErrorService errorService=ApplicationContextUtils.getBean("usErrorService",UsErrorService.class);
+		errorService.addUsError("后台短信发送模块", errors.toString(),"sms");
+	}
+	
+	private String getSmsParams(){
+		String address=rescueApply.getShortAddress();
+		address=StringUtils.isBlank(address)?"本市":address.substring(0, Math.min(25, address.length())); //限定地址字符数,避免短信内容超过70字符
+		String emergency=rescueApply.getEmergencyName();
+		emergency=StringUtils.isBlank(emergency)?"紧急事故":emergency.substring(0, Math.min(8, emergency.length()));
+		String timestr=DateUtil.format(rescueApply.getPublishTime(), "HH:mm");
+		return String.format("[\"%s\",\"%s\",\"%s\"]", address,timestr,emergency);
+	}
+	
+	/* public static void main(String[] args) throws Exception {
+	    	System.out.println(String.format("[\"%s\",\"%s\",\"%s\"]", "荆州","10:30","紧急事故"));
+	 }*/
+
+}

+ 71 - 0
src/main/java/com/jpsoft/proj/rescue/service/MemberService.java

@@ -0,0 +1,71 @@
+package com.jpsoft.proj.rescue.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.proj.model.MemberPO;
+import com.jpsoft.proj.model.MemberVO;
+import com.jpsoft.proj.utils.RequestParams;
+
+@Service
+public class MemberService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	public PageModel<Map<String,Object>> query(RequestParams args,int pageNo,int pageSize){
+		List<Object> sqlParams=new ArrayList<Object>();
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select * from t_member");
+		sql.append(" where 1=1 ");
+		
+		//按姓名、拼音模糊查询
+		if(StringUtils.isNoneEmpty(args.get("member"))){
+			sql.append(" and ( lower(real_name) like ? or lower(name_py) like ?)");
+			sqlParams.add("%"+args.get("member").toLowerCase()+"%");
+			sqlParams.add("%"+args.get("member").toLowerCase()+"%");
+		}
+		
+		
+		sql.append(" order by real_name");
+		Object[] sqlArgs=sqlParams.size()>0?sqlParams.toArray():null;
+		return dao.queryForPagedListMap(sql.toString(),pageNo, pageSize,sqlArgs);
+	}
+	
+	public Map<String,Object> get(String userId){
+		return dao.queryForSingleMap("select * from t_member where member_id=?", userId);
+	}
+	
+	
+	public String addMember(MemberPO member){
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		member.setMemberId(uuid.generate());
+		dao.insertPojo(member, "t_member");
+		return member.getMemberId();
+	}
+	
+	public void updateMemberByOpenid(MemberPO member){
+		dao.updatePojo(member, "t_member", "openid");
+	}
+	
+	public MemberPO  getByPhone(String phone){
+		return dao.queryForObject("select * from t_member where phone=?", MemberPO.class, phone);
+	}
+	
+	public MemberVO  getByOpenid(String openid){
+		return dao.queryForObject("select m.* from t_member m  where m.openid=?", MemberVO.class, openid);
+	}
+	
+	public void delForUnregister(String openid){
+		dao.getJdbcTemplate().update("delete from t_member where openid=?", openid);
+	}
+	
+}

+ 77 - 0
src/main/java/com/jpsoft/proj/rescue/service/RescueConfigService.java

@@ -0,0 +1,77 @@
+package com.jpsoft.proj.rescue.service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.proj.model.SortCodePO;
+import com.jpsoft.proj.utils.CodeConstants;
+
+@Service
+public class RescueConfigService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	public List<Map<String,Object>> loadEmergencySort(){
+		return dao.queryForListMap("select code_name emergency_name,code_id emergency_sort,spare1 icon from t_sort_code where father_code_id=?",CodeConstants.EMERGENCY_SORT);
+	}
+	
+	public Set<String> loadSceneDescSort(){
+		Map<String,Object> rst=dao.queryForMapping("select code_name,code_id from t_sort_code where father_code_id=?","code_name","code_id",CodeConstants.SCENE_DESC_SORT);
+	    return rst!=null&&rst.size()>0?rst.keySet():null;
+	}
+	
+	public void delEmergencySortAndRescues(String emergencySort){
+		//先从编码表中删除突发事件类别
+		String sql="delete from t_sort_code where code_id=?";
+		dao.getJdbcTemplate().update(sql, emergencySort);
+		//再从事件---救援类别映射表中删除相关救援配置
+		sql="delete from t_emergency_rescue_mapping where emergency_sort=?";
+		dao.getJdbcTemplate().update(sql, emergencySort);
+	}
+	
+	public List<Map<String,Object>> loadRescueConfig(String emergencySort){
+		String sql="select * from t_emergency_rescue_mapping where emergency_sort=?";
+		return dao.queryForListMap(sql, emergencySort);
+	}
+	
+	public String getEmergencyIcon(String emergencySort){
+		Map<String,Object> data=dao.queryForSingleMap("select spare1 from t_sort_code  where code_id=?", emergencySort);
+		return data!=null?(String)data.get("spare1"):null;
+	}
+	
+	public SortCodePO getEmergencyCode(String emergencySort){
+		return dao.queryForObject("select * from t_sort_code  where code_id=?", SortCodePO.class, emergencySort);
+	}
+	
+	
+	public void saveRescueConfig(String emergencySort,String emergencyIcon,Double nearbyDistance,Integer volunteerCount,List<Map<String,Object>> rescueConfigs,String caller){
+		if(StringUtils.isEmpty(emergencySort)||rescueConfigs.size()==0){
+			return ;
+		}
+		String sql="delete from t_emergency_rescue_mapping where emergency_sort=?";
+		dao.getJdbcTemplate().update(sql, emergencySort);
+		sql="insert into t_emergency_rescue_mapping(record_id,emergency_sort,rescue_sort,person_amount,equipments,modifier,modify_time) values(?,?,?,?,?,?,?)";
+	    List<Object[]> batchArgs=new ArrayList<Object[]>(rescueConfigs.size());
+	    UUIDHexGenerator uuid=UUIDHexGenerator.getInstance();
+	    for(Map<String,Object> itm : rescueConfigs){
+	    	batchArgs.add(new Object[]{uuid.generate(),emergencySort,itm.get("rescueSort"),itm.get("personAmount"),itm.get("equipments"),caller,new Date()});
+	    }
+	    dao.getJdbcTemplate().batchUpdate(sql, batchArgs);
+	    
+	    
+	    //设置突发事件类别图标,微信小程序端使用
+	    dao.getJdbcTemplate().update("update t_sort_code set spare1=?,spare2=?,spare3=? where code_id=?", emergencyIcon,volunteerCount,nearbyDistance,emergencySort);
+	
+	}
+	
+}

+ 25 - 0
src/main/java/com/jpsoft/proj/rescue/service/RescueEquipmentService.java

@@ -0,0 +1,25 @@
+package com.jpsoft.proj.rescue.service;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+
+@Service
+public class RescueEquipmentService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	public List<Map<String,Object>> loadSortEquipments(String sort,int count){
+		StringBuilder sql=new StringBuilder();
+		sql.append("select * ");
+		sql.append(" from t_equipment");
+		sql.append(" where equ_sort=?");
+		sql.append(" order by display_num  limit ?");
+		return dao.queryForListMap(sql.toString(), sort,count);
+	}
+}

+ 57 - 0
src/main/java/com/jpsoft/proj/rescue/service/RescueFlowService.java

@@ -0,0 +1,57 @@
+package com.jpsoft.proj.rescue.service;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.proj.model.RescueFlowPO;
+
+@Service
+public class RescueFlowService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	public void addFlow(String step,String rescueApplyId,String caller){
+		addFlow(step,null,null,rescueApplyId,caller);
+	}
+	
+	public void addFlow(String step,String note,String rescueApplyId,String caller){
+		addFlow(step,note,null,rescueApplyId,caller);
+	}
+	
+	public void addFlow(String step,String note,String dataId,String rescueApplyId,String caller){
+		UUIDHexGenerator uuid=UUIDHexGenerator.getInstance();
+		Map<String,Object> flow=new HashMap<String,Object>();
+		flow.put("flowId", uuid.generate());
+		flow.put("step", step);
+		flow.put("createTime", new Date());
+		flow.put("creator", caller);
+		flow.put("rescueId", rescueApplyId);
+		flow.put("dataId", dataId);
+		flow.put("note", note);
+		dao.insert(flow, "t_rescue_workflow");
+		
+		//更新求救申请的最新流程未当前流程步骤
+		String sql="update t_rescue_apply set flow_id=? where rescue_id=?";
+		dao.getJdbcTemplate().update(sql,flow.get("flowId"),rescueApplyId);
+	}
+	
+	public Map<String,Object> get(String flowId){
+		if(StringUtils.isBlank(flowId)){
+			return null;
+		}
+		return dao.queryForSingleMap("select * from t_rescue_workflow where flow_id=?", flowId);
+	}
+	
+	public List<RescueFlowPO> loadRescueFlow(String rescueId){
+		return dao.queryForListPojo("select * from t_rescue_workflow where rescue_id=? order by create_time desc", RescueFlowPO.class, rescueId);
+	}
+}

+ 228 - 0
src/main/java/com/jpsoft/proj/rescue/service/RescueSeekerService.java

@@ -0,0 +1,228 @@
+package com.jpsoft.proj.rescue.service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.proj.model.MemberPO;
+import com.jpsoft.proj.model.RescueApplyPO;
+import com.jpsoft.proj.model.RescueApplyVO;
+import com.jpsoft.proj.model.RescueFlow;
+import com.jpsoft.proj.utils.RequestParams;
+
+@Service
+public class RescueSeekerService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	@Autowired
+	private RescueFlowService flowService;
+	
+	/**
+	 * 关闭求救记录
+	 * @param rescueId
+	 */
+	public void updateForCloseRescue(String rescueId,String caller){
+		flowService.addFlow(RescueFlow.COMPLETE.getCode(),"结束求救",rescueId, caller);
+		//求救被发布人关闭后,参与者的任务自动结束
+		dao.getJdbcTemplate().update("update t_rescue_apply_accept set finish_time=now(),note='求救关闭任务自动结束' where rescue_id=? and accept_if=1", rescueId);
+	}
+	
+	/**
+	 * 查找指定坐标附近的用户(求救者,包含了求救发布者)
+	 * @param latitude
+	 * @param longitude
+	 * @param distance
+	 * @return
+	 */
+	public List<MemberPO> loadNearbyMembers(String latitude,String longitude,Double distance){
+		if(distance==null){  //没有距离范围就不查找
+			return null;
+		}
+		StringBuilder sql=new StringBuilder(200);
+		sql.append("select member_id,real_name,phone ");
+		sql.append(" from t_member  ");
+		sql.append(" where ST_Distance_sphere(point(lon,lat), point(?,?)) <? ");
+		return dao.queryForListPojo(sql.toString(), MemberPO.class, longitude,latitude,distance);
+	}
+	
+	/**
+	 * 获取单个已发布的求救记录
+	 * @param rescueId
+	 * @return
+	 */
+	public RescueApplyVO  getPublishedRescue(String rescueId){
+		StringBuilder sql=new StringBuilder(300);
+		sql.append("select r.*,wf.step,cast(d.spare3 as decimal) nearby_distance,cast(d.spare2 as SIGNED) need_volunteer_count ,d.code_name emergency_name,");
+		sql.append(" m.real_name publisher_name,m.phone publisher_phone,");
+		sql.append(" (select count(1) from t_rescue_apply_accept rac where rac.accept_if=1 and rac.rescue_id=r.rescue_id) accept_count ");
+		sql.append(" from t_rescue_apply r");
+		sql.append(" left join t_member m  on r.publisher=m.member_id");
+		sql.append(" left join t_rescue_workflow wf on r.flow_id=wf.flow_id");
+		sql.append(" left join t_sort_code d on r.emergency_sort=d.code_id");
+		sql.append(" where r.rescue_id=? ");
+		return dao.queryForObject(sql.toString(), RescueApplyVO.class,rescueId);
+	}
+	
+	/**
+	 * 发布求救信息
+	 * @param apply
+	 * @param fileIds
+	 * @param caller
+	 * @return
+	 */
+	public String createRescuePublish(RescueApplyPO  apply,String fileIds,String caller){
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		apply.setRescueId(uuid.generate());
+		apply.setPublishTime(new Date());
+		//apply.setLastCallTime(new Date());  //在实际发送通知时进行更新
+		apply.setCallCount(0);
+		dao.insertPojo(apply, "t_rescue_apply");
+		
+		if(StringUtils.isNotBlank(fileIds)){
+			fileIds="'"+fileIds.replaceAll(",", "','")+"'";
+			String sql="update t_rescue_file set rescue_id=? where file_id in ("+fileIds+")";
+			dao.getJdbcTemplate().update(sql, apply.getRescueId());
+		}
+		
+		flowService.addFlow(RescueFlow.PUBLISHED.getCode(),"发布了求救信息", apply.getRescueId(), caller);
+		
+		return apply.getRescueId();
+		
+	}
+	
+	/**
+	 * 修改求救记录
+	 * @param rescueId
+	 * @param caller
+	 */
+	public String updateRescue(RescueApplyPO  apply,String fileIds){
+		RescueApplyVO oldApply=dao.queryForObject("select r.*,wf.step from t_rescue_apply r left join t_rescue_workflow wf on r.flow_id=wf.flow_id where r.rescue_id=?", RescueApplyVO.class, apply.getRescueId());
+		if(oldApply==null||oldApply.getStep().equals(RescueFlow.COMPLETE.getCode())){ //记录不存在或已结束,不继续处理了
+			return "求助记录不存在或已结束";
+		}
+		
+		//非前台字段内容要重新设置避免冲掉
+		apply.setPublishTime(oldApply.getPublishTime());
+		apply.setFlowId(oldApply.getFlowId());
+		apply.setLastCallTime(oldApply.getLastCallTime());
+		apply.setCallCount(oldApply.getCallCount());
+		
+		
+		dao.updatePojo(apply, "t_rescue_apply", "rescue_id");
+		
+		//更新后删除的文件已经在删除操作中处理了
+		if(StringUtils.isNotBlank(fileIds)){
+			fileIds="'"+fileIds.replaceAll(",", "','")+"'";
+			String sql="update t_rescue_file set rescue_id=? where file_id in ("+fileIds+")";
+			dao.getJdbcTemplate().update(sql, apply.getRescueId());
+		}
+		
+		return null;
+		
+	}
+	
+	//重新呼救后更新相关字段,并增加流程
+	public void updateForRecall(String rescueId,int callCount,String caller){
+		if(StringUtils.isBlank(rescueId)){
+			return;
+		}
+		dao.getJdbcTemplate().update("update t_rescue_apply set call_count=?,last_call_time=now() where rescue_id=?", callCount,rescueId);
+		
+		flowService.addFlow(RescueFlow.RECALL.getCode(),"呼救志愿者",rescueId,caller);
+	}
+	
+	/**
+	 * 查询求救记录(web端,求救者微信端)
+	 * @param args
+	 * @param pageNo
+	 * @param pageSize
+	 * @return
+	 */
+	public PageModel<RescueApplyVO> queryMyPublishedRescue(RequestParams args,int pageNo,int pageSize){
+		List<Object> sqlParams=new ArrayList<Object>();
+		StringBuilder sql=new StringBuilder(300);
+		sql.append("select r.*,wf.step,d.code_name emergency_name,accept_count,m.real_name publisher_name,m.phone  publisher_phone");
+		sql.append(" from t_rescue_apply r");
+		sql.append(" left join t_member m  on r.publisher=m.member_id");
+		sql.append(" left join t_rescue_workflow wf on r.flow_id=wf.flow_id");
+		sql.append(" left join t_sort_code d on r.emergency_sort=d.code_id");
+		sql.append(" left join (select rescue_id,count(1) accept_count from t_rescue_apply_accept group by rescue_id) apt on r.rescue_id=apt.rescue_id");
+		sql.append(" where 1=1 ");
+		
+		//按发布人姓名、手机号
+		if(!StringUtils.isBlank(args.get("publisher"))){
+			sql.append(" and (real_name like ? or phone like ?) ");
+			sqlParams.add("%"+args.get("publisher")+"%");
+			sqlParams.add("%"+args.get("publisher")+"%");
+		}
+		
+		//指定具体求救者
+		if(!StringUtils.isBlank(args.get("publisherId"))){
+			sql.append(" and r.publisher=?");
+			sqlParams.add(args.get("publisherId"));
+		}
+		
+		//按发布时间
+		if(StringUtils.isNoneBlank(args.get("publishStart"))){
+			sql.append(" and publish_time>=?");
+			sqlParams.add(args.get("publishStart"));
+		}
+		if(StringUtils.isNoneBlank(args.get("publishEnd"))){
+			sql.append(" and publish_time<=?");
+			sqlParams.add(args.get("publishEnd"));
+		}
+		
+		sql.append(" order by publish_time desc");
+		Object[] sqlArgs=sqlParams.size()>0?sqlParams.toArray():null;
+		return dao.queryForPagedListPojo(sql.toString(),pageNo, pageSize,RescueApplyVO.class,sqlArgs);
+	}
+	
+	
+	/**
+	 * 增加求救上传文件信息
+	 * @param fileInfo
+	 * @return
+	 */
+	public String addSeekRescueFile(Map<String,Object> fileInfo){
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		fileInfo.put("createTime", new Date());
+		fileInfo.put("fileId", uuid.generate());
+		dao.insert(fileInfo, "t_rescue_file");
+		return (String)fileInfo.get("fileId");
+	}
+	
+	/**
+	 * 删除指定求救文件信息
+	 * @param fileId
+	 */
+	public void delRescueFile(String fileId){
+		dao.getJdbcTemplate().update("delete  from t_rescue_file where file_id=?",fileId);
+	}
+	
+	
+	/**
+	 * 统计我发布的求救
+	 * @param seekerId
+	 * @return
+	 */
+	public Map<String,Object>  rptMyRescue(String seekerId){
+		StringBuilder sql=new StringBuilder(200);
+		sql.append("select count(1) rescue_total,sum(case when wf.step='rescuing' then 1 else 0 end) rescue_running,");
+		sql.append(" sum(case when wf.step='complete' then 1 else 0 end) rescue_complete ");
+		sql.append(" from t_rescue_apply r ");
+		sql.append(" left join t_rescue_workflow wf on r.flow_id=wf.flow_id ");
+		sql.append(" where publisher=? ");
+		
+		return dao.queryForSingleMap(sql.toString(), seekerId);
+	}
+}

+ 87 - 0
src/main/java/com/jpsoft/proj/rescue/service/RescueService.java

@@ -0,0 +1,87 @@
+package com.jpsoft.proj.rescue.service;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.proj.model.RescueApplyVO;
+/**
+ * 救援通用服务
+ * @author cwen
+ *
+ */
+@Service
+public class RescueService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	
+	
+	/**
+	 * 加载救援明细记录(求助信息、我的参与信息(其它参与者信息不加载))
+	 * @param volunteerId
+	 * @param rescueId
+	 * @return
+	 */
+	/*public Map<String,Object> getMyJoinRescueDtl2(String volunteerId,String rescueId){
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select r.*,ra.accept_time");
+		sql.append(" from t_rescue_apply_accept  ra inner join t_rescue_apply r  on ra.rescue_id=r.rescue_id");
+		sql.append(" where ra.volunteer_id=? and ra.rescue_id=?");
+		return dao.queryForSingleMap(sql.toString(), volunteerId,rescueId);
+	}*/
+	
+	
+	
+	
+	
+	
+	
+	
+	
+	/**
+	 * 加载求助现场文件
+	 * @param rescueId
+	 * @return
+	 */
+	public List<Map<String,Object>> loadRescueFiles(String rescueId){
+		return dao.queryForListMap("select * from t_rescue_file where rescue_id=?", rescueId);
+	}
+	
+	/**
+	 * 获取指定求助参与者
+	 * @param rescueId
+	 * @return
+	 */
+	public List<Map<String,Object>> loadRescueJoiners(String rescueId){
+		StringBuilder sql=new StringBuilder(200);
+		sql.append("select v.real_name,v.profession from t_rescue_apply_accept  ac ");
+		sql.append(" inner join t_volunteer v on ac.volunteer_id=v.volunteer_id ");
+		sql.append(" where ac.rescue_id=?");
+		return dao.queryForListMap(sql.toString(),rescueId);
+	}
+	
+	/**
+	 * 获得指定求助文件信息
+	 * @param fileId
+	 * @return
+	 */
+	public Map<String,Object>  getRescueFile(String fileId){
+		return dao.queryForSingleMap("select * from t_rescue_file where file_id=?", fileId);
+	}
+	
+	public RescueApplyVO  getRescue(String rescueId){
+		StringBuilder sql=new StringBuilder(300);
+		sql.append("select r.*,d.code_name emergency_name,m.real_name publisher_name,m.phone publisher_phone");
+		sql.append(" from t_rescue_apply r");
+		sql.append(" left join t_member m  on r.publisher=m.member_id");
+		sql.append(" left join t_sort_code d on r.emergency_sort=d.code_id");
+		sql.append(" where r.rescue_id=? ");
+		return dao.queryForObject(sql.toString(), RescueApplyVO.class,rescueId);
+	}
+	
+}

+ 327 - 0
src/main/java/com/jpsoft/proj/rescue/service/RescueVolunteerService.java

@@ -0,0 +1,327 @@
+package com.jpsoft.proj.rescue.service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.framework.util.DateUtil;
+import com.jpsoft.proj.model.MatchedVolunteer;
+import com.jpsoft.proj.model.RescueAcceptPO;
+import com.jpsoft.proj.model.RescueAcceptVO;
+import com.jpsoft.proj.model.RescueApplyPO;
+import com.jpsoft.proj.model.RescueApplyVO;
+import com.jpsoft.proj.model.RescueFlow;
+import com.jpsoft.proj.utils.RequestParams;
+
+@Service
+public class RescueVolunteerService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	@Autowired
+	private RescueFlowService flowService;
+	
+	
+	/**
+	 * 结束志愿者自己的救援
+	 * @param rescueId
+	 */
+	public void updateForCloseSelfRescue(String volunteerId,String rescueId,String caller){
+		flowService.addFlow(RescueFlow.RESCUING.getCode(),"结束自己的救援",rescueId, caller);
+		dao.getJdbcTemplate().update("update t_rescue_apply_accept set finish_time=now(),note='结束自己的救援' where rescue_id=? and volunteer_id=? and accept_if=1", rescueId,volunteerId);
+	}
+	
+	
+	
+	/**
+	 * 接受或忽略求助申请(acceptIf)
+	 * @param accept
+	 * @return
+	 */
+	public String addRescueApplyAccept(RescueAcceptPO accept){
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		accept.setAcceptId(uuid.generate());
+		accept.setAcceptTime(new Date());
+		dao.insertPojo(accept, "t_rescue_apply_accept");
+		return accept.getAcceptId();
+	}
+	
+	public void updateRescueApplyAccept(RescueAcceptPO accept){
+		dao.updatePojo(accept, "t_rescue_apply_accept", "accept_id");
+	}
+	
+
+	/**
+	 * 加载我能参与的求助信息(按时间倒序,能力匹配,未完结的求助,未忽略的(未参与或正参与))
+	 * 当已经有足够志愿者参与时,是否还显示?
+	 * @param pageNo
+	 * @param pageSize
+	 * @return
+	 */
+	public PageModel<RescueApplyVO> loadCanJoinRescueApply(String volunteerId,String longitude,String latitude,int pageNum,int pageSize,String sortField,String sortAction){
+		StringBuilder sql=new StringBuilder();
+		sql.append("select m.real_name publisher_name,m.phone  publisher_phone,d.code_name  emergency_name,ac.accept_if,rap.*, ");
+		sql.append(" ST_Distance_sphere(point(rap.lon,rap.lat), point(?,?)) distance");
+		sql.append(" from t_rescue_apply  rap ");
+		sql.append(" left join t_member m on rap.publisher=m.member_id");
+		sql.append(" left join t_sort_code d on rap.emergency_sort=d.code_id");
+		sql.append(" left join t_rescue_workflow rwf on rap.flow_id=rwf.flow_id");
+		sql.append(" left join t_rescue_apply_accept ac on ac.rescue_id=rap.rescue_id and ac.volunteer_id=?");
+		sql.append(" where rwf.step!=?");
+		sql.append(" and (ac.accept_id is null or (ac.accept_if=1 and ac.finish_time is null))"); //未忽略的(未参与或正参与)
+		//能力匹配
+		sql.append(" and exists(select volunteer_id from t_volunteer  vter inner join t_emergency_rescue_mapping erm on instr(vter.rescue_sort,erm.rescue_sort)>0");
+		sql.append(" where erm.emergency_sort=rap.emergency_sort  and vter.volunteer_id=?");
+		sql.append(" )");
+		
+		if(StringUtils.isNoneBlank(sortField)){
+			sortField=sortField.equalsIgnoreCase("time")?"publish_time":"distance";
+			if(StringUtils.isNoneBlank(sortAction)){
+				sortAction=sortAction.equalsIgnoreCase("desc")?"desc":"asc";
+			}
+			else{
+				sortAction=sortField.equals("publish_time")?"desc":"asc";
+			}
+			
+			sql.append(" order by "+sortField+" "+sortAction);
+		}
+		else{
+			sql.append(" order by distance");
+		}
+		
+		
+		return dao.queryForPagedListPojo(sql.toString(), pageNum, pageSize,RescueApplyVO.class,longitude,latitude,volunteerId,RescueFlow.COMPLETE.getCode(),volunteerId);
+	}
+	
+	
+	/**
+	 * 获取单个求助记录(用于志愿者端参与)
+	 * @param rescueId
+	 * @return
+	 */
+	public RescueApplyVO  getRescueApplyForJoin(String rescueId,String longitude,String latitude){
+		StringBuilder sql=new StringBuilder(300);
+		sql.append("select m.real_name publisher_name,m.phone  publisher_phone,d.code_name  emergency_name,r.*, ");
+		sql.append(" wf.step,(select count(1) from t_rescue_apply_accept rac where rac.accept_if=1 and rac.rescue_id=r.rescue_id) accept_count,");
+		sql.append(" ST_Distance_sphere(point(r.lon,r.lat), point(?,?)) distance");
+		sql.append(" from t_rescue_apply r");
+		sql.append(" left join t_member m  on r.publisher=m.member_id");
+		sql.append(" left join t_rescue_workflow wf on r.flow_id=wf.flow_id");
+		sql.append(" left join t_sort_code d on r.emergency_sort=d.code_id");
+		//sql.append(" left join (select rescue_id,count(1) accept_count from t_rescue_apply_accept where accept_if=1 group by rescue_id) apt on r.rescue_id=apt.rescue_id");
+		sql.append(" where r.rescue_id=? ");
+		return dao.queryForObject(sql.toString(), RescueApplyVO.class,longitude,latitude,rescueId);
+	}
+	
+	/**
+	 * 查询志愿者参与的救援记录(移动端)救援状态以个人参与状态显示
+	 * @param volunteerId
+	 * @param args
+	 * @param pageNo
+	 * @param pageSize
+	 * @return
+	 */
+	public PageModel<RescueAcceptVO> queryMyJoinedRescues(RequestParams args,int pageNo,int pageSize){
+		List<Object> sqlParams=new ArrayList<Object>();
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select r.*,ra.volunteer_id,ra.finish_time,ra.accept_time,v.real_name volunteer_name,wf.step,");
+		sql.append(" m.real_name publisher_name,m.phone  publisher_phone,d.code_name  emergency_name");
+		sql.append(" from t_rescue_apply_accept  ra inner join t_rescue_apply r  on ra.rescue_id=r.rescue_id");
+		sql.append(" left join t_member m  on r.publisher=m.member_id");
+		sql.append(" left join t_volunteer v  on ra.volunteer_id=v.volunteer_id");
+		sql.append(" left join t_sort_code d on r.emergency_sort=d.code_id");
+		sql.append(" left join t_rescue_workflow wf on r.flow_id=wf.flow_id");
+		sql.append(" where ra.accept_if=1 ");
+		
+		
+		//按参与人id
+		if(StringUtils.isNoneBlank(args.get("volunteerId"))){
+			sql.append(" and ra.volunteer_id=? ");
+			sqlParams.add(args.get("volunteerId"));
+		}
+		
+		//按参与人名字关键字
+		if(StringUtils.isNoneBlank(args.get("volunteer"))){
+			sql.append(" and (v.real_name like ? or v.phone like ?)");
+			sqlParams.add("%"+args.get("volunteer")+"%");
+			sqlParams.add("%"+args.get("volunteer")+"%");
+		}
+		
+		//按发布时间
+		if(StringUtils.isNoneBlank(args.get("publishStart"))){
+			sql.append(" and r.publish_time>=?");
+			sqlParams.add(args.get("publishStart"));
+		}
+		if(StringUtils.isNoneBlank(args.get("publishEnd"))){
+			sql.append(" and r.publish_time<=?");
+			sqlParams.add(args.get("publishEnd"));
+		}
+		
+		
+		//按参加时间
+		if(StringUtils.isNoneBlank(args.get("joinStart"))){
+			sql.append(" and accept_time>=?");
+			sqlParams.add(args.get("joinStart"));
+		}
+		if(StringUtils.isNoneBlank(args.get("joinEnd"))){
+			sql.append(" and accept_time<=?");
+			sqlParams.add(args.get("joinEnd"));
+		}
+		//按救援状态(以个人的状态为准)
+		if(StringUtils.isNoneBlank(args.get("joinStatus"))){
+			if(args.get("joinStatus").equals(RescueFlow.RESCUING.getCode())){
+				sql.append(" and ra.finish_time is null");
+			}
+			else if(args.get("joinStatus").equals(RescueFlow.COMPLETE.getCode())){
+				sql.append(" and ra.finish_time is not null");
+			}
+			
+		}
+		
+		sql.append(" order by accept_time desc");
+		Object[] sqlArgs=sqlParams.size()>0?sqlParams.toArray():null;
+		return dao.queryForPagedListPojo(sql.toString(),pageNo, pageSize,RescueAcceptVO.class,sqlArgs);
+	}
+	
+	/**
+	 * 统计志愿者 总救援数、本月救援数、漏单数
+	 * @param volunteerId
+	 * @return
+	 */
+	public Map<String,Object> getJoinRescueRpt(String volunteerId){
+		String crtMonth=DateUtil.format(new Date(), "yyyy-MM");
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select count(1) total_rescue,sum((case when DATE_FORMAT(accept_time,'%Y-%m')=? then 1 else 0 end)) month_rescue,");
+		sql.append(" sum((case when finish_time is not null then 1 else 0 end)) finish_rescue");
+		sql.append(" from t_rescue_apply_accept where volunteer_id=? and accept_if=1"); //accept_if=1  接受求助
+		
+		Map<String,Object> rpt=dao.queryForSingleMap(sql.toString(), crtMonth,volunteerId);
+		
+		sql.setLength(0);
+		
+		sql.append("select count(volunteer_id is null) lack_count from t_rescue_apply a  ");
+		sql.append(" left join t_rescue_apply_accept ac on a.rescue_id=ac.rescue_id and ac.volunteer_id=? and accept_if=0");  //accept_if=0  忽略的求助单
+		sql.append(" where DATE_FORMAT(publish_time,'%Y-%m')=? ");
+		
+		Map<String,Object> rpt2=dao.queryForSingleMap(sql.toString(), volunteerId,crtMonth);
+		
+		if(rpt==null){
+			return rpt2;
+		}
+		else{
+			rpt.putAll(rpt2);
+			return rpt;
+		}
+		
+	}
+	
+	/**
+	 * 是否正在救援中
+	 * @param volunteerId
+	 * @return
+	 */
+	public boolean isInRescuing(String volunteerId){
+		StringBuilder sql=new StringBuilder(200);
+		sql.append("select ac.volunteer_id ");
+		sql.append(" from t_rescue_apply_accept  ac ");
+		sql.append(" where ac.volunteer_id=? and ac.accept_if=1 and ac.finish_time is null ");
+		
+		Map<String,Object> rst=dao.queryForSingleMap(sql.toString(), volunteerId);
+		return rst!=null&&rst.size()>0;
+	}
+	
+	/**
+	 * 指定志愿者指定求助记录的参与状态
+	 * @param volunteerId
+	 * @param rescueId
+	 * @return
+	 */
+	public RescueAcceptPO getRescueAcceptInfo(String volunteerId,String rescueId){
+		StringBuilder sql=new StringBuilder(200);
+		sql.append("select ac.* ");
+		sql.append(" from t_rescue_apply_accept  ac ");
+		sql.append(" where ac.volunteer_id=? and ac.rescue_id=? ");
+		
+		return dao.queryForObject(sql.toString(), RescueAcceptPO.class, volunteerId,rescueId);
+	}
+	
+	/**
+	 * 查找匹配的志愿者短信通知救援
+	 * @param rescueApply
+	 * @return
+	 */
+	public List<MatchedVolunteer>  findMatchedVolunteer(RescueApplyVO rescueApply){
+		StringBuilder sql=new StringBuilder(300);
+		sql.append("select distinct v.phone,v.volunteer_id");
+		sql.append(" from t_rescue_apply rap ");
+		sql.append(" inner join t_emergency_rescue_mapping erm on rap.emergency_sort=erm.emergency_sort ");
+		sql.append(" inner join t_volunteer v on  instr(v.rescue_sort,erm.rescue_sort)>0 ");  //能力匹配
+		sql.append(" where rap.rescue_id=? ");
+		
+		sql.append(" and not exists( ");  //无任务
+		sql.append(" select rescue_id from t_rescue_apply_accept ac where ac.volunteer_id=v.volunteer_id and ac.accept_if=1 and ac.finish_time is null ");
+		sql.append(" ) ");
+		
+		sql.append(" and not exists( ");  //没有忽略该任务
+		sql.append(" select rescue_id from t_rescue_apply_accept ac where ac.volunteer_id=v.volunteer_id and ac.rescue_id=rap.rescue_id and ac.accept_if=0 ");
+		sql.append(" ) ");
+		
+		//注册地或最后位置要在事发地50公里范围内,否则排除(由于最后位置可能是很求以前的不可靠,可能会有不该发通知的也发了,不该发的却发了)
+		sql.append(" and ( ifnull(ST_Distance_sphere(point(rap.lon,rap.lat), point(v.reg_lon,v.reg_lat)),1000000)<50000 or ");
+		sql.append(" ifnull(ST_Distance_sphere(point(rap.lon,rap.lat), point(v.last_lon,v.last_lat)),1000000)<50000 ) ");
+		
+		sql.append(" limit ?");
+		
+		return dao.queryForListPojo(sql.toString(), MatchedVolunteer.class,rescueApply.getRescueId(),rescueApply.getNeedVolunteerCount());
+		
+	}
+	
+	/**
+	 * 是否已经有足够的志愿者参与
+	 * @param rescueId
+	 * @return
+	 */
+	public boolean hadEnoughVolunteer(String rescueId){
+		StringBuilder sql=new StringBuilder();
+		sql.append("select cast(d.spare2 as signed) req_count, ");
+		sql.append(" (select count(1) from t_rescue_apply_accept raa where raa.rescue_id=ra.rescue_id and raa.accept_if=1) accept_count ");
+		sql.append(" from t_rescue_apply ra ");
+		sql.append(" inner join t_sort_code d on ra.emergency_sort=d.code_id ");
+		sql.append(" where ra.rescue_id=? ");
+		
+		Map<String,Object> rst=dao.queryForSingleMap(sql.toString(), rescueId);
+		if(rst==null||rst.get("reqCount")==null||rst.get("acceptCount")==null){
+			return false;
+		}
+		
+		return ((Number)rst.get("acceptCount")).doubleValue()>=((Number)rst.get("reqCount")).doubleValue();
+	}
+	
+	public void addCallLog(List<MatchedVolunteer> volunteers,RescueApplyPO  rescueApply){
+		if(volunteers==null||volunteers.size()==0){
+			return;
+		}
+		String sql="insert into t_volunteer_call_log(call_id,volunteer_id,seek_time,rescue_id,call_time) values(?,?,?,?,?)";
+		List<Object[]> args=new ArrayList<Object[]>(volunteers.size());
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		for(MatchedVolunteer vol : volunteers){
+			args.add(new Object[]{uuid.generate(),vol.getVolunteerId(),rescueApply.getPublishTime(),rescueApply.getRescueId(),new Date()});
+		}
+		dao.getJdbcTemplate().batchUpdate(sql, args);
+	}
+	
+	public boolean updateForNeedSound(String volunteerId){
+		String sql="update t_volunteer_call_log set sound_if=1 where sound_if=0 and volunteer_id=? and DATE_FORMAT(call_time,'%Y-%m-%d')>=?";
+		int num=dao.getJdbcTemplate().update(sql, volunteerId,DateUtil.format(new Date(), "yyyy-MM-dd"));
+		return num>0;
+	}
+}

+ 42 - 0
src/main/java/com/jpsoft/proj/sms/service/SMSErrorCoder.java

@@ -0,0 +1,42 @@
+package com.jpsoft.proj.sms.service;
+
+public enum SMSErrorCoder {
+	
+	APP_STATUSERR("E000103","app_key不可用,检查app_key状态是否正常"),
+	//APP_NOAUTH("E000106","app_key没有调用本API的权限,检查app_key具有“短信能力开放”能力"),
+	APP_UNACTIVE("E000109","用户状态未激活"),
+	//APP_TIMEOUT("E000110","时间超出限制,请确认X-WSSE鉴权时,生成随机数的时间(Created)与发送请求时的本地时间不能相差太大"),
+	APP_LOCKED("E000112","用户状态已冻结,账户欠费冻结或业务违规冻结"),
+	APP_MAXLIMIT("E000623","SP短信发送量达到限额,请联系运营经理协商调整SP短信发送量最大限额");
+	
+	private String code;
+	
+	private String desc;
+	
+	
+	private SMSErrorCoder(String code,String desc){
+		this.code=code;
+		this.desc=desc;
+	}
+
+
+	public String getCode() {
+		return code;
+	}
+
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+
+	public String getDesc() {
+		return desc;
+	}
+
+
+	public void setDesc(String desc) {
+		this.desc = desc;
+	}
+	
+}

+ 60 - 0
src/main/java/com/jpsoft/proj/sms/service/SmsProvider.java

@@ -0,0 +1,60 @@
+package com.jpsoft.proj.sms.service;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.Base64;
+import java.util.Date;
+import java.util.UUID;
+
+public class SmsProvider {
+
+	public static final String API_URL = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1"; //APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
+	public static final String APP_KEY = "fM3AYb2XM9aI2HlFQHfBph9xvqW2"; //APP_Key
+	public static final String APP_SECRET = "nEynF2GxBgpmYuQvPZ5VOQx4NxNJ"; //APP_Secret
+	
+	public static final String SMS_PROVIDER = "8822021823061"; //国内短信签名通道号或国际/港澳台短信通道号  志愿者召集通知短信
+	public static final String SMS_PROVIDER_VALID_CODE = "8822022204498";  //通道号  验证码短信
+	
+	public static final String TEMPLATE_ID = "4083f3c86ee44dc3ad0ba134f20dd1b2"; //志愿者召集模板ID 通知短信
+	
+	public static final String TEMPLATE_ID_VALID_CODE="e0fc33b43e47434880c58e7dc2f89686";  //模板ID  验证码短信
+	
+	public static final String TEMPLATE_ID_NEARBY = "453fcd97be1d42dab82c14aef37d3511"; //附近用户通知模板ID 通知短信
+	
+	 //无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
+    private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
+    
+	
+	
+	/**
+     * 构造X-WSSE参数值
+     * @param appKey
+     * @param appSecret
+     * @return
+     */
+    public static String buildWsseHeader() {
+        
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+        String time = sdf.format(new Date()); //Created
+        String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce
+
+        MessageDigest md;
+        byte[] passwordDigest = null;
+
+        try {
+            md = MessageDigest.getInstance("SHA-256");
+            md.update((nonce + time + APP_SECRET).getBytes());
+            passwordDigest = md.digest();
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+        //如果JDK版本是1.8,请加载原生Base64类,并使用如下代码
+        String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
+        //如果JDK版本低于1.8,请加载三方库提供Base64类,并使用如下代码
+        //String passwordDigestBase64Str = Base64.encodeBase64String(passwordDigest); //PasswordDigest
+        //若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正
+        //passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", "");
+        return String.format(WSSE_HEADER_FORMAT, APP_KEY, passwordDigestBase64Str, nonce, time);
+    }
+}

+ 266 - 0
src/main/java/com/jpsoft/proj/sms/service/SmsSender.java

@@ -0,0 +1,266 @@
+package com.jpsoft.proj.sms.service;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import okhttp3.Call;
+import okhttp3.ConnectionPool;
+import okhttp3.FormBody;
+import okhttp3.FormBody.Builder;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jpsoft.proj.utils.OkhttpUtils;
+/**
+ * 1.群发短信时,如果“to”参数携带的号码中包含除数字和+之外的其他字符,则无法向该参数携带的所有号码发送短信。如果“to”参数携带的所有号码只包含数字和+,但部分号码不符合号码规则要求,则在响应消息中会通过状态码标识发送失败的号码,不影响其他正常号码的短信发送。号码之间以英文逗号分隔,每个号码最大长度为21位,最多允许携带1000个号码。如果携带超过1000个号码,则全部号码都会发送失败。
+ * 2.根据短信内容的长度,一条长短信可能会被拆分为多条短信发送,拆分规则详见短信发送规则。
+ * 3.通过X-WSSE方式鉴权时,生成随机数的时间不能与发送请求时的本地时间相差太大(具体差值请与管理员确认),否则会导致鉴权失败。
+ * 4.请求body中的参数需要进行urlencode。
+ * @author cwen
+ *
+ */
+public class SmsSender {
+	
+	public static Logger logger=LoggerFactory.getLogger(SmsSender.class);
+	
+	public static  Map<String,String>  errorCodeMap=null;
+	
+	static{
+		errorCodeMap=new HashMap<String,String>();
+		errorCodeMap.put("E000103","app_key不可用,检查app_key状态是否正常");
+		errorCodeMap.put("E000109","用户状态未激活");
+		errorCodeMap.put("E000112","用户状态已冻结,账户欠费冻结或业务违规冻结");
+		errorCodeMap.put("E000623","SP短信发送量达到限额,请联系运营经理协商调整SP短信发送量最大限额");
+		
+		errorCodeMap=Collections.unmodifiableMap(errorCodeMap);
+	}
+	
+	
+
+	//无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
+    private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";
+
+    private static OkHttpClient singletonClient;
+    
+    private static String[] preSend(String receiver){
+    	
+    	logger.debug("手机号码预处理");
+    	
+    	if(!Pattern.matches("^(\\+|\\d)[\\+\\d,]+\\d",receiver)){
+    		throw new IllegalArgumentException("短信接收号码含有不合规字符:"+receiver);
+    	}
+    	String[] phones=receiver.split(",");
+    	List<String> phoneList=new ArrayList<String>(phones.length);
+    	for(String phone : phones){
+    		if(!phone.startsWith("+86")&&phone.length()!=11){
+    			logger.warn("手机号不符合规范:"+phone);
+    			continue;
+    		}
+    		if(!phone.startsWith("+86")){  //根据接口要求手机号要全局格式
+    			phone="+86"+phone;
+    		}
+    		phoneList.add(phone);
+    	}
+    	
+    	if(phoneList.size()<=1000){  //最大群发手机号个数
+    		return new String[]{StringUtils.join(phoneList,",")};
+    	}
+    	
+    	int slice=(int)Math.ceil(phoneList.size()/1000.0);
+    	String[] receiverPhones=new String[slice];
+    	for(int i=1;i<=slice;i++){
+    		if(i==slice){  //最后一次可能没有1000
+    			receiverPhones[i-1]=StringUtils.join(phoneList.subList((i-1)*1000, phoneList.size()),",");
+    		}
+    		receiverPhones[i-1]=StringUtils.join(phoneList.subList((i-1)*1000, i*1000+1),",");
+    		
+    	}
+    	
+    	return receiverPhones;
+    }
+    
+    /**
+     * 发送通知类短信
+     * @param receiver
+     * @param templateParas
+     * @return
+     */
+    public static List<String> sendForNotice(String receiver,String templateParas){
+    	return send(receiver,SmsProvider.SMS_PROVIDER,SmsProvider.TEMPLATE_ID,templateParas,null);
+    }
+    
+    /**
+     * 发送附近用户通知类短信
+     * @param receiver
+     * @param templateParas
+     * @return
+     */
+    public static List<String> sendForNearbyNotice(String receiver,String templateParas){
+    	return send(receiver,SmsProvider.SMS_PROVIDER,SmsProvider.TEMPLATE_ID_NEARBY,templateParas,null);
+    }
+    
+    /**
+     * 发送验证码类短信
+     * @param receiver
+     * @param templateParas
+     * @return
+     */
+    public static List<String> sendForValidCode(String receiver,String templateParas){
+    	return send(receiver,SmsProvider.SMS_PROVIDER_VALID_CODE,SmsProvider.TEMPLATE_ID_VALID_CODE,templateParas,null);
+    }
+    
+    
+    public static List<String> send(String receiver,String channelNum,String templateId,String templateParas,String statusCallBack){
+    	
+    	String[] receiverAry=preSend(receiver);
+    	
+    	logger.debug("开始群发短信");
+    	
+    	List<String>  rptResult=new ArrayList<String>(receiverAry.length);
+    	
+    	String rst=null;
+    	for(String rec : receiverAry){
+    		rst=sendSms(rec,channelNum,templateId,templateParas,statusCallBack);
+    		if(rst!=null){
+    			rptResult.add(rst);
+    		}
+			
+    	}
+    	logger.debug("结束群发短信");
+    	
+    	return rptResult.size()>0?rptResult:null;
+    	
+    }
+    
+    /**
+     * 
+     * @param receiver  //必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
+     * @param templateParas  //模板变量,“发送短信API” 中为字符数组,整体为字符串,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。
+     * @param statusCallBack  //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
+     * @return
+     */
+    private static String sendSms(String receiver,String channelNum,String templateId,String templateParas,String statusCallBack){
+  	  try {
+  		logger.debug("call sm api");
+  		OkHttpClient client=OkhttpUtils.getInstance();
+  		Builder formBuilder=new FormBody.Builder();
+  		formBuilder.add("from", channelNum); //短信服务商通道号,签名记录中查找
+  		formBuilder.add("to",receiver);
+  		formBuilder.add("templateId",templateId);  //短信模板
+  		if(StringUtils.isNotBlank(templateParas)){
+  			formBuilder.add("templateParas", templateParas);
+  		}
+  		if (StringUtils.isNotBlank(statusCallBack)) {
+  			formBuilder.add("statusCallback", statusCallBack);
+        }
+        /*if (StringUtils.isNotBlank(signature)) {  //自动带有签名
+        	formBuilder.add("signature", signature);
+        }*/
+  		
+  		okhttp3.Request.Builder rb=new Request.Builder();
+  		
+  		rb.addHeader("Content-Type", "application/x-www-form-urlencoded");
+  		rb.addHeader("Authorization", AUTH_HEADER_VALUE);
+  		rb.addHeader("X-WSSE", SmsProvider.buildWsseHeader());
+  		
+  		Request req=rb.url(SmsProvider.API_URL)
+          .post(formBuilder.build())
+          .build();
+          final Call call = client.newCall(req);
+          Response rmtResp=call.execute();
+          
+          String resp=rmtResp.isSuccessful()?rmtResp.body().string():null;
+          logger.debug(resp);
+          
+          return resp;
+  	    } 
+  	    catch (Exception e) {
+  	      e.printStackTrace();
+  	      return null;
+        }
+         
+  	}
+    
+    public static OkHttpClient getInstance() {
+    	if (singletonClient == null)
+        {
+    		synchronized (SmsSender.class){
+    			if (singletonClient == null){
+					singletonClient = new OkHttpClient.Builder()
+							.connectTimeout(10, TimeUnit.SECONDS)
+							.retryOnConnectionFailure(true)
+							.sslSocketFactory(createSSLSocketFactory(),getTrustManager())
+							.hostnameVerifier(new HostnameVerifier() {
+
+								@Override
+								public boolean verify(String hostname,
+										SSLSession session) {
+									return true;
+								}
+							}).connectionPool(new  ConnectionPool(3, 3, TimeUnit.MINUTES)).build();
+    			}
+    		}
+        }
+    	return singletonClient;
+    }
+    
+    private static X509TrustManager getTrustManager(){
+    	return new X509TrustManager() {
+            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                return;
+            }
+            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                return;
+            }
+            public X509Certificate[] getAcceptedIssuers() {
+                return null;
+            }
+        };
+    }
+    
+    
+    private static SSLSocketFactory createSSLSocketFactory() {
+    	
+    	
+        SSLSocketFactory ssfFactory = null;
+        try {
+            SSLContext sc = SSLContext.getInstance("SSL");
+            sc.init(null, new TrustManager[]{getTrustManager()}, null);
+            ssfFactory = sc.getSocketFactory();
+        } catch (Exception e) {
+        	e.printStackTrace();
+        }
+
+        return ssfFactory;
+    }
+    
+    public static void main(String[] args) throws Exception {
+    	//System.out.println(Pattern.matches("^(\\+|\\d)[\\+\\d,]+\\d","+q23345,189"));
+    	/*String phone="18956897845,+8613645678956";
+    	String[] rst=SmsSender.preSend(phone);
+    	System.out.println(rst.length);
+    	for(String str : rst){
+    		System.out.println(str);
+    	}*/
+    	//send("18986656325","[\"123789\"]");
+    }
+}

+ 238 - 0
src/main/java/com/jpsoft/proj/sms/service/SmsService.java

@@ -0,0 +1,238 @@
+package com.jpsoft.proj.sms.service;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
+import java.util.Base64;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+public class SmsService {
+
+	 //无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
+    private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
+    //无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
+    private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";
+
+    public static void main(String[] args) throws Exception {
+    	//必填,请参考"开发准备"获取如下数据,替换为实际值
+        String url = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1"; //APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
+        String appKey = "fM3AYb2XM9aI2HlFQHfBph9xvqW2"; //APP_Key
+        String appSecret = "nEynF2GxBgpmYuQvPZ5VOQx4NxNJ"; //APP_Secret
+        String sender = "1069368924410001367"; //国内短信签名通道号或国际/港澳台短信通道号
+        String templateId = "e25ba923309742b080cea46be7cdfe35"; //模板ID
+        
+        //条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
+        //国际/港澳台短信不用关注该参数
+        String signature = "华为云短信测试"; //签名名称
+
+        //必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
+        String receiver = "+8618986656325"; //短信接收人号码
+
+        //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
+        String statusCallBack = "";
+        
+        String templateParas = "[\"369751\"]"; //模板变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。
+
+        //请求Body,不携带签名名称时,signature请填null
+        String body = buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature);
+        if (null == body || body.isEmpty()) {
+            System.out.println("body is null.");
+            return;
+        }
+        
+        //请求Headers中的X-WSSE参数值
+        String wsseHeader = buildWsseHeader(appKey, appSecret);
+        if (null == wsseHeader || wsseHeader.isEmpty()) {
+            System.out.println("wsse header is null.");
+            return;
+        }
+
+        Writer out = null;
+        BufferedReader in = null;
+        StringBuffer result = new StringBuffer();
+        HttpsURLConnection connection = null;
+        InputStream is = null;
+        
+        HostnameVerifier hv = new HostnameVerifier() {
+
+            @Override
+            public boolean verify(String hostname, SSLSession session) {
+                return true;
+            }
+
+        };
+        
+        trustAllHttpsCertificates();
+        
+        try {
+            URL realUrl = new URL(url);
+            connection = (HttpsURLConnection) realUrl.openConnection();
+
+            connection.setHostnameVerifier(hv);
+            connection.setDoOutput(true);
+            connection.setDoInput(true);
+            connection.setUseCaches(true);
+            //请求方法
+            connection.setRequestMethod("POST");
+            //请求Headers参数
+            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+            connection.setRequestProperty("Authorization", AUTH_HEADER_VALUE);
+            connection.setRequestProperty("X-WSSE", wsseHeader);
+
+            connection.connect();
+            out = new OutputStreamWriter(connection.getOutputStream());
+            out.write(body); //发送请求Body参数
+            out.flush();
+            out.close();
+
+            int status = connection.getResponseCode();
+            if (200 == status) { //200
+                is = connection.getInputStream();
+            } else { //400/401
+                is = connection.getErrorStream();
+            }
+            in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+            String line = "";
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+            System.out.println(result.toString()); //打印响应消息实体
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (null != out) {
+                    out.close();
+                }
+                if (null != is) {
+                    is.close();
+                }
+                if (null != in) {
+                    in.close();
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+    
+    /**
+     * 构造请求Body体
+     * @param sender
+     * @param receiver
+     * @param templateId
+     * @param templateParas
+     * @param statusCallBack
+     * @param signature | 签名名称,使用国内短信通用模板时填写
+     * @return
+     */
+    static String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
+            String statusCallBack, String signature) {
+        if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty()
+                || templateId.isEmpty()) {
+            System.out.println("buildRequestBody(): sender, receiver or templateId is null.");
+            return null;
+        }
+        Map<String, String> map = new HashMap<String, String>();
+
+        map.put("from", sender);
+        map.put("to", receiver);
+        map.put("templateId", templateId);
+        if (null != templateParas && !templateParas.isEmpty()) {
+            map.put("templateParas", templateParas);
+        }
+        if (null != statusCallBack && !statusCallBack.isEmpty()) {
+            map.put("statusCallback", statusCallBack);
+        }
+        if (null != signature && !signature.isEmpty()) {
+            map.put("signature", signature);
+        }
+
+        StringBuilder sb = new StringBuilder();
+        String temp = "";
+
+        for (String s : map.keySet()) {
+            try {
+                temp = URLEncoder.encode(map.get(s), "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                e.printStackTrace();
+            }
+            sb.append(s).append("=").append(temp).append("&");
+        }
+
+        return sb.deleteCharAt(sb.length()-1).toString();
+    }
+
+    /**
+     * 构造X-WSSE参数值
+     * @param appKey
+     * @param appSecret
+     * @return
+     */
+    static String buildWsseHeader(String appKey, String appSecret) {
+        if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
+            System.out.println("buildWsseHeader(): appKey or appSecret is null.");
+            return null;
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+        String time = sdf.format(new Date()); //Created
+        String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce
+
+        MessageDigest md;
+        byte[] passwordDigest = null;
+
+        try {
+            md = MessageDigest.getInstance("SHA-256");
+            md.update((nonce + time + appSecret).getBytes());
+            passwordDigest = md.digest();
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+        //如果JDK版本是1.8,请加载原生Base64类,并使用如下代码
+        String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
+        //如果JDK版本低于1.8,请加载三方库提供Base64类,并使用如下代码
+        //String passwordDigestBase64Str = Base64.encodeBase64String(passwordDigest); //PasswordDigest
+        //若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正
+        //passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", "");
+        return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
+    }
+    
+    static void trustAllHttpsCertificates() throws Exception {
+        TrustManager[] trustAllCerts = new TrustManager[] {
+                new X509TrustManager() {
+                    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                        return;
+                    }
+                    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                        return;
+                    }
+                    public X509Certificate[] getAcceptedIssuers() {
+                        return null;
+                    }
+                }
+        };
+        SSLContext sc = SSLContext.getInstance("SSL");
+        sc.init(null, trustAllCerts, null);
+        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+    }
+}

+ 46 - 0
src/main/java/com/jpsoft/proj/sysdata/controller/AutoCompleteController.java

@@ -0,0 +1,46 @@
+package com.jpsoft.proj.sysdata.controller;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import com.jpsoft.proj.sysdata.service.AutoCompleteService;
+
+@Controller
+@RequestMapping("/**/auto")
+public class AutoCompleteController {
+	
+	@Autowired
+	private AutoCompleteService service;
+	
+	
+	
+	
+	
+	
+	
+	private Map<String,Object>  getAutoCompleteResp(boolean success){
+		return getAutoCompleteResp(success,null);
+	}
+	
+	private Map<String,Object>  getAutoCompleteResp(boolean success,Object datas){
+		Map<String,Object> resp=new HashMap<String,Object>();
+		if(success){
+			resp.put("type","success");
+			resp.put("code",0);
+			if(datas!=null){
+				resp.put("data",datas);
+			}
+		}
+		else{
+			resp.put("type","failed");
+			resp.put("code",1);
+		}
+		return resp;
+		
+	}
+
+}

+ 27 - 0
src/main/java/com/jpsoft/proj/sysdata/controller/ErrorsController.java

@@ -0,0 +1,27 @@
+package com.jpsoft.proj.sysdata.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.proj.sysdata.service.UsErrorService;
+import com.jpsoft.proj.utils.LayGridResp;
+import com.jpsoft.proj.utils.RequestParams;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+@RestController
+@RequestMapping("/**/error")
+@Validated
+public class ErrorsController {
+
+	@Autowired
+	private UsErrorService  service;
+	
+	@RequestMapping("/query")
+	public LayGridResp query(RequestParams requestParams,@RequestParam(value="page",defaultValue="1") Integer page,@RequestParam(value="limit",defaultValue="20") Integer limit){
+		return RespVOBuilder.getLayGridResp(service.query(requestParams,page, limit));
+	}
+
+}

+ 53 - 0
src/main/java/com/jpsoft/proj/sysdata/controller/OptionsController.java

@@ -0,0 +1,53 @@
+package com.jpsoft.proj.sysdata.controller;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import com.jpsoft.framework.util.JsonParamHandler;
+import com.jpsoft.proj.sysdata.service.SortCodeService;
+import com.jpsoft.proj.utils.JsonOutUtils;
+
+
+@Controller
+@RequestMapping("/**/opts")
+public class OptionsController {
+
+	@Autowired
+	private SortCodeService service;
+	
+	
+	
+	//config: [{selectId:'',valkey:'',txtkey:'',pn:'',pv:'',logic:''}]
+	@RequestMapping("/load")
+	public void load(String jsonConfig,HttpServletResponse response){
+		if(StringUtils.isEmpty(jsonConfig)){
+			JsonOutUtils.returnError(response, "缺少参数");
+			return;
+		}
+		List<Map<String,Object>> qparams=JsonParamHandler.getListMapParams(jsonConfig);
+		Map<String,Object> rstCodes=new HashMap<String,Object>(qparams.size());
+		List<Map<String,Object>> optionsData=null;
+		for(Map<String,Object> itm : qparams){
+			
+			optionsData=service.loadCodes((String)itm.get("pn"), (String)itm.get("pv"),  (String)itm.get("logic"),  (String)itm.get("valkey"),  (String)itm.get("txtkey"));
+			
+			
+			rstCodes.put((String)itm.get("selectId"), optionsData);
+			
+		}
+		JsonOutUtils.returnOkWithData(response, rstCodes);
+	}
+	
+	
+	
+	
+	
+}

+ 83 - 0
src/main/java/com/jpsoft/proj/sysdata/controller/SortCodeController.java

@@ -0,0 +1,83 @@
+package com.jpsoft.proj.sysdata.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.validation.constraints.NotBlank;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.framework.util.SessionThreadLocal;
+import com.jpsoft.framework.util.SessionUser;
+import com.jpsoft.proj.model.SortCodePO;
+import com.jpsoft.proj.sysdata.service.SortCodeService;
+import com.jpsoft.proj.utils.JsonOutUtils;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+
+@RestController
+@RequestMapping("/**/code")
+@Validated
+public class SortCodeController {
+
+	@Autowired
+	private SortCodeService service;
+	
+	@RequestMapping("/treeNode")
+	public List<Map<String,Object>> treeNode(String fatherCodeId){
+		List<Map<String,Object>> nodes=service.queryCodeNodes(StringUtils.isEmpty(fatherCodeId)?"0":fatherCodeId);
+		if(nodes==null){
+			return null;
+		}
+		JsonOutUtils.preprocessTreeNodes(nodes, null);
+		return nodes;
+	}
+	
+	
+	@RequestMapping("/get")
+	public RespVO get(@NotBlank(message = "缺少参数") String codeId){
+		return RespVOBuilder.ok(service.getSortCode(codeId));
+	}
+	
+	@PostMapping("/save")
+	public RespVO save(SortCodePO code,String action,String oldFatherCodeId,String domainCodeOverSub){
+		SessionUser su=SessionThreadLocal.getSessionUser();
+		code.setModifier(su!=null?su.getUserName():"unknow");
+		if(StringUtils.isEmpty(code.getFatherCodeId())){
+			code.setFatherCodeId("0");
+		}
+		
+		
+		if("add".equals(action)){
+			if(service.existsCode(code.getFatherCodeId(),code.getCodeName())){
+				return RespVOBuilder.error("该编码已经存在");
+			}
+			return RespVOBuilder.ok(service.addSortCode(code));
+		}
+		else if("update".equals(action)){
+			if(!oldFatherCodeId.equals(code.getFatherCodeId())){  // 隶属关系变动后,修正assistCode
+				String newAssistCode =service.generateLeveledCode(code.getFatherCodeId());
+				if(newAssistCode==null){
+					return RespVOBuilder.error("指定的父编码不存在");
+				}
+				service.updateSortCode(code,newAssistCode);
+			}
+			else{
+				service.updateSortCode(code,null);
+			}
+			
+			return RespVOBuilder.ok();
+		}
+		else if("del".equals(action)){
+			service.deleteSortCode(code.getCodeId());
+			return RespVOBuilder.ok();
+		}
+		return RespVOBuilder.ok();
+	   
+	}
+}

+ 15 - 0
src/main/java/com/jpsoft/proj/sysdata/service/AutoCompleteService.java

@@ -0,0 +1,15 @@
+package com.jpsoft.proj.sysdata.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+
+@Service
+public class AutoCompleteService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	
+}

+ 151 - 0
src/main/java/com/jpsoft/proj/sysdata/service/SortCodeService.java

@@ -0,0 +1,151 @@
+package com.jpsoft.proj.sysdata.service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.FieldAttrConverter;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.proj.model.SortCodePO;
+
+@Service
+public class SortCodeService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	public List<Map<String, Object>> loadSubCodesByDomain(String fatherId,String domainCode){
+		String sql="select code_id,code_name from t_sort_code where father_code_id=? and domain_code=?";
+		return dao.queryForListMap(sql,fatherId,domainCode);
+	}
+	
+	public Map<String,Object> loadSortCodeMapping(String fatherId){
+		String sql="select code_id,code_name from t_sort_code where father_code_id=?";
+		return dao.queryForMapping(sql, "code_name", "code_id",fatherId);
+	}
+	
+	public List<Map<String, Object>> loadCodes(String field,String val,String logic,String valkey,String txtkey) {
+		field=FieldAttrConverter.getFieldName(field);
+		valkey=FieldAttrConverter.getFieldName(valkey);
+		txtkey=FieldAttrConverter.getFieldName(txtkey);
+		if(StringUtils.isEmpty(logic)){
+			logic="=";
+		}
+		StringBuilder sql = new StringBuilder();
+		sql.append("select "+valkey+" val, "+txtkey+"  txt,domain_code from t_sort_code d");
+		sql.append(" where "+field+" "+logic+"'"+val+"'");
+		sql.append(" order by display_num");
+		return dao.queryForListMap(sql.toString());
+	}
+	
+	public List<Map<String, Object>> queryCodeNodes(String fatherCodeId) {
+		StringBuilder sql = new StringBuilder();
+		sql.append("select code_id id,code_name name,father_code_id p_id,'code' node_type,");
+		sql.append("(select count(1) from t_sort_code sub where sub.father_code_id=d.code_id) child_count");
+		sql.append(" from t_sort_code d");
+		sql.append(" where d.father_code_id=?");
+		sql.append(" order by d.display_num");
+		return dao.queryForListMap(sql.toString(), fatherCodeId);
+	}
+	
+	public Map<String,Object> getSortCode(String codeId) {
+		StringBuilder sql = new StringBuilder();
+		sql.append("select d.*,ifnull(supd.code_name,'') father_code_name from t_sort_code d");
+		sql.append(" left join t_sort_code supd on d.father_code_id=supd.code_id");
+		sql.append(" where d.code_id=?");
+		return dao.queryForSingleMap(sql.toString(), codeId);
+	}
+	
+	public String addSortCode(SortCodePO code) {
+		String nextAssistCode = generateLeveledCode(code.getFatherCodeId());
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		code.setCodeId(uuid.generate());
+		code.setAssistCode(nextAssistCode);
+		code.setModifyTime(new Date());
+		dao.insertPojo(code, "t_sort_code");
+		return code.getCodeId();
+	}
+	
+	
+	
+	public boolean updateSortCode(SortCodePO code, String newAssistCode) {
+		if(StringUtils.isNotEmpty(newAssistCode)){
+			updateAssistCode(code.getAssistCode(),newAssistCode);
+			code.setAssistCode(newAssistCode);
+		}
+		code.setModifyTime(new Date());
+		dao.updatePojo(code, "t_sort_code", "code_id");
+		return true;
+	}
+	
+	public boolean deleteSortCode(String codeId) {
+		StringBuilder sql = new StringBuilder(100);
+		sql.append("delete from t_sort_code  where code_id=?");
+		dao.getJdbcTemplate().update(sql.toString(), codeId);
+		return true;
+	}
+	
+	
+	public String generateLeveledCode(String fatherCodeId) {
+		String superAssistCode=null;
+		if("0".equals(fatherCodeId)){  //顶级父节点
+			superAssistCode="C";
+		}
+		else{
+			Map<String,Object> superCode = getSortCode(fatherCodeId);
+			if (superCode == null) {
+				return null;
+			}
+			superAssistCode=(String)superCode.get("assistCode");
+			if(StringUtils.isEmpty(superAssistCode)){
+				superAssistCode="C";
+			}
+		}
+		
+		String subMaxSortCode = getMaxAssistCode(fatherCodeId);
+		String superSortCode = superAssistCode != null ? superAssistCode : "C";
+		
+		if (subMaxSortCode == null || "null".equalsIgnoreCase(subMaxSortCode)) { // 没有子编码时
+			return superSortCode + "000";
+		}
+		String subSerial = subMaxSortCode.replaceFirst(superSortCode, ""); // 子编码流水号
+
+		int nextNum = Integer.parseInt(subSerial) + 1;
+		subSerial = String.format("%1$03d", nextNum);
+		return superSortCode + subSerial;
+	}
+
+	
+	
+	public boolean existsCode(String fatherCodeId, String codeName) {
+		String sql = "select count(1) from t_sort_code where code_name=? and father_code_id=?";
+		int num = dao.getJdbcTemplate().queryForObject(sql, Integer.class,codeName, fatherCodeId);
+		return num > 0;
+	}
+	
+	public boolean existsDomainCode(String fatherCodeId, String domainCode) {
+		String sql = "select count(1) from t_sort_code where domain_code=? and father_code_id=?";
+		int num = dao.getJdbcTemplate().queryForObject(sql,Integer.class, domainCode, fatherCodeId);
+		return num > 0;
+	}
+	
+	
+	private boolean updateAssistCode(String assistCode, String newAssistCode) {
+		StringBuilder sql = new StringBuilder();
+		sql.append("update t_sort_code set assist_code=replace(assist_code,?,?)");
+		sql.append(" where assist_code like ?");
+		dao.getJdbcTemplate().update(sql.toString(), assistCode, newAssistCode, assistCode + "%");
+		return true;
+	}
+	
+	private String getMaxAssistCode(String fatherCodeId) {
+		String sql = "select max(assist_code) assist_code from t_sort_code where father_code_id=?";
+		Map<String,Object> rst=dao.queryForSingleMap(sql, fatherCodeId);
+		return rst!=null?(String)rst.get("assistCode"):null;
+	}
+}

+ 67 - 0
src/main/java/com/jpsoft/proj/sysdata/service/UsErrorService.java

@@ -0,0 +1,67 @@
+package com.jpsoft.proj.sysdata.service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.proj.utils.MapUtils;
+import com.jpsoft.proj.utils.RequestParams;
+
+/**
+ * 使用时上报的错误
+ * @author cwen
+ *
+ */
+
+@Service
+public class UsErrorService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	
+	public PageModel<Map<String,Object>> query(RequestParams args,int pageNo,int pageSize){
+		List<Object> sqlParams=new ArrayList<Object>();
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select e.*, date_format(create_time,'%Y-%m-%d %H:%i:%s') create_time_fmt from t_error e");
+		sql.append(" where 1=1 ");
+		
+		//按错误关键字查询
+		if(StringUtils.isNoneEmpty(args.get("error"))){
+			sql.append(" and ( lower(us_device) like ? or lower(us_error) like ?)");
+			sqlParams.add("%"+args.get("error").toLowerCase()+"%");
+			sqlParams.add("%"+args.get("error").toLowerCase()+"%");
+		}
+		
+		if(StringUtils.isNoneEmpty(args.get("startDate"))){
+			sql.append(" and date_format(create_time,'%Y-%m-%d')>=?");
+			sqlParams.add(args.get("startDate"));
+		}
+		
+		if(StringUtils.isNoneEmpty(args.get("endDate"))){
+			sql.append(" and date_format(create_time,'%Y-%m-%d')<=?");
+			sqlParams.add(args.get("endDate"));
+		}
+		
+		sql.append(" order by create_time desc");
+		Object[] sqlArgs=sqlParams.size()>0?sqlParams.toArray():null;
+		return dao.queryForPagedListMap(sql.toString(),pageNo, pageSize,sqlArgs);
+	}
+	
+	public void addUsError(String usDevice,String usError,String errorSort){
+		if(StringUtils.isBlank(usError)){
+			return;
+		}
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		Map<String,Object> record=MapUtils.builder("errorSort",errorSort,"usDevice",usDevice,"usError",usError,"recordId",uuid.generate(),"createTime",new Date());
+		dao.insert(record, "t_error");
+	}
+}

+ 92 - 0
src/main/java/com/jpsoft/proj/sysdata/service/UserService.java

@@ -0,0 +1,92 @@
+package com.jpsoft.proj.sysdata.service;
+
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.proj.model.User;
+
+@Deprecated
+@Service
+public class UserService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	//t_user 表功能将弱化,只存储登录相关信息
+	
+	
+	
+	
+	public Map<String, Object> getUserByLoginId(String loginId){  
+		if(StringUtils.isEmpty(loginId)){
+			return null;
+		}
+		loginId=loginId.trim();
+		String sql="select u.*,g.assist_code,g.org_name  from T_USER u left join t_org g on u.org_id=g.org_id  where login_id=?";
+		return dao.queryForSingleMap(sql, loginId);
+	}
+	
+	/**
+	 * 通过登录账号或手机号进行验证
+	 * @param loginId
+	 * @return
+	 */
+	public Map<String, Object> getUserForLogin(String loginId){  
+		if(StringUtils.isEmpty(loginId)){
+			return null;
+		}
+		loginId=loginId.trim().toLowerCase();
+		StringBuilder sql=new StringBuilder(200);
+		sql.append("select * from (");
+		sql.append("select u.user_id,u.login_id,u.pwd,ifnull(m.real_name,v.real_name) real_name,ifnull(m.phone,v.phone) phone, u.user_type from t_user u");
+		sql.append(" left join t_member m on u.user_id=m.member_id");
+		sql.append(" left join t_volunteer v on u.user_id=v.volunteer_id");
+		sql.append(" ) tab where lower(login_id)=? or phone=?");
+		return dao.queryForSingleMap(sql.toString(), loginId,loginId);
+	}
+	
+	public Map<String, Object> loginForVolunteer(String phone){  
+		if(StringUtils.isEmpty(phone)){
+			return null;
+		}
+		phone=phone.trim().toLowerCase();
+		StringBuilder sql=new StringBuilder(200);
+		sql.append("select * from (");
+		sql.append("select u.user_id,u.login_id,u.pwd,v.real_name,v.phone from t_user u");
+		sql.append(" left join t_volunteer v on u.user_id=v.volunteer_id");
+		sql.append(" ) tab where phone=?");
+		return dao.queryForSingleMap(sql.toString(),phone);
+	}
+	
+	
+	public boolean existsUser(String loginId){
+		return getUserByLoginId(loginId)!=null;
+	}
+	
+	
+	public User getUserByOpenid(String openid){
+		StringBuilder sql=new StringBuilder();
+		sql.append("select t.*,m.real_name user_name,m.phone  from t_user t inner join t_member m on t.user_id=m.member_id ");
+		sql.append(" where t.openid=?");
+		return dao.queryForObject(sql.toString(),User.class, openid);
+	}
+	
+	public void addUser(String openid,String userId){
+		dao.getJdbcTemplate().update("insert into t_user(user_id,openid) values(?,?)",userId,openid);
+	}
+	
+	
+	
+	public boolean delUserByOpenid(String openid){
+		String sql="delete from t_user  where openid=?";
+		dao.getJdbcTemplate().update(sql,openid);
+		return true;
+	}
+	
+	
+	
+}

+ 126 - 0
src/main/java/com/jpsoft/proj/utils/AESEncryptUtils.java

@@ -0,0 +1,126 @@
+package com.jpsoft.proj.utils;
+
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+
+
+import sun.misc.BASE64Decoder;
+
+public class AESEncryptUtils {
+
+	/**
+     * 密钥
+     */
+    private static final String KEY = "hbgr202109bycwen";// AES加密要求key必须要128个比特位(这里需要长度为16,否则会报错)
+    
+    public static final String SALT="_cwmade";
+
+    /**
+     * 算法
+     */
+    private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";
+
+    public static void main(String[] args) throws Exception {
+        String content = "url:findNames.action";
+        System.out.println("加密前:" + content);
+
+        System.out.println("加密密钥和解密密钥:" + KEY);
+
+        String encrypt = aesEncrypt(content, KEY);
+        System.out.println("加密后:" + encrypt);
+
+        String decrypt = aesDecrypt(encrypt, KEY);
+
+        System.out.println("解密后:" + decrypt);
+    }
+    
+    public static String aesEncrypt(String content) throws Exception {
+    	return aesEncrypt(content, KEY);
+    }
+    
+    public static String aesDecrypt(String encrypt) throws Exception{
+    	return aesDecrypt(encrypt, KEY);
+    }
+
+    /**
+     * base 64 encode
+     * @param bytes 待编码的byte[]
+     * @return 编码后的base 64 code
+     */
+      private static String base64Encode(byte[] bytes){
+        return Base64.encodeBase64String(bytes);
+    }
+
+    /**
+     * base 64 decode
+     * @param base64Code 待解码的base 64 code
+     * @return 解码后的byte[]
+     * @throws Exception 抛出异常
+     */
+    private static byte[] base64Decode(String base64Code) throws Exception{
+        return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code);
+    }
+
+
+    /**
+     * AES加密
+     * @param content 待加密的内容
+     * @param encryptKey 加密密钥
+     * @return 加密后的byte[]
+     */
+    private static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
+        KeyGenerator kgen = KeyGenerator.getInstance("AES");
+        kgen.init(128);
+        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
+        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));
+
+        return cipher.doFinal(content.getBytes("utf-8"));
+    }
+
+
+    /**
+     * AES加密为base 64 code
+     *
+     * @param content 待加密的内容
+     * @param encryptKey 加密密钥
+     * @return 加密后的base 64 code
+     */
+    private static String aesEncrypt(String content, String encryptKey) throws Exception {
+        return base64Encode(aesEncryptToBytes(content, encryptKey));
+    }
+
+    /**
+     * AES解密
+     *
+     * @param encryptBytes 待解密的byte[]
+     * @param decryptKey 解密密钥
+     * @return 解密后的String
+     */
+    private static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
+        KeyGenerator kgen = KeyGenerator.getInstance("AES");
+        kgen.init(128);
+
+        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
+        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES"));
+        byte[] decryptBytes = cipher.doFinal(encryptBytes);
+
+        return new String(decryptBytes);
+    }
+
+
+    /**
+     * 将base 64 code AES解密
+     *
+     * @param encryptStr 待解密的base 64 code
+     * @param decryptKey 解密密钥
+     * @return 解密后的string
+     */
+    private static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {
+        return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
+    }
+}

+ 38 - 0
src/main/java/com/jpsoft/proj/utils/APIControllerInterceptor.java

@@ -0,0 +1,38 @@
+package com.jpsoft.proj.utils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+public class APIControllerInterceptor implements HandlerInterceptor {
+
+	@Override
+	public void afterCompletion(HttpServletRequest request,
+			HttpServletResponse response, Object obj, Exception exception)
+			throws Exception {
+		
+
+	}
+
+	@Override
+	public void postHandle(HttpServletRequest request, HttpServletResponse response,
+			Object obj, ModelAndView modelAndView) throws Exception {
+			
+		
+		
+	}
+
+	@Override
+	public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
+			Object obj) throws Exception {
+		response.setHeader("Access-Control-Allow-Origin", "*");
+		response.setHeader("Access-Control-Allow-Headers", "*");
+		response.setHeader("Access-Control-Allow-Methods", "*");
+		response.setHeader("Cache-Control", "no-cache");
+		
+		return true;
+	}
+
+}

+ 11 - 0
src/main/java/com/jpsoft/proj/utils/BusinessException.java

@@ -0,0 +1,11 @@
+package com.jpsoft.proj.utils;
+
+public class BusinessException extends RuntimeException {
+
+	private static final long serialVersionUID = 1999999L;
+	
+	public BusinessException(String exception) {
+		super(exception);
+	}
+
+}

+ 45 - 0
src/main/java/com/jpsoft/proj/utils/CodeConstants.java

@@ -0,0 +1,45 @@
+package com.jpsoft.proj.utils;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.jpsoft.proj.model.RescueFlow;
+
+
+public class CodeConstants {
+
+	public static final String EMERGENCY_SORT="4028cde37d79e160017d79e160b20000"; //突发事件类别编码
+	
+	public static final String RESCUE_SORT="4028cde37d79e160017d79e6219c0002"; //救援服务类别
+	
+	public static final String SCENE_DESC_SORT="4028cde37e24306e017e24306ecc0000";  //常用求援场景描述语
+	
+	public static final String INFO_SORT="4028cde37e04d35d017e04d35d020000";  //新闻资讯类别
+	
+	public static final String SOCIETY_RESCUE_SORT="4028cde3801613f401801613f4c30000"; //新闻资讯类别下的社会救援类别
+	
+	
+	public static final String ONLINE_STUDY_SORT="ff808081804efe4e01804efe4e490000";  //在线学习类别
+	
+	public static final String RESCUE_KNOWLEDGE_SORT="4028cde37e04d35d017e04d478660002";  //急救知识类别
+	
+	public static final String SELF_RESCUE_SORT="4028cde37e04d35d017e04d4c9940003";  //自救技巧
+	
+	public static final String SEEK_RESCUE_SORT="4028cde37e04d35d017e04d514730004";  //求救方法
+	
+	public static final String NEWSONLY_SORT="4028cde37e04d35d017e04d3a2340001";  //新闻资讯
+	
+	public static final String EQUIPMENT_RESCUE="rescue"; //救援设备
+	
+	public static final String EQUIPMENT_INTELLIGENT="intelligent"; //智能设备
+	
+	public static Map<String,String> loadRescueFlowStepMap(){
+		Map<String,String>  rtn=new LinkedHashMap<String,String>(3);
+		rtn.put(RescueFlow.PUBLISHED.getCode(), RescueFlow.PUBLISHED.getName());
+		rtn.put(RescueFlow.WAITRESCUE.getCode(), RescueFlow.WAITRESCUE.getName());
+		rtn.put(RescueFlow.RESCUING.getCode(), RescueFlow.RESCUING.getName());
+		rtn.put(RescueFlow.COMPLETE.getCode(), RescueFlow.COMPLETE.getName());
+		rtn.put(RescueFlow.RECALL.getCode(), RescueFlow.RECALL.getName());
+		return rtn;
+	}
+}

+ 49 - 0
src/main/java/com/jpsoft/proj/utils/CustomMethodArgumentResolver.java

@@ -0,0 +1,49 @@
+package com.jpsoft.proj.utils;
+
+import java.util.Iterator;
+
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+import com.jpsoft.framework.util.SessionThreadLocal;
+import com.jpsoft.framework.util.SessionUser;
+
+public class CustomMethodArgumentResolver implements
+		HandlerMethodArgumentResolver {
+
+	@Override
+	public boolean supportsParameter(MethodParameter parameter) {
+		if(parameter.getParameterType().isAssignableFrom(SessionUser.class)){
+			return true;
+		}
+		if(parameter.getParameterType().isAssignableFrom(RequestParams.class)){
+			return true;
+		}
+		return false;
+	}
+
+	@Override
+	public Object resolveArgument(MethodParameter parameter,
+			ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
+			WebDataBinderFactory binderFactory) throws Exception {
+		if(parameter.getParameterType().isAssignableFrom(SessionUser.class)){
+			return SessionThreadLocal.getSessionUser();
+		}
+		if(parameter.getParameterType().equals(RequestParams.class)){
+			 RequestParams reqParams=new RequestParams();
+			 Iterator<String> paramNames=webRequest.getParameterNames();
+			 String pn=null;
+			 while(paramNames.hasNext()){
+				 pn=paramNames.next();
+				 reqParams.put(pn, webRequest.getParameter(pn));
+			 }
+			 
+			return reqParams;
+		}
+		return null;
+	}
+
+}

+ 132 - 0
src/main/java/com/jpsoft/proj/utils/DataAuthUtils.java

@@ -0,0 +1,132 @@
+package com.jpsoft.proj.utils;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.jpsoft.framework.dao.util.FieldAttrConverter;
+
+/**
+ * 数据权限统一处理类
+ * 处理流程:用户个人数据权限(所属组织+权限组织)--》组织下的车辆数据(所属、所在组织)
+ * authOrgs只能存储子节点全选中的父节点或者将所有子节点存储
+ *
+ */
+public class DataAuthUtils {
+
+	
+	/**
+	 * 用户个人数据权限
+	 * @return
+	 */
+	public static String getUserAuthSQL(MySessionUser us){
+		String orgId=us.getOrgId();
+		String authOrg=us.getAuthOrgs();
+		if(StringUtils.isNotEmpty(authOrg)){
+			orgId="'"+orgId+"','"+authOrg.replaceAll("[,;\\s]+", "','")+"'";
+		}
+		else{
+			orgId="'"+orgId+"'";
+		}
+		return getInOrgAuthSQL(orgId);
+	}
+	
+	/**
+	 * 根据起始组织获取按组织及下级过滤sql
+	 * @param inOrg
+	 * @return
+	 */
+	public static String getInOrgAuthSQL(String inOrg){
+		StringBuilder sql=new StringBuilder();
+		sql.append(" in (select org_id from t_org  start with department_id in ("+inOrg+")");
+		sql.append(" connect by prior department_id=parent_id)");
+		return sql.toString();
+	}
+	
+	/**
+	 * 在权限内找指定组织机构
+	 * @param us
+	 * @param orgId
+	 * @return
+	 */
+	public static Set<String>  getChildOrgIds(MySessionUser us,String orgId){
+		Set<String> orgs=getChildOrgIds(us);
+		Set<String> orgs2=getChildOrgIds("'"+orgId+"'");
+		orgs.retainAll(orgs2);
+		return orgs;
+	}
+	
+	/**
+	 * 根据用户信息获取下级组织
+	 * @param startOrgIds
+	 * @return
+	 */
+	public static Set<String>  getChildOrgIds(MySessionUser us){
+		String orgId=us.getOrgId();
+		String authOrg=us.getAuthOrgs();
+		if(StringUtils.isNotEmpty(authOrg)){
+			orgId="'"+orgId+"','"+authOrg.replaceAll("[,;\\s]+", "','")+"'";
+		}
+		else{
+			orgId="'"+orgId+"'";
+		}
+		return getChildOrgIds(orgId);
+	}
+	
+	public static Set<String>  getChildOrgIds(String startOrgIds){
+		return null;
+	}
+	
+	
+	public static String getUserAuthOrg(MySessionUser us){
+		String orgId=us.getOrgId();
+		String authOrg=us.getAuthOrgs();
+		if(StringUtils.isNotEmpty(authOrg)){
+			orgId="'"+orgId+"','"+authOrg.replaceAll("[,;\\s]+", "','")+"'";
+		}
+		else{
+			orgId="'"+orgId+"'";
+		}
+		return orgId;
+	}
+	
+	public static Set<String> getAuthOrgAssistCode(MySessionUser us){
+		if(us==null||us.isSysAdmin()){
+			return null;
+		}
+		String orgAssistCode=us.getOrgAssistCode();
+		//String authOrg=us.getAuthOrgs();
+		Set<String> codes=new HashSet<String>();
+		/*if(StringUtils.isNotEmpty(authOrg)){
+			codes.addAll(Arrays.asList((authOrg.split("[,;\\s]+"))));
+		}*/
+		/*if(StringUtils.isNotEmpty(orgAssistCode)){
+			codes.add(orgAssistCode);
+		}
+		return codes.size()>0?codes:null;*/
+		codes.add(StringUtils.isNotEmpty(orgAssistCode)?orgAssistCode:"unknow");
+		return codes;
+	}
+	
+	public static String getPartSQL(Set<String> authAssistCodes,List<Object> sqlParams){
+		StringBuilder sql=new StringBuilder();
+		for(String c : authAssistCodes){
+			sql.append("or assist_code like ?");
+			sqlParams.add(c+"%");
+		}
+		return " and ("+sql.substring(2)+")";
+	}
+	
+	
+	public static String getCustomOrderSQL(Map<String,Object> args,String defaultOrder){
+		if(args!=null&&StringUtils.isNotEmpty((String)args.get("sortField"))){
+			String sortField=FieldAttrConverter.getFieldName((String)args.get("sortField"));
+			return " order by "+sortField+" "+(args.get("sortAction")==null?"asc":args.get("sortAction"));
+		}
+		return defaultOrder;
+	}
+	
+}

+ 25 - 0
src/main/java/com/jpsoft/proj/utils/DataUtils.java

@@ -0,0 +1,25 @@
+package com.jpsoft.proj.utils;
+
+import java.util.regex.Pattern;
+
+public class DataUtils {
+	
+	private static Pattern pattern = Pattern.compile("^[-\\+]?\\d+(\\.\\d+)?$"); 
+
+	public static Double round(Number d,int decimalCount){
+		if(d==null){
+			return null;
+		}
+		return Double.parseDouble(String.format("%1$."+decimalCount+"f", d.doubleValue()));
+	}
+	
+	public static String format(Object d,int decimalCount){
+		return String.format("%1$."+decimalCount+"f", d);
+	}
+	
+	public static boolean isDouble(String str){
+		
+		return pattern.matcher(str).matches();  
+	}
+	
+}

+ 66 - 0
src/main/java/com/jpsoft/proj/utils/DateEditor.java

@@ -0,0 +1,66 @@
+package com.jpsoft.proj.utils;
+
+import org.springframework.util.StringUtils;
+
+import java.beans.PropertyEditorSupport;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+ 
+public class DateEditor extends PropertyEditorSupport {
+ 
+    private static final DateFormat DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd");
+    private static final DateFormat TIMEFORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ 
+    private DateFormat dateFormat;
+    private boolean allowEmpty = true;
+ 
+    public DateEditor() {
+    }
+ 
+    public DateEditor(DateFormat dateFormat) {
+        this.dateFormat = dateFormat;
+    }
+ 
+    public DateEditor(DateFormat dateFormat, boolean allowEmpty) {
+        this.dateFormat = dateFormat;
+        this.allowEmpty = allowEmpty;
+    }
+ 
+    /**
+     * Parse the Date from the given text, using the specified DateFormat.
+     */
+    @Override
+    public void setAsText(String text) throws IllegalArgumentException {
+        if (this.allowEmpty && !StringUtils.hasText(text)) {
+            // Treat empty String as null value.
+            setValue(null);
+        } else {
+            try {
+                if(this.dateFormat != null)
+                    setValue(this.dateFormat.parse(text));
+                else {
+                    if(text.contains(":"))
+                        setValue(TIMEFORMAT.parse(text));
+                    else
+                        setValue(DATEFORMAT.parse(text));
+                }
+            } catch (ParseException ex) {
+                throw new IllegalArgumentException("Could not parse date: " + ex.getMessage(), ex);
+            }
+        }
+    }
+ 
+    /**
+     * Format the Date as String, using the specified DateFormat.
+     */
+    @Override
+    public String getAsText() {
+        Date value = (Date) getValue();
+        DateFormat dateFormat = this.dateFormat;
+        if(dateFormat == null)
+            dateFormat = TIMEFORMAT;
+        return (value != null ? dateFormat.format(value) : "");
+    }
+}

+ 102 - 0
src/main/java/com/jpsoft/proj/utils/EncryptUtil.java

@@ -0,0 +1,102 @@
+package com.jpsoft.proj.utils;
+
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.DESKeySpec;
+
+
+public class EncryptUtil {
+
+	 /** 加密、解密key. */
+    private static final String PASSWORD_CRYPT_KEY ="webappbyhbwen";//"kEHrDooxWHCWtfeSxvDvgqZq";
+    /** 加密算法,可用 DES,DESede,Blowfish. */
+    private final static String ALGORITHM = "DES";
+    public static void main(String[] args) throws Exception {
+        String md5Password = "zyz";
+        String str = EncryptUtil.encrypt(md5Password);
+        System.out.println("str: " + str);
+        str = EncryptUtil.decrypt(str);
+        System.out.println("str: " + str);
+  }
+    
+    public final static String decrypt(String data) throws Exception {
+        return new String(decrypt(hex2byte(data.getBytes()),
+                PASSWORD_CRYPT_KEY.getBytes()));
+    }
+    
+    
+    public final static String encrypt(String data) throws Exception  {
+        return byte2hex(encrypt(data.getBytes(), PASSWORD_CRYPT_KEY
+                .getBytes()));
+    }
+    
+    
+    private static byte[] encrypt(byte[] data, byte[] key) throws Exception {
+        // DES算法要求有一个可信任的随机数源
+        SecureRandom sr = new SecureRandom();
+        // 从原始密匙数据创建DESKeySpec对象
+        DESKeySpec dks = new DESKeySpec(key);
+        // 创建一个密匙工厂,然后用它把DESKeySpec转换成
+        // 一个SecretKey对象
+        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
+        SecretKey securekey = keyFactory.generateSecret(dks);
+        // Cipher对象实际完成加密操作
+        Cipher cipher = Cipher.getInstance(ALGORITHM);
+        // 用密匙初始化Cipher对象
+        cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
+        // 现在,获取数据并加密
+        // 正式执行加密操作
+        return cipher.doFinal(data);
+    }
+    /**
+     * 用指定的key对数据进行DES解密.
+     * @param data 待解密的数据
+     * @param key DES解密的key
+     * @return 返回DES解密后的数据
+     * @throws Exception
+     * @author <a href="mailto:xiexingxing1121@126.com" mce_href="mailto:xiexingxing1121@126.com">AmigoXie</a>
+     * Creation date: 2007-7-31 - 下午12:10:34
+     */
+    private static byte[] decrypt(byte[] data, byte[] key) throws Exception {
+        // DES算法要求有一个可信任的随机数源
+        SecureRandom sr = new SecureRandom();
+        // 从原始密匙数据创建一个DESKeySpec对象
+        DESKeySpec dks = new DESKeySpec(key);
+        // 创建一个密匙工厂,然后用它把DESKeySpec对象转换成
+        // 一个SecretKey对象
+        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
+        SecretKey securekey = keyFactory.generateSecret(dks);
+        // Cipher对象实际完成解密操作
+        Cipher cipher = Cipher.getInstance(ALGORITHM);
+        // 用密匙初始化Cipher对象
+        cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
+        // 现在,获取数据并解密
+        // 正式执行解密操作
+        return cipher.doFinal(data);
+    }
+    public static byte[] hex2byte(byte[] b) {
+        if ((b.length % 2) != 0)
+            throw new IllegalArgumentException("长度不是偶数");
+        byte[] b2 = new byte[b.length / 2];
+        for (int n = 0; n < b.length; n += 2) {
+            String item = new String(b, n, 2);
+            b2[n / 2] = (byte) Integer.parseInt(item, 16);
+        }
+        return b2;
+    }
+    public static String byte2hex(byte[] b) {
+        String hs = "";
+        String stmp = "";
+        for (int n = 0; n < b.length; n++) {
+            stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
+            if (stmp.length() == 1)
+                hs = hs + "0" + stmp;
+            else
+                hs = hs + stmp;
+        }
+        return hs.toUpperCase();
+    }
+}

+ 125 - 0
src/main/java/com/jpsoft/proj/utils/JsonOutUtils.java

@@ -0,0 +1,125 @@
+package com.jpsoft.proj.utils;
+
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.jpsoft.framework.dao.util.PageModel;
+
+
+public class JsonOutUtils {
+
+	public static void preprocessTreeNodes(List<Map<String,Object>> nodes,String depth){
+		preprocessTreeNodes(nodes,depth,depth);
+	}
+	
+	public static void preprocessTreeNodes(List<Map<String,Object>> nodes,String depth,String defLeafSkin){
+		for(Map<String,Object> itm : nodes){
+			if(((Number)itm.get("childCount")).intValue()>0){
+				itm.put("isParent", true);
+				itm.put("typeEnd", false);
+				itm.put("iconSkin",null);
+			}
+			else if(StringUtils.isNotEmpty(depth)&&!depth.equals(itm.get("nodeType"))){
+				itm.put("isParent", true);
+				itm.put("typeEnd", true);
+				itm.put("iconSkin",null);
+			}
+			else{
+				itm.put("isParent", false);
+				itm.put("typeEnd", true);
+				itm.put("iconSkin", itm.get("iconSkin")!=null?itm.get("iconSkin"):defLeafSkin);
+			}
+			
+		}
+	}
+
+	
+	public static void writeToResponse(HttpServletResponse response,String message){
+		writeToResponse(response,message,null);
+	}
+	
+	public static void writeToResponse(HttpServletResponse response,String message,String contentType){
+		PrintWriter out=null;
+		try {
+			response.setContentType(contentType!=null?contentType:"text/json;charset=UTF-8");
+			out = response.getWriter();
+			out.write(message);
+			
+		} catch (Exception ex) {
+			ex.printStackTrace();
+		}finally{
+			if (out != null) {
+				out.flush();
+				out.close();
+			}
+		}
+	}
+	
+	public static void returnData(HttpServletResponse response,Object data){
+		writeToResponse(response,JSON.toJSONString(data));
+	}
+	
+	public static void returnOkWithData(HttpServletResponse response,Object data,String datefmt){
+		  returnJsonResult(response,true,data,datefmt);
+	}
+	
+	public static void returnOkWithData(HttpServletResponse response,Object data){
+		  returnJsonResult(response,true,data,null);
+	}
+	
+	public static void returnError(HttpServletResponse response,String exception){
+		  writeToResponse(response,"{\"success\":false,\"exception\":\""+exception+"\"}");
+	}
+	 
+	 public static void returnJsonResult(HttpServletResponse response,boolean success,Object data,String datefmt){
+		 StringBuilder json=new StringBuilder("{\"success\":");
+		 json.append(success?"true":"false");
+		 if(data!=null){
+			 if(StringUtils.isNotEmpty(datefmt)){
+				 json.append(",\"data\":"+JSONObject.toJSONStringWithDateFormat(data,datefmt,SerializerFeature.WriteMapNullValue));   
+			 }
+			 else{
+				 json.append(",\"data\":"+JSONObject.toJSONString(data,SerializerFeature.WriteMapNullValue));  
+			 }
+		 }
+		 
+		 json.append("}");
+		 writeToResponse(response,json.toString());
+	 }
+	 
+	 public static void returnOk(HttpServletResponse response){
+		 writeToResponse(response,"{\"success\":true}");
+	 }
+	
+	 public  static void returnPage(HttpServletResponse response,PageModel<?> pageData,String datefmt){
+			if(pageData==null||pageData.getData()==null||pageData.getData().size()==0){
+				JsonOutUtils.writeToResponse(response, "{\"success\":false,\"exception\":\"未找到数据\"}");
+				return;
+			}
+			List<?> datas=pageData.getData();
+			StringBuilder  json=new StringBuilder();
+			json.append("{\"success\":true");
+			json.append(",\"pageInfo\":{\"totalRow\":"+pageData.getTotalRow()+",\"pageCount\":"+pageData.getPageCount()+",\"pageSize\":"+pageData.getPageSize()+",\"currPage\":"+pageData.getCurrPage()+"}");
+			
+			
+			 if(StringUtils.isNotEmpty(datefmt)){
+				 json.append(",\"data\":"+JSONObject.toJSONStringWithDateFormat(datas,datefmt,SerializerFeature.WriteMapNullValue));   
+			 }
+			 else{
+				 json.append(",\"data\":"+JSONObject.toJSONString(datas,SerializerFeature.WriteMapNullValue));  
+			 }
+			
+			json.append("}");
+			JsonOutUtils.writeToResponse(response, json.toString());
+		}
+	 
+	
+}

+ 100 - 0
src/main/java/com/jpsoft/proj/utils/LayGridJsonUtils.java

@@ -0,0 +1,100 @@
+package com.jpsoft.proj.utils;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.util.StringUtils;
+
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.util.JsonParamHandler;
+
+public class LayGridJsonUtils {
+	
+	public static void ok(HttpServletResponse response){
+		JsonOutUtils.writeToResponse(response, "{\"code\":0,\"msg\":\"操作成功\"}");
+	}
+	
+	public static void error(HttpServletResponse response,String exception){
+		JsonOutUtils.writeToResponse(response, "{\"code\":1,\"msg\":\""+exception+"\"}");
+	}
+	
+	public static void witeOne(HttpServletResponse response,Object obj){
+		witeOne(response,obj,null);
+	}
+	
+	public static void witeOne(HttpServletResponse response,Object obj,String datePattern){
+		StringBuilder  json=new StringBuilder();
+		json.append("{\"code\":0");
+		json.append(",\"data\":");
+		if(obj==null){
+			json.append("null");
+		}
+		else{
+			if(StringUtils.hasText(datePattern)){
+				json.append(JsonParamHandler.getJson(obj,datePattern));
+			}
+			else{
+				json.append(JsonParamHandler.getJson(obj));
+			}
+		}
+		json.append("}");
+		JsonOutUtils.writeToResponse(response, json.toString());
+	}
+
+	public static void wirte(HttpServletResponse response,List<?> datas,int totalRows){
+		wirte(response,datas,totalRows,null);
+	}
+	
+	public static void wirte(HttpServletResponse response,List<?> datas,int totalRows,String datePattern){
+		StringBuilder  json=new StringBuilder();
+		json.append("{\"code\":0");
+		//json.append("pageInfo:{totalRowNum:"+String.valueOf(totalRows)+"},");
+		json.append(",\"count\":"+totalRows);
+		json.append(",\"data\":");
+		if(datas==null||datas.size()==0){
+		  json.append("[]");
+        }
+		else{
+			if(StringUtils.hasText(datePattern)){
+				json.append(JsonParamHandler.getJson(datas,datePattern));
+			}
+			else{
+				json.append(JsonParamHandler.getJson(datas));
+			}
+			
+			
+			
+		}
+		json.append("}");
+		JsonOutUtils.writeToResponse(response, json.toString());
+	}
+	
+	public  static void wirtePage(HttpServletResponse response,PageModel<?> pageData,String datePattern){
+		if(pageData==null||pageData.getData().size()==0){
+			JsonOutUtils.writeToResponse(response, "{\"code\":0,\"msg\":\"未找到数据\"}");
+			return;
+		}
+		List<?> datas=pageData.getData();
+		StringBuilder  json=new StringBuilder();
+		json.append("{\"code\":0");
+		json.append(",\"pageInfo\":{\"totalRow\":"+pageData.getTotalRow()+",\"pageCount\":"+pageData.getPageCount()+",\"pageSize\":"+pageData.getPageSize()+",\"currPage\":"+pageData.getCurrPage()+"}");
+		json.append(",\"data\":");
+		if(datas==null||datas.size()==0){
+		  json.append("[]");
+        }
+		else{
+			if(StringUtils.hasText(datePattern)){
+				json.append(JsonParamHandler.getJson(datas,datePattern));
+			}
+			else{
+				json.append(JsonParamHandler.getJson(datas));
+			}
+			
+			
+			
+		}
+		json.append("}");
+		JsonOutUtils.writeToResponse(response, json.toString());
+	}
+}

+ 52 - 0
src/main/java/com/jpsoft/proj/utils/LayGridResp.java

@@ -0,0 +1,52 @@
+package com.jpsoft.proj.utils;
+
+
+public class LayGridResp {
+
+	private int  code;
+	
+	private int  count;
+	
+	private Object data;
+	
+	private Boolean success;
+	
+	public LayGridResp(int code,int count,Object data){
+		this.code=code;
+		this.success=code==0;
+		this.count=count;
+		this.data=data;
+	}
+
+	public int getCode() {
+		return code;
+	}
+
+	public void setCode(int code) {
+		this.code = code;
+	}
+
+	public int getCount() {
+		return count;
+	}
+
+	public void setCount(int count) {
+		this.count = count;
+	}
+
+	public Object getData() {
+		return data;
+	}
+
+	public void setData(Object data) {
+		this.data = data;
+	}
+
+	public Boolean getSuccess() {
+		return success;
+	}
+
+	public void setSuccess(Boolean success) {
+		this.success = success;
+	}
+}

+ 13 - 0
src/main/java/com/jpsoft/proj/utils/MVCConstants.java

@@ -0,0 +1,13 @@
+package com.jpsoft.proj.utils;
+
+public class MVCConstants {
+
+	 public static final String CUR_USER_SESSION = "current_user_session";
+	 
+	 public static final String RETURN_MESSAGE="rtnMessage";
+	 
+	 public static final String FOCUS_ROOT="focusroot";
+	 
+	 
+	
+}

+ 106 - 0
src/main/java/com/jpsoft/proj/utils/MapUtils.java

@@ -0,0 +1,106 @@
+package com.jpsoft.proj.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.jpsoft.framework.util.DateUtil;
+
+public class MapUtils {
+
+	/**
+	 * 为空串的value设置为null
+	 * @param args
+	 */
+	public static void blankValToNull(Map<String,Object> args){
+		for(String key : args.keySet()){
+			if(args.get(key) instanceof String && StringUtils.isEmpty((String)args.get(key))){
+				args.put(key,null);
+			}
+		}
+	}
+	
+	/**
+	 * 为空串的value设置为null,指定的keys有效
+	 * @param args
+	 */
+    public static void blankValToNull(Map<String,Object> args,String... keys){
+		for(String key : keys){
+			if(args.get(key) instanceof String && StringUtils.isEmpty((String)args.get(key))){
+				args.put(key,null);
+			}
+		}
+	}
+    
+    
+    /**
+     * 字符串时间value转换为Date对象值,指定的keys有效
+     * @param args
+     * @param fields
+     */
+    public static void strValToDate(Map<String,Object> args,String... keys){
+		for(String key : keys){
+			if(StringUtils.isNotEmpty((String)args.get(key))){
+				args.put(key,DateUtil.parse((String)args.get(key)));
+			}
+		}
+	}
+    
+    /**
+     * 提取部分键值对作为新的Map返回,原Map去掉被提取的键值对
+     * @param <V>
+     * @param srcMap
+     * @param keys
+     * @return
+     */
+    public static <V>  Map<String,V>  trackMap(Map<String,V> srcMap,String... keys){
+		Map<String,V> rtnMap=new HashMap<String,V>(keys.length);
+		for(String key : keys){
+			rtnMap.put(key, srcMap.get(key));
+		}
+		for(String key : keys){
+			srcMap.remove(key);
+		}
+		return rtnMap;
+	}
+	
+    /**
+     * 提取部分键值对作为新的Map返回
+     * @param <V>
+     * @param srcMap
+     * @param keys
+     * @return
+     */
+	public static <V> Map<String,V> clone(Map<String,V> srcMap,String... keys){
+		Map<String,V> rtnMap=new HashMap<String,V>(keys.length);
+		for(String key : keys){
+			rtnMap.put(key, srcMap.get(key));
+		}
+		return rtnMap;
+	}
+	
+	public static Map<String,Object> builder(String... keyVal){
+		if(keyVal==null){
+			return null;
+		}
+		Map<String,Object>  rtnMap=new HashMap<String,Object>();
+		for(int i=0,len=keyVal.length;i<len;i++){
+			rtnMap.put(keyVal[i], (i+1)<len?keyVal[i+1]:null);
+			i+=1;
+		}
+		return rtnMap;
+	}
+	
+	public static Map<String,Object> builder(Object... keyVal){
+		if(keyVal==null){
+			return null;
+		}
+		Map<String,Object>  rtnMap=new HashMap<String,Object>();
+		for(int i=0,len=keyVal.length;i<len;i++){
+			rtnMap.put(keyVal[i].toString(), (i+1)<len?keyVal[i+1]:null);
+			i+=1;
+		}
+		return rtnMap;
+	}
+}

+ 38 - 0
src/main/java/com/jpsoft/proj/utils/MySessionUser.java

@@ -0,0 +1,38 @@
+package com.jpsoft.proj.utils;
+
+import com.jpsoft.framework.util.SessionUser;
+
+public class MySessionUser extends SessionUser {
+
+	private String authOrgs;  //存储org的assistCode便于应用
+	
+	private String orgAssistCode; //用户单位assistCode
+	
+	private String userType;
+
+	public String getAuthOrgs() {
+		return authOrgs;
+	}
+
+	public void setAuthOrgs(String authOrgs) {
+		this.authOrgs = authOrgs;
+	}
+
+	public String getOrgAssistCode() {
+		return orgAssistCode;
+	}
+
+	public void setOrgAssistCode(String orgAssistCode) {
+		this.orgAssistCode = orgAssistCode;
+	}
+
+	public String getUserType() {
+		return userType;
+	}
+
+	public void setUserType(String userType) {
+		this.userType = userType;
+	}
+	
+	
+}

+ 96 - 0
src/main/java/com/jpsoft/proj/utils/OkhttpUtils.java

@@ -0,0 +1,96 @@
+package com.jpsoft.proj.utils;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.Call;
+import okhttp3.FormBody;
+import okhttp3.FormBody.Builder;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+public class OkhttpUtils {
+
+	private static OkHttpClient singleton;
+	
+	
+	private OkhttpUtils(){
+
+    }
+	
+	public static OkHttpClient getInstance() {
+        if (singleton == null)
+        {
+            synchronized (OkhttpUtils.class)
+            {
+                if (singleton == null)
+                {
+                    singleton = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(10, TimeUnit.SECONDS).build();
+                }
+            }
+        }
+        return singleton;
+    }
+	
+	
+	public static String get(String url){
+		try{
+			OkHttpClient client=OkhttpUtils.getInstance();
+			Request req=new Request.Builder()
+			.url(url)
+			.get()
+			.build();
+			Response response = client.newCall(req).execute();
+		    if (response.isSuccessful()) {
+		        return response.body().string();
+		    } else {
+		        return null;
+		    }
+
+	
+		}
+		catch (Exception e) {
+		      e.printStackTrace();
+		      return null;
+	     }
+		
+	}
+	
+	public static String post(String url,Map<String,String> args){
+		return post(url,args,null);
+	}
+	
+	
+	public static String post(String url,Map<String,String> args,Map<String,String> headers){
+	  try {
+		
+		OkHttpClient client=OkhttpUtils.getInstance();
+		Builder b=new FormBody.Builder();
+		for(String key : args.keySet()){
+			b.add(key, args.get(key));
+		}
+		
+		okhttp3.Request.Builder rb=new Request.Builder();
+		if(headers!=null&&headers.size()>0){
+			for(String h : headers.keySet()){
+				rb.addHeader(h, headers.get(h));
+			}
+		}
+		
+		Request req=rb.url(url)
+        .post(b.build())
+        .build();
+        final Call call = client.newCall(req);
+        Response rmtResp=call.execute();
+        return rmtResp.isSuccessful()?rmtResp.body().string():null;
+	    } catch (Exception e) {
+	      e.printStackTrace();
+	      return null;
+        }
+       
+	}
+	
+	
+
+}

+ 40 - 0
src/main/java/com/jpsoft/proj/utils/RequestParams.java

@@ -0,0 +1,40 @@
+package com.jpsoft.proj.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class RequestParams {
+
+	
+	private Map<String, String> args;
+	
+	public RequestParams(){
+		this.args=new HashMap<String,String>();
+	}
+	
+	public RequestParams(Map<String,String> args){
+		this.args=args;
+	}
+
+	public RequestParams(int initialCapacity){
+		this.args=new HashMap<String,String>(initialCapacity);
+	}
+	
+	public String get(String key){
+		return args.get(key);
+	}
+	
+	public void put(String key,String value){
+		args.put(key, value);
+	}
+	
+	public Map<String,String> get(){
+		return args;
+	}
+	
+	public Map<String,Object> getObjectMap(){
+		Map<String,Object> addArgs=new HashMap<String,Object>();
+		addArgs.putAll(args);
+		return addArgs;
+	}
+}

+ 25 - 0
src/main/java/com/jpsoft/proj/utils/RequestUtils.java

@@ -0,0 +1,25 @@
+package com.jpsoft.proj.utils;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class RequestUtils {
+
+	public static Map<String, Object> getParams(HttpServletRequest request){
+		 Map<String, Object> requestMap = new HashMap<String, Object>();
+		 Enumeration<?> enum_term = request.getParameterNames();
+		 String paramName=null;
+		 while (enum_term.hasMoreElements()) {
+		   paramName = (String) enum_term.nextElement();
+		   requestMap.put(paramName, request.getParameter(paramName));
+		}
+		 return requestMap;
+	}
+	
+	public static String getRequestUrl(HttpServletRequest request){
+		return request.getScheme()+"://"+request.getLocalAddr()+":"+request.getLocalPort()+request.getContextPath();
+	}
+}

+ 69 - 0
src/main/java/com/jpsoft/proj/utils/RespVO.java

@@ -0,0 +1,69 @@
+package com.jpsoft.proj.utils;
+
+public class RespVO {
+
+	private Boolean success;
+	
+	private Object  data;
+	
+	private String  error;
+	
+	private int code;  //兼容laygrid
+	
+	private String msg;  //兼容laygrid
+	
+	public RespVO() {
+		this.success=true;
+		this.code=0;
+	}
+	
+	public RespVO(Boolean success,Object  data,String  error) {
+		this.success=success;
+		this.data=data;
+		this.error=error;
+		this.msg=error;
+		this.code=success?0:1;
+	}
+
+	public Boolean getSuccess() {
+		return success;
+	}
+
+	public void setSuccess(Boolean success) {
+		this.success = success;
+	}
+
+	public Object getData() {
+		return data;
+	}
+
+	public void setData(Object data) {
+		this.data = data;
+	}
+
+	public String getError() {
+		return error;
+	}
+
+	public void setError(String error) {
+		this.error = error;
+	}
+
+	public int getCode() {
+		return code;
+	}
+
+	public void setCode(int code) {
+		this.code = code;
+	}
+
+	public String getMsg() {
+		return msg;
+	}
+
+	public void setMsg(String msg) {
+		this.msg = msg;
+	}
+	
+	
+}

+ 32 - 0
src/main/java/com/jpsoft/proj/utils/RespVOBuilder.java

@@ -0,0 +1,32 @@
+package com.jpsoft.proj.utils;
+
+import com.jpsoft.framework.dao.util.PageModel;
+
+public class RespVOBuilder {
+
+	public static RespVO  ok() {  //默认只设置success=true
+		return new RespVO();
+	}
+	
+	public static RespVO  ok(Object data) {
+		return new RespVO(true,data,null);
+	}
+	
+	public static RespVO  error(String error) {
+		return new RespVO(false,null,error);
+	}
+	
+	public static RespVO  error(String error,Object data) {
+		return new RespVO(false,data,error);
+	}
+	
+	public static LayGridResp getLayGridResp(PageModel<?> pageData){
+		if(pageData==null){
+			return new LayGridResp(0,0,null);
+		}
+		else{
+			return new LayGridResp(0,pageData.getTotalRow(),pageData.getData());
+		}
+		
+	}
+}

+ 140 - 0
src/main/java/com/jpsoft/proj/utils/SessionTimeoutFilter.java

@@ -0,0 +1,140 @@
+package com.jpsoft.proj.utils;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jpsoft.framework.util.SessionThreadLocal;
+import com.jpsoft.framework.util.SessionUser;
+
+
+
+public class SessionTimeoutFilter implements Filter {
+
+	private  static Logger logger=LoggerFactory.getLogger(SessionTimeoutFilter.class);
+	
+	private FilterConfig filterConfig;
+	private String[] extPaths; //需要排除的路径
+	
+	
+	
+	@Override
+	public void destroy() {
+		filterConfig=null;
+		extPaths=null;
+	}
+
+	@Override
+	public void doFilter(ServletRequest request, ServletResponse response,
+			FilterChain filterChain) throws IOException, ServletException {
+		HttpServletRequest httpRequest=(HttpServletRequest)request;
+		HttpSession session=httpRequest.getSession(true);
+		
+		if(session.getAttribute(MVCConstants.CUR_USER_SESSION)==null)
+		{   
+			//String cookieUsId=getCookieUsId(httpRequest.getCookies());
+			String requestURI=httpRequest.getRequestURI();
+			logger.debug("请求地址为:"+requestURI);
+			if(requestURI.endsWith(httpRequest.getContextPath()+"/")){
+				filterChain.doFilter(request,response);
+			}
+			else if(isExtendPath(requestURI))
+			{
+				filterChain.doFilter(request,response);
+			}
+			/*else if(StringUtils.isNotEmpty(cookieUsId)){  //自动登录
+				SessionUser su=AutoLoginUtils.getSessionUser(cookieUsId);
+				session.setAttribute(MVCConstants.CUR_USER_SESSION,su);
+				SessionThreadLocal.setSessionUser(su);
+				filterChain.doFilter(request,response);
+			}*/
+			else
+			{    logger.debug("no auth to access");
+				 String contextPath=httpRequest.getContextPath();
+				 String loginPage=filterConfig.getInitParameter("loginPage");
+				 loginPage=loginPage.startsWith("/")?loginPage:"/"+loginPage;
+				 loginPage=contextPath+loginPage+"?needLogin=1";  //未登录标志
+				((HttpServletResponse)response).sendRedirect(loginPage);
+			   
+			}
+		}
+		else
+		{
+			SessionUser su=(SessionUser)session.getAttribute(MVCConstants.CUR_USER_SESSION);
+			SessionThreadLocal.setSessionUser(su);
+			filterChain.doFilter(request,response);
+
+		}
+	}
+	
+	/*private String  getCookieUsId(Cookie[] cookies){
+		if(cookies==null){
+			return null;
+		}
+		for(Cookie c  : cookies){
+			if("usId".equals(c.getName())){
+				return c.getValue();
+			}
+		}
+		return null;
+	}*/
+
+	@Override
+	public void init(FilterConfig filterConfig) throws ServletException {
+		this.filterConfig=filterConfig;
+		String val=filterConfig.getInitParameter("extpaths");
+		if(val!=null)
+		{
+			extPaths=val.replaceAll("\\t|\\r|\\n|\\u3000|\\s", "").split(",");
+			logger.debug("the following path will be extended:"+filterConfig.getInitParameter("extpaths"));
+		}
+		/*val=filterConfig.getInitParameter("autoIP");
+		if(val!=null)
+		{
+			autoLoginIPs=val.replaceAll("\\t|\\r|\\n|\\u3000|\\s", "").split(",");
+		}*/
+		logger.debug("SessionTimeOutFilter success init");
+
+	}
+	
+	private boolean isExtendPath(String path)
+	{
+		if(extPaths==null||extPaths.length==0){
+			return false;
+		}
+		for(String pth : extPaths)
+		{
+			if(path.indexOf(pth)>=0)
+			{
+				logger.debug("该请求地址为排除地址:"+path);
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/*private boolean isAutoLogin(String ip){
+		if(autoLoginIPs==null||autoLoginIPs.length==0){
+			return false;
+		}
+		for(String p : autoLoginIPs)
+		{
+			if(ip.indexOf(p)>=0)
+			{
+				return true;
+			}
+		}
+		return false;
+	}*/
+}

+ 106 - 0
src/main/java/com/jpsoft/proj/utils/UploadUtils.java

@@ -0,0 +1,106 @@
+package com.jpsoft.proj.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.framework.util.SysConfigUtil;
+
+public class UploadUtils {
+
+	
+	public static Map<String,Object> process(HttpServletRequest request){
+		try{
+			request.setCharacterEncoding("UTF-8");
+			DiskFileItemFactory factory = new DiskFileItemFactory();
+			factory.setSizeThreshold(10*1024*1024);
+			ServletFileUpload sfu = new ServletFileUpload(factory);
+			sfu.setSizeMax(11024 * 1024); // img size 1m
+			List<FileItem> items =sfu.parseRequest(request);
+			
+			Map<String,Object> uploadDatas=new HashMap<String,Object>();
+			FileItem uploadFile=null;
+			for(FileItem itm : items){
+				if(itm.isFormField()){
+					uploadDatas.put(itm.getFieldName(), itm.getString("UTF-8"));
+				}
+				else{
+					uploadFile=itm;
+				}
+				
+			}
+			String fileName=uploadFile.getName();
+			String suffix=fileName.substring(fileName.lastIndexOf(".")+1);  //获取文件后缀
+			
+			uploadDatas.put("fileSort", suffix);
+			uploadDatas.put("fileName", fileName);
+			uploadDatas.put("fileCon", uploadFile);
+			
+			return uploadDatas;
+		}
+		catch(Exception e){
+			e.printStackTrace();
+			return null;
+		}
+	}
+	
+	public static RespVO saveFile(HttpServletRequest request,String subFolder) throws IOException{
+		
+		Map<String,Object> uploadDatas=UploadUtils.process(request);
+		if(uploadDatas==null||uploadDatas.size()==0){
+			return RespVOBuilder.error("未获取到上传数据");
+		}
+		
+		String[] subPaths=subFolder.split("\\.|/");
+		
+		String basePath=request.getSession().getServletContext().getRealPath("");
+		String relPath="upload"+File.separator+StringUtils.join(subPaths,File.separator);
+		basePath=basePath+File.separator+relPath;
+		
+		FileUtils.forceMkdir(new File(basePath));
+		
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		String newFileName=uuid.generate()+"."+uploadDatas.get("fileSort");
+		
+		FileItem  fileCon=(FileItem)uploadDatas.remove("fileCon");
+		
+		FileUtils.writeByteArrayToFile(new File(basePath+File.separator+newFileName), fileCon.get());
+		
+		//String accessPath=request.getScheme()+"://"+request.getLocalAddr()+":"+request.getLocalPort()+request.getContextPath()+"/upload/"+folderSort+"/"+DateUtil.format(new Date(), "yyyyMM")+"/"+newFileName;
+		String accessPath=SysConfigUtil.getConfig("accessPath")+"/upload/"+StringUtils.join(subPaths,"/")+"/"+newFileName;
+		
+		uploadDatas.put("fileName", newFileName);
+		uploadDatas.put("filePath", relPath);
+		uploadDatas.put("accessPath", accessPath);
+		
+		return RespVOBuilder.ok(uploadDatas);
+	}
+	
+	public static void removeFile(HttpServletRequest request,String filePath){
+		
+		
+			try {
+				String basePath=request.getSession().getServletContext().getRealPath("");
+				filePath=basePath+File.separator+filePath;
+				File filedev=new File(filePath);
+				if(filedev.exists()){
+					FileUtils.forceDelete(filedev);
+				}
+				
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		
+	}
+}

+ 22 - 0
src/main/java/com/jpsoft/proj/utils/WebListener.java

@@ -0,0 +1,22 @@
+package com.jpsoft.proj.utils;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import com.jpsoft.proj.rescue.service.CallVolunteerExecutor;
+
+public class WebListener implements ServletContextListener {
+
+	@Override
+	public void contextInitialized(ServletContextEvent sce) {
+		
+
+	}
+
+	@Override
+	public void contextDestroyed(ServletContextEvent sce) {
+		CallVolunteerExecutor.shutdown();
+
+	}
+
+}

+ 52 - 0
src/main/java/com/jpsoft/proj/volunteer/controller/VolunteerBaseController.java

@@ -0,0 +1,52 @@
+package com.jpsoft.proj.volunteer.controller;
+
+import javax.validation.constraints.NotBlank;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jpsoft.proj.utils.LayGridResp;
+import com.jpsoft.proj.utils.RequestParams;
+import com.jpsoft.proj.utils.RespVO;
+import com.jpsoft.proj.utils.RespVOBuilder;
+import com.jpsoft.proj.volunteer.service.VolunteerService;
+
+@RestController
+@RequestMapping("/**/volunteer")
+@Validated
+public class VolunteerBaseController {
+
+	@Autowired
+	private VolunteerService  service;
+	
+	@RequestMapping("/query")
+	public LayGridResp query(RequestParams requestParams,@RequestParam(value="page",defaultValue="1") Integer page,@RequestParam(value="limit",defaultValue="20") Integer limit){
+		return RespVOBuilder.getLayGridResp(service.query(requestParams,page, limit));
+	}
+	
+	/**
+	 * 志愿者认证操作保存
+	 * @param volunteerIds
+	 * @param rescueSorts
+	 * @return
+	 */
+	@RequestMapping("/saveVerify")
+	public RespVO saveVerify(@NotBlank(message = "缺少被认证的志愿者") String volunteerIds,@NotBlank(message = "缺少认证结果") String verifyRst){
+		service.saveVerifyPass(volunteerIds,verifyRst);
+		return RespVOBuilder.ok();
+	}
+	
+	/**
+	 * 加载志愿者认证资料(目前为图片)
+	 * @param volunteerId
+	 * @return
+	 */
+	
+	@RequestMapping("/loadIdentifyArchives")
+	public RespVO  loadIdentifyArchives(@NotBlank(message = "缺少被认证的志愿者") String volunteerId){
+		return RespVOBuilder.ok(service.loadIdentifyArchives(volunteerId));
+	}
+}

+ 133 - 0
src/main/java/com/jpsoft/proj/volunteer/service/VolunteerService.java

@@ -0,0 +1,133 @@
+package com.jpsoft.proj.volunteer.service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.jpsoft.framework.dao.core.SpringJdbcDAO;
+import com.jpsoft.framework.dao.util.PageModel;
+import com.jpsoft.framework.dao.util.UUIDHexGenerator;
+import com.jpsoft.proj.model.FileUseForEm;
+import com.jpsoft.proj.model.VolunteerPO;
+import com.jpsoft.proj.model.VolunteerVO;
+import com.jpsoft.proj.utils.RequestParams;
+
+@Service
+public class VolunteerService {
+
+	@Autowired
+	private SpringJdbcDAO  dao;
+	
+	public void updateLocation(String volunteerId,String lat,String lon){
+		dao.getJdbcTemplate().update("update t_volunteer set last_lat=?,last_lon=?,locate_update_time=sysdate() where volunteer_id=?", lat,lon,volunteerId);
+	}
+	
+	public Map<String,Object> getLocation(String volunteerId){
+		return dao.queryForSingleMap("select last_lat,last_lon,date_format(locate_update_time,'%Y-%m-%d %H:%i:%s') locate_update_time from t_volunteer where volunteer_id=?", volunteerId);
+	}
+	
+	public int getVolunteerCount(){
+		return dao.getJdbcTemplate().queryForObject("select count(1) from t_volunteer", Integer.class);
+	}
+	
+	public String getRealName(String volunteerId){
+		Map<String,Object> rst=dao.queryForSingleMap("select real_name from t_volunteer where volunteer_id=?", volunteerId);
+		return rst!=null&&rst.size()>0?(String)rst.get("realName"):null;
+	}
+	
+	public PageModel<Map<String,Object>> query(RequestParams args,int pageNo,int pageSize){
+		List<Object> sqlParams=new ArrayList<Object>();
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select v.*, ");
+		sql.append(" (select group_concat(code_name) from t_sort_code d where instr(v.rescue_sort,d.code_id)>0 group by father_code_id) rescue_sort_name");
+		sql.append(" from t_volunteer v");
+		sql.append(" where 1=1 ");
+		
+		//按姓名、拼音模糊查询
+		if(StringUtils.isNoneEmpty(args.get("volunteer"))){
+			sql.append(" and ( lower(real_name) like ? or lower(name_py) like ?)");
+			sqlParams.add("%"+args.get("volunteer").toLowerCase()+"%");
+			sqlParams.add("%"+args.get("volunteer").toLowerCase()+"%");
+		}
+		
+		//按认证状态查询
+		if(StringUtils.isNoneEmpty(args.get("verifyStatus"))){
+			sql.append(" and verify_status=?");
+			sqlParams.add("1".equals(args.get("verifyStatus"))?1:0);
+			
+		}
+		sql.append(" order by real_name");
+		Object[] sqlArgs=sqlParams.size()>0?sqlParams.toArray():null;
+		return dao.queryForPagedListMap(sql.toString(),pageNo, pageSize,sqlArgs);
+	}
+	
+	public boolean saveVerifyPass(String volunteerIds,String verifyRst){
+		String sql="update t_volunteer set verify_status=? where volunteer_id in ('"+(volunteerIds.replaceAll(",","','"))+"')";
+		int num=dao.getJdbcTemplate().update(sql,verifyRst);
+		return num>0;
+	}
+	
+	public void addVolunteer(VolunteerPO volunteer){
+		if(volunteer==null){
+			return ;
+		}
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		volunteer.setVolunteerId(uuid.generate());
+		volunteer.setModifyTime(new Date());
+		
+		dao.insertPojo(volunteer, "t_volunteer");
+	}
+	
+	public void updateVolunteer(VolunteerPO volunteer){
+		volunteer.setModifyTime(new Date());
+		dao.updatePojo(volunteer, "t_volunteer","volunteer_id");
+	}
+	
+	public VolunteerPO getVolunteerByPhone(String phone){
+		return dao.queryForObject("select * from t_volunteer where phone=?", VolunteerPO.class, phone);
+	}
+	
+	public VolunteerVO getById(String volunteerId){
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select v.*,f.access_path head_photo_path ");
+		sql.append(" from t_volunteer v");
+		sql.append(" left join t_volunteer_file f on v.head_photo=f.file_id");
+		sql.append(" where v.volunteer_id=?");
+		
+		return dao.queryForObject(sql.toString(),VolunteerVO.class,volunteerId);
+	}
+	
+	public Map<String,Object>  getInfoByPhone(String phone){
+		StringBuilder sql=new StringBuilder(100);
+		sql.append("select v.*,f.access_path, ");
+		sql.append(" (select group_concat(code_name) from t_sort_code d where instr(v.rescue_sort,d.code_id)>0 group by father_code_id) rescue_sort_name");
+		sql.append(" from t_volunteer v");
+		sql.append(" left join t_volunteer_file f on v.head_photo=f.file_id");
+		sql.append(" where phone=?");
+		
+		return dao.queryForSingleMap(sql.toString(), phone);
+	}
+	
+	public String addVolunteerFile(Map<String,Object> fileInfo){
+		UUIDHexGenerator  uuid=UUIDHexGenerator.getInstance();
+		fileInfo.put("createTime", new Date());
+		fileInfo.put("fileId", uuid.generate());
+		dao.insert(fileInfo, "t_volunteer_file");
+		return (String)fileInfo.get("fileId");
+	}
+	
+	public List<Map<String,Object>> loadIdentifyArchives(String volunteerId){
+		return dao.queryForListMap("select * from t_volunteer_file where volunteer_id=? and use_for=?", volunteerId,FileUseForEm.VOLUNTEER_IDENTIFY.getName());
+	}
+	
+	public Map<String,Object> deleteVolunteerFile(String fileId){
+		Map<String,Object> file=dao.queryForSingleMap("select * from t_volunteer_file where file_id=?", fileId);
+		dao.getJdbcTemplate().update("delete from t_volunteer_file where file_id=?",fileId);
+		return file;
+	}
+}

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików