20240419更新

master
RENCHAO 1 year ago
parent 596717b85a
commit 4fb66d1e61

@ -44,11 +44,11 @@
<version>2.2.6</version>
</dependency>
<!--常用工具库-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.google.guava</groupId>-->
<!-- <artifactId>guava</artifactId>-->
<!-- <version>29.0-jre</version>-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>

@ -332,101 +332,9 @@ public class Test01 {
@Test
public void test16() {
// System.out.println(isMatch("abcdef", "a*ef"));
// System.out.println(isMatch("aaaef", "a*ef"));
// System.out.println(isMatch("abcdefghi", ".*"));
// System.out.println(isMatch("aa", "a"));
System.out.println(isMatch("aa", "a*"));
// System.out.println(isMatch("aa", "a*b*"));
// System.out.println(isMatch("aab", "c*a*b"));
System.out.println(isMatch("aab", ".*"));
// System.out.println(isMatch("abcde", ".*de"));
// System.out.println(isMatch("mississippi", "mis*is*ip*."));
// System.out.println(isMatch("mississippi", "mis*is*p*."));
// System.out.println(isMatch("issippi", "is*p*."));
// System.out.println("abcd".substring(1));
}
public boolean isMatch(String s, String p) {
int row = s.length();
int col = p.length();
boolean[][] dp = new boolean[row + 1][col + 1];
// 初始化首行
dp[0][0] = true;
for (int j = 2; j <= col; j += 2) {
if (dp[0][j - 2] && p.charAt(j - 1) == '*') {
dp[0][j] = true;
}
}
// 状态转移
for (int i = 1; i <= row; i++) {
for (int j = 1; j <= col; j++) {
if (p.charAt(j - 1) == '*') {
if (dp[i][j - 2] || (dp[i - 1][j] && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.'))) {
dp[i][j] = true;
}
} else {
if (dp[i - 1][j - 1] && (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.')) {
dp[i][j] = true;
}
}
}
}
return dp[row][col];
}
public boolean isMatch4(String s, String p) {
if (s.isEmpty() && p.isEmpty()) {
return true;
}
if (s.isEmpty() || p.isEmpty()) {
return false;
}
if (p.length() > 1 && p.charAt(1) == '*') {
if (p.charAt(0) == '.') {
return true;
}
int i = 0;
while (i < s.length() && s.charAt(i) == p.charAt(0)) {
i++;
}
return isMatch4(s.substring(i), p.substring(2));
} else if (p.charAt(0) == s.charAt(0) || p.charAt(0) == '.') {
return isMatch4(s.substring(1), p.substring(1));
} else {
return false;
}
}
@Test
public void test21() {
System.out.println(isMatch22("aa", "a*"));
}
public boolean isMatch22(String s, String p) {
int m = s.length() + 1, n = p.length() + 1;
boolean[][] dp = new boolean[m][n];
dp[0][0] = true;
for (int j = 2; j < n; j += 2)
dp[0][j] = dp[0][j - 2] && p.charAt(j - 1) == '*';
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = p.charAt(j - 1) == '*' ?
dp[i][j - 2] || dp[i - 1][j] && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.') :
dp[i - 1][j - 1] && (p.charAt(j - 1) == '.' || s.charAt(i - 1) == p.charAt(j - 1));
}
}
return dp[m - 1][n - 1];
}
}

@ -1,9 +1,18 @@
package com.renchao;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@ -49,4 +58,96 @@ public class ZipDemo {
in.close();
}
}
@Test
public void test01() throws IOException {
// ArrayList<File> list = new ArrayList<>();
// collect(new File("C:\\Users\\RENCHAO\\Desktop\\temp-sss\\aa\\20240314094355A076"), list);
long l = System.currentTimeMillis();
File file = new File("C:\\Users\\RENCHAO\\Desktop\\aa.pdf");
try (/*FileInputStream in = new FileInputStream(file);*/
ZipOutputStream out = new ZipOutputStream(new FileOutputStream("C:\\Users\\RENCHAO\\Desktop\\temp-sss\\aa\\output.zip"))) {
ZipEntry entry = new ZipEntry("aa.pdf");
entry.setMethod(ZipEntry.STORED);
// Set the size of the file
entry.setSize(file.length());
// Set the CRC-32 checksum of the file
entry.setCrc(calcChecksum(file));
out.setLevel(Deflater.NO_COMPRESSION);
out.putNextEntry(entry);
System.out.println("创建entry:" + (System.currentTimeMillis() - l));
l = System.currentTimeMillis();
// byte[] buffer = new byte[2048];
// int len;
// while ((len = in.read(buffer)) > 0) {
// out.write(buffer, 0, len);
// }
try (FileChannel fileChannel = FileChannel.open(file.toPath(), StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(8192); // 8KB缓冲区
int bytesRead;
while ((bytesRead = fileChannel.read(buffer)) != -1) {
buffer.flip(); // 切换到读模式
out.write(buffer.array(), 0, bytesRead);
buffer.clear(); // 清空缓冲区
}
}
//ZipOutputStream.write可以使用NIO操作吗以提高效率
System.out.println("压缩完成:" + (System.currentTimeMillis() - l));
}
}
public void collect(File file, List<File> list) {
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null && files.length > 0) {
for (File f : files) {
collect(f, list);
}
}
} else {
list.add(file);
}
}
// Calculate CRC-32 checksum of a file
private static long calcChecksum(File file) throws IOException {
try (FileInputStream fis = new FileInputStream(file);
CheckedInputStream cis = new CheckedInputStream(fis, new CRC32())) {
byte[] buffer = new byte[2048];
while (cis.read(buffer) >= 0) {
// Reading the file to calculate CRC-32 checksum
}
return cis.getChecksum().getValue();
}
}
private static long calcChecksum2(File file) throws IOException {
try (FileChannel fileChannel = FileChannel.open(file.toPath(), StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
CRC32 crc32 = new CRC32();
while (fileChannel.read(buffer) != -1) {
buffer.flip();
crc32.update(buffer);
buffer.clear();
}
return crc32.getValue();
}
}
}

@ -3,7 +3,7 @@
<parent>
<artifactId>agile-portal</artifactId>
<groupId>com.jiuyv.sptcc.agile</groupId>
<version>0.2.6-SNAPSHOT</version>
<version>0.2.8-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -21,8 +21,8 @@ public class IpUtils {
// 未知地址
public static final String UNKNOWN = "XX XX";
private static final Logger LOGGER = LoggerFactory.getLogger(IpUtils.class);
private static final RestTemplate restTemplate = new RestTemplate();
private static final ObjectMapper objectMapper = new ObjectMapper();
private static RestTemplate restTemplate = new RestTemplate();
private static ObjectMapper objectMapper = new ObjectMapper();
public static String getRealAddressByIP(String ip, boolean isAddressEnabled) {
// 内网不查询

@ -127,6 +127,11 @@ public class PortalUserDTO implements Serializable {
*/
private String firstFlag;
/**
*
*/
private Integer pwdRemainderDate;
/**
* Getid
*/
@ -456,4 +461,12 @@ public class PortalUserDTO implements Serializable {
public void setFirstFlag(String firstFlag) {
this.firstFlag = firstFlag;
}
public Integer getPwdRemainderDate() {
return pwdRemainderDate;
}
public void setPwdRemainderDate(Integer pwdRemainderDate) {
this.pwdRemainderDate = pwdRemainderDate;
}
}

@ -12,7 +12,6 @@ import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import static com.jiuyv.sptccc.agile.common.utils.IpUtils.UNKNOWN;
import static org.junit.jupiter.api.Assertions.*;
@ -40,17 +39,13 @@ class IpUtilsTest {
@BeforeEach
void setUp() throws ReflectiveOperationException {
Field modifier = Field.class.getDeclaredField("modifiers");
modifier.setAccessible(true);
Field restTemplateField = IpUtils.class.getDeclaredField("restTemplate");
restTemplateField.setAccessible(true);
modifier.setInt(restTemplateField, restTemplateField.getModifiers() & ~Modifier.FINAL);
restTemplateField.set(null, restTemplate);
Field objectMapperField = IpUtils.class.getDeclaredField("objectMapper");
objectMapperField.setAccessible(true);
modifier.setInt(objectMapperField, objectMapperField.getModifiers() & ~Modifier.FINAL);
objectMapperField.set(null, objectMapper);
}

@ -3,7 +3,7 @@
<parent>
<groupId>com.jiuyv.sptcc.agile</groupId>
<artifactId>agile-portal</artifactId>
<version>0.2.6-SNAPSHOT</version>
<version>0.2.8-SNAPSHOT</version>
</parent>
<artifactId>agile-portal-gateway</artifactId>

@ -147,7 +147,7 @@ public class SysLoginService {
captcha = StringUtil.randomNumber(6);
localCache.setValueOfCacheName(CacheNames.CACHE_1MIN, phone, captcha);
localCache.setValueOfCacheName(CacheNames.CACHE_5MIN, phone, captcha);
LOGGER.info("手机{}验证码:{}", phone, captcha);
LOGGER.info("手机{}验证码:{}", StringUtil.strHide(phone), captcha);
if (consoleProperties.isCaptchaTest()) {
return captcha;
@ -302,7 +302,11 @@ public class SysLoginService {
if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
throw new ServiceException("原密码错误");
}
String encodePassword = passwordEncoder.encode(secretService.decodePassword(userPasswordDTO.getPassword()));
String password = secretService.decodePassword(userPasswordDTO.getPassword());
if (password.equals(oldPassword)) {
throw new ServiceException("新密码不能与原密码相同");
}
String encodePassword = passwordEncoder.encode(password);
userPasswordDTO.setPassword(encodePassword);
userPasswordDTO.setUserId(user.getUserId());
R<Void> r = userService.resetUserPwd(userPasswordDTO);

@ -59,7 +59,8 @@ public class UserDetailsServiceImpl implements UserDetailsService {
// 第一次登录,只有基本权限,修改密码后获取所有权限
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(FrontConstant.AUTHORITY_BASE));
if (userDTO.getFirstFlag() != null) {
Integer pwdUpdateTime = userDTO.getPwdRemainderDate();
if (userDTO.getFirstFlag() != null && (pwdUpdateTime == null || pwdUpdateTime >= 0)) {
authorities.add(new SimpleGrantedAuthority(FrontConstant.AUTHORITY_ALL));
}
return new LoginUser(user.getUserId(), user.getDeptId(), user, authorities);

@ -120,6 +120,7 @@ public class ContentController extends BaseController {
Response feignResponse = portalContentFeign.sdkDownload();
response.addHeader("Content-Length", getResponseHeader(feignResponse,"content-length"));
response.addHeader("Content-Disposition", "attachment;filename=sdk.rar");
response.addHeader("Content-Type", "text/html; charset=utf-8");
IOUtils.copy(feignResponse.body().asInputStream(), response.getOutputStream());
}
@ -134,6 +135,7 @@ public class ContentController extends BaseController {
response.addHeader("Content-Length", getResponseHeader(feignResponse,"content-length"));
String fileName = URLEncoder.encode("(用户版)实验室使用说明.pdf", "UTF-8");
response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
response.addHeader("Content-Type", "text/html; charset=utf-8");
IOUtils.copy(feignResponse.body().asInputStream(), response.getOutputStream());
}

@ -175,6 +175,10 @@ public class TblPortalUser implements Serializable {
*/
private String firstFlag;
/**
*
*/
private Integer pwdRemainderDate;
/**
*
@ -623,6 +627,14 @@ public class TblPortalUser implements Serializable {
this.firstFlag = firstFlag;
}
public Integer getPwdRemainderDate() {
return pwdRemainderDate;
}
public void setPwdRemainderDate(Integer pwdRemainderDate) {
this.pwdRemainderDate = pwdRemainderDate;
}
public Map<String, Object> getParams() {
if (params == null) {
params = new HashMap<>();

@ -91,6 +91,11 @@ public class UserInfoDTO implements Serializable {
*/
private String firstFlag;
/**
*
*/
private Integer pwdRemainderDate;
public Long getUserId() {
return userId;
}
@ -226,4 +231,12 @@ public class UserInfoDTO implements Serializable {
public void setFirstFlag(String firstFlag) {
this.firstFlag = firstFlag;
}
public Integer getPwdRemainderDate() {
return pwdRemainderDate;
}
public void setPwdRemainderDate(Integer pwdRemainderDate) {
this.pwdRemainderDate = pwdRemainderDate;
}
}

@ -1 +1 @@
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>久事大数据开放平台</title><script defer="defer" src="static/js/chunk-vendors.7aad466f.js"></script><script defer="defer" src="static/js/app.11080c4c.js"></script><link href="static/css/chunk-vendors.d47dac03.css" rel="stylesheet"><link href="static/css/app.f5ad523a.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but 久事大数据开放平台 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>久事大数据开放平台</title><script defer="defer" src="static/js/chunk-vendors.7aad466f.js"></script><script defer="defer" src="static/js/app.ad9f929b.js"></script><link href="static/css/chunk-vendors.d47dac03.css" rel="stylesheet"><link href="static/css/app.f5ad523a.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but 久事大数据开放平台 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

@ -1 +0,0 @@
.personal-info .el-form-item__label{text-align:left!important;font-size:16px!important}.personal-info .el-form-item__content{font-size:16px!important}.personal-info .el-form-item{margin-bottom:0}.personal-info[data-v-5e8b44ac]{padding-top:20px;font-size:16px}.personal-info .el-icon-success[data-v-5e8b44ac]{margin-right:6px;color:#6cbd7f}.personal-info .change-pwd-link[data-v-5e8b44ac]{margin-left:15px;color:#3165db}

@ -1 +0,0 @@
.lab-apply .top-filter[data-v-de1afecc]{margin-top:24px}.lab-apply .tale-list[data-v-de1afecc] .el-table th.el-table__cell{color:#333;background:#fafafa;padding:5px 0;font-size:16px}.lab-apply .tale-list[data-v-de1afecc] .el-table .cell.el-tooltip{font-size:16px}.lab-apply .tale-list .review-status[data-v-de1afecc]{display:flex;align-items:center}.lab-apply .tale-list .review-status .icon-circle[data-v-de1afecc]{width:6px;height:6px;border-radius:3px;margin-right:8px;background:#52c41a}.lab-apply .tale-list .review-status .icon-circle.grey[data-v-de1afecc]{background:#d9d9d9}.lab-apply .tale-list .review-status .icon-circle.orange[data-v-de1afecc]{background:#ffd859}.lab-apply .tale-list .review-status .icon-circle.green[data-v-de1afecc]{background:#52c41a}.lab-apply .tale-list .review-status .icon-circle.red[data-v-de1afecc]{background:#ff4d4f}.lab-apply[data-v-de1afecc] .el-pagination{text-align:right}

@ -1 +0,0 @@
.find-password[data-v-2725d968]{width:100%;min-height:500px;background:#fff}.find-password[data-v-2725d968] .el-step__title{text-align:center}.find-password .title[data-v-2725d968]{padding:40px 20px;text-align:center;font-size:26px;line-height:40px;font-weight:400}.find-password .el-form[data-v-2725d968]{width:382px;margin:60px auto 20px auto}.find-password .procees-contaner[data-v-2725d968]{width:700px;padding:60px 200px;margin:0 auto 50px auto;background:#fff}.divClass[data-v-2725d968]{width:100%;height:10px;margin:5px 0}.divClass span[data-v-2725d968]{float:left;background:#ccc;height:10px;width:31%;margin:0 1%}.divClass .weak[data-v-2725d968]{background-color:#f56c6c}.divClass .medium[data-v-2725d968]{background-color:#e6a23c}.divClass .strong[data-v-2725d968]{background-color:#67c23a}

@ -1 +0,0 @@
.inner-container[data-v-351a2db2]{margin:20px auto;background:#fff}.routerList[data-v-351a2db2]{background:#ecf5ff;height:100vh;border-radius:10px 10px 0 0}.routerList h2[data-v-351a2db2]{text-align:center;font-size:24px;background:#e6171e;color:#fff;line-height:45px;border-radius:10px 10px 0 0}.routerList ul[data-v-351a2db2]{line-height:45px;padding:20px 0}.routerList ul li[data-v-351a2db2]{font-size:18px;font-weight:600;padding:0 20px}.routerList ul li.on[data-v-351a2db2]{background:#fff;border-left:5px solid #e6171e}.routerList ul li.on a[data-v-351a2db2]{color:#e6171e}.api-list-container[data-v-351a2db2]{background:#f9f9f9}.api-list-container .guide-pic[data-v-351a2db2]{background:url(../../static/img/data-service.82b45c45.jpg) no-repeat top;background-size:100%}.api-list-container .api-list ul[data-v-351a2db2]{width:100%;align-items:flex-start;flex-wrap:wrap;justify-content:space-between;padding-top:30px;overflow:hidden}.api-list-container .api-list ul li[data-v-351a2db2]{padding:15px;margin-bottom:50px;box-sizing:border-box;width:32%;height:296px;background:#fff;box-shadow:0 0 6px 0 rgba(217,225,238,.47);border-radius:2px;transition-property:box-shadow transform;transition-duration:.25s,1s;float:left;margin-left:1%;cursor:pointer;border:2px solid #409eff}.api-list-container .api-list ul li[data-v-351a2db2]:hover{transform:translateY(-10px);box-shadow:0 0 16px 0 rgba(217,225,238,.47);background:linear-gradient(180deg,#2980b9,#87ceeb);border:2px solid #adb5bd}.api-list-container .api-list ul li:hover .aip-intro[data-v-351a2db2],.api-list-container .api-list ul li:hover .api-info .others b[data-v-351a2db2],.api-list-container .api-list ul li:hover .api-info[data-v-351a2db2],.api-list-container .api-list ul li:hover .api-name[data-v-351a2db2]{color:#fff}.api-list-container .api-list ul li .api-name[data-v-351a2db2]{font-size:18px;color:#181818;font-weight:700;line-height:18px;height:18px;margin-bottom:15px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.api-list-container .api-list ul li .aip-intro[data-v-351a2db2]{height:120px;overflow:hidden;display:-webkit-box;-webkit-line-clamp:5;-webkit-box-orient:vertical;color:#666;line-height:24px;margin-bottom:20px;font-size:14px}.api-list-container .api-list ul li .api-info[data-v-351a2db2]{padding:20px 0;color:#ababab;font-size:14px;border-top:1px solid #d8d8d8}.api-list-container .api-list ul li .api-info .others[data-v-351a2db2]{display:flex;justify-content:space-between}.api-list-container .api-list ul li .api-info .others b[data-v-351a2db2]{font-weight:400;font-size:12px;color:#5274ca;line-height:1;padding:4px 5px;border-radius:2px;border:1px solid #5274ca}.api-list-container .api-list ul li .api-info .data-from[data-v-351a2db2]{padding-bottom:15px}.api-list-container .api-list .pagination-container[data-v-351a2db2]{background:transparent}.api-list-container .api-list[data-v-351a2db2] .el-pagination{text-align:center}

@ -1 +0,0 @@
.el-table--scrollable-x .el-table__body-wrapper{height:355px}.lab-apply .top-filter[data-v-b3b3944e]{margin-top:24px}.lab-apply .tale-list[data-v-b3b3944e] .el-table th.el-table__cell{color:#333;background:#fafafa;padding:5px 0;font-size:16px}.lab-apply .tale-list[data-v-b3b3944e] .el-table .cell.el-tooltip{font-size:16px}.lab-apply .tale-list .review-status[data-v-b3b3944e]{display:flex;align-items:center}.lab-apply .tale-list .review-status .icon-circle[data-v-b3b3944e]{width:6px;height:6px;border-radius:3px;margin-right:8px;background:#52c41a}.lab-apply .tale-list .review-status .icon-circle.grey[data-v-b3b3944e]{background:#d9d9d9}.lab-apply .tale-list .review-status .icon-circle.orange[data-v-b3b3944e]{background:#ffd859}.lab-apply .tale-list .review-status .icon-circle.green[data-v-b3b3944e]{background:#52c41a}.lab-apply .tale-list .review-status .icon-circle.red[data-v-b3b3944e]{background:#ff4d4f}.lab-apply[data-v-b3b3944e] .el-pagination,[data-v-b3b3944e] .el-pagination{text-align:right}[data-v-b3b3944e] .el-dialog__body{padding:10px}::-webkit-scrollbar{width:7px;height:7px}::-webkit-scrollbar-thumb{border-radius:7px;background-color:rgba(0,0,0,.25)}::-webkit-scrollbar-track{background-color:#f6f6f6}::-webkit-scrollbar-thumb,::-webkit-scrollbar-track{border:0}

@ -1 +0,0 @@
"use strict";(self["webpackChunkagile_portal_front"]=self["webpackChunkagile_portal_front"]||[]).push([[146],{53146:(t,e,s)=>{s.r(e),s.d(e,{default:()=>c});var a=function(){var t=this,e=t._self._c;return e("div",{staticClass:"personal-info"},[e("el-form",{ref:"form1",attrs:{model:t.form,"label-width":"140px",align:"left"}},[e("el-row",[e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{align:"left",label:"用户名"}},[e("span",[t._v(t._s(t.form.userName))])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"手机号"}},[e("span",[t._v(t._s(t.form.phonenumber))])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"状态"}},["0"==t.form.status?e("span",{staticStyle:{color:"#6CBD7F"}},[t._v("正常")]):e("span",{staticStyle:{color:"red"}},[t._v("停用")])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"企业名"}},[e("span",[t._v(t._s(t.form.enterpriseName))])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"社会统一信用代码"}},[e("span",[t._v(t._s(t.form.socialCreditCode))])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"行业类型"}},[e("span",[t._v(t._s(t.form.industryCategory))])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"地址"}},[e("span",[t._v(t._s(t.form.enterpriseAddress))])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"登录密码"}},[e("i",{staticClass:"icon el-icon-success"}),e("span",[t._v("已设置")]),e("router-link",{staticClass:"change-pwd-link",attrs:{to:"/resetpwd"}},[t._v("更改密码")])],1)],1)],1)],1)],1)},r=[],l=s(12223);const o={name:"UserInfo",data:function(){return{form:{}}},created:function(){this.getUserInfo()},methods:{getUserInfo:function(){var t=this;(0,l.C5)().then((function(e){t.form=e.data,"0"==t.form.firstFlag&&t.$router.push("/resetpwd")["catch"]((function(){}))}))}}},n=o;var f=s(1001),i=(0,f.Z)(n,a,r,!1,null,"5e8b44ac",null);const c=i.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
"use strict";(self["webpackChunkagile_portal_front"]=self["webpackChunkagile_portal_front"]||[]).push([[53],{77053:(t,a,s)=>{s.r(a),s.d(a,{default:()=>u});var i=function(){var t=this,a=t._self._c;return a("div",{staticClass:"api-list-container container"},[t._m(0),a("div",{staticClass:"inner-container"},[a("el-row",{attrs:{gutter:20}},[a("el-col",{attrs:{span:4,xs:24}},[a("div",{staticClass:"routerList"},[a("h2",[t._v("数据服务")]),a("ul",[a("li",[a("router-link",{attrs:{to:"/service/introduce"}},[t._v("服务介绍")])],1),a("li",[a("router-link",{attrs:{to:"/service/guide"}},[t._v("服务指南")])],1),a("li",{staticClass:"on"},[a("router-link",{attrs:{to:"/service/api"}},[t._v("API列表")])],1)])])]),a("el-col",{attrs:{span:20,xs:24}},[a("div",{staticClass:"api-list",staticStyle:{overflow:"auto"}},[a("ul",{staticClass:"list"},t._l(t.apiList,(function(s){return a("li",{key:s.id},[a("div",{staticClass:"api-name"},[t._v(t._s(s.apiName))]),a("div",{staticClass:"aip-intro"},[t._v(" "+t._s(s.apiName)+" ")]),a("div",{staticClass:"api-info"},[a("div",{staticClass:"data-from"},[t._v("数据提供方:上海公共交通卡有限公司")]),a("div",{staticClass:"others"},[a("span",[t._v("更新时间:"+t._s(s.createTime))]),a("b",[t._v("有条件开放")])])])])})),0),a("pagination",{directives:[{name:"show",rawName:"v-show",value:t.total>0,expression:"total > 0"}],attrs:{total:t.total,page:t.queryParams.pageNum,limit:t.queryParams.pageSize},on:{"update:page":function(a){return t.$set(t.queryParams,"pageNum",a)},"update:limit":function(a){return t.$set(t.queryParams,"pageSize",a)},pagination:t.getList}})],1)])],1)],1)])},e=[function(){var t=this,a=t._self._c;return a("div",{staticClass:"top-banner guide-pic"},[a("div",{staticClass:"slogan"},[a("h3",{staticClass:"title"},[t._v("API列表 ")]),a("div",{staticClass:"summary"},[t._v("旨在优化数据对外服务方式,提高开发效率,为用户提供规范化数据服务")])])])}],r=s(47121);const n={name:"ApiList",data:function(){return{total:0,apiList:[],queryParams:{pageNum:1,pageSize:9}}},computed:{},mounted:function(){this.backToTop(),this.$parent.$parent.$parent.$refs.topnav.topbg=""},created:function(){this.getList()},methods:{backToTop:function(){window.scrollTo({top:0,behavior:"smooth"})},getList:function(){var t=this;(0,r.ZF)(this.queryParams).then((function(a){t.apiList=a.rows,t.total=a.total}))}}},o=n;var l=s(1001),c=(0,l.Z)(o,i,e,!1,null,"351a2db2",null);const u=c.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -283,6 +283,10 @@ class SysLoginServiceTest {
@Test
void changePassword() {
ResUserPasswordDTO userPasswordDTO = mock(ResUserPasswordDTO.class);
when(userPasswordDTO.getPassword()).thenReturn("a");
when(userPasswordDTO.getOldPassword()).thenReturn("a");
when(secretService.decodePassword("a")).thenReturn("A");
when(secretService.decodePassword("b")).thenReturn("B");
LoginUser loginUser = mock(LoginUser.class);
securityUtilsMockedStatic.when(SecurityUtils::getLoginUser).thenReturn(loginUser);
TblPortalUser portalUser = mock(TblPortalUser.class);
@ -304,6 +308,14 @@ class SysLoginServiceTest {
assertTrue(e instanceof ServiceException);
}
when(userPasswordDTO.getOldPassword()).thenReturn("b");
try {
sysLoginService.changePassword(userPasswordDTO);
fail();
} catch (Exception e) {
assertTrue(e instanceof ServiceException);
}
when(userService.resetUserPwd(userPasswordDTO)).thenReturn(R.ok());
servletUtilsMockedStatic.when(ServletUtils::getSession).thenReturn(mock(HttpSession.class));
try {

@ -3,7 +3,7 @@
<parent>
<artifactId>agile-portal</artifactId>
<groupId>com.jiuyv.sptcc.agile</groupId>
<version>0.2.6-SNAPSHOT</version>
<version>0.2.8-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -3,7 +3,7 @@
<parent>
<groupId>com.jiuyv.sptcc.agile</groupId>
<artifactId>agile-portal</artifactId>
<version>0.2.6-SNAPSHOT</version>
<version>0.2.8-SNAPSHOT</version>
</parent>
<artifactId>agile-portal-service</artifactId>

@ -25,7 +25,7 @@ public class ConsoleProperties {
/**
*
*/
private String wordFileName = "word.docx";
private String wordFileName = "LaboratoryOperationsManual.pdf";
/**
* sdk
@ -43,6 +43,16 @@ public class ConsoleProperties {
*/
private long downloadMaxFile = 100L;
/**
* 1
*/
private int passwordExpirationDates = 365;
/**
* 15
*/
private int passwordExpirationWarnDates = 15;
public boolean isAddressEnabled() {
return addressEnabled;
@ -91,4 +101,20 @@ public class ConsoleProperties {
public void setDownloadMaxFile(long downloadMaxFile) {
this.downloadMaxFile = downloadMaxFile;
}
public int getPasswordExpirationDates() {
return passwordExpirationDates;
}
public void setPasswordExpirationDates(int passwordExpirationDates) {
this.passwordExpirationDates = passwordExpirationDates;
}
public int getPasswordExpirationWarnDates() {
return passwordExpirationWarnDates;
}
public void setPasswordExpirationWarnDates(int passwordExpirationWarnDates) {
this.passwordExpirationWarnDates = passwordExpirationWarnDates;
}
}

@ -7,13 +7,15 @@ import com.jiuyv.sptccc.agile.common.core.domain.R;
import com.jiuyv.sptccc.agile.common.enums.BusinessType;
import com.jiuyv.sptccc.agile.dto.PortalUserDTO;
import com.jiuyv.sptccc.agile.dto.ResUserPasswordDTO;
import com.jiuyv.sptccc.agile.framework.config.ConsoleProperties;
import com.jiuyv.sptccc.agile.portal.domain.TblPortalUser;
import com.jiuyv.sptccc.agile.portal.service.IPortalUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.jiuyv.sptccc.agile.portal.domain.TblPortalUser;
import com.jiuyv.sptccc.agile.portal.service.IPortalUserService;
import java.util.Date;
import static com.jiuyv.sptccc.agile.api.PortalUserFeignApi.API_PATH_PREFIX;
@ -28,9 +30,11 @@ import static com.jiuyv.sptccc.agile.api.PortalUserFeignApi.API_PATH_PREFIX;
public class PortalUserController implements PortalUserFeignApi {
private final IPortalUserService userService;
private final ConsoleProperties consoleProperties;
public PortalUserController(IPortalUserService userService) {
public PortalUserController(IPortalUserService userService, ConsoleProperties consoleProperties) {
this.userService = userService;
this.consoleProperties = consoleProperties;
}
/**
@ -49,6 +53,13 @@ public class PortalUserController implements PortalUserFeignApi {
String[] split = avatar.split("/");
userDTO.setAvatar("content/images/" + split[split.length - 1]);
}
// 计算密码过期时间
Date pwdUpdateTime = user.getPwdUpdateTime();
long diff = consoleProperties.getPasswordExpirationDates()
- (System.currentTimeMillis() - pwdUpdateTime.getTime()) / 1000 / 60 / 60 / 24;
if (diff <= consoleProperties.getPasswordExpirationWarnDates()) {
userDTO.setPwdRemainderDate((int) diff);
}
return R.ok(userDTO);
}

@ -173,6 +173,11 @@ public class TblPortalUser extends BaseEntity {
*/
private String firstFlag;
/**
*
*/
private Date pwdUpdateTime;
/**
* Getid
@ -615,4 +620,12 @@ public class TblPortalUser extends BaseEntity {
public void setFirstFlag(String firstFlag) {
this.firstFlag = firstFlag;
}
public Date getPwdUpdateTime() {
return pwdUpdateTime;
}
public void setPwdUpdateTime(Date pwdUpdateTime) {
this.pwdUpdateTime = pwdUpdateTime;
}
}

@ -38,7 +38,7 @@ import java.util.stream.Collectors;
@Service
public class FileServiceImpl extends AbstractSftpFileService implements IFileService {
private static final Logger LOGGER = LoggerFactory.getLogger(FileServiceImpl.class);
private static final List<String> ALLOW_IMAGE_EXT = Arrays.asList("bmp", "gif", "jpg", "jpeg", "png");
private static final List<String> ALLOW_IMAGE_EXT = Arrays.asList("bmp", "gif", "jpg", "jpeg", "png", "BMP", "GIF", "JPG", "JPEG", "PNG");
private static final List<String> ALLOW_PYTHON_EXT = Arrays.asList("zip", "tar", "gz", "bz2");
private static final List<String> ALLOW_DATA_EXT = Arrays.asList("zip", "tar", "gz", "csv", "txt", "xls", "xlsx");

@ -23,7 +23,7 @@ import java.util.regex.Pattern;
@Service
public class PortalContentServiceImpl implements IPortalContentService {
private static final Logger LOGGER = LoggerFactory.getLogger(PortalContentServiceImpl.class);
private static final Pattern IMG_P = Pattern.compile("/?images/console/[a-fA-F0-9]+\\.(jpg|jpeg|png|gif|bmp|tiff|tif|webp|svg|ico)");
private static final Pattern IMG_P = Pattern.compile("(?i)/?images/console/[a-f0-9]+\\.(jpg|jpeg|png|gif|bmp|tiff|tif|webp|svg|ico)");
private final PortalContentMapper portalContentMapper;
private final IFileService fileService;

@ -34,6 +34,7 @@
<result property="loginErrorCount" column="login_error_count"/>
<result property="lastLoginErrorTime" column="last_login_error_time"/>
<result property="firstFlag" column="first_flag"/>
<result property="pwdUpdateTime" column="pwd_update_time"/>
</resultMap>
<sql id="selectUserVo">
@ -63,7 +64,8 @@
u.social_credit_code,
u.enterprise_industry,
u.enterprise_address,
u.first_flag
u.first_flag,
u.pwd_update_time
from tbl_portal_user u
</sql>
@ -118,7 +120,9 @@
update tbl_portal_user
set version_num = version_num + 1,
password= #{password},
first_flag='1'
first_flag='1',
update_time = now(),
pwd_update_time = now()
where user_id = #{userId}
</update>
</mapper>

@ -2,6 +2,7 @@ package com.jiuyv.sptccc.agile.portal.service.impl;
import com.github.pagehelper.Page;
import com.jiuyv.sptccc.agile.common.exception.ServiceException;
import com.jiuyv.sptccc.agile.framework.config.ConsoleProperties;
import com.jiuyv.sptccc.agile.portal.domain.TblPortalContent;
import com.jiuyv.sptccc.agile.portal.mapper.PortalContentMapper;
import com.jiuyv.sptccc.agile.portal.service.IFileService;
@ -35,6 +36,9 @@ class PortalContentServiceImplTest {
@Mock
private IFileService fileService;
@Mock
private ConsoleProperties consoleProperties;
@Test
void getContentList() {
String showType = "1";

@ -1 +1 @@
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>久事大数据开放平台</title><script defer="defer" src="static/js/chunk-vendors.7aad466f.js"></script><script defer="defer" src="static/js/app.11080c4c.js"></script><link href="static/css/chunk-vendors.d47dac03.css" rel="stylesheet"><link href="static/css/app.f5ad523a.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but 久事大数据开放平台 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>久事大数据开放平台</title><script defer="defer" src="static/js/chunk-vendors.7aad466f.js"></script><script defer="defer" src="static/js/app.ad9f929b.js"></script><link href="static/css/chunk-vendors.d47dac03.css" rel="stylesheet"><link href="static/css/app.f5ad523a.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but 久事大数据开放平台 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

@ -1 +0,0 @@
.personal-info .el-form-item__label{text-align:left!important;font-size:16px!important}.personal-info .el-form-item__content{font-size:16px!important}.personal-info .el-form-item{margin-bottom:0}.personal-info[data-v-5e8b44ac]{padding-top:20px;font-size:16px}.personal-info .el-icon-success[data-v-5e8b44ac]{margin-right:6px;color:#6cbd7f}.personal-info .change-pwd-link[data-v-5e8b44ac]{margin-left:15px;color:#3165db}

@ -1 +0,0 @@
.lab-apply .top-filter[data-v-de1afecc]{margin-top:24px}.lab-apply .tale-list[data-v-de1afecc] .el-table th.el-table__cell{color:#333;background:#fafafa;padding:5px 0;font-size:16px}.lab-apply .tale-list[data-v-de1afecc] .el-table .cell.el-tooltip{font-size:16px}.lab-apply .tale-list .review-status[data-v-de1afecc]{display:flex;align-items:center}.lab-apply .tale-list .review-status .icon-circle[data-v-de1afecc]{width:6px;height:6px;border-radius:3px;margin-right:8px;background:#52c41a}.lab-apply .tale-list .review-status .icon-circle.grey[data-v-de1afecc]{background:#d9d9d9}.lab-apply .tale-list .review-status .icon-circle.orange[data-v-de1afecc]{background:#ffd859}.lab-apply .tale-list .review-status .icon-circle.green[data-v-de1afecc]{background:#52c41a}.lab-apply .tale-list .review-status .icon-circle.red[data-v-de1afecc]{background:#ff4d4f}.lab-apply[data-v-de1afecc] .el-pagination{text-align:right}

@ -1 +0,0 @@
.find-password[data-v-2725d968]{width:100%;min-height:500px;background:#fff}.find-password[data-v-2725d968] .el-step__title{text-align:center}.find-password .title[data-v-2725d968]{padding:40px 20px;text-align:center;font-size:26px;line-height:40px;font-weight:400}.find-password .el-form[data-v-2725d968]{width:382px;margin:60px auto 20px auto}.find-password .procees-contaner[data-v-2725d968]{width:700px;padding:60px 200px;margin:0 auto 50px auto;background:#fff}.divClass[data-v-2725d968]{width:100%;height:10px;margin:5px 0}.divClass span[data-v-2725d968]{float:left;background:#ccc;height:10px;width:31%;margin:0 1%}.divClass .weak[data-v-2725d968]{background-color:#f56c6c}.divClass .medium[data-v-2725d968]{background-color:#e6a23c}.divClass .strong[data-v-2725d968]{background-color:#67c23a}

@ -1 +0,0 @@
.inner-container[data-v-351a2db2]{margin:20px auto;background:#fff}.routerList[data-v-351a2db2]{background:#ecf5ff;height:100vh;border-radius:10px 10px 0 0}.routerList h2[data-v-351a2db2]{text-align:center;font-size:24px;background:#e6171e;color:#fff;line-height:45px;border-radius:10px 10px 0 0}.routerList ul[data-v-351a2db2]{line-height:45px;padding:20px 0}.routerList ul li[data-v-351a2db2]{font-size:18px;font-weight:600;padding:0 20px}.routerList ul li.on[data-v-351a2db2]{background:#fff;border-left:5px solid #e6171e}.routerList ul li.on a[data-v-351a2db2]{color:#e6171e}.api-list-container[data-v-351a2db2]{background:#f9f9f9}.api-list-container .guide-pic[data-v-351a2db2]{background:url(../../static/img/data-service.82b45c45.jpg) no-repeat top;background-size:100%}.api-list-container .api-list ul[data-v-351a2db2]{width:100%;align-items:flex-start;flex-wrap:wrap;justify-content:space-between;padding-top:30px;overflow:hidden}.api-list-container .api-list ul li[data-v-351a2db2]{padding:15px;margin-bottom:50px;box-sizing:border-box;width:32%;height:296px;background:#fff;box-shadow:0 0 6px 0 rgba(217,225,238,.47);border-radius:2px;transition-property:box-shadow transform;transition-duration:.25s,1s;float:left;margin-left:1%;cursor:pointer;border:2px solid #409eff}.api-list-container .api-list ul li[data-v-351a2db2]:hover{transform:translateY(-10px);box-shadow:0 0 16px 0 rgba(217,225,238,.47);background:linear-gradient(180deg,#2980b9,#87ceeb);border:2px solid #adb5bd}.api-list-container .api-list ul li:hover .aip-intro[data-v-351a2db2],.api-list-container .api-list ul li:hover .api-info .others b[data-v-351a2db2],.api-list-container .api-list ul li:hover .api-info[data-v-351a2db2],.api-list-container .api-list ul li:hover .api-name[data-v-351a2db2]{color:#fff}.api-list-container .api-list ul li .api-name[data-v-351a2db2]{font-size:18px;color:#181818;font-weight:700;line-height:18px;height:18px;margin-bottom:15px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.api-list-container .api-list ul li .aip-intro[data-v-351a2db2]{height:120px;overflow:hidden;display:-webkit-box;-webkit-line-clamp:5;-webkit-box-orient:vertical;color:#666;line-height:24px;margin-bottom:20px;font-size:14px}.api-list-container .api-list ul li .api-info[data-v-351a2db2]{padding:20px 0;color:#ababab;font-size:14px;border-top:1px solid #d8d8d8}.api-list-container .api-list ul li .api-info .others[data-v-351a2db2]{display:flex;justify-content:space-between}.api-list-container .api-list ul li .api-info .others b[data-v-351a2db2]{font-weight:400;font-size:12px;color:#5274ca;line-height:1;padding:4px 5px;border-radius:2px;border:1px solid #5274ca}.api-list-container .api-list ul li .api-info .data-from[data-v-351a2db2]{padding-bottom:15px}.api-list-container .api-list .pagination-container[data-v-351a2db2]{background:transparent}.api-list-container .api-list[data-v-351a2db2] .el-pagination{text-align:center}

@ -1 +0,0 @@
.el-table--scrollable-x .el-table__body-wrapper{height:355px}.lab-apply .top-filter[data-v-b3b3944e]{margin-top:24px}.lab-apply .tale-list[data-v-b3b3944e] .el-table th.el-table__cell{color:#333;background:#fafafa;padding:5px 0;font-size:16px}.lab-apply .tale-list[data-v-b3b3944e] .el-table .cell.el-tooltip{font-size:16px}.lab-apply .tale-list .review-status[data-v-b3b3944e]{display:flex;align-items:center}.lab-apply .tale-list .review-status .icon-circle[data-v-b3b3944e]{width:6px;height:6px;border-radius:3px;margin-right:8px;background:#52c41a}.lab-apply .tale-list .review-status .icon-circle.grey[data-v-b3b3944e]{background:#d9d9d9}.lab-apply .tale-list .review-status .icon-circle.orange[data-v-b3b3944e]{background:#ffd859}.lab-apply .tale-list .review-status .icon-circle.green[data-v-b3b3944e]{background:#52c41a}.lab-apply .tale-list .review-status .icon-circle.red[data-v-b3b3944e]{background:#ff4d4f}.lab-apply[data-v-b3b3944e] .el-pagination,[data-v-b3b3944e] .el-pagination{text-align:right}[data-v-b3b3944e] .el-dialog__body{padding:10px}::-webkit-scrollbar{width:7px;height:7px}::-webkit-scrollbar-thumb{border-radius:7px;background-color:rgba(0,0,0,.25)}::-webkit-scrollbar-track{background-color:#f6f6f6}::-webkit-scrollbar-thumb,::-webkit-scrollbar-track{border:0}

@ -1 +0,0 @@
"use strict";(self["webpackChunkagile_portal_front"]=self["webpackChunkagile_portal_front"]||[]).push([[146],{53146:(t,e,s)=>{s.r(e),s.d(e,{default:()=>c});var a=function(){var t=this,e=t._self._c;return e("div",{staticClass:"personal-info"},[e("el-form",{ref:"form1",attrs:{model:t.form,"label-width":"140px",align:"left"}},[e("el-row",[e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{align:"left",label:"用户名"}},[e("span",[t._v(t._s(t.form.userName))])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"手机号"}},[e("span",[t._v(t._s(t.form.phonenumber))])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"状态"}},["0"==t.form.status?e("span",{staticStyle:{color:"#6CBD7F"}},[t._v("正常")]):e("span",{staticStyle:{color:"red"}},[t._v("停用")])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"企业名"}},[e("span",[t._v(t._s(t.form.enterpriseName))])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"社会统一信用代码"}},[e("span",[t._v(t._s(t.form.socialCreditCode))])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"行业类型"}},[e("span",[t._v(t._s(t.form.industryCategory))])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"地址"}},[e("span",[t._v(t._s(t.form.enterpriseAddress))])])],1),e("el-col",{attrs:{span:24}},[e("el-form-item",{attrs:{label:"登录密码"}},[e("i",{staticClass:"icon el-icon-success"}),e("span",[t._v("已设置")]),e("router-link",{staticClass:"change-pwd-link",attrs:{to:"/resetpwd"}},[t._v("更改密码")])],1)],1)],1)],1)],1)},r=[],l=s(12223);const o={name:"UserInfo",data:function(){return{form:{}}},created:function(){this.getUserInfo()},methods:{getUserInfo:function(){var t=this;(0,l.C5)().then((function(e){t.form=e.data,"0"==t.form.firstFlag&&t.$router.push("/resetpwd")["catch"]((function(){}))}))}}},n=o;var f=s(1001),i=(0,f.Z)(n,a,r,!1,null,"5e8b44ac",null);const c=i.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
"use strict";(self["webpackChunkagile_portal_front"]=self["webpackChunkagile_portal_front"]||[]).push([[53],{77053:(t,a,s)=>{s.r(a),s.d(a,{default:()=>u});var i=function(){var t=this,a=t._self._c;return a("div",{staticClass:"api-list-container container"},[t._m(0),a("div",{staticClass:"inner-container"},[a("el-row",{attrs:{gutter:20}},[a("el-col",{attrs:{span:4,xs:24}},[a("div",{staticClass:"routerList"},[a("h2",[t._v("数据服务")]),a("ul",[a("li",[a("router-link",{attrs:{to:"/service/introduce"}},[t._v("服务介绍")])],1),a("li",[a("router-link",{attrs:{to:"/service/guide"}},[t._v("服务指南")])],1),a("li",{staticClass:"on"},[a("router-link",{attrs:{to:"/service/api"}},[t._v("API列表")])],1)])])]),a("el-col",{attrs:{span:20,xs:24}},[a("div",{staticClass:"api-list",staticStyle:{overflow:"auto"}},[a("ul",{staticClass:"list"},t._l(t.apiList,(function(s){return a("li",{key:s.id},[a("div",{staticClass:"api-name"},[t._v(t._s(s.apiName))]),a("div",{staticClass:"aip-intro"},[t._v(" "+t._s(s.apiName)+" ")]),a("div",{staticClass:"api-info"},[a("div",{staticClass:"data-from"},[t._v("数据提供方:上海公共交通卡有限公司")]),a("div",{staticClass:"others"},[a("span",[t._v("更新时间:"+t._s(s.createTime))]),a("b",[t._v("有条件开放")])])])])})),0),a("pagination",{directives:[{name:"show",rawName:"v-show",value:t.total>0,expression:"total > 0"}],attrs:{total:t.total,page:t.queryParams.pageNum,limit:t.queryParams.pageSize},on:{"update:page":function(a){return t.$set(t.queryParams,"pageNum",a)},"update:limit":function(a){return t.$set(t.queryParams,"pageSize",a)},pagination:t.getList}})],1)])],1)],1)])},e=[function(){var t=this,a=t._self._c;return a("div",{staticClass:"top-banner guide-pic"},[a("div",{staticClass:"slogan"},[a("h3",{staticClass:"title"},[t._v("API列表 ")]),a("div",{staticClass:"summary"},[t._v("旨在优化数据对外服务方式,提高开发效率,为用户提供规范化数据服务")])])])}],r=s(47121);const n={name:"ApiList",data:function(){return{total:0,apiList:[],queryParams:{pageNum:1,pageSize:9}}},computed:{},mounted:function(){this.backToTop(),this.$parent.$parent.$parent.$refs.topnav.topbg=""},created:function(){this.getList()},methods:{backToTop:function(){window.scrollTo({top:0,behavior:"smooth"})},getList:function(){var t=this;(0,r.ZF)(this.queryParams).then((function(a){t.apiList=a.rows,t.total=a.total}))}}},o=n;var l=s(1001),c=(0,l.Z)(o,i,e,!1,null,"351a2db2",null);const u=c.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -4,7 +4,7 @@
<parent>
<groupId>com.jiuyv.sptcc.agile</groupId>
<artifactId>agile-portal</artifactId>
<version>0.2.6-SNAPSHOT</version>
<version>0.2.8-SNAPSHOT</version>
</parent>
<artifactId>agile-portal-ui</artifactId>

@ -37,11 +37,11 @@
<li v-for="item in apiList" :key="item.id">
<div class="api-name">{{ item.apiName }}</div>
<div class="aip-intro">
{{ item.apiName }}
{{ item.remark }}
</div>
<div class="api-info">
<div class="data-from">数据提供方上海公共交通卡有限公司</div>
<div class="others"><span>更新时间:{{ item.createTime }}</span><b>有条件开放</b></div>
<div class="others"><span>更新时间:{{ item.createTime }}</span></div>
</div>
</li>
</ul>

@ -1,7 +1,16 @@
<template>
<div class="find-password container">
<h3 class="title">修改密码<span v-if="firstFlag == '0'"
style="text-align: center;padding: 10px;color: red;">(初次登陆需修改初始密码)</span>
<h3 class="title">
修改密码<span
v-if="firstFlag == '0'"
style="text-align: center; padding: 10px; color: red"
>(初次登陆需修改初始密码)</span
>
<span
v-if="pwdRemainderDate <= 15 && pwdRemainderDate != null"
style="text-align: center; padding: 10px; color: red"
>(密码失效修改密码)</span
>
</h3>
<el-card class="procees-contaner">
@ -10,163 +19,228 @@
<el-step title="完成" description=""></el-step>
</el-steps>
<!-- step2 -->
<el-form v-if="processActive == 1" ref="form" :rules="rules" :model="form" label-width="100px">
<el-form
v-if="processActive == 1"
ref="form"
:rules="rules"
:model="form"
label-width="100px"
>
<el-form-item label="原密码" prop="oldPassword">
<el-input v-model="form.oldPassword" type="password"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="password">
<el-input v-model="form.password" :type="flagType" auto-complete="off" placeholder="" @input="strengthColor">
<i style="cursor: pointer;" slot="suffix" class="el-input__icon el-icon-view" @click="getFlageye()"></i>
<el-input
v-model="form.password"
:type="flagType"
auto-complete="off"
placeholder=""
@input="strengthColor"
>
<i
style="cursor: pointer"
slot="suffix"
class="el-input__icon el-icon-view"
@click="getFlageye()"
></i>
</el-input>
<div class="divClass">
<span
:class="passwords == '1' ? 'weak' : '' || passwords == '2' ? 'medium' : '' || passwords == '3' ? 'strong' : ''"></span>
<span :class="passwords == '2' ? 'medium' : '' || passwords == '3' ? 'strong' : ''"></span>
:class="
passwords == '1'
? 'weak'
: '' || passwords == '2'
? 'medium'
: '' || passwords == '3'
? 'strong'
: ''
"
></span>
<span
:class="
passwords == '2'
? 'medium'
: '' || passwords == '3'
? 'strong'
: ''
"
></span>
<span :class="passwords == '3' ? 'strong' : ''"></span>
</div>
</el-form-item>
<el-form-item label="确认密码" prop="passwords">
<el-input v-model="form.passwords" type="password"></el-input>
</el-form-item>
<el-form-item label=""> <el-button type="primary" @click="handleAuthon">
提交</el-button></el-form-item>
<el-form-item label="">
<el-button type="primary" @click="handleAuthon">
提交</el-button
></el-form-item
>
</el-form>
<!-- step3 -->
<el-form v-if="processActive == 2" ref="form" :model="form" label-width="0px">
<el-form
v-if="processActive == 2"
ref="form"
:model="form"
label-width="0px"
>
<el-form-item label="">
<div class="success-tips" style="color: #1ae51ad1;font-size: 24px;font-weight: 600;text-align: center;"><i
class="icon el-icon-success"></i> 修改成功</div>
<div class="go-back" style="text-align: center;"><span style="color: red;font-size: 18px;font-weight: bold;">{{
remainingTime }}</span>秒后 <span>自动返回登录页</span></div>
<div class="btn-back" style="text-align: center;"><el-button type="primary" @click="logout"></el-button>
<div
class="success-tips"
style="
color: #1ae51ad1;
font-size: 24px;
font-weight: 600;
text-align: center;
"
>
<i class="icon el-icon-success"></i> 修改成功
</div>
<div class="go-back" style="text-align: center">
<span style="color: red; font-size: 18px; font-weight: bold">{{
remainingTime
}}</span
>秒后 <span>自动返回登录页</span>
</div>
<div class="btn-back" style="text-align: center">
<el-button type="primary" @click="logout"></el-button>
</div>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
import { changePassword } from "@/api/user";
import { getKeyiv } from "@/api/login";
import { encrypt } from '@/utils/jsencrypt'
import { encrypt } from "@/utils/jsencrypt";
export default {
name: "ResetPwd",
data() {
return {
isShowMenu: false,
passwords: '1',
flagType: 'password',
passwords: "1",
flagType: "password",
processActive: 1,
form: {
oldPassword: '',
password: '',
passwords: '',
oldPassword: "",
password: "",
passwords: "",
},
firstFlag: '',
firstFlag: "",
pwdRemainderDate: "",
remainingTime: 5,
keyiv: '',
keyiv: "",
countDown: 10,
//
rules: {
oldPassword: [
{ required: true, message: "原密码不能为空", trigger: "blur" }
{ required: true, message: "原密码不能为空", trigger: "blur" },
],
password: [
{ required: true, message: "密码不能为空", trigger: "blur" },
{
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/,
message: '密码须包含数字、大小写字母且长度在8-16之间',
trigger: 'blur'
message: "密码须包含数字、大小写字母且长度在8-16之间",
trigger: "blur",
},
],
passwords: [
{ required: true, message: "密码不能为空", trigger: "blur" },
{
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/,
message: '密码须包含数字、大小写字母且长度在8-16之间',
trigger: 'blur'
message: "密码须包含数字、大小写字母且长度在8-16之间",
trigger: "blur",
},
],
}
}
},
};
},
created() {
const storedData = localStorage.getItem('myData');
const storedData = localStorage.getItem("myData");
if (storedData) {
const data = JSON.parse(storedData);
this.firstFlag = data.firstFlag
this.firstFlag = data.firstFlag;
this.pwdRemainderDate = data.pwdRemainderDate;
}
this.getKeyiv();
},
methods: {
getFlageye() {
this.flagType = this.flagType == 'password' ? 'text' : 'password';//textpassword
this.flagType = this.flagType == "password" ? "text" : "password"; //textpassword
},
strengthColor() {
if (this.form.password.length <= 6) {
this.passwords = '1'//
this.passwords = "1"; //
} else if (this.form.password.length <= 10) {
this.passwords = '2'//
this.passwords = "2"; //
} else {
this.passwords = '3' // 绿
this.passwords = "3"; // 绿
}
},
getKeyiv() {
getKeyiv().then((response) => {
this.keyiv = response.data
})
this.keyiv = response.data;
});
},
logout() {
this.$store.dispatch('LogOut').then(() => {
this.$router.push("/login")
})
this.$store.dispatch("LogOut").then(() => {
this.$router.push("/login");
});
},
//
handleAuthon() {
if (this.form.password != this.form.passwords) {
this.$message({
type: 'warning',
message: '新密码与确认密码不一致!'
type: "warning",
message: "新密码与确认密码不一致!",
});
return
return;
}
this.$refs["form"].validate(valid => {
this.$refs["form"].validate((valid) => {
if (valid) {
this.form.passwords = ''
this.form.oldPassword = encrypt(this.keyiv, this.form.oldPassword + ',' + new Date().getTime())
this.form.password = encrypt(this.keyiv, this.form.password + ',' + new Date().getTime())
changePassword(this.form).then(res => {
this.processActive++
this.countdownInterval = setInterval(() => {
//
console.log('倒计时结束');
if (this.remainingTime > 0) {
this.remainingTime--; // 1
} else {
clearInterval(this.countdownInterval); //
}
this.$store.dispatch('LogOut').then(() => {
this.$router.push("/login")
})
}, 1000);
})
this.form.passwords = "";
this.form.oldPassword = encrypt(
this.keyiv,
this.form.oldPassword + "," + new Date().getTime()
);
this.form.password = encrypt(
this.keyiv,
this.form.password + "," + new Date().getTime()
);
changePassword(this.form).then((res) => {
debugger;
if (res.code == 200) {
this.processActive++;
this.countdownInterval = setInterval(() => {
//
console.log("倒计时结束");
if (this.remainingTime > 0) {
this.remainingTime--; // 1
} else {
clearInterval(this.countdownInterval); //
}
this.$store.dispatch("LogOut").then(() => {
this.$router.push("/login");
});
}, 1000);
} else {
this.form.oldPassword = "";
this.form.password = "";
this.strengthColor();
}
});
}
})
}
});
},
},
beforeDestroy() {
clearTimeout(this.countdownInterval); //
},
}
};
</script>
<style lang="scss" scoped>
@ -214,21 +288,18 @@ export default {
}
.weak {
background-color: #F56C6C;
background-color: #f56c6c;
/* 红色 */
}
.medium {
background-color: #E6A23C;
background-color: #e6a23c;
/* 橙色 */
}
.strong {
background-color: #67C23A;
background-color: #67c23a;
/* 绿色 */
}
}
</style>

@ -14,8 +14,8 @@
</el-col>
<el-col :span="24">
<el-form-item label="状态">
<span v-if="form.status == '0'" style="color: #6CBD7F;"></span>
<span v-else style="color: red;">停用</span>
<span v-if="form.status == '0'" style="color: #6cbd7f"></span>
<span v-else style="color: red">停用</span>
</el-form-item>
</el-col>
<!-- <el-col :span="24">
@ -45,41 +45,82 @@
</el-col>
<el-col :span="24">
<el-form-item label="登录密码">
<i class="icon el-icon-success"></i><span>已设置</span><router-link to="/resetpwd"
class="change-pwd-link">更改密码</router-link>
<i class="icon el-icon-success"></i><span>已设置</span
><router-link to="/resetpwd" class="change-pwd-link"
>更改密码</router-link
>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-dialog
width="400px"
title="密码到期提示"
:visible.sync="opens"
append-to-body
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<div style="text-align: center; font-size: 18px">
登录密码还有<span
style="color: red; font-size: 25px; font-weight: bold"
>{{ form.pwdRemainderDate }}</span
>天到期请尽快修改密码
</div>
<div slot="footer" class="dialog-footer" style="text-align: right">
<el-button @click="opens = false">关闭</el-button>
<el-button type="primary" @click="handlefile"></el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getInfo } from '@/api/login'
import { getInfo } from "@/api/login";
export default {
name: "UserInfo",
data() {
return {
form: {
}
}
opens: false,
form: {},
};
},
created() {
this.getUserInfo()
this.getUserInfo();
},
methods: {
getUserInfo() {
getInfo().then(res => {
this.form = res.data
if (this.form.firstFlag == '0') {
this.$router.push("/resetpwd").catch(() => {
getInfo().then((res) => {
this.form = res.data;
if (this.form.firstFlag == "0") {
this.$router.push("/resetpwd").catch(() => {});
}
debugger;
if (
this.form.pwdRemainderDate >= 0 &&
this.form.pwdRemainderDate != null
) {
this.opens = true;
}
if (
this.form.pwdRemainderDate < 0 &&
this.form.pwdRemainderDate != null
) {
this.$message({
type: "error",
message: "登陆密码到期,请修改密码!",
});
this.$router.push("/resetpwd").catch(() => {});
}
})
}
}
}
});
},
handlefile() {
this.$router.push("/resetpwd").catch(() => {});
},
},
};
</script>
<style lang="scss">
.personal-info {
@ -104,14 +145,13 @@ export default {
.el-icon-success {
margin-right: 6px;
color: #6CBD7F;
color: #6cbd7f;
}
.change-pwd-link {
// font-weight: 600;
margin-left: 15px;
color: #3165DB;
color: #3165db;
}
}
</style>

@ -10,7 +10,7 @@
<groupId>com.jiuyv.sptcc.agile</groupId>
<artifactId>agile-portal</artifactId>
<version>0.2.6-SNAPSHOT</version>
<version>0.2.8-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>

@ -23,10 +23,13 @@ import org.springframework.web.bind.annotation.RestController;
import com.jcraft.jsch.ChannelSftp;
import com.jiuyv.sptcc.agile.batch.batchTask.entity.vo.TblBatchTaskVO;
import com.jiuyv.sptcc.agile.batch.common.BaseTime;
import com.jiuyv.sptcc.agile.batch.common.Constants;
import com.jiuyv.sptcc.agile.batch.common.R;
import com.jiuyv.sptcc.agile.batch.common.constant.TblPublicFilesEnum;
import com.jiuyv.sptcc.agile.batch.common.util.Sm4Util;
import com.jiuyv.sptcc.agile.batch.common.util.sftp.SFTPChannel;
import com.jiuyv.sptcc.agile.batch.common.util.sftp.model.SFTPConfig;
import com.jiuyv.sptcc.agile.batch.entity.TblDockerClientInfo;
import com.jiuyv.sptcc.agile.batch.entity.TblPublicFiles;
import com.jiuyv.sptcc.agile.batch.framework.SftpConfigProperties;
import com.jiuyv.sptcc.agile.batch.service.ISftpTempFileClearService;
@ -171,43 +174,60 @@ public class SftpTempFileClearController {
public R<String> clearDockerLogFile() throws Exception{
BaseTime timeVO = sftpTempFileClearService.getSysDate();
//查询实验室服务器清单,遍历
List<TblDockerClientInfo> clientlist = sftpTempFileClearService.getDockerClientList();
boolean flag=true;
//连接文件服务器清理批处理
SFTPChannel channelx = new SFTPChannel();
try {
//创建容器前先创建目录挂载目录(后续要使用sftp操作)
channelx.setConnect(createSftpCfg(), 60000);
//因为实验室总数量就几个,直接扫描实验室总目录下的文件
if(!dockerDataPath.endsWith("/")) {
dockerDataPath+="/";
}
String lscommand= String.format(DOCKER_SHELL_STAT_GREP, dockerDataPath,dockerDataPath);
List<String> list =channelx.commitCurrShellCmd(lscommand);
if(!list.isEmpty()) {
for(String x:list) {
if(StringUtils.isBlank(x)) {
continue;
}
String[] arrs= x.split(" +");
if(arrs[1].length()==10) {//没有毫秒,补全
arrs[1]=arrs[1]+"000";
}
// 获取文件的修改时间
Date modifiedDate = new Date(Long.valueOf(arrs[1]));
long d= timeVO.getDate().getTime()-modifiedDate.getTime();
Duration duration = Duration.of(d, ChronoUnit.MILLIS);
if(duration.toDays()>=clearLogFileDays) {
channelx.commitCurrShellCmd(String.format(SHELL_RM, arrs[0])
+" && "+ String.format(SHELL_RM, arrs[0].replace(".pid", "")));
}
//因为实验室总数量就几个,直接扫描实验室总目录下的文件
if(!dockerDataPath.endsWith("/")) {
dockerDataPath+="/";
}
String lscommand= String.format(DOCKER_SHELL_STAT_GREP, dockerDataPath,dockerDataPath);
for(TblDockerClientInfo clientlistx:clientlist) {
//连接文件服务器清理批处理
SFTPChannel channelx = new SFTPChannel();
try {
SFTPConfig cfg = new SFTPConfig();
cfg.setUsername(clientlistx.getDockerServerUsername());
try {
cfg.setPassword(Sm4Util.decryptEcb(Constants.SM4_SECERT_KEY, clientlistx.getDockerServerPassword()));
}catch (Exception e) {
// 兼容写法,不加密也可以
cfg.setPassword(clientlistx.getDockerServerPassword());
}
cfg.setPort(Integer.valueOf(clientlistx.getDockerServerPort()));
cfg.setHost(clientlistx.getDockerServerIp());
//创建容器前先创建目录挂载目录(后续要使用sftp操作)
channelx.setConnect(cfg, 60000);
List<String> list =channelx.commitCurrShellCmd(lscommand);
if(!list.isEmpty()) {
for(String x:list) {
if(StringUtils.isBlank(x)) {
continue;
}
String[] arrs= x.split(" +");
if(arrs[1].length()==10) {//没有毫秒,补全
arrs[1]=arrs[1]+"000";
}
// 获取文件的修改时间
Date modifiedDate = new Date(Long.valueOf(arrs[1]));
long d= timeVO.getDate().getTime()-modifiedDate.getTime();
Duration duration = Duration.of(d, ChronoUnit.MILLIS);
if(duration.toDays()>=clearLogFileDays) {
channelx.commitCurrShellCmd(String.format(SHELL_RM, arrs[0])
+" && "+ String.format(SHELL_RM, arrs[0].replace(".pid", "")));
}
}
}
} catch (Exception e) {
LOGGER.info("initDocker error>>" ,e);
flag=false;
}finally {
channelx.closeChannel();
}
} catch (Exception e) {
LOGGER.info("initDocker error>>" ,e);
flag=false;
}finally {
channelx.closeChannel();
}
if(!flag) {

@ -3,6 +3,7 @@ package com.jiuyv.sptcc.agile.batch.service;
import java.util.List;
import com.jiuyv.sptcc.agile.batch.common.BaseTime;
import com.jiuyv.sptcc.agile.batch.entity.TblDockerClientInfo;
import com.jiuyv.sptcc.agile.batch.entity.TblPublicFiles;
@ -24,5 +25,7 @@ public interface ISftpTempFileClearService {
/** 更新文件状态*/
public void doUpdateDataStatus(List<Long> filedIds) throws Exception;
/** 查询实验室服务器*/
public List<TblDockerClientInfo> getDockerClientList() throws Exception;
}

@ -12,7 +12,9 @@ import org.springframework.stereotype.Service;
import com.jiuyv.sptcc.agile.batch.common.BaseTime;
import com.jiuyv.sptcc.agile.batch.common.constant.TblPublicFilesEnum;
import com.jiuyv.sptcc.agile.batch.dao.ISysTimeBaseMapper;
import com.jiuyv.sptcc.agile.batch.dao.TblDockerClientInfoMapper;
import com.jiuyv.sptcc.agile.batch.dao.TblPublicFilesMapper;
import com.jiuyv.sptcc.agile.batch.entity.TblDockerClientInfo;
import com.jiuyv.sptcc.agile.batch.entity.TblPublicFiles;
import com.jiuyv.sptcc.agile.batch.entity.vo.TblPublicFilesVO;
@ -28,6 +30,8 @@ public class SftpTempFileClearImpl implements ISftpTempFileClearService {
private TblPublicFilesMapper tblPublicFilesMapper;
@Autowired
private ISysTimeBaseMapper sysTimeBaseMapper;
@Autowired
private TblDockerClientInfoMapper tblDockerClientInfoMapper;
@Override
public List<TblPublicFiles> getTempFileList(String dateStr,List<String> excludeFileSourceTypes) throws Exception {
@ -72,5 +76,11 @@ public class SftpTempFileClearImpl implements ISftpTempFileClearService {
public BaseTime getSysDate() throws Exception {
return sysTimeBaseMapper.selectSysCurrentTime();
}
@Override
public List<TblDockerClientInfo> getDockerClientList() throws Exception {
TblDockerClientInfo param = new TblDockerClientInfo();
param.setDataStatus(TblPublicFilesEnum.DATA_STATUS.NORMAL.getCode());
return tblDockerClientInfoMapper.selectItemList(param);
}
}

@ -43,7 +43,14 @@ console:
remoteApiService: http://172.16.12.107:18090
#测试模式默认关闭false开启测试为true会跳过一些逻辑
testFlag: false
#密码过期天数
passwordExpireDays: 9999
#密码过期提醒天数
passwordExpireReminderDays: 7
#登陆错误/密码修改错误次数
passwordErrorNum: 3
#登陆错误/密码修改错误锁定时间(分)
loginErrorLockTime: 30
spring:
application:
@ -102,6 +109,9 @@ filesftp:
maxIdle: 3
minIdle: 1
maxWait: PT3M
minEvictableIdleTime: PT15M
timeBetweenEvictionRuns: PT1M
softMinEvictableIdleTime: PT10M
sftpUserFile:
# host: 172.16.12.104
# port: 22
@ -133,6 +143,8 @@ operationtoken:
tokenTimes: 100
skipUrls:
- '/getInfo'
- '/sftp/file/addUpload'
- '/files/upload/**'
# 日志配置
logging:

@ -3,7 +3,7 @@
<parent>
<artifactId>agile-portal</artifactId>
<groupId>com.jiuyv.sptcc.agile</groupId>
<version>0.2.8-SNAPSHOT</version>
<version>0.2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -21,8 +21,8 @@ public class IpUtils {
// 未知地址
public static final String UNKNOWN = "XX XX";
private static final Logger LOGGER = LoggerFactory.getLogger(IpUtils.class);
private static final RestTemplate restTemplate = new RestTemplate();
private static final ObjectMapper objectMapper = new ObjectMapper();
private static RestTemplate restTemplate = new RestTemplate();
private static ObjectMapper objectMapper = new ObjectMapper();
public static String getRealAddressByIP(String ip, boolean isAddressEnabled) {
// 内网不查询

@ -127,6 +127,11 @@ public class PortalUserDTO implements Serializable {
*/
private String firstFlag;
/**
*
*/
private Integer pwdRemainderDate;
/**
* Getid
*/
@ -456,4 +461,12 @@ public class PortalUserDTO implements Serializable {
public void setFirstFlag(String firstFlag) {
this.firstFlag = firstFlag;
}
public Integer getPwdRemainderDate() {
return pwdRemainderDate;
}
public void setPwdRemainderDate(Integer pwdRemainderDate) {
this.pwdRemainderDate = pwdRemainderDate;
}
}

@ -12,7 +12,6 @@ import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import static com.jiuyv.sptccc.agile.common.utils.IpUtils.UNKNOWN;
import static org.junit.jupiter.api.Assertions.*;
@ -40,17 +39,13 @@ class IpUtilsTest {
@BeforeEach
void setUp() throws ReflectiveOperationException {
Field modifier = Field.class.getDeclaredField("modifiers");
modifier.setAccessible(true);
Field restTemplateField = IpUtils.class.getDeclaredField("restTemplate");
restTemplateField.setAccessible(true);
modifier.setInt(restTemplateField, restTemplateField.getModifiers() & ~Modifier.FINAL);
restTemplateField.set(null, restTemplate);
Field objectMapperField = IpUtils.class.getDeclaredField("objectMapper");
objectMapperField.setAccessible(true);
modifier.setInt(objectMapperField, objectMapperField.getModifiers() & ~Modifier.FINAL);
objectMapperField.set(null, objectMapper);
}

@ -3,7 +3,7 @@
<parent>
<groupId>com.jiuyv.sptcc.agile</groupId>
<artifactId>agile-portal</artifactId>
<version>0.2.8-SNAPSHOT</version>
<version>0.2.9-SNAPSHOT</version>
</parent>
<artifactId>agile-portal-gateway</artifactId>

@ -147,7 +147,7 @@ public class SysLoginService {
captcha = StringUtil.randomNumber(6);
localCache.setValueOfCacheName(CacheNames.CACHE_1MIN, phone, captcha);
localCache.setValueOfCacheName(CacheNames.CACHE_5MIN, phone, captcha);
LOGGER.info("手机{}验证码:{}", phone, captcha);
LOGGER.info("手机{}验证码:{}", StringUtil.strHide(phone), captcha);
if (consoleProperties.isCaptchaTest()) {
return captcha;
@ -302,7 +302,11 @@ public class SysLoginService {
if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
throw new ServiceException("原密码错误");
}
String encodePassword = passwordEncoder.encode(secretService.decodePassword(userPasswordDTO.getPassword()));
String password = secretService.decodePassword(userPasswordDTO.getPassword());
if (password.equals(oldPassword)) {
throw new ServiceException("新密码不能与原密码相同");
}
String encodePassword = passwordEncoder.encode(password);
userPasswordDTO.setPassword(encodePassword);
userPasswordDTO.setUserId(user.getUserId());
R<Void> r = userService.resetUserPwd(userPasswordDTO);

@ -59,7 +59,8 @@ public class UserDetailsServiceImpl implements UserDetailsService {
// 第一次登录,只有基本权限,修改密码后获取所有权限
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(FrontConstant.AUTHORITY_BASE));
if (userDTO.getFirstFlag() != null) {
Integer pwdUpdateTime = userDTO.getPwdRemainderDate();
if (userDTO.getFirstFlag() != null && (pwdUpdateTime == null || pwdUpdateTime >= 0)) {
authorities.add(new SimpleGrantedAuthority(FrontConstant.AUTHORITY_ALL));
}
return new LoginUser(user.getUserId(), user.getDeptId(), user, authorities);

@ -175,6 +175,10 @@ public class TblPortalUser implements Serializable {
*/
private String firstFlag;
/**
*
*/
private Integer pwdRemainderDate;
/**
*
@ -623,6 +627,14 @@ public class TblPortalUser implements Serializable {
this.firstFlag = firstFlag;
}
public Integer getPwdRemainderDate() {
return pwdRemainderDate;
}
public void setPwdRemainderDate(Integer pwdRemainderDate) {
this.pwdRemainderDate = pwdRemainderDate;
}
public Map<String, Object> getParams() {
if (params == null) {
params = new HashMap<>();

@ -91,6 +91,11 @@ public class UserInfoDTO implements Serializable {
*/
private String firstFlag;
/**
*
*/
private Integer pwdRemainderDate;
public Long getUserId() {
return userId;
}
@ -226,4 +231,12 @@ public class UserInfoDTO implements Serializable {
public void setFirstFlag(String firstFlag) {
this.firstFlag = firstFlag;
}
public Integer getPwdRemainderDate() {
return pwdRemainderDate;
}
public void setPwdRemainderDate(Integer pwdRemainderDate) {
this.pwdRemainderDate = pwdRemainderDate;
}
}

@ -283,6 +283,10 @@ class SysLoginServiceTest {
@Test
void changePassword() {
ResUserPasswordDTO userPasswordDTO = mock(ResUserPasswordDTO.class);
when(userPasswordDTO.getPassword()).thenReturn("a");
when(userPasswordDTO.getOldPassword()).thenReturn("a");
when(secretService.decodePassword("a")).thenReturn("A");
when(secretService.decodePassword("b")).thenReturn("B");
LoginUser loginUser = mock(LoginUser.class);
securityUtilsMockedStatic.when(SecurityUtils::getLoginUser).thenReturn(loginUser);
TblPortalUser portalUser = mock(TblPortalUser.class);
@ -304,6 +308,14 @@ class SysLoginServiceTest {
assertTrue(e instanceof ServiceException);
}
when(userPasswordDTO.getOldPassword()).thenReturn("b");
try {
sysLoginService.changePassword(userPasswordDTO);
fail();
} catch (Exception e) {
assertTrue(e instanceof ServiceException);
}
when(userService.resetUserPwd(userPasswordDTO)).thenReturn(R.ok());
servletUtilsMockedStatic.when(ServletUtils::getSession).thenReturn(mock(HttpSession.class));
try {

@ -3,7 +3,7 @@
<parent>
<artifactId>agile-portal</artifactId>
<groupId>com.jiuyv.sptcc.agile</groupId>
<version>0.2.8-SNAPSHOT</version>
<version>0.2.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -30,4 +30,4 @@ eureka:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://172.16.12.107:8761/eureka/
defaultZone: http://172.16.12.104:8761/eureka/

@ -3,7 +3,7 @@
<parent>
<groupId>com.jiuyv.sptcc.agile</groupId>
<artifactId>agile-portal</artifactId>
<version>0.2.8-SNAPSHOT</version>
<version>0.2.9-SNAPSHOT</version>
</parent>
<artifactId>agile-portal-service</artifactId>

@ -43,6 +43,16 @@ public class ConsoleProperties {
*/
private long downloadMaxFile = 100L;
/**
* 1
*/
private int passwordExpirationDates = 60;
/**
* 7
*/
private int passwordExpirationWarnDates = 15;
public boolean isAddressEnabled() {
return addressEnabled;
@ -91,4 +101,20 @@ public class ConsoleProperties {
public void setDownloadMaxFile(long downloadMaxFile) {
this.downloadMaxFile = downloadMaxFile;
}
public int getPasswordExpirationDates() {
return passwordExpirationDates;
}
public void setPasswordExpirationDates(int passwordExpirationDates) {
this.passwordExpirationDates = passwordExpirationDates;
}
public int getPasswordExpirationWarnDates() {
return passwordExpirationWarnDates;
}
public void setPasswordExpirationWarnDates(int passwordExpirationWarnDates) {
this.passwordExpirationWarnDates = passwordExpirationWarnDates;
}
}

@ -7,13 +7,15 @@ import com.jiuyv.sptccc.agile.common.core.domain.R;
import com.jiuyv.sptccc.agile.common.enums.BusinessType;
import com.jiuyv.sptccc.agile.dto.PortalUserDTO;
import com.jiuyv.sptccc.agile.dto.ResUserPasswordDTO;
import com.jiuyv.sptccc.agile.framework.config.ConsoleProperties;
import com.jiuyv.sptccc.agile.portal.domain.TblPortalUser;
import com.jiuyv.sptccc.agile.portal.service.IPortalUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.jiuyv.sptccc.agile.portal.domain.TblPortalUser;
import com.jiuyv.sptccc.agile.portal.service.IPortalUserService;
import java.util.Date;
import static com.jiuyv.sptccc.agile.api.PortalUserFeignApi.API_PATH_PREFIX;
@ -28,9 +30,11 @@ import static com.jiuyv.sptccc.agile.api.PortalUserFeignApi.API_PATH_PREFIX;
public class PortalUserController implements PortalUserFeignApi {
private final IPortalUserService userService;
private final ConsoleProperties consoleProperties;
public PortalUserController(IPortalUserService userService) {
public PortalUserController(IPortalUserService userService, ConsoleProperties consoleProperties) {
this.userService = userService;
this.consoleProperties = consoleProperties;
}
/**
@ -49,6 +53,13 @@ public class PortalUserController implements PortalUserFeignApi {
String[] split = avatar.split("/");
userDTO.setAvatar("content/images/" + split[split.length - 1]);
}
// 计算密码过期时间
Date pwdUpdateTime = user.getPwdUpdateTime();
long diff = consoleProperties.getPasswordExpirationDates()
- (System.currentTimeMillis() - pwdUpdateTime.getTime()) / 1000 / 60 / 60 / 24;
if (diff <= consoleProperties.getPasswordExpirationWarnDates()) {
userDTO.setPwdRemainderDate((int) diff);
}
return R.ok(userDTO);
}

@ -173,6 +173,11 @@ public class TblPortalUser extends BaseEntity {
*/
private String firstFlag;
/**
*
*/
private Date pwdUpdateTime;
/**
* Getid
@ -615,4 +620,12 @@ public class TblPortalUser extends BaseEntity {
public void setFirstFlag(String firstFlag) {
this.firstFlag = firstFlag;
}
public Date getPwdUpdateTime() {
return pwdUpdateTime;
}
public void setPwdUpdateTime(Date pwdUpdateTime) {
this.pwdUpdateTime = pwdUpdateTime;
}
}

@ -20,4 +20,4 @@ eureka:
register-with-eureka: true
fetchRegistry: true
service-url:
defaultZone: http://172.16.12.107:8761/eureka/
defaultZone: http://172.16.12.104:8761/eureka/

@ -34,6 +34,7 @@
<result property="loginErrorCount" column="login_error_count"/>
<result property="lastLoginErrorTime" column="last_login_error_time"/>
<result property="firstFlag" column="first_flag"/>
<result property="pwdUpdateTime" column="pwd_update_time"/>
</resultMap>
<sql id="selectUserVo">
@ -63,7 +64,8 @@
u.social_credit_code,
u.enterprise_industry,
u.enterprise_address,
u.first_flag
u.first_flag,
u.pwd_update_time
from tbl_portal_user u
</sql>
@ -118,7 +120,9 @@
update tbl_portal_user
set version_num = version_num + 1,
password= #{password},
first_flag='1'
first_flag='1',
update_time = now(),
pwd_update_time = now()
where user_id = #{userId}
</update>
</mapper>

@ -2,6 +2,7 @@ package com.jiuyv.sptccc.agile.portal.service.impl;
import com.github.pagehelper.Page;
import com.jiuyv.sptccc.agile.common.exception.ServiceException;
import com.jiuyv.sptccc.agile.framework.config.ConsoleProperties;
import com.jiuyv.sptccc.agile.portal.domain.TblPortalContent;
import com.jiuyv.sptccc.agile.portal.mapper.PortalContentMapper;
import com.jiuyv.sptccc.agile.portal.service.IFileService;
@ -35,6 +36,9 @@ class PortalContentServiceImplTest {
@Mock
private IFileService fileService;
@Mock
private ConsoleProperties consoleProperties;
@Test
void getContentList() {
String showType = "1";

@ -4,7 +4,7 @@
<parent>
<groupId>com.jiuyv.sptcc.agile</groupId>
<artifactId>agile-portal</artifactId>
<version>0.2.8-SNAPSHOT</version>
<version>0.2.9-SNAPSHOT</version>
</parent>
<artifactId>agile-portal-ui</artifactId>

@ -1,7 +1,16 @@
<template>
<div class="find-password container">
<h3 class="title">修改密码<span v-if="firstFlag == '0'"
style="text-align: center;padding: 10px;color: red;">(初次登陆需修改初始密码)</span>
<h3 class="title">
修改密码<span
v-if="firstFlag == '0'"
style="text-align: center; padding: 10px; color: red"
>(初次登陆需修改初始密码)</span
>
<span
v-if="pwdRemainderDate <= 15 && pwdRemainderDate != null"
style="text-align: center; padding: 10px; color: red"
>(密码失效修改密码)</span
>
</h3>
<el-card class="procees-contaner">
@ -10,163 +19,228 @@
<el-step title="完成" description=""></el-step>
</el-steps>
<!-- step2 -->
<el-form v-if="processActive == 1" ref="form" :rules="rules" :model="form" label-width="100px">
<el-form
v-if="processActive == 1"
ref="form"
:rules="rules"
:model="form"
label-width="100px"
>
<el-form-item label="原密码" prop="oldPassword">
<el-input v-model="form.oldPassword" type="password"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="password">
<el-input v-model="form.password" :type="flagType" auto-complete="off" placeholder="" @input="strengthColor">
<i style="cursor: pointer;" slot="suffix" class="el-input__icon el-icon-view" @click="getFlageye()"></i>
<el-input
v-model="form.password"
:type="flagType"
auto-complete="off"
placeholder=""
@input="strengthColor"
>
<i
style="cursor: pointer"
slot="suffix"
class="el-input__icon el-icon-view"
@click="getFlageye()"
></i>
</el-input>
<div class="divClass">
<span
:class="passwords == '1' ? 'weak' : '' || passwords == '2' ? 'medium' : '' || passwords == '3' ? 'strong' : ''"></span>
<span :class="passwords == '2' ? 'medium' : '' || passwords == '3' ? 'strong' : ''"></span>
:class="
passwords == '1'
? 'weak'
: '' || passwords == '2'
? 'medium'
: '' || passwords == '3'
? 'strong'
: ''
"
></span>
<span
:class="
passwords == '2'
? 'medium'
: '' || passwords == '3'
? 'strong'
: ''
"
></span>
<span :class="passwords == '3' ? 'strong' : ''"></span>
</div>
</el-form-item>
<el-form-item label="确认密码" prop="passwords">
<el-input v-model="form.passwords" type="password"></el-input>
</el-form-item>
<el-form-item label=""> <el-button type="primary" @click="handleAuthon">
提交</el-button></el-form-item>
<el-form-item label="">
<el-button type="primary" @click="handleAuthon">
提交</el-button
></el-form-item
>
</el-form>
<!-- step3 -->
<el-form v-if="processActive == 2" ref="form" :model="form" label-width="0px">
<el-form
v-if="processActive == 2"
ref="form"
:model="form"
label-width="0px"
>
<el-form-item label="">
<div class="success-tips" style="color: #1ae51ad1;font-size: 24px;font-weight: 600;text-align: center;"><i
class="icon el-icon-success"></i> 修改成功</div>
<div class="go-back" style="text-align: center;"><span style="color: red;font-size: 18px;font-weight: bold;">{{
remainingTime }}</span>秒后 <span>自动返回登录页</span></div>
<div class="btn-back" style="text-align: center;"><el-button type="primary" @click="logout"></el-button>
<div
class="success-tips"
style="
color: #1ae51ad1;
font-size: 24px;
font-weight: 600;
text-align: center;
"
>
<i class="icon el-icon-success"></i> 修改成功
</div>
<div class="go-back" style="text-align: center">
<span style="color: red; font-size: 18px; font-weight: bold">{{
remainingTime
}}</span
>秒后 <span>自动返回登录页</span>
</div>
<div class="btn-back" style="text-align: center">
<el-button type="primary" @click="logout"></el-button>
</div>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
import { changePassword } from "@/api/user";
import { getKeyiv } from "@/api/login";
import { encrypt } from '@/utils/jsencrypt'
import { encrypt } from "@/utils/jsencrypt";
export default {
name: "ResetPwd",
data() {
return {
isShowMenu: false,
passwords: '1',
flagType: 'password',
passwords: "1",
flagType: "password",
processActive: 1,
form: {
oldPassword: '',
password: '',
passwords: '',
oldPassword: "",
password: "",
passwords: "",
},
firstFlag: '',
firstFlag: "",
pwdRemainderDate: "",
remainingTime: 5,
keyiv: '',
keyiv: "",
countDown: 10,
//
rules: {
oldPassword: [
{ required: true, message: "原密码不能为空", trigger: "blur" }
{ required: true, message: "原密码不能为空", trigger: "blur" },
],
password: [
{ required: true, message: "密码不能为空", trigger: "blur" },
{
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/,
message: '密码须包含数字、大小写字母且长度在8-16之间',
trigger: 'blur'
message: "密码须包含数字、大小写字母且长度在8-16之间",
trigger: "blur",
},
],
passwords: [
{ required: true, message: "密码不能为空", trigger: "blur" },
{
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/,
message: '密码须包含数字、大小写字母且长度在8-16之间',
trigger: 'blur'
message: "密码须包含数字、大小写字母且长度在8-16之间",
trigger: "blur",
},
],
}
}
},
};
},
created() {
const storedData = localStorage.getItem('myData');
const storedData = localStorage.getItem("myData");
if (storedData) {
const data = JSON.parse(storedData);
this.firstFlag = data.firstFlag
this.firstFlag = data.firstFlag;
this.pwdRemainderDate = data.pwdRemainderDate;
}
this.getKeyiv();
},
methods: {
getFlageye() {
this.flagType = this.flagType == 'password' ? 'text' : 'password';//textpassword
this.flagType = this.flagType == "password" ? "text" : "password"; //textpassword
},
strengthColor() {
if (this.form.password.length <= 6) {
this.passwords = '1'//
this.passwords = "1"; //
} else if (this.form.password.length <= 10) {
this.passwords = '2'//
this.passwords = "2"; //
} else {
this.passwords = '3' // 绿
this.passwords = "3"; // 绿
}
},
getKeyiv() {
getKeyiv().then((response) => {
this.keyiv = response.data
})
this.keyiv = response.data;
});
},
logout() {
this.$store.dispatch('LogOut').then(() => {
this.$router.push("/login")
})
this.$store.dispatch("LogOut").then(() => {
this.$router.push("/login");
});
},
//
handleAuthon() {
if (this.form.password != this.form.passwords) {
this.$message({
type: 'warning',
message: '新密码与确认密码不一致!'
type: "warning",
message: "新密码与确认密码不一致!",
});
return
return;
}
this.$refs["form"].validate(valid => {
this.$refs["form"].validate((valid) => {
if (valid) {
this.form.passwords = ''
this.form.oldPassword = encrypt(this.keyiv, this.form.oldPassword + ',' + new Date().getTime())
this.form.password = encrypt(this.keyiv, this.form.password + ',' + new Date().getTime())
changePassword(this.form).then(res => {
this.processActive++
this.countdownInterval = setInterval(() => {
//
console.log('倒计时结束');
if (this.remainingTime > 0) {
this.remainingTime--; // 1
} else {
clearInterval(this.countdownInterval); //
}
this.$store.dispatch('LogOut').then(() => {
this.$router.push("/login")
})
}, 1000);
})
this.form.passwords = "";
this.form.oldPassword = encrypt(
this.keyiv,
this.form.oldPassword + "," + new Date().getTime()
);
this.form.password = encrypt(
this.keyiv,
this.form.password + "," + new Date().getTime()
);
changePassword(this.form).then((res) => {
debugger;
if (res.code == 200) {
this.processActive++;
this.countdownInterval = setInterval(() => {
//
console.log("倒计时结束");
if (this.remainingTime > 0) {
this.remainingTime--; // 1
} else {
clearInterval(this.countdownInterval); //
}
this.$store.dispatch("LogOut").then(() => {
this.$router.push("/login");
});
}, 1000);
} else {
this.form.oldPassword = "";
this.form.password = "";
this.strengthColor();
}
});
}
})
}
});
},
},
beforeDestroy() {
clearTimeout(this.countdownInterval); //
},
}
};
</script>
<style lang="scss" scoped>
@ -214,21 +288,18 @@ export default {
}
.weak {
background-color: #F56C6C;
background-color: #f56c6c;
/* 红色 */
}
.medium {
background-color: #E6A23C;
background-color: #e6a23c;
/* 橙色 */
}
.strong {
background-color: #67C23A;
background-color: #67c23a;
/* 绿色 */
}
}
</style>

@ -14,8 +14,8 @@
</el-col>
<el-col :span="24">
<el-form-item label="状态">
<span v-if="form.status == '0'" style="color: #6CBD7F;"></span>
<span v-else style="color: red;">停用</span>
<span v-if="form.status == '0'" style="color: #6cbd7f"></span>
<span v-else style="color: red">停用</span>
</el-form-item>
</el-col>
<!-- <el-col :span="24">
@ -45,41 +45,82 @@
</el-col>
<el-col :span="24">
<el-form-item label="登录密码">
<i class="icon el-icon-success"></i><span>已设置</span><router-link to="/resetpwd"
class="change-pwd-link">更改密码</router-link>
<i class="icon el-icon-success"></i><span>已设置</span
><router-link to="/resetpwd" class="change-pwd-link"
>更改密码</router-link
>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-dialog
width="400px"
title="密码到期提示"
:visible.sync="opens"
append-to-body
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<div style="text-align: center; font-size: 18px">
登录密码还有<span
style="color: red; font-size: 25px; font-weight: bold"
>{{ form.pwdRemainderDate }}</span
>天到期请尽快修改密码
</div>
<div slot="footer" class="dialog-footer" style="text-align: right">
<el-button @click="opens = false">关闭</el-button>
<el-button type="primary" @click="handlefile"></el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getInfo } from '@/api/login'
import { getInfo } from "@/api/login";
export default {
name: "UserInfo",
data() {
return {
form: {
}
}
opens: false,
form: {},
};
},
created() {
this.getUserInfo()
this.getUserInfo();
},
methods: {
getUserInfo() {
getInfo().then(res => {
this.form = res.data
if (this.form.firstFlag == '0') {
this.$router.push("/resetpwd").catch(() => {
getInfo().then((res) => {
this.form = res.data;
if (this.form.firstFlag == "0") {
this.$router.push("/resetpwd").catch(() => {});
}
debugger;
if (
this.form.pwdRemainderDate >= 0 &&
this.form.pwdRemainderDate != null
) {
this.opens = true;
}
if (
this.form.pwdRemainderDate < 0 &&
this.form.pwdRemainderDate != null
) {
this.$message({
type: "error",
message: "登陆密码到期,请修改密码!",
});
this.$router.push("/resetpwd").catch(() => {});
}
})
}
}
}
});
},
handlefile() {
this.$router.push("/resetpwd").catch(() => {});
},
},
};
</script>
<style lang="scss">
.personal-info {
@ -104,14 +145,13 @@ export default {
.el-icon-success {
margin-right: 6px;
color: #6CBD7F;
color: #6cbd7f;
}
.change-pwd-link {
// font-weight: 600;
margin-left: 15px;
color: #3165DB;
color: #3165db;
}
}
</style>

@ -10,7 +10,7 @@
<groupId>com.jiuyv.sptcc.agile</groupId>
<artifactId>agile-portal</artifactId>
<version>0.2.8-SNAPSHOT</version>
<version>0.2.9-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>

@ -1,14 +1,14 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>agile-common</artifactId>
<version>1.0.14-SNAPSHOT</version>
<version>1.2.2-SNAPSHOT</version>
<packaging>jar</packaging>
<description />
<parent>
<groupId>com.jiuyv.sptcc.agile</groupId>
<artifactId>agile-system</artifactId>
<version>1.0.14-SNAPSHOT</version>
<version>1.2.2-SNAPSHOT</version>
</parent>
<dependencies>

@ -5,12 +5,12 @@
<parent>
<groupId>com.jiuyv.sptcc.agile</groupId>
<artifactId>agile-system</artifactId>
<version>1.0.14-SNAPSHOT</version>
<version>1.2.2-SNAPSHOT</version>
</parent>
<groupId>com.jiuyv.sptcc.agile</groupId>
<artifactId>agile-system-console-ui</artifactId>
<version>1.0.14-SNAPSHOT</version>
<version>1.2.2-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<frontend-maven-plugin.version>1.12.1</frontend-maven-plugin.version>

@ -64,3 +64,17 @@ export function getCodeImg() {
closeLoading : true
})
}
// 登录前重置密码
export function loginResetPwd(data) {
return request({
url: '/loginResetPwd',
headers: {
isToken: false
},
method: 'post',
data: data,
closeLoading : true
})
}

@ -16,7 +16,7 @@ import { isRelogin } from '@/utils/request'
NProgress.configure({ showSpinner: false })
const whiteList = ['/login']
const whiteList = ['/login','/loginResetPwd','/loginResetPassword']
router.beforeEach((to, from, next) => {

@ -66,6 +66,16 @@ export const constantRoutes = [
component: () => import('@/views/error/401'),
hidden: true
},
{
path: '/loginResetPwd',
component: () => import('@/views/loginResetPwd'),
hidden: true
},
{
path: '/loginResetPassword',
component: () => import('@/views/loginResetPassword'),
hidden: true
},
// {
// path: '',
// component: Layout,

@ -1,6 +1,9 @@
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { getToken, setToken, removeToken,setLoginPasswordExpiringReminder,
setLoginPasswordExpiringReminderFinished,getLoginPasswordExpiringReminder,getLoginPasswordExpiringReminderFinished
} from '@/utils/auth'
import { encrypt, decrypt } from '@/utils/jsencrypt'
import { MessageBox} from 'element-ui'
const user = {
state: {
token: getToken(),
@ -47,6 +50,11 @@ const user = {
const uuid = userInfo.uuid
return new Promise((resolve, reject) => {
login(username, password, code, uuid).then(res => {
if(res === 'password_expired'){
reject('password_expired');
return;
}
setLoginPasswordExpiringReminder();//每次登陆校验密码是否快过期,只要不清理缓存,每次都会提醒
setToken('res.token')
commit('SET_TOKEN', 'res.token')
resolve()
@ -62,6 +70,22 @@ const user = {
getInfo().then(res => {
const user = res.user
const avatar = (user.avatar == "" || user.avatar == null) ? require("@/assets/images/profile.png") : process.env.VUE_APP_BASE_API + user.avatar;
//只要没有缓存标志,每次登陆都会都提醒
if(res.passwordExpiringReminder && getLoginPasswordExpiringReminder()!=null && getLoginPasswordExpiringReminderFinished()==null){
setLoginPasswordExpiringReminderFinished();//设置已提醒
MessageBox.confirm('您的密码将于'+res.passwordExpiredDate+'过期,请及时修改密码', '系统提示', {
confirmButtonText: '立即修改',
cancelButtonText: '稍后修改',
type: 'warning'
}
).then(() => {
location.href = '/#/user/profile';
}).catch(() => {
});
}
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
commit('SET_ROLES', res.roles)
commit('SET_PERMISSIONS', res.permissions)

@ -4,21 +4,38 @@ import cache from '@/plugins/cache'
const TokenKey = 'Admin-Token'
const passwordExpiringReminder = 'passwordExpiringReminder'
const passwordExpiringReminderFinished = 'passwordExpiringReminderFinished'
//其实这个token没用只是保留了原来的写法
export function getToken() {
//return Cookies.get(TokenKey)
return cache.local.get(TokenKey);
return Cookies.get(TokenKey)
//return cache.local.get(TokenKey);
}
export function setToken(token) {
//return Cookies.set(TokenKey, token)
cache.local.set(TokenKey,token);
return token;
return Cookies.set(TokenKey, token)
//cache.local.set(TokenKey,token);
//return token;
}
export function removeToken() {
//return Cookies.remove(TokenKey);
cache.local.remove(TokenKey);
return TokenKey;
return Cookies.remove(TokenKey);
//cache.local.remove(TokenKey);
//return TokenKey;
}
export function setLoginPasswordExpiringReminder() {
cache.local.set(passwordExpiringReminder,"1");
}
export function setLoginPasswordExpiringReminderFinished() {
cache.local.remove(passwordExpiringReminder);
cache.local.set(passwordExpiringReminderFinished,"1");
}
export function getLoginPasswordExpiringReminder() {
return cache.local.get(passwordExpiringReminder);
}
export function getLoginPasswordExpiringReminderFinished() {
return cache.local.get(passwordExpiringReminderFinished);
}

@ -153,7 +153,20 @@ service.interceptors.response.use(res => {
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
}
if (code === '401') {
if (code === 'password_expired') {
MessageBox.confirm('密码已过期,请修改密码', '系统提示', {
confirmButtonText: '立即修改',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
location.href = '/#/loginResetPassword';
}).catch(() => {
});
return code;
}
else if (code === '401') {
if (!isRelogin.show) {
isRelogin.show = true;
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {

@ -234,3 +234,14 @@ export async function blobValidate(data) {
return true;
}
}
// 生成一个唯一ID处理过程临时使用应该不会重复
export function generateUniqueID() {
// 获取当前时间戳
const timestamp = new Date().getTime();
// 生成一个随机10位整数
const randomNum = Math.floor(Math.random() * 10000000000);
// 组合时间戳和随机数来创建唯一ID
const uniqueID = timestamp.toString() + randomNum.toString();
return uniqueID;
}

@ -37,7 +37,9 @@
<span v-else> ...</span>
</el-button>
<div style="float: right;" v-if="register">
<router-link class="link-type" :to="'/register'">立即注册</router-link>
<!-- <router-link class="link-type" :to="'/register'">立即注册</router-link> -->
<!-- <router-link class="link-type" :to="'/loginResetPwd'"> 修改密码 </router-link> -->
<router-link class="link-type" :to="'/loginResetPassword'"> 修改密码 </router-link>
</div>
</el-form-item>
</div>
@ -82,7 +84,7 @@ export default {
//
captchaOnOff: true,
//
register: false,
register: true,
redirect: undefined
};
},
@ -148,8 +150,11 @@ export default {
});
// this.$router.push({ path: this.redirect || "/" }).catch(() => {
// });
}).catch(() => {
}).catch((err) => {
this.loading = false;
if(err === 'password_expired'){
return;
}
if (this.captchaOnOff) {
this.getCode();
}
@ -204,7 +209,7 @@ export default {
.divform {
width: 400px;
padding: 70px 40px 50px;
padding: 70px 40px 10px;
border-radius: 6px;
background: #ffffff;
margin: auto;

@ -328,20 +328,20 @@ export default {
//
rules: {
userName: [
{required: true, message: "户名不能为空", trigger: "blur"},
{min: 2, max: 20, message: '户名长度必须介于 2 和 20 之间', trigger: 'blur'}
{required: true, message: "户名不能为空", trigger: "blur"},
{min: 2, max: 20, message: '户名长度必须介于 2 和 20 之间', trigger: 'blur'}
],
nickName: [
{required: true, message: "用户姓名不能为空", trigger: "blur"}
{required: true, message: "姓名不能为空", trigger: "blur"}
],
enterpriseIndustry: [
{required: true, message: "企业行业不能为空", trigger: "blur"}
],
password: [
{required: true, message: "账户密码不能为空", trigger: "blur"},
{required: true, message: "密码不能为空", trigger: "blur"},
{
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/,
message: '密码须包含数字、大小写字母且长度在8-16之间',
message: '须含大小写字母、数字长度8-16',
trigger: 'blur'
},
],

@ -1,7 +1,7 @@
<template>
<div class="register">
<el-form ref="registerForm" :model="registerForm" :rules="registerRules" class="register-form">
<h3 class="title">若依后台管理系统</h3>
<h3 class="title">用户注册</h3>
<el-form-item prop="username">
<el-input v-model="registerForm.username" type="text" auto-complete="off" placeholder="账号">
<svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />

@ -344,17 +344,17 @@ export default {
rules: {
deptId: [{required: true, message: "归属部门不能为空", trigger: "blur"}],
userName: [
{required: true, message: "用户账号不能为空", trigger: "blur"},
{min: 2, max: 20, message: '用户账号长度必须介于 2 和 20 之间', trigger: 'blur'}
{required: true, message: "用户不能为空", trigger: "blur"},
{min: 2, max: 20, message: '用户长度必须介于 2 和 20 之间', trigger: 'blur'}
],
nickName: [
{required: true, message: "用户姓名不能为空", trigger: "blur"}
{required: true, message: "姓名不能为空", trigger: "blur"}
],
password: [
{required: true, message: "账户密码不能为空", trigger: "blur"},
{required: true, message: "密码不能为空", trigger: "blur"},
{
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/,
message: '密码须包含数字、大小写字母且长度在8-16之间',
message: '须含大小写字母、数字长度8-16',
trigger: 'blur'
},
],

@ -30,12 +30,9 @@ import { encrypt, decrypt } from '@/utils/jsencrypt'
export default {
data() {
const equalToPassword = (rule, value, callback) => {
const reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/
if (this.user.newPassword !== value) {
callback(new Error("两次输入的密码不一致"));
} else if (reg.test(value) === false) {
callback(new Error('密码须包含数字、大小写字母且长度在8-16之间'))
} else {
}else {
callback();
}
};
@ -55,7 +52,9 @@ export default {
],
confirmPassword: [
{ required: true, message: "确认密码不能为空", trigger: "blur" },
{ required: true, validator: equalToPassword, trigger: "blur" }
{ required: true, validator: equalToPassword, trigger: "blur" },
{ pattern: /[^\u4e00-\u9fa5\s]+/, message: '不允许有中文或空格', trigger: 'blur' },
{ pattern: /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!@#$%^&*()_+])[A-Za-z\d!@#$%^&*()_+]{8,20}$/, message: '需包含大、小写字母+数字+特殊字符长度8-20', trigger: 'blur' },
]
}
};

@ -3,7 +3,7 @@
<parent>
<groupId>com.jiuyv.sptcc.agile</groupId>
<artifactId>agile-system</artifactId>
<version>1.0.14-SNAPSHOT</version>
<version>1.2.2-SNAPSHOT</version>
</parent>
<artifactId>agile-system-console</artifactId>

@ -41,6 +41,17 @@ public class ConsoleConfig {
/** 测试类标志,为了屏蔽一些远程结果 */
private static boolean testFlag=false;
//密码过期时间,登陆前修改密码
private static int passwordExpireDays=90;
//密码快期天数提醒
private static int passwordExpireReminderDays=7;
//密码过期后,修改错误次数
private static int passwordErrorNum=3;
//登陆错误/密码修改错误锁定时间(分)
private static int loginErrorLockTime=30;
public String getName() {
return name;
}
@ -183,5 +194,72 @@ public class ConsoleConfig {
public static void setTestFlag2(boolean testFlag) {
ConsoleConfig.testFlag = testFlag;
}
/**
* @return the passwordExpireDays
*/
public static int getPasswordExpireDays() {
return passwordExpireDays;
}
/**
* @param passwordExpireDays the passwordExpireDays to set
*/
public static void setPasswordExpireDays(int passwordExpireDays) {
setPasswordExpireDays2(passwordExpireDays);
}
public static void setPasswordExpireDays2(int passwordExpireDays) {
ConsoleConfig.passwordExpireDays = passwordExpireDays;
}
/**
* @return the passwordErrorNum
*/
public static int getPasswordErrorNum() {
return passwordErrorNum;
}
/**
* @param passwordErrorNum the passwordErrorNum to set
*/
public static void setPasswordErrorNum(int passwordErrorNum) {
setPasswordErrorNum2(passwordErrorNum);
}
public static void setPasswordErrorNum2(int passwordErrorNum) {
ConsoleConfig.passwordErrorNum = passwordErrorNum;
}
/**
* @return the loginErrorLockTime
*/
public static int getLoginErrorLockTime() {
return loginErrorLockTime;
}
/**
* @param loginErrorLockTime the loginErrorLockTime to set
*/
public static void setLoginErrorLockTime(int loginErrorLockTime) {
setLoginErrorLockTime2(loginErrorLockTime);
}
public static void setLoginErrorLockTime2(int loginErrorLockTime) {
ConsoleConfig.loginErrorLockTime = loginErrorLockTime;
}
/**
* @return the passwordExpireReminderDays
*/
public static int getPasswordExpireReminderDays() {
return passwordExpireReminderDays;
}
/**
* @param passwordExpireReminderDays the passwordExpireReminderDays to set
*/
public static void setPasswordExpireReminderDays(int passwordExpireReminderDays) {
setPasswordExpireReminderDays2(passwordExpireReminderDays);
}
public static void setPasswordExpireReminderDays2(int passwordExpireReminderDays) {
ConsoleConfig.passwordExpireReminderDays = passwordExpireReminderDays;
}
}

@ -92,6 +92,15 @@ public class SFTPChannel {
}
LOG.debug("Channel connection close!");
}
/**
*
*/
public void closeInnerChannel() {
if (channel != null) {
channel.disconnect();
}
}
/**
* ,
* 使exec(shell)

@ -1,19 +1,22 @@
package com.jiuyv.sptccc.agile.common.utils.sftp;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StreamUtils;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jiuyv.sptccc.agile.common.exception.UtilException;
@ -23,8 +26,6 @@ import com.jiuyv.sptccc.agile.common.utils.sftp.model.SftpProgress;
import com.jiuyv.sptccc.agile.common.utils.sftp.monitor.SftpComplexProgressMonitor;
import com.jiuyv.sptccc.agile.common.utils.sftp.monitor.SftpSimpleProgressMonitor;
import org.apache.commons.lang3.StringUtils;
/**
* sftp
* SFTPChannel
@ -39,7 +40,7 @@ public class SftpFileUtils
private static final Logger LOG = LoggerFactory.getLogger(SftpFileUtils.class);
//sftp等待时间单位毫秒
private static final int TIMEOUT = 30000;
private static final int TIMEOUT = 120000;
//是文件路径
private static final Pattern FILE_SUFFIX = Pattern.compile("\\.[A-Z0-9]+$",Pattern.CASE_INSENSITIVE);
@ -206,6 +207,7 @@ public class SftpFileUtils
throw new UtilException("uploadComplexMonitor sftp error");
}finally {
// channel.closeChannel();
channel.closeInnerChannel();
hostPool.returnSession(session);//不再关闭,直接返还连接
}
}
@ -241,12 +243,16 @@ public class SftpFileUtils
return;
}
long start=progress.getTransferedSize();
//response.setHeader(HttpHeaders.LAST_MODIFIED, "");//基于文件修改时间的字符串
response.setHeader(HttpHeaders.LAST_MODIFIED, attrs.getSize()+"");//基于文件修改时间的字符串
response.setHeader(HttpHeaders.CONTENT_TYPE, "application/octet-stream");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+ServletUtils.urlEncode(progress.getFileName()));
response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
response.setHeader(HttpHeaders.CONTENT_LENGTH, (attrs.getSize() - start)+"");
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + (start>0?start+1:start) + "-" + (attrs.getSize() - 1) + "/" +attrs.getSize());
if(start==0) {
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes 0-" + (attrs.getSize() - 1) + "/" +attrs.getSize());
}else {
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + start + "-" + (attrs.getSize()-1) + "/" +attrs.getSize());
}
}catch(Exception e){
//返回
LOG.info("downloadComplexMonitor sftp error :file not exist");
@ -265,6 +271,93 @@ public class SftpFileUtils
}
}
public static void downloadComplexMonitorEach(SFTPConfig sftpDetails,String src, HttpServletResponse response
,SftpProgress progress,ISftpProressService progressService) throws Exception {
if(!isFile(src)) {
throw new UtilException("Not a file path");
}
SFTPChannel channel = new SFTPChannel();
try {
ChannelSftp chSftp = channel.getChannel(sftpDetails, TIMEOUT);
String oldMtime=progress.getFactLastTime();
try {
//自动判断是否存在文件
SftpATTRS attrs = chSftp.stat(src);
progress.setFactLastTime(attrs.getMTime()+"");//最后修改时间
progress.setFileSize(attrs.getSize());//文件总大小
//如果传入了oldMtime有变更则不会进行下载
if(progress.getFileChangeFlag(oldMtime)) {
chSftp.quit();
return;
}
long start=progress.getTransferedSize();
response.setHeader(HttpHeaders.LAST_MODIFIED, attrs.getSize()+"");//基于文件修改时间的字符串
response.setHeader(HttpHeaders.CONTENT_TYPE, "application/octet-stream");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+ServletUtils.urlEncode(progress.getFileName()));
response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
response.setHeader(HttpHeaders.CONTENT_LENGTH, (attrs.getSize() - start)+"");
if(start==0) {
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes 0-" + (attrs.getSize() - 1) + "/" +attrs.getSize());
}else {
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + start + "-" + (attrs.getSize()-1) + "/" +attrs.getSize());
}
}catch(Exception e){
//返回
LOG.info("downloadComplexMonitor sftp error :file not exist");
progress.setFactLastTime("-1");//文件不存在,方便判断
chSftp.quit();
return;
}
ServletOutputStream outputStream = response.getOutputStream();
handleSftpInputStream(sftpDetails, channel, chSftp, src, outputStream, progress, progressService, 1, progress.getTransferedSize());
outputStream.flush();
chSftp.quit();
}catch(Exception e) {
LOG.error("downloadComplexMonitor sftp error :{}",e.getMessage(),e);
throw new UtilException("downloadComplexMonitor sftp error");
}finally {
channel.closeChannel();
}
}
private static void handleSftpInputStream(SFTPConfig sftpDetails,SFTPChannel channel ,ChannelSftp chSftp,String src,
ServletOutputStream outputStream,SftpProgress progress,ISftpProressService progressService,int num,long totalSize) throws IOException {
if(num>4) {
channel.closeInnerChannel();
return;
}
num++;//加一次
long nowTotalSize=0L;
InputStream in = null;
try {
in = chSftp.get(src,new SftpComplexProgressMonitor(progress,progressService) ,totalSize);
byte[] buffer = new byte[1024 * 1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
nowTotalSize = nowTotalSize + bytesRead;
outputStream.write(buffer,0,bytesRead);
outputStream.flush();
}
chSftp.quit();
} catch (Exception e) {
LOG.info("downloadComplexMonitor sftp again connect");
totalSize = totalSize + nowTotalSize;
channel.closeChannel();
try {
channel = new SFTPChannel();
chSftp = channel.getChannel(sftpDetails, TIMEOUT);
} catch (JSchException e1) {
LOG.info("downloadComplexMonitor sftp reconnect error>> num = {}",num);
}
handleSftpInputStream(sftpDetails,channel, chSftp, src, outputStream, progress, progressService, num, totalSize);
}finally {
if(in!=null) {
in.close();
}
}
}
/**
*

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

Loading…
Cancel
Save